feat: initialize Kurdistan SDK - independent fork of Polkadot SDK

This commit is contained in:
2025-12-13 15:44:15 +03:00
commit 286de54384
6841 changed files with 1848356 additions and 0 deletions
+136
View File
@@ -0,0 +1,136 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
use crate::MAX_XCM_DECODE_DEPTH;
use alloc::vec::Vec;
use codec::{Decode, DecodeLimit, DecodeWithMemTracking, Encode};
/// Wrapper around the encoded and decoded versions of a value.
/// Caches the decoded value once computed.
#[derive(Encode, Decode, DecodeWithMemTracking, scale_info::TypeInfo)]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(T))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct DoubleEncoded<T> {
encoded: Vec<u8>,
#[codec(skip)]
decoded: Option<T>,
}
impl<T> Clone for DoubleEncoded<T> {
fn clone(&self) -> Self {
Self { encoded: self.encoded.clone(), decoded: None }
}
}
impl<T> PartialEq for DoubleEncoded<T> {
fn eq(&self, other: &Self) -> bool {
self.encoded.eq(&other.encoded)
}
}
impl<T> Eq for DoubleEncoded<T> {}
impl<T> core::fmt::Debug for DoubleEncoded<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
array_bytes::bytes2hex("0x", &self.encoded).fmt(f)
}
}
impl<T> From<Vec<u8>> for DoubleEncoded<T> {
fn from(encoded: Vec<u8>) -> Self {
Self { encoded, decoded: None }
}
}
impl<T> DoubleEncoded<T> {
pub fn into<S>(self) -> DoubleEncoded<S> {
DoubleEncoded::from(self)
}
pub fn from<S>(e: DoubleEncoded<S>) -> Self {
Self { encoded: e.encoded, decoded: None }
}
/// Provides an API similar to `AsRef` that provides access to the inner value.
/// `AsRef` implementation would expect an `&Option<T>` return type.
pub fn as_ref(&self) -> Option<&T> {
self.decoded.as_ref()
}
/// Access the encoded data.
pub fn into_encoded(self) -> Vec<u8> {
self.encoded
}
}
impl<T: Decode> DoubleEncoded<T> {
/// Decode the inner encoded value and store it.
/// Returns a reference to the value in case of success and `Err(())` in case the decoding
/// fails.
pub fn ensure_decoded(&mut self) -> Result<&T, ()> {
if self.decoded.is_none() {
self.decoded =
T::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut &self.encoded[..]).ok();
}
self.decoded.as_ref().ok_or(())
}
/// Move the decoded value out or (if not present) decode `encoded`.
pub fn take_decoded(&mut self) -> Result<T, ()> {
self.decoded
.take()
.or_else(|| {
T::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut &self.encoded[..]).ok()
})
.ok_or(())
}
/// Provides an API similar to `TryInto` that allows fallible conversion to the inner value
/// type. `TryInto` implementation would collide with std blanket implementation based on
/// `TryFrom`.
pub fn try_into(mut self) -> Result<T, ()> {
self.ensure_decoded()?;
self.decoded.ok_or(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ensure_decoded_works() {
let val: u64 = 42;
let mut encoded: DoubleEncoded<_> = Encode::encode(&val).into();
assert_eq!(encoded.ensure_decoded(), Ok(&val));
}
#[test]
fn take_decoded_works() {
let val: u64 = 42;
let mut encoded: DoubleEncoded<_> = Encode::encode(&val).into();
assert_eq!(encoded.take_decoded(), Ok(val));
}
#[test]
fn try_into_works() {
let val: u64 = 42;
let encoded: DoubleEncoded<_> = Encode::encode(&val).into();
assert_eq!(encoded.try_into(), Ok(val));
}
}
+700
View File
@@ -0,0 +1,700 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! 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 codec::{
Decode, DecodeLimit, DecodeWithMemTracking, Encode, Error as CodecError, Input, MaxEncodedLen,
};
use derive_where::derive_where;
use frame_support::dispatch::GetDispatchInfo;
use scale_info::TypeInfo;
pub mod v3;
pub mod v4;
pub mod v5;
pub mod lts {
pub use super::v4::*;
}
pub mod latest {
pub use super::v5::*;
}
mod double_encoded;
pub use double_encoded::DoubleEncoded;
mod utils;
#[cfg(test)]
mod tests;
/// Maximum nesting level for XCM decoding.
pub const MAX_XCM_DECODE_DEPTH: u32 = 8;
/// The maximal number of instructions in an XCM before decoding fails.
///
/// This is a deliberate limit - not a technical one.
pub const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100;
/// 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<I: Input>(_: &mut I) -> Result<Self, CodecError> {
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<Self, ()>;
/// Consume `self` and return same value expressed the latest version of XCM.
fn into_latest(self) -> Result<Self, ()> {
self.into_version(latest::VERSION)
}
}
pub trait TryAs<T> {
fn try_as(&self) -> Result<&T, ()>;
}
// Macro that generated versioned wrapper types.
// NOTE: converting a v4 type into a versioned type will make it v5.
macro_rules! versioned_type {
($(#[$attr:meta])* pub enum $n:ident {
$(#[$index3:meta])+
V3($v3:ty),
$(#[$index4:meta])+
V4($v4:ty),
$(#[$index5:meta])+
V5($v5:ty),
}) => {
#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
$(#[$attr])*
pub enum $n {
$(#[$index3])*
V3($v3),
$(#[$index4])*
V4($v4),
$(#[$index5])*
V5($v5),
}
impl $n {
pub fn try_as<T>(&self) -> Result<&T, ()> where Self: TryAs<T> {
<Self as TryAs<T>>::try_as(&self)
}
}
impl TryAs<$v3> for $n {
fn try_as(&self) -> Result<&$v3, ()> {
match &self {
Self::V3(ref x) => Ok(x),
_ => Err(()),
}
}
}
impl TryAs<$v4> for $n {
fn try_as(&self) -> Result<&$v4, ()> {
match &self {
Self::V4(ref x) => Ok(x),
_ => Err(()),
}
}
}
impl TryAs<$v5> for $n {
fn try_as(&self) -> Result<&$v5, ()> {
match &self {
Self::V5(ref x) => Ok(x),
_ => Err(()),
}
}
}
impl IntoVersion for $n {
fn into_version(self, n: Version) -> Result<Self, ()> {
let version = self.identify_version();
if version == n {
Ok(self)
} else {
Ok(match n {
3 => Self::V3(self.try_into()?),
4 => Self::V4(self.try_into()?),
5 => Self::V5(self.try_into()?),
_ => return Err(()),
})
}
}
}
impl From<$v3> for $n {
fn from(x: $v3) -> Self {
$n::V3(x.into())
}
}
impl<T: Into<$v5>> From<T> for $n {
fn from(x: T) -> Self {
$n::V5(x.into())
}
}
impl TryFrom<$n> for $v3 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V3(x) => Ok(x),
V4(x) => x.try_into().map_err(|_| ()),
V5(x) => {
let v4: $v4 = x.try_into().map_err(|_| ())?;
v4.try_into().map_err(|_| ())
}
}
}
}
impl TryFrom<$n> for $v4 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V3(x) => x.try_into().map_err(|_| ()),
V4(x) => Ok(x),
V5(x) => x.try_into().map_err(|_| ()),
}
}
}
impl TryFrom<$n> for $v5 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V3(x) => {
let v4: $v4 = x.try_into().map_err(|_| ())?;
v4.try_into().map_err(|_| ())
},
V4(x) => x.try_into().map_err(|_| ()),
V5(x) => Ok(x),
}
}
}
impl MaxEncodedLen for $n {
fn max_encoded_len() -> usize {
<$v3>::max_encoded_len()
}
}
impl IdentifyVersion for $n {
fn identify_version(&self) -> Version {
use $n::*;
match self {
V3(_) => v3::VERSION,
V4(_) => v4::VERSION,
V5(_) => v5::VERSION,
}
}
}
};
}
versioned_type! {
/// A single version's `AssetId` value, together with its version code.
pub enum VersionedAssetId {
#[codec(index = 3)]
V3(v3::AssetId),
#[codec(index = 4)]
V4(v4::AssetId),
#[codec(index = 5)]
V5(v5::AssetId),
}
}
versioned_type! {
/// A single version's `Response` value, together with its version code.
pub enum VersionedResponse {
#[codec(index = 3)]
V3(v3::Response),
#[codec(index = 4)]
V4(v4::Response),
#[codec(index = 5)]
V5(v5::Response),
}
}
versioned_type! {
/// A single `NetworkId` value, together with its version code.
pub enum VersionedNetworkId {
#[codec(index = 3)]
V3(v3::NetworkId),
#[codec(index = 4)]
V4(v4::NetworkId),
#[codec(index = 5)]
V5(v5::NetworkId),
}
}
versioned_type! {
/// A single `Junction` value, together with its version code.
pub enum VersionedJunction {
#[codec(index = 3)]
V3(v3::Junction),
#[codec(index = 4)]
V4(v4::Junction),
#[codec(index = 5)]
V5(v5::Junction),
}
}
versioned_type! {
/// A single `Location` value, together with its version code.
#[derive(Ord, PartialOrd)]
pub enum VersionedLocation {
#[codec(index = 3)]
V3(v3::MultiLocation),
#[codec(index = 4)]
V4(v4::Location),
#[codec(index = 5)]
V5(v5::Location),
}
}
versioned_type! {
/// A single `InteriorLocation` value, together with its version code.
pub enum VersionedInteriorLocation {
#[codec(index = 3)]
V3(v3::InteriorMultiLocation),
#[codec(index = 4)]
V4(v4::InteriorLocation),
#[codec(index = 5)]
V5(v5::InteriorLocation),
}
}
versioned_type! {
/// A single `Asset` value, together with its version code.
pub enum VersionedAsset {
#[codec(index = 3)]
V3(v3::MultiAsset),
#[codec(index = 4)]
V4(v4::Asset),
#[codec(index = 5)]
V5(v5::Asset),
}
}
versioned_type! {
/// A single `MultiAssets` value, together with its version code.
pub enum VersionedAssets {
#[codec(index = 3)]
V3(v3::MultiAssets),
#[codec(index = 4)]
V4(v4::Assets),
#[codec(index = 5)]
V5(v5::Assets),
}
}
/// A single XCM message, together with its version code.
#[derive(Encode, Decode, DecodeWithMemTracking, TypeInfo)]
#[derive_where(Clone, Eq, PartialEq, Debug)]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
pub enum VersionedXcm<RuntimeCall> {
#[codec(index = 3)]
V3(v3::Xcm<RuntimeCall>),
#[codec(index = 4)]
V4(v4::Xcm<RuntimeCall>),
#[codec(index = 5)]
V5(v5::Xcm<RuntimeCall>),
}
impl<C: Decode + GetDispatchInfo> IntoVersion for VersionedXcm<C> {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
3 => Self::V3(self.try_into()?),
4 => Self::V4(self.try_into()?),
5 => Self::V5(self.try_into()?),
_ => return Err(()),
})
}
}
impl<C> IdentifyVersion for VersionedXcm<C> {
fn identify_version(&self) -> Version {
match self {
Self::V3(_) => v3::VERSION,
Self::V4(_) => v4::VERSION,
Self::V5(_) => v5::VERSION,
}
}
}
impl<C> VersionedXcm<C> {
/// Checks if the XCM is decodable. Consequently, it checks all decoding constraints,
/// such as `MAX_XCM_DECODE_DEPTH`, `MAX_ITEMS_IN_ASSETS` or `MAX_INSTRUCTIONS_TO_DECODE`.
///
/// Note that this uses the limit of the sender - not the receiver. It is a best effort.
pub fn check_is_decodable(&self) -> Result<(), ()> {
self.using_encoded(|mut enc| {
Self::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut enc).map(|_| ())
})
.map_err(|e| {
tracing::error!(target: "xcm::check_is_decodable", error=?e, xcm=?self, "Decode error!");
()
})
}
}
impl<RuntimeCall> From<v3::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v3::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V3(x)
}
}
impl<RuntimeCall> From<v4::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v4::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V4(x)
}
}
impl<RuntimeCall> From<v5::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v5::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V5(x)
}
}
impl<Call: Decode + GetDispatchInfo> TryFrom<VersionedXcm<Call>> for v3::Xcm<Call> {
type Error = ();
fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
use VersionedXcm::*;
match x {
V3(x) => Ok(x),
V4(x) => x.try_into(),
V5(x) => {
let v4: v4::Xcm<Call> = x.try_into()?;
v4.try_into()
},
}
}
}
impl<Call: Decode + GetDispatchInfo> TryFrom<VersionedXcm<Call>> for v4::Xcm<Call> {
type Error = ();
fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
use VersionedXcm::*;
match x {
V3(x) => x.try_into(),
V4(x) => Ok(x),
V5(x) => x.try_into(),
}
}
}
impl<Call: Decode + GetDispatchInfo> TryFrom<VersionedXcm<Call>> for v5::Xcm<Call> {
type Error = ();
fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
use VersionedXcm::*;
match x {
V3(x) => {
let v4: v4::Xcm<Call> = x.try_into()?;
v4.try_into()
},
V4(x) => x.try_into(),
V5(x) => Ok(x),
}
}
}
/// Convert an `Xcm` datum into a `VersionedXcm`, based on a destination `Location` which will
/// interpret it.
pub trait WrapVersion {
fn wrap_version<RuntimeCall: Decode + GetDispatchInfo>(
dest: &latest::Location,
xcm: impl Into<VersionedXcm<RuntimeCall>>,
) -> Result<VersionedXcm<RuntimeCall>, ()>;
}
/// Used to get the version out of a versioned type.
// TODO(XCMv5): This could be `GetVersion` and we change the current one to `GetVersionFor`.
pub trait IdentifyVersion {
fn identify_version(&self) -> Version;
}
/// Check and return the `Version` that should be used for the `Xcm` datum for the destination
/// `Location`, which will interpret it.
pub trait GetVersion {
fn get_version_for(dest: &latest::Location) -> Option<Version>;
}
/// `()` implementation does nothing with the XCM, just sending with whatever version it was
/// authored as.
impl WrapVersion for () {
fn wrap_version<RuntimeCall>(
_: &latest::Location,
xcm: impl Into<VersionedXcm<RuntimeCall>>,
) -> Result<VersionedXcm<RuntimeCall>, ()> {
Ok(xcm.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<Call: Decode + GetDispatchInfo>(
_: &latest::Location,
xcm: impl Into<VersionedXcm<Call>>,
) -> Result<VersionedXcm<Call>, ()> {
Ok(VersionedXcm::<Call>::V3(xcm.into().try_into()?))
}
}
impl GetVersion for AlwaysV3 {
fn get_version_for(_dest: &latest::Location) -> Option<Version> {
Some(v3::VERSION)
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to version 4 before
/// wrapping it.
pub struct AlwaysV4;
impl WrapVersion for AlwaysV4 {
fn wrap_version<Call: Decode + GetDispatchInfo>(
_: &latest::Location,
xcm: impl Into<VersionedXcm<Call>>,
) -> Result<VersionedXcm<Call>, ()> {
Ok(VersionedXcm::<Call>::V4(xcm.into().try_into()?))
}
}
impl GetVersion for AlwaysV4 {
fn get_version_for(_dest: &latest::Location) -> Option<Version> {
Some(v4::VERSION)
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to version 5 before
/// wrapping it.
pub struct AlwaysV5;
impl WrapVersion for AlwaysV5 {
fn wrap_version<Call: Decode + GetDispatchInfo>(
_: &latest::Location,
xcm: impl Into<VersionedXcm<Call>>,
) -> Result<VersionedXcm<Call>, ()> {
Ok(VersionedXcm::<Call>::V5(xcm.into().try_into()?))
}
}
impl GetVersion for AlwaysV5 {
fn get_version_for(_dest: &latest::Location) -> Option<Version> {
Some(v5::VERSION)
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to the latest version
/// before wrapping it.
pub type AlwaysLatest = AlwaysV5;
/// `WrapVersion` implementation which attempts to always convert the XCM to the most recent Long-
/// Term-Support version before wrapping it.
pub type AlwaysLts = AlwaysV4;
pub mod prelude {
pub use super::{
latest::prelude::*, AlwaysLatest, AlwaysLts, AlwaysV3, AlwaysV4, AlwaysV5, GetVersion,
IdentifyVersion, IntoVersion, Unsupported, Version as XcmVersion, VersionedAsset,
VersionedAssetId, VersionedAssets, VersionedInteriorLocation, VersionedLocation,
VersionedResponse, VersionedXcm, WrapVersion,
};
/// The minimal supported XCM version
pub const MIN_XCM_VERSION: XcmVersion = 3;
}
pub mod opaque {
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 v4 {
// Everything from v4
pub use crate::v4::*;
// Then override with the opaque types in v4
pub use crate::v4::opaque::{Instruction, Xcm};
}
pub mod v5 {
// Everything from v4
pub use crate::v5::*;
// Then override with the opaque types in v5
pub use crate::v5::opaque::{Instruction, Xcm};
}
pub mod latest {
pub use super::v5::*;
}
pub mod lts {
pub use super::v4::*;
}
/// The basic `VersionedXcm` type which just uses the `Vec<u8>` as an encoded call.
pub type VersionedXcm = super::VersionedXcm<()>;
}
#[test]
fn conversion_works() {
use latest::prelude::*;
let assets: Assets = (Here, 1u128).into();
let _: VersionedAssets = assets.into();
}
#[test]
fn size_limits() {
extern crate std;
let mut test_failed = false;
macro_rules! check_sizes {
($(($kind:ty, $expected:expr),)+) => {
$({
let s = core::mem::size_of::<$kind>();
// Since the types often affect the size of other types in which they're included
// it is more convenient to check multiple types at the same time and only fail
// the test at the end. For debugging it's also useful to print out all of the sizes,
// even if they're within the expected range.
if s > $expected {
test_failed = true;
std::eprintln!(
"assertion failed: size of '{}' is {} (which is more than the expected {})",
stringify!($kind),
s,
$expected
);
} else {
std::println!(
"type '{}' is of size {} which is within the expected {}",
stringify!($kind),
s,
$expected
);
}
})+
}
}
check_sizes! {
(crate::latest::Instruction<()>, 128),
(crate::latest::Asset, 80),
(crate::latest::Location, 24),
(crate::latest::AssetId, 40),
(crate::latest::Junctions, 16),
(crate::latest::Junction, 88),
(crate::latest::Response, 40),
(crate::latest::AssetInstance, 48),
(crate::latest::NetworkId, 48),
(crate::latest::BodyId, 32),
(crate::latest::Assets, 24),
(crate::latest::BodyPart, 12),
}
assert!(!test_failed);
}
#[test]
fn check_is_decodable_works() {
use crate::{
latest::{
prelude::{GeneralIndex, ReserveAssetDeposited, SetAppendix},
Assets, Xcm, MAX_ITEMS_IN_ASSETS,
},
MAX_INSTRUCTIONS_TO_DECODE,
};
// closure generates assets of `count`
let assets = |count| {
let mut assets = Assets::new();
for i in 0..count {
assets.push((GeneralIndex(i as u128), 100).into());
}
assets
};
// closer generates `Xcm` with nested instructions of `depth`
let with_instr = |depth| {
let mut xcm = Xcm::<()>(vec![]);
for _ in 0..depth - 1 {
xcm = Xcm::<()>(vec![SetAppendix(xcm)]);
}
xcm
};
// `MAX_INSTRUCTIONS_TO_DECODE` check
assert!(VersionedXcm::<()>::from(Xcm(vec![
ReserveAssetDeposited(assets(1));
(MAX_INSTRUCTIONS_TO_DECODE - 1) as usize
]))
.check_is_decodable()
.is_ok());
assert!(VersionedXcm::<()>::from(Xcm(vec![
ReserveAssetDeposited(assets(1));
MAX_INSTRUCTIONS_TO_DECODE as usize
]))
.check_is_decodable()
.is_ok());
assert!(VersionedXcm::<()>::from(Xcm(vec![
ReserveAssetDeposited(assets(1));
(MAX_INSTRUCTIONS_TO_DECODE + 1) as usize
]))
.check_is_decodable()
.is_err());
// `MAX_XCM_DECODE_DEPTH` check
assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH - 1))
.check_is_decodable()
.is_ok());
assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH))
.check_is_decodable()
.is_ok());
assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH + 1))
.check_is_decodable()
.is_err());
// `MAX_ITEMS_IN_ASSETS` check
assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
MAX_ITEMS_IN_ASSETS
))]))
.check_is_decodable()
.is_ok());
assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
MAX_ITEMS_IN_ASSETS - 1
))]))
.check_is_decodable()
.is_ok());
assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
MAX_ITEMS_IN_ASSETS + 1
))]))
.check_is_decodable()
.is_err());
}
+267
View File
@@ -0,0 +1,267 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
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_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_response_v4() {
let response = VersionedResponse::V4(v4::Response::Null);
let encoded = response.encode();
assert_eq!(encoded, hex_literal::hex!("0400"), "encode format changed");
assert_eq!(encoded[0], 4, "bad version number");
let decoded = VersionedResponse::decode(&mut &encoded[..]).unwrap();
assert_eq!(response, decoded);
}
#[test]
fn encode_decode_versioned_response_v5() {
let response = VersionedResponse::V5(v5::Response::Null);
let encoded = response.encode();
assert_eq!(encoded, hex_literal::hex!("0500"), "encode format changed");
assert_eq!(encoded[0], 5, "bad version number");
let decoded = VersionedResponse::decode(&mut &encoded[..]).unwrap();
assert_eq!(response, decoded);
}
#[test]
fn encode_decode_versioned_location_v3() {
let location = VersionedLocation::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 = VersionedLocation::decode(&mut &encoded[..]).unwrap();
assert_eq!(location, decoded);
}
#[test]
fn encode_decode_versioned_location_v4() {
let location = VersionedLocation::V4(v4::Location::new(0, v4::Junctions::Here));
let encoded = location.encode();
assert_eq!(encoded, hex_literal::hex!("040000"), "encode format changed");
assert_eq!(encoded[0], 4, "bad version number");
let decoded = VersionedLocation::decode(&mut &encoded[..]).unwrap();
assert_eq!(location, decoded);
}
#[test]
fn encode_decode_versioned_location_v5() {
let location = VersionedLocation::V5(v5::Location::new(0, v5::Junctions::Here));
let encoded = location.encode();
assert_eq!(encoded, hex_literal::hex!("050000"), "encode format changed");
assert_eq!(encoded[0], 5, "bad version number");
let decoded = VersionedLocation::decode(&mut &encoded[..]).unwrap();
assert_eq!(location, decoded);
}
#[test]
fn encode_decode_versioned_interior_location_v3() {
let location = VersionedInteriorLocation::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 = VersionedInteriorLocation::decode(&mut &encoded[..]).unwrap();
assert_eq!(location, decoded);
}
#[test]
fn encode_decode_versioned_interior_location_v4() {
let location = VersionedInteriorLocation::V4(v4::InteriorLocation::Here);
let encoded = location.encode();
assert_eq!(encoded, hex_literal::hex!("0400"), "encode format changed");
assert_eq!(encoded[0], 4, "bad version number");
let decoded = VersionedInteriorLocation::decode(&mut &encoded[..]).unwrap();
assert_eq!(location, decoded);
}
#[test]
fn encode_decode_versioned_interior_location_v5() {
let location = VersionedInteriorLocation::V5(v5::InteriorLocation::Here);
let encoded = location.encode();
assert_eq!(encoded, hex_literal::hex!("0500"), "encode format changed");
assert_eq!(encoded[0], 5, "bad version number");
let decoded = VersionedInteriorLocation::decode(&mut &encoded[..]).unwrap();
assert_eq!(location, decoded);
}
#[test]
fn encode_decode_versioned_asset_v3() {
let asset = VersionedAsset::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 = VersionedAsset::decode(&mut &encoded[..]).unwrap();
assert_eq!(asset, decoded);
}
#[test]
fn encode_decode_versioned_asset_v4() {
let asset = VersionedAsset::V4(v4::Asset::from((v4::Location::default(), 1)));
let encoded = asset.encode();
assert_eq!(encoded, hex_literal::hex!("0400000004"), "encode format changed");
assert_eq!(encoded[0], 4, "bad version number");
let decoded = VersionedAsset::decode(&mut &encoded[..]).unwrap();
assert_eq!(asset, decoded);
}
#[test]
fn encode_decode_versioned_asset_v5() {
let asset = VersionedAsset::V5(v5::Asset::from((v5::Location::default(), 1)));
let encoded = asset.encode();
assert_eq!(encoded, hex_literal::hex!("0500000004"), "encode format changed");
assert_eq!(encoded[0], 5, "bad version number");
let decoded = VersionedAsset::decode(&mut &encoded[..]).unwrap();
assert_eq!(asset, decoded);
}
#[test]
fn encode_decode_versioned_assets_v3() {
let assets = VersionedAssets::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 = VersionedAssets::decode(&mut &encoded[..]).unwrap();
assert_eq!(assets, decoded);
}
#[test]
fn encode_decode_versioned_assets_v4() {
let assets = VersionedAssets::V4(v4::Assets::from(vec![
(v4::Asset::from((v4::Location::default(), 1))),
]));
let encoded = assets.encode();
assert_eq!(encoded, hex_literal::hex!("040400000004"), "encode format changed");
assert_eq!(encoded[0], 4, "bad version number");
let decoded = VersionedAssets::decode(&mut &encoded[..]).unwrap();
assert_eq!(assets, decoded);
}
#[test]
fn encode_decode_versioned_assets_v5() {
let assets = VersionedAssets::V5(v5::Assets::from(vec![
(v5::Asset::from((v5::Location::default(), 1))),
]));
let encoded = assets.encode();
assert_eq!(encoded, hex_literal::hex!("050400000004"), "encode format changed");
assert_eq!(encoded[0], 5, "bad version number");
let decoded = VersionedAssets::decode(&mut &encoded[..]).unwrap();
assert_eq!(assets, 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);
}
#[test]
fn encode_decode_versioned_xcm_v4() {
let xcm = VersionedXcm::V4(v4::Xcm::<()>::new());
let encoded = xcm.encode();
assert_eq!(encoded, hex_literal::hex!("0400"), "encode format changed");
assert_eq!(encoded[0], 4, "bad version number");
let decoded = VersionedXcm::decode(&mut &encoded[..]).unwrap();
assert_eq!(xcm, decoded);
}
#[test]
fn encode_decode_versioned_xcm_v5() {
let xcm = VersionedXcm::V5(v5::Xcm::<()>::new());
let encoded = xcm.encode();
assert_eq!(encoded, hex_literal::hex!("0500"), "encode format changed");
assert_eq!(encoded[0], 5, "bad version number");
let decoded = VersionedXcm::decode(&mut &encoded[..]).unwrap();
assert_eq!(xcm, decoded);
}
// With the renaming of the crate to `staging-xcm` the naming in the metadata changed as well and
// this broke downstream users. This test ensures that the name in the metadata isn't changed.
#[test]
fn ensure_type_info_is_correct() {
let type_info = VersionedXcm::<()>::type_info();
assert_eq!(type_info.path.segments, vec!["xcm", "VersionedXcm"]);
let type_info = VersionedAssetId::type_info();
assert_eq!(type_info.path.segments, vec!["xcm", "VersionedAssetId"]);
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! XCM utils for internal use.
use crate::MAX_INSTRUCTIONS_TO_DECODE;
use alloc::vec::Vec;
use codec::{decode_vec_with_len, Compact, Decode};
environmental::environmental!(instructions_count: u8);
/// Decode a `vec` of XCM instructions.
///
/// This function keeps track of nested XCM instructions and enforces a total limit of
/// `MAX_INSTRUCTIONS_TO_DECODE`.
pub fn decode_xcm_instructions<I: codec::Input, T: Decode>(
input: &mut I,
) -> Result<Vec<T>, codec::Error> {
instructions_count::using_once(&mut 0, || {
let vec_len: u32 = <Compact<u32>>::decode(input)?.into();
instructions_count::with(|count| {
*count = count.saturating_add(vec_len as u8);
if *count > MAX_INSTRUCTIONS_TO_DECODE {
return Err(codec::Error::from("Max instructions exceeded"));
}
Ok(())
})
.unwrap_or(Err(codec::Error::from("Error calling `instructions_count::with()`")))?;
let decoded_instructions = decode_vec_with_len(input, vec_len as usize)?;
Ok(decoded_instructions)
})
}
+404
View File
@@ -0,0 +1,404 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! Support data structures for `MultiLocation`, primarily the `Junction` datatype.
use super::{Junctions, MultiLocation};
use crate::{
v4::{Junction as NewJunction, NetworkId as NewNetworkId},
VersionedLocation,
};
use bounded_collections::{BoundedSlice, BoundedVec, ConstU32};
use codec::{self, Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
/// A global identifier of a data structure existing within consensus.
///
/// Maintenance note: Networks with global consensus and which are practically bridgeable within the
/// Pezkuwi ecosystem are given preference over explicit naming in this enumeration.
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
pub enum NetworkId {
/// Network specified by the first 32 bytes of its genesis block.
ByGenesis([u8; 32]),
/// Network defined by the first 32-bytes of the hash and number of some block it contains.
ByFork { block_number: u64, block_hash: [u8; 32] },
/// The Pezkuwi mainnet Relay-chain.
Pezkuwi,
/// The Kusama canary-net Relay-chain.
Kusama,
/// The Zagros testnet Relay-chain.
Zagros,
/// The Pezkuwichain testnet Relay-chain.
Pezkuwichain,
/// The Wococo testnet Relay-chain.
Wococo,
/// An Ethereum network specified by its chain ID.
Ethereum {
/// The EIP-155 chain ID.
#[codec(compact)]
chain_id: u64,
},
/// The Bitcoin network, including hard-forks supported by Bitcoin Core development team.
BitcoinCore,
/// The Bitcoin network, including hard-forks supported by Bitcoin Cash developers.
BitcoinCash,
/// The Pezkuwi Bulletin chain.
PezkuwiBulletin,
}
impl From<NewNetworkId> for Option<NetworkId> {
fn from(new: NewNetworkId) -> Self {
Some(NetworkId::from(new))
}
}
impl From<NewNetworkId> for NetworkId {
fn from(new: NewNetworkId) -> Self {
use NewNetworkId::*;
match new {
ByGenesis(hash) => Self::ByGenesis(hash),
ByFork { block_number, block_hash } => Self::ByFork { block_number, block_hash },
Pezkuwi => Self::Pezkuwi,
Kusama => Self::Kusama,
Zagros => Self::Zagros,
Pezkuwichain => Self::Pezkuwichain,
Wococo => Self::Wococo,
Ethereum { chain_id } => Self::Ethereum { chain_id },
BitcoinCore => Self::BitcoinCore,
BitcoinCash => Self::BitcoinCash,
PezkuwiBulletin => Self::PezkuwiBulletin,
}
}
}
/// An identifier of a pluralistic body.
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
pub enum BodyId {
/// The only body in its context.
Unit,
/// A named body.
Moniker([u8; 4]),
/// An indexed body.
Index(#[codec(compact)] u32),
/// The unambiguous executive body (for Pezkuwi, this would be the Pezkuwi council).
Executive,
/// The unambiguous technical body (for Pezkuwi, this would be the Technical Committee).
Technical,
/// The unambiguous legislative body (for Pezkuwi, this could be considered the opinion of a
/// majority of lock-voters).
Legislative,
/// The unambiguous judicial body (this doesn't exist on Pezkuwi, but if it were to get a
/// "grand oracle", it may be considered as that).
Judicial,
/// The unambiguous defense body (for Pezkuwi, an opinion on the topic given via a public
/// referendum on the `staking_admin` track).
Defense,
/// The unambiguous administration body (for Pezkuwi, an opinion on the topic given via a
/// public referendum on the `general_admin` track).
Administration,
/// The unambiguous treasury body (for Pezkuwi, an opinion on the topic given via a public
/// referendum on the `treasurer` track).
Treasury,
}
/// A part of a pluralistic body.
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
pub enum BodyPart {
/// The body's declaration, under whatever means it decides.
Voice,
/// A given number of members of the body.
Members {
#[codec(compact)]
count: u32,
},
/// A given number of members of the body, out of some larger caucus.
Fraction {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
/// No less than the given proportion of members of the body.
AtLeastProportion {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
/// More than the given proportion of members of the body.
MoreThanProportion {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
}
impl BodyPart {
/// Returns `true` if the part represents a strict majority (> 50%) of the body in question.
pub fn is_majority(&self) -> bool {
match self {
BodyPart::Fraction { nom, denom } if *nom * 2 > *denom => true,
BodyPart::AtLeastProportion { nom, denom } if *nom * 2 > *denom => true,
BodyPart::MoreThanProportion { nom, denom } if *nom * 2 >= *denom => true,
_ => false,
}
}
}
/// A single item in a path to describe the relative location of a consensus system.
///
/// Each item assumes a pre-existing location as its context and is defined in terms of it.
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
pub enum Junction {
/// An indexed teyrchain belonging to and operated by the context.
///
/// Generally used when the context is a Pezkuwi Relay-chain.
Teyrchain(#[codec(compact)] u32),
/// A 32-byte identifier for an account of a specific network that is respected as a sovereign
/// endpoint within the context.
///
/// Generally used when the context is a Substrate-based chain.
AccountId32 { network: Option<NetworkId>, id: [u8; 32] },
/// An 8-byte index for an account of a specific network that is respected as a sovereign
/// endpoint within the context.
///
/// May be used when the context is a Frame-based chain and includes e.g. an indices pallet.
AccountIndex64 {
network: Option<NetworkId>,
#[codec(compact)]
index: u64,
},
/// A 20-byte identifier for an account of a specific network that is respected as a sovereign
/// endpoint within the context.
///
/// May be used when the context is an Ethereum or Bitcoin chain or smart-contract.
AccountKey20 { network: Option<NetworkId>, key: [u8; 20] },
/// An instanced, indexed pallet that forms a constituent part of the context.
///
/// Generally used when the context is a Frame-based chain.
// TODO XCMv4 inner should be `Compact<u32>`.
PalletInstance(u8),
/// A non-descript index within the context location.
///
/// Usage will vary widely owing to its generality.
///
/// NOTE: Try to avoid using this and instead use a more specific item.
GeneralIndex(#[codec(compact)] u128),
/// A nondescript array datum, 32 bytes, acting as a key within the context
/// location.
///
/// Usage will vary widely owing to its generality.
///
/// NOTE: Try to avoid using this and instead use a more specific item.
// Note this is implemented as an array with a length rather than using `BoundedVec` owing to
// the bound for `Copy`.
GeneralKey { length: u8, data: [u8; 32] },
/// The unambiguous child.
///
/// Not currently used except as a fallback when deriving context.
OnlyChild,
/// A pluralistic body existing within consensus.
///
/// Typical to be used to represent a governance origin of a chain, but could in principle be
/// used to represent things such as multisigs also.
Plurality { id: BodyId, part: BodyPart },
/// A global network capable of externalizing its own consensus. This is not generally
/// meaningful outside of the universal level.
GlobalConsensus(NetworkId),
}
impl From<NetworkId> for Junction {
fn from(n: NetworkId) -> Self {
Self::GlobalConsensus(n)
}
}
impl From<[u8; 32]> for Junction {
fn from(id: [u8; 32]) -> Self {
Self::AccountId32 { network: None, id }
}
}
impl From<BoundedVec<u8, ConstU32<32>>> for Junction {
fn from(key: BoundedVec<u8, ConstU32<32>>) -> Self {
key.as_bounded_slice().into()
}
}
impl<'a> From<BoundedSlice<'a, u8, ConstU32<32>>> for Junction {
fn from(key: BoundedSlice<'a, u8, ConstU32<32>>) -> Self {
let mut data = [0u8; 32];
data[..key.len()].copy_from_slice(&key[..]);
Self::GeneralKey { length: key.len() as u8, data }
}
}
impl<'a> TryFrom<&'a Junction> for BoundedSlice<'a, u8, ConstU32<32>> {
type Error = ();
fn try_from(key: &'a Junction) -> Result<Self, ()> {
match key {
Junction::GeneralKey { length, data } =>
BoundedSlice::try_from(&data[..data.len().min(*length as usize)]).map_err(|_| ()),
_ => Err(()),
}
}
}
impl From<[u8; 20]> for Junction {
fn from(key: [u8; 20]) -> Self {
Self::AccountKey20 { network: None, key }
}
}
impl From<u64> for Junction {
fn from(index: u64) -> Self {
Self::AccountIndex64 { network: None, index }
}
}
impl From<u128> for Junction {
fn from(id: u128) -> Self {
Self::GeneralIndex(id)
}
}
impl TryFrom<NewJunction> for Junction {
type Error = ();
fn try_from(value: NewJunction) -> Result<Self, Self::Error> {
use NewJunction::*;
Ok(match value {
Teyrchain(id) => Self::Teyrchain(id),
AccountId32 { network: maybe_network, id } =>
Self::AccountId32 { network: maybe_network.map(|network| network.into()), id },
AccountIndex64 { network: maybe_network, index } =>
Self::AccountIndex64 { network: maybe_network.map(|network| network.into()), index },
AccountKey20 { network: maybe_network, key } =>
Self::AccountKey20 { network: maybe_network.map(|network| network.into()), key },
PalletInstance(index) => Self::PalletInstance(index),
GeneralIndex(id) => Self::GeneralIndex(id),
GeneralKey { length, data } => Self::GeneralKey { length, data },
OnlyChild => Self::OnlyChild,
Plurality { id, part } => Self::Plurality { id, part },
GlobalConsensus(network) => Self::GlobalConsensus(network.into()),
})
}
}
impl Junction {
/// Convert `self` into a `MultiLocation` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub const fn into_location(self) -> MultiLocation {
MultiLocation { parents: 0, interior: Junctions::X1(self) }
}
/// Convert `self` into a `MultiLocation` containing `n` parents.
///
/// Similar to `Self::into_location`, with the added ability to specify the number of parent
/// junctions.
pub const fn into_exterior(self, n: u8) -> MultiLocation {
MultiLocation { parents: n, interior: Junctions::X1(self) }
}
/// Convert `self` into a `VersionedLocation` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub const fn into_versioned(self) -> VersionedLocation {
self.into_location().into_versioned()
}
/// Remove the `NetworkId` value.
pub fn remove_network_id(&mut self) {
use Junction::*;
match self {
AccountId32 { ref mut network, .. } |
AccountIndex64 { ref mut network, .. } |
AccountKey20 { ref mut network, .. } => *network = None,
_ => {},
}
}
}
+738
View File
@@ -0,0 +1,738 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! XCM `Junctions`/`InteriorMultiLocation` datatype.
use super::{Junction, MultiLocation, NetworkId};
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use core::{mem, result};
use scale_info::TypeInfo;
/// Maximum number of `Junction`s that a `Junctions` can contain.
pub(crate) const MAX_JUNCTIONS: usize = 8;
/// Non-parent junctions that can be constructed, up to the length of 8. This specific `Junctions`
/// implementation uses a Rust `enum` in order to make pattern matching easier.
///
/// Parent junctions cannot be constructed with this type. Refer to `MultiLocation` for
/// instructions on constructing parent junctions.
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
serde::Serialize,
serde::Deserialize,
)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
pub enum Junctions {
/// The interpreting consensus system.
Here,
/// A relative path comprising 1 junction.
X1(Junction),
/// A relative path comprising 2 junctions.
X2(Junction, Junction),
/// A relative path comprising 3 junctions.
X3(Junction, Junction, Junction),
/// A relative path comprising 4 junctions.
X4(Junction, Junction, Junction, Junction),
/// A relative path comprising 5 junctions.
X5(Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 6 junctions.
X6(Junction, Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 7 junctions.
X7(Junction, Junction, Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 8 junctions.
X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction),
}
macro_rules! impl_junction {
($count:expr, $variant:ident, ($($index:literal),+)) => {
/// Additional helper for building junctions
/// Useful for converting to future XCM versions
impl From<[Junction; $count]> for Junctions {
fn from(junctions: [Junction; $count]) -> Self {
Self::$variant($(junctions[$index]),*)
}
}
};
}
impl_junction!(1, X1, (0));
impl_junction!(2, X2, (0, 1));
impl_junction!(3, X3, (0, 1, 2));
impl_junction!(4, X4, (0, 1, 2, 3));
impl_junction!(5, X5, (0, 1, 2, 3, 4));
impl_junction!(6, X6, (0, 1, 2, 3, 4, 5));
impl_junction!(7, X7, (0, 1, 2, 3, 4, 5, 6));
impl_junction!(8, X8, (0, 1, 2, 3, 4, 5, 6, 7));
pub struct JunctionsIterator(Junctions);
impl Iterator for JunctionsIterator {
type Item = Junction;
fn next(&mut self) -> Option<Junction> {
self.0.take_first()
}
}
impl DoubleEndedIterator for JunctionsIterator {
fn next_back(&mut self) -> Option<Junction> {
self.0.take_last()
}
}
pub struct JunctionsRefIterator<'a> {
junctions: &'a Junctions,
next: usize,
back: usize,
}
impl<'a> Iterator for JunctionsRefIterator<'a> {
type Item = &'a Junction;
fn next(&mut self) -> Option<&'a Junction> {
if self.next.saturating_add(self.back) >= self.junctions.len() {
return None;
}
let result = self.junctions.at(self.next);
self.next += 1;
result
}
}
impl<'a> DoubleEndedIterator for JunctionsRefIterator<'a> {
fn next_back(&mut self) -> Option<&'a Junction> {
let next_back = self.back.saturating_add(1);
// checked_sub here, because if the result is less than 0, we end iteration
let index = self.junctions.len().checked_sub(next_back)?;
if self.next > index {
return None;
}
self.back = next_back;
self.junctions.at(index)
}
}
impl<'a> IntoIterator for &'a Junctions {
type Item = &'a Junction;
type IntoIter = JunctionsRefIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
JunctionsRefIterator { junctions: self, next: 0, back: 0 }
}
}
impl IntoIterator for Junctions {
type Item = Junction;
type IntoIter = JunctionsIterator;
fn into_iter(self) -> Self::IntoIter {
JunctionsIterator(self)
}
}
impl Junctions {
/// Convert `self` into a `MultiLocation` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub const fn into_location(self) -> MultiLocation {
MultiLocation { parents: 0, interior: self }
}
/// Convert `self` into a `MultiLocation` containing `n` parents.
///
/// Similar to `Self::into_location`, with the added ability to specify the number of parent
/// junctions.
pub const fn into_exterior(self, n: u8) -> MultiLocation {
MultiLocation { parents: n, interior: self }
}
/// Remove the `NetworkId` value in any `Junction`s.
pub fn remove_network_id(&mut self) {
self.for_each_mut(Junction::remove_network_id);
}
/// Treating `self` as the universal context, return the location of the local consensus system
/// from the point of view of the given `target`.
pub fn invert_target(mut self, target: &MultiLocation) -> Result<MultiLocation, ()> {
let mut junctions = Self::Here;
for _ in 0..target.parent_count() {
junctions = junctions
.pushed_front_with(self.take_last().unwrap_or(Junction::OnlyChild))
.map_err(|_| ())?;
}
let parents = target.interior().len() as u8;
Ok(MultiLocation::new(parents, junctions))
}
/// Execute a function `f` on every junction. We use this since we cannot implement a mutable
/// `Iterator` without unsafe code.
pub fn for_each_mut(&mut self, mut x: impl FnMut(&mut Junction)) {
match self {
Junctions::Here => {},
Junctions::X1(a) => {
x(a);
},
Junctions::X2(a, b) => {
x(a);
x(b);
},
Junctions::X3(a, b, c) => {
x(a);
x(b);
x(c);
},
Junctions::X4(a, b, c, d) => {
x(a);
x(b);
x(c);
x(d);
},
Junctions::X5(a, b, c, d, e) => {
x(a);
x(b);
x(c);
x(d);
x(e);
},
Junctions::X6(a, b, c, d, e, f) => {
x(a);
x(b);
x(c);
x(d);
x(e);
x(f);
},
Junctions::X7(a, b, c, d, e, f, g) => {
x(a);
x(b);
x(c);
x(d);
x(e);
x(f);
x(g);
},
Junctions::X8(a, b, c, d, e, f, g, h) => {
x(a);
x(b);
x(c);
x(d);
x(e);
x(f);
x(g);
x(h);
},
}
}
/// Extract the network ID treating this value as a universal location.
///
/// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate
/// that this value is not a universal location.
pub fn global_consensus(&self) -> Result<NetworkId, ()> {
if let Some(Junction::GlobalConsensus(network)) = self.first() {
Ok(*network)
} else {
Err(())
}
}
/// Extract the network ID and the interior consensus location, treating this value as a
/// universal location.
///
/// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate
/// that this value is not a universal location.
pub fn split_global(self) -> Result<(NetworkId, Junctions), ()> {
match self.split_first() {
(location, Some(Junction::GlobalConsensus(network))) => Ok((network, location)),
_ => return Err(()),
}
}
/// Treat `self` as a universal location and the context of `relative`, returning the universal
/// location of relative.
///
/// This will return an error if `relative` has as many (or more) parents than there are
/// junctions in `self`, implying that relative refers into a different global consensus.
pub fn within_global(mut self, relative: MultiLocation) -> Result<Self, ()> {
if self.len() <= relative.parents as usize {
return Err(());
}
for _ in 0..relative.parents {
self.take_last();
}
for j in relative.interior {
self.push(j).map_err(|_| ())?;
}
Ok(self)
}
/// Consumes `self` and returns how `viewer` would address it locally.
pub fn relative_to(mut self, viewer: &Junctions) -> MultiLocation {
let mut i = 0;
while match (self.first(), viewer.at(i)) {
(Some(x), Some(y)) => x == y,
_ => false,
} {
self = self.split_first().0;
// NOTE: Cannot overflow as loop can only iterate at most `MAX_JUNCTIONS` times.
i += 1;
}
// AUDIT NOTES:
// - above loop ensures that `i <= viewer.len()`.
// - `viewer.len()` is at most `MAX_JUNCTIONS`, so won't overflow a `u8`.
MultiLocation { parents: (viewer.len() - i) as u8, interior: self }
}
/// Returns first junction, or `None` if the location is empty.
pub fn first(&self) -> Option<&Junction> {
match &self {
Junctions::Here => None,
Junctions::X1(ref a) => Some(a),
Junctions::X2(ref a, ..) => Some(a),
Junctions::X3(ref a, ..) => Some(a),
Junctions::X4(ref a, ..) => Some(a),
Junctions::X5(ref a, ..) => Some(a),
Junctions::X6(ref a, ..) => Some(a),
Junctions::X7(ref a, ..) => Some(a),
Junctions::X8(ref a, ..) => Some(a),
}
}
/// Returns last junction, or `None` if the location is empty.
pub fn last(&self) -> Option<&Junction> {
match &self {
Junctions::Here => None,
Junctions::X1(ref a) => Some(a),
Junctions::X2(.., ref a) => Some(a),
Junctions::X3(.., ref a) => Some(a),
Junctions::X4(.., ref a) => Some(a),
Junctions::X5(.., ref a) => Some(a),
Junctions::X6(.., ref a) => Some(a),
Junctions::X7(.., ref a) => Some(a),
Junctions::X8(.., ref a) => Some(a),
}
}
/// Splits off the first junction, returning the remaining suffix (first item in tuple) and the
/// first element (second item in tuple) or `None` if it was empty.
pub fn split_first(self) -> (Junctions, Option<Junction>) {
match self {
Junctions::Here => (Junctions::Here, None),
Junctions::X1(a) => (Junctions::Here, Some(a)),
Junctions::X2(a, b) => (Junctions::X1(b), Some(a)),
Junctions::X3(a, b, c) => (Junctions::X2(b, c), Some(a)),
Junctions::X4(a, b, c, d) => (Junctions::X3(b, c, d), Some(a)),
Junctions::X5(a, b, c, d, e) => (Junctions::X4(b, c, d, e), Some(a)),
Junctions::X6(a, b, c, d, e, f) => (Junctions::X5(b, c, d, e, f), Some(a)),
Junctions::X7(a, b, c, d, e, f, g) => (Junctions::X6(b, c, d, e, f, g), Some(a)),
Junctions::X8(a, b, c, d, e, f, g, h) => (Junctions::X7(b, c, d, e, f, g, h), Some(a)),
}
}
/// Splits off the last junction, returning the remaining prefix (first item in tuple) and the
/// last element (second item in tuple) or `None` if it was empty.
pub fn split_last(self) -> (Junctions, Option<Junction>) {
match self {
Junctions::Here => (Junctions::Here, None),
Junctions::X1(a) => (Junctions::Here, Some(a)),
Junctions::X2(a, b) => (Junctions::X1(a), Some(b)),
Junctions::X3(a, b, c) => (Junctions::X2(a, b), Some(c)),
Junctions::X4(a, b, c, d) => (Junctions::X3(a, b, c), Some(d)),
Junctions::X5(a, b, c, d, e) => (Junctions::X4(a, b, c, d), Some(e)),
Junctions::X6(a, b, c, d, e, f) => (Junctions::X5(a, b, c, d, e), Some(f)),
Junctions::X7(a, b, c, d, e, f, g) => (Junctions::X6(a, b, c, d, e, f), Some(g)),
Junctions::X8(a, b, c, d, e, f, g, h) => (Junctions::X7(a, b, c, d, e, f, g), Some(h)),
}
}
/// Removes the first element from `self`, returning it (or `None` if it was empty).
pub fn take_first(&mut self) -> Option<Junction> {
let mut d = Junctions::Here;
mem::swap(&mut *self, &mut d);
let (tail, head) = d.split_first();
*self = tail;
head
}
/// Removes the last element from `self`, returning it (or `None` if it was empty).
pub fn take_last(&mut self) -> Option<Junction> {
let mut d = Junctions::Here;
mem::swap(&mut *self, &mut d);
let (head, tail) = d.split_last();
*self = head;
tail
}
/// Mutates `self` to be appended with `new` or returns an `Err` with `new` if would overflow.
pub fn push(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
let new = new.into();
let mut dummy = Junctions::Here;
mem::swap(self, &mut dummy);
match dummy.pushed_with(new) {
Ok(s) => {
*self = s;
Ok(())
},
Err((s, j)) => {
*self = s;
Err(j)
},
}
}
/// Mutates `self` to be prepended with `new` or returns an `Err` with `new` if would overflow.
pub fn push_front(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
let new = new.into();
let mut dummy = Junctions::Here;
mem::swap(self, &mut dummy);
match dummy.pushed_front_with(new) {
Ok(s) => {
*self = s;
Ok(())
},
Err((s, j)) => {
*self = s;
Err(j)
},
}
}
/// Consumes `self` and returns a `Junctions` suffixed with `new`, or an `Err` with the
/// original value of `self` and `new` in case of overflow.
pub fn pushed_with(self, new: impl Into<Junction>) -> result::Result<Self, (Self, Junction)> {
let new = new.into();
Ok(match self {
Junctions::Here => Junctions::X1(new),
Junctions::X1(a) => Junctions::X2(a, new),
Junctions::X2(a, b) => Junctions::X3(a, b, new),
Junctions::X3(a, b, c) => Junctions::X4(a, b, c, new),
Junctions::X4(a, b, c, d) => Junctions::X5(a, b, c, d, new),
Junctions::X5(a, b, c, d, e) => Junctions::X6(a, b, c, d, e, new),
Junctions::X6(a, b, c, d, e, f) => Junctions::X7(a, b, c, d, e, f, new),
Junctions::X7(a, b, c, d, e, f, g) => Junctions::X8(a, b, c, d, e, f, g, new),
s => Err((s, new))?,
})
}
/// Consumes `self` and returns a `Junctions` prefixed with `new`, or an `Err` with the
/// original value of `self` and `new` in case of overflow.
pub fn pushed_front_with(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
let new = new.into();
Ok(match self {
Junctions::Here => Junctions::X1(new),
Junctions::X1(a) => Junctions::X2(new, a),
Junctions::X2(a, b) => Junctions::X3(new, a, b),
Junctions::X3(a, b, c) => Junctions::X4(new, a, b, c),
Junctions::X4(a, b, c, d) => Junctions::X5(new, a, b, c, d),
Junctions::X5(a, b, c, d, e) => Junctions::X6(new, a, b, c, d, e),
Junctions::X6(a, b, c, d, e, f) => Junctions::X7(new, a, b, c, d, e, f),
Junctions::X7(a, b, c, d, e, f, g) => Junctions::X8(new, a, b, c, d, e, f, g),
s => Err((s, new))?,
})
}
/// Mutate `self` so that it is suffixed with `suffix`.
///
/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation};
/// let mut m = X1(Teyrchain(21));
/// assert_eq!(m.append_with(X1(PalletInstance(3))), Ok(()));
/// assert_eq!(m, X2(Teyrchain(21), PalletInstance(3)));
/// ```
pub fn append_with(&mut self, suffix: impl Into<Junctions>) -> Result<(), Junctions> {
let suffix = suffix.into();
if self.len().saturating_add(suffix.len()) > MAX_JUNCTIONS {
return Err(suffix);
}
for j in suffix.into_iter() {
self.push(j).expect("Already checked the sum of the len()s; qed")
}
Ok(())
}
/// Returns the number of junctions in `self`.
pub const fn len(&self) -> usize {
match &self {
Junctions::Here => 0,
Junctions::X1(..) => 1,
Junctions::X2(..) => 2,
Junctions::X3(..) => 3,
Junctions::X4(..) => 4,
Junctions::X5(..) => 5,
Junctions::X6(..) => 6,
Junctions::X7(..) => 7,
Junctions::X8(..) => 8,
}
}
/// Returns the junction at index `i`, or `None` if the location doesn't contain that many
/// elements.
pub fn at(&self, i: usize) -> Option<&Junction> {
Some(match (i, self) {
(0, Junctions::X1(ref a)) => a,
(0, Junctions::X2(ref a, ..)) => a,
(0, Junctions::X3(ref a, ..)) => a,
(0, Junctions::X4(ref a, ..)) => a,
(0, Junctions::X5(ref a, ..)) => a,
(0, Junctions::X6(ref a, ..)) => a,
(0, Junctions::X7(ref a, ..)) => a,
(0, Junctions::X8(ref a, ..)) => a,
(1, Junctions::X2(_, ref a)) => a,
(1, Junctions::X3(_, ref a, ..)) => a,
(1, Junctions::X4(_, ref a, ..)) => a,
(1, Junctions::X5(_, ref a, ..)) => a,
(1, Junctions::X6(_, ref a, ..)) => a,
(1, Junctions::X7(_, ref a, ..)) => a,
(1, Junctions::X8(_, ref a, ..)) => a,
(2, Junctions::X3(_, _, ref a)) => a,
(2, Junctions::X4(_, _, ref a, ..)) => a,
(2, Junctions::X5(_, _, ref a, ..)) => a,
(2, Junctions::X6(_, _, ref a, ..)) => a,
(2, Junctions::X7(_, _, ref a, ..)) => a,
(2, Junctions::X8(_, _, ref a, ..)) => a,
(3, Junctions::X4(_, _, _, ref a)) => a,
(3, Junctions::X5(_, _, _, ref a, ..)) => a,
(3, Junctions::X6(_, _, _, ref a, ..)) => a,
(3, Junctions::X7(_, _, _, ref a, ..)) => a,
(3, Junctions::X8(_, _, _, ref a, ..)) => a,
(4, Junctions::X5(_, _, _, _, ref a)) => a,
(4, Junctions::X6(_, _, _, _, ref a, ..)) => a,
(4, Junctions::X7(_, _, _, _, ref a, ..)) => a,
(4, Junctions::X8(_, _, _, _, ref a, ..)) => a,
(5, Junctions::X6(_, _, _, _, _, ref a)) => a,
(5, Junctions::X7(_, _, _, _, _, ref a, ..)) => a,
(5, Junctions::X8(_, _, _, _, _, ref a, ..)) => a,
(6, Junctions::X7(_, _, _, _, _, _, ref a)) => a,
(6, Junctions::X8(_, _, _, _, _, _, ref a, ..)) => a,
(7, Junctions::X8(_, _, _, _, _, _, _, ref a)) => a,
_ => return None,
})
}
/// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't
/// contain that many elements.
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
Some(match (i, self) {
(0, Junctions::X1(ref mut a)) => a,
(0, Junctions::X2(ref mut a, ..)) => a,
(0, Junctions::X3(ref mut a, ..)) => a,
(0, Junctions::X4(ref mut a, ..)) => a,
(0, Junctions::X5(ref mut a, ..)) => a,
(0, Junctions::X6(ref mut a, ..)) => a,
(0, Junctions::X7(ref mut a, ..)) => a,
(0, Junctions::X8(ref mut a, ..)) => a,
(1, Junctions::X2(_, ref mut a)) => a,
(1, Junctions::X3(_, ref mut a, ..)) => a,
(1, Junctions::X4(_, ref mut a, ..)) => a,
(1, Junctions::X5(_, ref mut a, ..)) => a,
(1, Junctions::X6(_, ref mut a, ..)) => a,
(1, Junctions::X7(_, ref mut a, ..)) => a,
(1, Junctions::X8(_, ref mut a, ..)) => a,
(2, Junctions::X3(_, _, ref mut a)) => a,
(2, Junctions::X4(_, _, ref mut a, ..)) => a,
(2, Junctions::X5(_, _, ref mut a, ..)) => a,
(2, Junctions::X6(_, _, ref mut a, ..)) => a,
(2, Junctions::X7(_, _, ref mut a, ..)) => a,
(2, Junctions::X8(_, _, ref mut a, ..)) => a,
(3, Junctions::X4(_, _, _, ref mut a)) => a,
(3, Junctions::X5(_, _, _, ref mut a, ..)) => a,
(3, Junctions::X6(_, _, _, ref mut a, ..)) => a,
(3, Junctions::X7(_, _, _, ref mut a, ..)) => a,
(3, Junctions::X8(_, _, _, ref mut a, ..)) => a,
(4, Junctions::X5(_, _, _, _, ref mut a)) => a,
(4, Junctions::X6(_, _, _, _, ref mut a, ..)) => a,
(4, Junctions::X7(_, _, _, _, ref mut a, ..)) => a,
(4, Junctions::X8(_, _, _, _, ref mut a, ..)) => a,
(5, Junctions::X6(_, _, _, _, _, ref mut a)) => a,
(5, Junctions::X7(_, _, _, _, _, ref mut a, ..)) => a,
(5, Junctions::X8(_, _, _, _, _, ref mut a, ..)) => a,
(6, Junctions::X7(_, _, _, _, _, _, ref mut a)) => a,
(6, Junctions::X8(_, _, _, _, _, _, ref mut a, ..)) => a,
(7, Junctions::X8(_, _, _, _, _, _, _, ref mut a)) => a,
_ => return None,
})
}
/// Returns a reference iterator over the junctions.
pub fn iter(&self) -> JunctionsRefIterator<'_> {
JunctionsRefIterator { junctions: self, next: 0, back: 0 }
}
/// Ensures that self begins with `prefix` and that it has a single `Junction` item following.
/// If so, returns a reference to this `Junction` item.
///
/// # Example
/// ```rust
/// # use staging_xcm::v3::{Junctions::*, Junction::*};
/// let mut m = X3(Teyrchain(2), PalletInstance(3), OnlyChild);
/// assert_eq!(m.match_and_split(&X2(Teyrchain(2), PalletInstance(3))), Some(&OnlyChild));
/// assert_eq!(m.match_and_split(&X1(Teyrchain(2))), None);
/// ```
pub fn match_and_split(&self, prefix: &Junctions) -> Option<&Junction> {
if prefix.len() + 1 != self.len() {
return None;
}
for i in 0..prefix.len() {
if prefix.at(i) != self.at(i) {
return None;
}
}
return self.at(prefix.len());
}
pub fn starts_with(&self, prefix: &Junctions) -> bool {
prefix.len() <= self.len() && prefix.iter().zip(self.iter()).all(|(x, y)| x == y)
}
}
impl TryFrom<MultiLocation> for Junctions {
type Error = MultiLocation;
fn try_from(x: MultiLocation) -> result::Result<Self, MultiLocation> {
if x.parents > 0 {
Err(x)
} else {
Ok(x.interior)
}
}
}
impl<T: Into<Junction>> From<T> for Junctions {
fn from(x: T) -> Self {
Self::X1(x.into())
}
}
impl From<[Junction; 0]> for Junctions {
fn from(_: [Junction; 0]) -> Self {
Self::Here
}
}
impl From<()> for Junctions {
fn from(_: ()) -> Self {
Self::Here
}
}
xcm_procedural::impl_conversion_functions_for_junctions_v3!();
#[cfg(test)]
mod tests {
use super::{super::prelude::*, *};
#[test]
fn inverting_works() {
let context: InteriorMultiLocation = (Teyrchain(1000), PalletInstance(42)).into();
let target = (Parent, PalletInstance(69)).into();
let expected = (Parent, PalletInstance(42)).into();
let inverted = context.invert_target(&target).unwrap();
assert_eq!(inverted, expected);
let context: InteriorMultiLocation =
(Teyrchain(1000), PalletInstance(42), GeneralIndex(1)).into();
let target = (Parent, Parent, PalletInstance(69), GeneralIndex(2)).into();
let expected = (Parent, Parent, PalletInstance(42), GeneralIndex(1)).into();
let inverted = context.invert_target(&target).unwrap();
assert_eq!(inverted, expected);
}
#[test]
fn relative_to_works() {
use Junctions::*;
use NetworkId::*;
assert_eq!(X1(Pezkuwi.into()).relative_to(&X1(Kusama.into())), (Parent, Pezkuwi).into());
let base = X3(Kusama.into(), Teyrchain(1), PalletInstance(1));
// Ancestors.
assert_eq!(Here.relative_to(&base), (Parent, Parent, Parent).into());
assert_eq!(X1(Kusama.into()).relative_to(&base), (Parent, Parent).into());
assert_eq!(X2(Kusama.into(), Teyrchain(1)).relative_to(&base), (Parent,).into());
assert_eq!(
X3(Kusama.into(), Teyrchain(1), PalletInstance(1)).relative_to(&base),
Here.into()
);
// Ancestors with one child.
assert_eq!(X1(Pezkuwi.into()).relative_to(&base), (Parent, Parent, Parent, Pezkuwi).into());
assert_eq!(
X2(Kusama.into(), Teyrchain(2)).relative_to(&base),
(Parent, Parent, Teyrchain(2)).into()
);
assert_eq!(
X3(Kusama.into(), Teyrchain(1), PalletInstance(2)).relative_to(&base),
(Parent, PalletInstance(2)).into()
);
assert_eq!(
X4(Kusama.into(), Teyrchain(1), PalletInstance(1), [1u8; 32].into()).relative_to(&base),
([1u8; 32],).into()
);
// Ancestors with grandchildren.
assert_eq!(
X2(Pezkuwi.into(), Teyrchain(1)).relative_to(&base),
(Parent, Parent, Parent, Pezkuwi, Teyrchain(1)).into()
);
assert_eq!(
X3(Kusama.into(), Teyrchain(2), PalletInstance(1)).relative_to(&base),
(Parent, Parent, Teyrchain(2), PalletInstance(1)).into()
);
assert_eq!(
X4(Kusama.into(), Teyrchain(1), PalletInstance(2), [1u8; 32].into()).relative_to(&base),
(Parent, PalletInstance(2), [1u8; 32]).into()
);
assert_eq!(
X5(Kusama.into(), Teyrchain(1), PalletInstance(1), [1u8; 32].into(), 1u128.into())
.relative_to(&base),
([1u8; 32], 1u128).into()
);
}
#[test]
fn global_consensus_works() {
use Junctions::*;
use NetworkId::*;
assert_eq!(X1(Pezkuwi.into()).global_consensus(), Ok(Pezkuwi));
assert_eq!(X2(Kusama.into(), 1u64.into()).global_consensus(), Ok(Kusama));
assert_eq!(Here.global_consensus(), Err(()));
assert_eq!(X1(1u64.into()).global_consensus(), Err(()));
assert_eq!(X2(1u64.into(), Kusama.into()).global_consensus(), Err(()));
}
#[test]
fn test_conversion() {
use super::{Junction::*, Junctions::*, NetworkId::*};
let x: Junctions = GlobalConsensus(Pezkuwi).into();
assert_eq!(x, X1(GlobalConsensus(Pezkuwi)));
let x: Junctions = Pezkuwi.into();
assert_eq!(x, X1(GlobalConsensus(Pezkuwi)));
let x: Junctions = (Pezkuwi, Kusama).into();
assert_eq!(x, X2(GlobalConsensus(Pezkuwi), GlobalConsensus(Kusama)));
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+754
View File
@@ -0,0 +1,754 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! XCM `MultiLocation` datatype.
use super::{Junction, Junctions};
use crate::{v4::Location as NewMultiLocation, VersionedLocation};
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use core::result;
use scale_info::TypeInfo;
/// A relative path between state-bearing consensus systems.
///
/// A location in a consensus system is defined as an *isolatable state machine* held within global
/// consensus. The location in question need not have a sophisticated consensus algorithm of its
/// own; a single account within Ethereum, for example, could be considered a location.
///
/// A very-much non-exhaustive list of types of location include:
/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a teyrchain.
/// - A layer-0 super-chain, e.g. the Pezkuwi Relay chain.
/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum.
/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based
/// Substrate chain.
/// - An account.
///
/// A `MultiLocation` is a *relative identifier*, meaning that it can only be used to define the
/// relative path between two locations, and cannot generally be used to refer to a location
/// universally. It is comprised of an integer number of parents specifying the number of times to
/// "escape" upwards into the containing consensus system and then a number of *junctions*, each
/// diving down and specifying some interior portion of state (which may be considered a
/// "sub-consensus" system).
///
/// This specific `MultiLocation` implementation uses a `Junctions` datatype which is a Rust `enum`
/// in order to make pattern matching easier. There are occasions where it is important to ensure
/// that a value is strictly an interior location, in those cases, `Junctions` may be used.
///
/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system.
#[derive(
Copy,
Clone,
Decode,
Encode,
DecodeWithMemTracking,
Eq,
PartialEq,
Ord,
PartialOrd,
Debug,
TypeInfo,
MaxEncodedLen,
serde::Serialize,
serde::Deserialize,
)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct MultiLocation {
/// The number of parent junctions at the beginning of this `MultiLocation`.
pub parents: u8,
/// The interior (i.e. non-parent) junctions that this `MultiLocation` contains.
pub interior: Junctions,
}
/// Type alias for a better transition to V4.
pub type Location = MultiLocation;
impl Default for MultiLocation {
fn default() -> Self {
Self { parents: 0, interior: Junctions::Here }
}
}
/// A relative location which is constrained to be an interior location of the context.
///
/// See also `MultiLocation`.
pub type InteriorMultiLocation = Junctions;
impl MultiLocation {
/// Creates a new `MultiLocation` with the given number of parents and interior junctions.
pub fn new(parents: u8, interior: impl Into<Junctions>) -> MultiLocation {
MultiLocation { parents, interior: interior.into() }
}
/// Consume `self` and return the equivalent `VersionedLocation` value.
pub const fn into_versioned(self) -> VersionedLocation {
VersionedLocation::V3(self)
}
/// Creates a new `MultiLocation` with 0 parents and a `Here` interior.
///
/// The resulting `MultiLocation` can be interpreted as the "current consensus system".
pub const fn here() -> MultiLocation {
MultiLocation { parents: 0, interior: Junctions::Here }
}
/// Creates a new `MultiLocation` which evaluates to the parent context.
pub const fn parent() -> MultiLocation {
MultiLocation { parents: 1, interior: Junctions::Here }
}
/// Creates a new `MultiLocation` which evaluates to the grand parent context.
pub const fn grandparent() -> MultiLocation {
MultiLocation { parents: 2, interior: Junctions::Here }
}
/// Creates a new `MultiLocation` with `parents` and an empty (`Here`) interior.
pub const fn ancestor(parents: u8) -> MultiLocation {
MultiLocation { parents, interior: Junctions::Here }
}
/// Whether the `MultiLocation` has no parents and has a `Here` interior.
pub const fn is_here(&self) -> bool {
self.parents == 0 && self.interior.len() == 0
}
/// Remove the `NetworkId` value in any interior `Junction`s.
pub fn remove_network_id(&mut self) {
self.interior.remove_network_id();
}
/// Return a reference to the interior field.
pub fn interior(&self) -> &Junctions {
&self.interior
}
/// Return a mutable reference to the interior field.
pub fn interior_mut(&mut self) -> &mut Junctions {
&mut self.interior
}
/// Returns the number of `Parent` junctions at the beginning of `self`.
pub const fn parent_count(&self) -> u8 {
self.parents
}
/// Returns boolean indicating whether `self` contains only the specified amount of
/// parents and no interior junctions.
pub const fn contains_parents_only(&self, count: u8) -> bool {
matches!(self.interior, Junctions::Here) && self.parents == count
}
/// Returns the number of parents and junctions in `self`.
pub const fn len(&self) -> usize {
self.parent_count() as usize + self.interior.len()
}
/// Returns the first interior junction, or `None` if the location is empty or contains only
/// parents.
pub fn first_interior(&self) -> Option<&Junction> {
self.interior.first()
}
/// Returns last junction, or `None` if the location is empty or contains only parents.
pub fn last(&self) -> Option<&Junction> {
self.interior.last()
}
/// Splits off the first interior junction, returning the remaining suffix (first item in tuple)
/// and the first element (second item in tuple) or `None` if it was empty.
pub fn split_first_interior(self) -> (MultiLocation, Option<Junction>) {
let MultiLocation { parents, interior: junctions } = self;
let (suffix, first) = junctions.split_first();
let multilocation = MultiLocation { parents, interior: suffix };
(multilocation, first)
}
/// Splits off the last interior junction, returning the remaining prefix (first item in tuple)
/// and the last element (second item in tuple) or `None` if it was empty or if `self` only
/// contains parents.
pub fn split_last_interior(self) -> (MultiLocation, Option<Junction>) {
let MultiLocation { parents, interior: junctions } = self;
let (prefix, last) = junctions.split_last();
let multilocation = MultiLocation { parents, interior: prefix };
(multilocation, last)
}
/// Mutates `self`, suffixing its interior junctions with `new`. Returns `Err` with `new` in
/// case of overflow.
pub fn push_interior(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
self.interior.push(new)
}
/// Mutates `self`, prefixing its interior junctions with `new`. Returns `Err` with `new` in
/// case of overflow.
pub fn push_front_interior(
&mut self,
new: impl Into<Junction>,
) -> result::Result<(), Junction> {
self.interior.push_front(new)
}
/// Consumes `self` and returns a `MultiLocation` suffixed with `new`, or an `Err` with
/// the original value of `self` in case of overflow.
pub fn pushed_with_interior(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
match self.interior.pushed_with(new) {
Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }),
Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)),
}
}
/// Consumes `self` and returns a `MultiLocation` prefixed with `new`, or an `Err` with the
/// original value of `self` in case of overflow.
pub fn pushed_front_with_interior(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
match self.interior.pushed_front_with(new) {
Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }),
Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)),
}
}
/// Returns the junction at index `i`, or `None` if the location is a parent or if the location
/// does not contain that many elements.
pub fn at(&self, i: usize) -> Option<&Junction> {
let num_parents = self.parents as usize;
if i < num_parents {
return None;
}
self.interior.at(i - num_parents)
}
/// Returns a mutable reference to the junction at index `i`, or `None` if the location is a
/// parent or if it doesn't contain that many elements.
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
let num_parents = self.parents as usize;
if i < num_parents {
return None;
}
self.interior.at_mut(i - num_parents)
}
/// Decrements the parent count by 1.
pub fn dec_parent(&mut self) {
self.parents = self.parents.saturating_sub(1);
}
/// Removes the first interior junction from `self`, returning it
/// (or `None` if it was empty or if `self` contains only parents).
pub fn take_first_interior(&mut self) -> Option<Junction> {
self.interior.take_first()
}
/// Removes the last element from `interior`, returning it (or `None` if it was empty or if
/// `self` only contains parents).
pub fn take_last(&mut self) -> Option<Junction> {
self.interior.take_last()
}
/// Ensures that `self` has the same number of parents as `prefix`, its junctions begins with
/// the junctions of `prefix` and that it has a single `Junction` item following.
/// If so, returns a reference to this `Junction` item.
///
/// # Example
/// ```rust
/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation};
/// let mut m = MultiLocation::new(1, X2(PalletInstance(3), OnlyChild));
/// assert_eq!(
/// m.match_and_split(&MultiLocation::new(1, X1(PalletInstance(3)))),
/// Some(&OnlyChild),
/// );
/// assert_eq!(m.match_and_split(&MultiLocation::new(1, Here)), None);
/// ```
pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> {
if self.parents != prefix.parents {
return None;
}
self.interior.match_and_split(&prefix.interior)
}
pub fn starts_with(&self, prefix: &MultiLocation) -> bool {
self.parents == prefix.parents && self.interior.starts_with(&prefix.interior)
}
/// Mutate `self` so that it is suffixed with `suffix`.
///
/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// let mut m: MultiLocation = (Parent, Teyrchain(21), 69u64).into();
/// assert_eq!(m.append_with((Parent, PalletInstance(3))), Ok(()));
/// assert_eq!(m, MultiLocation::new(1, X2(Teyrchain(21), PalletInstance(3))));
/// ```
pub fn append_with(&mut self, suffix: impl Into<Self>) -> Result<(), Self> {
let prefix = core::mem::replace(self, suffix.into());
match self.prepend_with(prefix) {
Ok(()) => Ok(()),
Err(prefix) => Err(core::mem::replace(self, prefix)),
}
}
/// Consume `self` and return its value suffixed with `suffix`.
///
/// Returns `Err` with the original value of `self` and `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// let mut m: MultiLocation = (Parent, Teyrchain(21), 69u64).into();
/// let r = m.appended_with((Parent, PalletInstance(3))).unwrap();
/// assert_eq!(r, MultiLocation::new(1, X2(Teyrchain(21), PalletInstance(3))));
/// ```
pub fn appended_with(mut self, suffix: impl Into<Self>) -> Result<Self, (Self, Self)> {
match self.append_with(suffix) {
Ok(()) => Ok(self),
Err(suffix) => Err((self, suffix)),
}
}
/// Mutate `self` so that it is prefixed with `prefix`.
///
/// Does not modify `self` and returns `Err` with `prefix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// let mut m: MultiLocation = (Parent, Parent, PalletInstance(3)).into();
/// assert_eq!(m.prepend_with((Parent, Teyrchain(21), OnlyChild)), Ok(()));
/// assert_eq!(m, MultiLocation::new(1, X1(PalletInstance(3))));
/// ```
pub fn prepend_with(&mut self, prefix: impl Into<Self>) -> Result<(), Self> {
// prefix self (suffix)
// P .. P I .. I p .. p i .. i
let mut prefix = prefix.into();
let prepend_interior = prefix.interior.len().saturating_sub(self.parents as usize);
let final_interior = self.interior.len().saturating_add(prepend_interior);
if final_interior > super::junctions::MAX_JUNCTIONS {
return Err(prefix);
}
let suffix_parents = (self.parents as usize).saturating_sub(prefix.interior.len());
let final_parents = (prefix.parents as usize).saturating_add(suffix_parents);
if final_parents > 255 {
return Err(prefix);
}
// cancel out the final item on the prefix interior for one of the suffix's parents.
while self.parents > 0 && prefix.take_last().is_some() {
self.dec_parent();
}
// now we have either removed all suffix's parents or prefix interior.
// this means we can combine the prefix's and suffix's remaining parents/interior since
// we know that with at least one empty, the overall order will be respected:
// prefix self (suffix)
// P .. P (I) p .. p i .. i => P + p .. (no I) i
// -- or --
// P .. P I .. I (p) i .. i => P (no p) .. I + i
self.parents = self.parents.saturating_add(prefix.parents);
for j in prefix.interior.into_iter().rev() {
self.push_front_interior(j)
.expect("final_interior no greater than MAX_JUNCTIONS; qed");
}
Ok(())
}
/// Consume `self` and return its value prefixed with `prefix`.
///
/// Returns `Err` with the original value of `self` and `prefix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// let m: MultiLocation = (Parent, Parent, PalletInstance(3)).into();
/// let r = m.prepended_with((Parent, Teyrchain(21), OnlyChild)).unwrap();
/// assert_eq!(r, MultiLocation::new(1, X1(PalletInstance(3))));
/// ```
pub fn prepended_with(mut self, prefix: impl Into<Self>) -> Result<Self, (Self, Self)> {
match self.prepend_with(prefix) {
Ok(()) => Ok(self),
Err(prefix) => Err((self, prefix)),
}
}
/// Mutate `self` so that it represents the same location from the point of view of `target`.
/// The context of `self` is provided as `context`.
///
/// Does not modify `self` in case of overflow.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
// TODO: https://github.com/paritytech/polkadot/issues/4489 Optimize this.
// 1. Use our `context` to figure out how the `target` would address us.
let inverted_target = context.invert_target(target)?;
// 2. Prepend `inverted_target` to `self` to get self's location from the perspective of
// `target`.
self.prepend_with(inverted_target).map_err(|_| ())?;
// 3. Given that we know some of `target` context, ensure that any parents in `self` are
// strictly needed.
self.simplify(target.interior());
Ok(())
}
/// Consume `self` and return a new value representing the same location from the point of view
/// of `target`. The context of `self` is provided as `context`.
///
/// Returns the original `self` in case of overflow.
pub fn reanchored(
mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<Self, Self> {
match self.reanchor(target, context) {
Ok(()) => Ok(self),
Err(()) => Err(self),
}
}
/// Remove any unneeded parents/junctions in `self` based on the given context it will be
/// interpreted in.
pub fn simplify(&mut self, context: &Junctions) {
if context.len() < self.parents as usize {
// Not enough context
return;
}
while self.parents > 0 {
let maybe = context.at(context.len() - (self.parents as usize));
match (self.interior.first(), maybe) {
(Some(i), Some(j)) if i == j => {
self.interior.take_first();
self.parents -= 1;
},
_ => break,
}
}
}
/// Return the MultiLocation subsection identifying the chain that `self` points to.
pub fn chain_location(&self) -> MultiLocation {
let mut clone = *self;
// start popping junctions until we reach chain identifier
while let Some(j) = clone.last() {
if matches!(j, Junction::Teyrchain(_) | Junction::GlobalConsensus(_)) {
// return chain subsection
return clone;
} else {
(clone, _) = clone.split_last_interior();
}
}
MultiLocation::new(clone.parents, Junctions::Here)
}
}
impl TryFrom<NewMultiLocation> for Option<MultiLocation> {
type Error = ();
fn try_from(new: NewMultiLocation) -> result::Result<Self, Self::Error> {
Ok(Some(MultiLocation::try_from(new)?))
}
}
impl TryFrom<NewMultiLocation> for MultiLocation {
type Error = ();
fn try_from(new: NewMultiLocation) -> result::Result<Self, ()> {
Ok(MultiLocation {
parents: new.parent_count(),
interior: new.interior().clone().try_into()?,
})
}
}
/// A unit struct which can be converted into a `MultiLocation` of `parents` value 1.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Parent;
impl From<Parent> for MultiLocation {
fn from(_: Parent) -> Self {
MultiLocation { parents: 1, interior: Junctions::Here }
}
}
/// A tuple struct which can be converted into a `MultiLocation` of `parents` value 1 with the inner
/// interior.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct ParentThen(pub Junctions);
impl From<ParentThen> for MultiLocation {
fn from(ParentThen(interior): ParentThen) -> Self {
MultiLocation { parents: 1, interior }
}
}
/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Ancestor(pub u8);
impl From<Ancestor> for MultiLocation {
fn from(Ancestor(parents): Ancestor) -> Self {
MultiLocation { parents, interior: Junctions::Here }
}
}
/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value and the
/// inner interior.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct AncestorThen<Interior>(pub u8, pub Interior);
impl<Interior: Into<Junctions>> From<AncestorThen<Interior>> for MultiLocation {
fn from(AncestorThen(parents, interior): AncestorThen<Interior>) -> Self {
MultiLocation { parents, interior: interior.into() }
}
}
xcm_procedural::impl_conversion_functions_for_multilocation_v3!();
#[cfg(test)]
mod tests {
use crate::v3::prelude::*;
use codec::{Decode, Encode};
#[test]
fn conversion_works() {
let x: MultiLocation = Parent.into();
assert_eq!(x, MultiLocation { parents: 1, interior: Here });
// let x: MultiLocation = (Parent,).into();
// assert_eq!(x, MultiLocation { parents: 1, interior: Here });
// let x: MultiLocation = (Parent, Parent).into();
// assert_eq!(x, MultiLocation { parents: 2, interior: Here });
let x: MultiLocation = (Parent, Parent, OnlyChild).into();
assert_eq!(x, MultiLocation { parents: 2, interior: OnlyChild.into() });
let x: MultiLocation = OnlyChild.into();
assert_eq!(x, MultiLocation { parents: 0, interior: OnlyChild.into() });
let x: MultiLocation = (OnlyChild,).into();
assert_eq!(x, MultiLocation { parents: 0, interior: OnlyChild.into() });
}
#[test]
fn simplify_basic_works() {
let mut location: MultiLocation =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X2(Teyrchain(1000), PalletInstance(42));
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into();
let context = X1(PalletInstance(42));
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into();
let context = X2(Teyrchain(1000), PalletInstance(42));
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X3(OnlyChild, Teyrchain(1000), PalletInstance(42));
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
}
#[test]
fn simplify_incompatible_location_fails() {
let mut location: MultiLocation =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X3(Teyrchain(1000), PalletInstance(42), GeneralIndex(42));
let expected =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X1(Teyrchain(1000));
let expected =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
}
#[test]
fn reanchor_works() {
let mut id: MultiLocation = (Parent, Teyrchain(1000), GeneralIndex(42)).into();
let context = Teyrchain(2000).into();
let target = (Parent, Teyrchain(1000)).into();
let expected = GeneralIndex(42).into();
id.reanchor(&target, context).unwrap();
assert_eq!(id, expected);
}
#[test]
fn encode_and_decode_works() {
let m = MultiLocation {
parents: 1,
interior: X2(Teyrchain(42), AccountIndex64 { network: None, index: 23 }),
};
let encoded = m.encode();
assert_eq!(encoded, [1, 2, 0, 168, 2, 0, 92].to_vec());
let decoded = MultiLocation::decode(&mut &encoded[..]);
assert_eq!(decoded, Ok(m));
}
#[test]
fn match_and_split_works() {
let m = MultiLocation {
parents: 1,
interior: X2(Teyrchain(42), AccountIndex64 { network: None, index: 23 }),
};
assert_eq!(m.match_and_split(&MultiLocation { parents: 1, interior: Here }), None);
assert_eq!(
m.match_and_split(&MultiLocation { parents: 1, interior: X1(Teyrchain(42)) }),
Some(&AccountIndex64 { network: None, index: 23 })
);
assert_eq!(m.match_and_split(&m), None);
}
#[test]
fn append_with_works() {
let acc = AccountIndex64 { network: None, index: 23 };
let mut m = MultiLocation { parents: 1, interior: X1(Teyrchain(42)) };
assert_eq!(m.append_with(X2(PalletInstance(3), acc)), Ok(()));
assert_eq!(
m,
MultiLocation { parents: 1, interior: X3(Teyrchain(42), PalletInstance(3), acc) }
);
// cannot append to create overly long multilocation
let acc = AccountIndex64 { network: None, index: 23 };
let m = MultiLocation {
parents: 254,
interior: X5(Teyrchain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild),
};
let suffix: MultiLocation = (PalletInstance(3), acc, OnlyChild, OnlyChild).into();
assert_eq!(m.clone().append_with(suffix), Err(suffix));
}
#[test]
fn prepend_with_works() {
let mut m = MultiLocation {
parents: 1,
interior: X2(Teyrchain(42), AccountIndex64 { network: None, index: 23 }),
};
assert_eq!(m.prepend_with(MultiLocation { parents: 1, interior: X1(OnlyChild) }), Ok(()));
assert_eq!(
m,
MultiLocation {
parents: 1,
interior: X2(Teyrchain(42), AccountIndex64 { network: None, index: 23 })
}
);
// cannot prepend to create overly long multilocation
let mut m = MultiLocation { parents: 254, interior: X1(Teyrchain(42)) };
let prefix = MultiLocation { parents: 2, interior: Here };
assert_eq!(m.prepend_with(prefix), Err(prefix));
let prefix = MultiLocation { parents: 1, interior: Here };
assert_eq!(m.prepend_with(prefix), Ok(()));
assert_eq!(m, MultiLocation { parents: 255, interior: X1(Teyrchain(42)) });
}
#[test]
fn double_ended_ref_iteration_works() {
let m = X3(Teyrchain(1000), Teyrchain(3), PalletInstance(5));
let mut iter = m.iter();
let first = iter.next().unwrap();
assert_eq!(first, &Teyrchain(1000));
let third = iter.next_back().unwrap();
assert_eq!(third, &PalletInstance(5));
let second = iter.next_back().unwrap();
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
assert_eq!(second, &Teyrchain(3));
let res = Here
.pushed_with(*first)
.unwrap()
.pushed_with(*second)
.unwrap()
.pushed_with(*third)
.unwrap();
assert_eq!(m, res);
// make sure there's no funny business with the 0 indexing
let m = Here;
let mut iter = m.iter();
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
}
#[test]
fn chain_location_works() {
// Relay-chain or teyrchain context pointing to local resource,
let relay_to_local = MultiLocation::new(0, (PalletInstance(42), GeneralIndex(42)));
assert_eq!(relay_to_local.chain_location(), MultiLocation::here());
// Relay-chain context pointing to child teyrchain,
let relay_to_child =
MultiLocation::new(0, (Teyrchain(42), PalletInstance(42), GeneralIndex(42)));
let expected = MultiLocation::new(0, Teyrchain(42));
assert_eq!(relay_to_child.chain_location(), expected);
// Relay-chain context pointing to different consensus relay,
let relay_to_remote_relay =
MultiLocation::new(1, (GlobalConsensus(Kusama), PalletInstance(42), GeneralIndex(42)));
let expected = MultiLocation::new(1, GlobalConsensus(Kusama));
assert_eq!(relay_to_remote_relay.chain_location(), expected);
// Relay-chain context pointing to different consensus teyrchain,
let relay_to_remote_para = MultiLocation::new(
1,
(GlobalConsensus(Kusama), Teyrchain(42), PalletInstance(42), GeneralIndex(42)),
);
let expected = MultiLocation::new(1, (GlobalConsensus(Kusama), Teyrchain(42)));
assert_eq!(relay_to_remote_para.chain_location(), expected);
// Teyrchain context pointing to relay chain,
let para_to_relay = MultiLocation::new(1, (PalletInstance(42), GeneralIndex(42)));
assert_eq!(para_to_relay.chain_location(), MultiLocation::parent());
// Teyrchain context pointing to sibling teyrchain,
let para_to_sibling =
MultiLocation::new(1, (Teyrchain(42), PalletInstance(42), GeneralIndex(42)));
let expected = MultiLocation::new(1, Teyrchain(42));
assert_eq!(para_to_sibling.chain_location(), expected);
// Teyrchain context pointing to different consensus relay,
let para_to_remote_relay =
MultiLocation::new(2, (GlobalConsensus(Kusama), PalletInstance(42), GeneralIndex(42)));
let expected = MultiLocation::new(2, GlobalConsensus(Kusama));
assert_eq!(para_to_remote_relay.chain_location(), expected);
// Teyrchain context pointing to different consensus teyrchain,
let para_to_remote_para = MultiLocation::new(
2,
(GlobalConsensus(Kusama), Teyrchain(42), PalletInstance(42), GeneralIndex(42)),
);
let expected = MultiLocation::new(2, (GlobalConsensus(Kusama), Teyrchain(42)));
assert_eq!(para_to_remote_para.chain_location(), expected);
}
}
+574
View File
@@ -0,0 +1,574 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use crate::v5::Error as NewError;
use core::result;
use scale_info::TypeInfo;
pub use sp_weights::Weight;
// A simple trait to get the weight of some object.
pub trait GetWeight<W> {
fn weight(&self) -> sp_weights::Weight;
}
use super::*;
/// Error codes used in XCM. The first errors codes have explicit indices and are part of the XCM
/// format. Those trailing are merely part of the XCM implementation; there is no expectation that
/// they will retain the same index over time.
#[derive(
Copy,
Clone,
Encode,
Decode,
DecodeWithMemTracking,
Eq,
PartialEq,
Debug,
TypeInfo,
MaxEncodedLen,
)]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub enum Error {
// Errors that happen due to instructions being executed. These alone are defined in the
// XCM specification.
/// An arithmetic overflow happened.
#[codec(index = 0)]
Overflow,
/// The instruction is intentionally unsupported.
#[codec(index = 1)]
Unimplemented,
/// Origin Register does not contain a value value for a reserve transfer notification.
#[codec(index = 2)]
UntrustedReserveLocation,
/// Origin Register does not contain a value value for a teleport notification.
#[codec(index = 3)]
UntrustedTeleportLocation,
/// `MultiLocation` value too large to descend further.
#[codec(index = 4)]
LocationFull,
/// `MultiLocation` value ascend more parents than known ancestors of local location.
#[codec(index = 5)]
LocationNotInvertible,
/// The Origin Register does not contain a valid value for instruction.
#[codec(index = 6)]
BadOrigin,
/// The location parameter is not a valid value for the instruction.
#[codec(index = 7)]
InvalidLocation,
/// The given asset is not handled.
#[codec(index = 8)]
AssetNotFound,
/// An asset transaction (like withdraw or deposit) failed (typically due to type conversions).
#[codec(index = 9)]
FailedToTransactAsset(#[codec(skip)] &'static str),
/// An asset cannot be withdrawn, potentially due to lack of ownership, availability or rights.
#[codec(index = 10)]
NotWithdrawable,
/// An asset cannot be deposited under the ownership of a particular location.
#[codec(index = 11)]
LocationCannotHold,
/// Attempt to send a message greater than the maximum supported by the transport protocol.
#[codec(index = 12)]
ExceedsMaxMessageSize,
/// The given message cannot be translated into a format supported by the destination.
#[codec(index = 13)]
DestinationUnsupported,
/// Destination is routable, but there is some issue with the transport mechanism.
#[codec(index = 14)]
Transport(#[codec(skip)] &'static str),
/// Destination is known to be unroutable.
#[codec(index = 15)]
Unroutable,
/// Used by `ClaimAsset` when the given claim could not be recognized/found.
#[codec(index = 16)]
UnknownClaim,
/// Used by `Transact` when the functor cannot be decoded.
#[codec(index = 17)]
FailedToDecode,
/// Used by `Transact` to indicate that the given weight limit could be breached by the
/// functor.
#[codec(index = 18)]
MaxWeightInvalid,
/// Used by `BuyExecution` when the Holding Register does not contain payable fees.
#[codec(index = 19)]
NotHoldingFees,
/// Used by `BuyExecution` when the fees declared to purchase weight are insufficient.
#[codec(index = 20)]
TooExpensive,
/// Used by the `Trap` instruction to force an error intentionally. Its code is included.
#[codec(index = 21)]
Trap(u64),
/// Used by `ExpectAsset`, `ExpectError` and `ExpectOrigin` when the expectation was not true.
#[codec(index = 22)]
ExpectationFalse,
/// The provided pallet index was not found.
#[codec(index = 23)]
PalletNotFound,
/// The given pallet's name is different to that expected.
#[codec(index = 24)]
NameMismatch,
/// The given pallet's version has an incompatible version to that expected.
#[codec(index = 25)]
VersionIncompatible,
/// The given operation would lead to an overflow of the Holding Register.
#[codec(index = 26)]
HoldingWouldOverflow,
/// The message was unable to be exported.
#[codec(index = 27)]
ExportError,
/// `MultiLocation` value failed to be reanchored.
#[codec(index = 28)]
ReanchorFailed,
/// No deal is possible under the given constraints.
#[codec(index = 29)]
NoDeal,
/// Fees were required which the origin could not pay.
#[codec(index = 30)]
FeesNotMet,
/// Some other error with locking.
#[codec(index = 31)]
LockError,
/// The state was not in a condition where the operation was valid to make.
#[codec(index = 32)]
NoPermission,
/// The universal location of the local consensus is improper.
#[codec(index = 33)]
Unanchored,
/// An asset cannot be deposited, probably because (too much of) it already exists.
#[codec(index = 34)]
NotDepositable,
// Errors that happen prior to instructions being executed. These fall outside of the XCM
// spec.
/// XCM version not able to be handled.
UnhandledXcmVersion,
/// Execution of the XCM would potentially result in a greater weight used than weight limit.
WeightLimitReached(Weight),
/// The XCM did not pass the barrier condition for execution.
///
/// The barrier condition differs on different chains and in different circumstances, but
/// generally it means that the conditions surrounding the message were not such that the chain
/// considers the message worth spending time executing. Since most chains lift the barrier to
/// execution on appropriate payment, presentation of an NFT voucher, or based on the message
/// origin, it means that none of those were the case.
Barrier,
/// The weight of an XCM message is not computable ahead of execution.
WeightNotComputable,
/// Recursion stack limit reached
ExceedsStackLimit,
}
impl TryFrom<NewError> for Error {
type Error = ();
fn try_from(new_error: NewError) -> result::Result<Error, ()> {
use NewError::*;
Ok(match new_error {
Overflow => Self::Overflow,
Unimplemented => Self::Unimplemented,
UntrustedReserveLocation => Self::UntrustedReserveLocation,
UntrustedTeleportLocation => Self::UntrustedTeleportLocation,
LocationFull => Self::LocationFull,
LocationNotInvertible => Self::LocationNotInvertible,
BadOrigin => Self::BadOrigin,
InvalidLocation => Self::InvalidLocation,
AssetNotFound => Self::AssetNotFound,
FailedToTransactAsset(s) => Self::FailedToTransactAsset(s),
NotWithdrawable => Self::NotWithdrawable,
LocationCannotHold => Self::LocationCannotHold,
ExceedsMaxMessageSize => Self::ExceedsMaxMessageSize,
DestinationUnsupported => Self::DestinationUnsupported,
Transport(s) => Self::Transport(s),
Unroutable => Self::Unroutable,
UnknownClaim => Self::UnknownClaim,
FailedToDecode => Self::FailedToDecode,
MaxWeightInvalid => Self::MaxWeightInvalid,
NotHoldingFees => Self::NotHoldingFees,
TooExpensive => Self::TooExpensive,
Trap(i) => Self::Trap(i),
ExpectationFalse => Self::ExpectationFalse,
PalletNotFound => Self::PalletNotFound,
NameMismatch => Self::NameMismatch,
VersionIncompatible => Self::VersionIncompatible,
HoldingWouldOverflow => Self::HoldingWouldOverflow,
ExportError => Self::ExportError,
ReanchorFailed => Self::ReanchorFailed,
NoDeal => Self::NoDeal,
FeesNotMet => Self::FeesNotMet,
LockError => Self::LockError,
NoPermission => Self::NoPermission,
Unanchored => Self::Unanchored,
NotDepositable => Self::NotDepositable,
_ => return Err(()),
})
}
}
impl From<SendError> for Error {
fn from(e: SendError) -> Self {
match e {
SendError::NotApplicable | SendError::Unroutable | SendError::MissingArgument =>
Error::Unroutable,
SendError::Transport(s) => Error::Transport(s),
SendError::DestinationUnsupported => Error::DestinationUnsupported,
SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize,
SendError::Fees => Error::FeesNotMet,
}
}
}
pub type Result = result::Result<(), Error>;
/// Outcome of an XCM execution.
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
pub enum Outcome {
/// Execution completed successfully; given weight was used.
Complete(Weight),
/// Execution started, but did not complete successfully due to the given error; given weight
/// was used.
Incomplete(Weight, Error),
/// Execution did not start due to the given error.
Error(Error),
}
impl Outcome {
pub fn ensure_complete(self) -> result::Result<Weight, Error> {
match self {
Outcome::Complete(weight) => Ok(weight),
Outcome::Incomplete(_, e) => Err(e),
Outcome::Error(e) => Err(e),
}
}
pub fn ensure_execution(self) -> result::Result<Weight, Error> {
match self {
Outcome::Complete(w) => Ok(w),
Outcome::Incomplete(w, _) => Ok(w),
Outcome::Error(e) => Err(e),
}
}
/// How much weight was used by the XCM execution attempt.
pub fn weight_used(&self) -> Weight {
match self {
Outcome::Complete(w) => *w,
Outcome::Incomplete(w, _) => *w,
Outcome::Error(_) => Weight::zero(),
}
}
}
pub trait PreparedMessage {
fn weight_of(&self) -> Weight;
}
/// Type of XCM message executor.
pub trait ExecuteXcm<Call> {
type Prepared: PreparedMessage;
fn prepare(message: Xcm<Call>) -> result::Result<Self::Prepared, Xcm<Call>>;
fn execute(
origin: impl Into<MultiLocation>,
pre: Self::Prepared,
id: &mut XcmHash,
weight_credit: Weight,
) -> Outcome;
fn prepare_and_execute(
origin: impl Into<MultiLocation>,
message: Xcm<Call>,
id: &mut XcmHash,
weight_limit: Weight,
weight_credit: Weight,
) -> Outcome {
let pre = match Self::prepare(message) {
Ok(x) => x,
Err(_) => return Outcome::Error(Error::WeightNotComputable),
};
let xcm_weight = pre.weight_of();
if xcm_weight.any_gt(weight_limit) {
return Outcome::Error(Error::WeightLimitReached(xcm_weight));
}
Self::execute(origin, pre, id, weight_credit)
}
/// Execute some XCM `message` with the message `hash` from `origin` using no more than
/// `weight_limit` weight.
///
/// The weight limit is a basic hard-limit and the implementation may place further
/// restrictions or requirements on weight and other aspects.
fn execute_xcm(
origin: impl Into<MultiLocation>,
message: Xcm<Call>,
hash: XcmHash,
weight_limit: Weight,
) -> Outcome {
let origin = origin.into();
tracing::trace!(
target: "xcm::execute_xcm",
?origin,
?message,
?weight_limit,
);
Self::execute_xcm_in_credit(origin, message, hash, weight_limit, Weight::zero())
}
/// Execute some XCM `message` with the message `hash` from `origin` using no more than
/// `weight_limit` weight.
///
/// Some amount of `weight_credit` may be provided which, depending on the implementation, may
/// allow execution without associated payment.
fn execute_xcm_in_credit(
origin: impl Into<MultiLocation>,
message: Xcm<Call>,
mut hash: XcmHash,
weight_limit: Weight,
weight_credit: Weight,
) -> Outcome {
let pre = match Self::prepare(message) {
Ok(x) => x,
Err(_) => return Outcome::Error(Error::WeightNotComputable),
};
let xcm_weight = pre.weight_of();
if xcm_weight.any_gt(weight_limit) {
return Outcome::Error(Error::WeightLimitReached(xcm_weight));
}
Self::execute(origin, pre, &mut hash, weight_credit)
}
/// Deduct some `fees` to the sovereign account of the given `location` and place them as per
/// the convention for fees.
fn charge_fees(location: impl Into<MultiLocation>, fees: MultiAssets) -> Result;
}
pub enum Weightless {}
impl PreparedMessage for Weightless {
fn weight_of(&self) -> Weight {
unreachable!()
}
}
impl<C> ExecuteXcm<C> for () {
type Prepared = Weightless;
fn prepare(message: Xcm<C>) -> result::Result<Self::Prepared, Xcm<C>> {
Err(message)
}
fn execute(
_: impl Into<MultiLocation>,
_: Self::Prepared,
_: &mut XcmHash,
_: Weight,
) -> Outcome {
unreachable!()
}
fn charge_fees(_location: impl Into<MultiLocation>, _fees: MultiAssets) -> Result {
Err(Error::Unimplemented)
}
}
/// Error result value when attempting to send an XCM message.
#[derive(
Clone, Encode, Decode, DecodeWithMemTracking, Eq, PartialEq, Debug, scale_info::TypeInfo,
)]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
pub enum SendError {
/// The message and destination combination was not recognized as being reachable.
///
/// This is not considered fatal: if there are alternative transport routes available, then
/// they may be attempted.
NotApplicable,
/// Destination is routable, but there is some issue with the transport mechanism. This is
/// considered fatal.
/// A human-readable explanation of the specific issue is provided.
Transport(#[codec(skip)] &'static str),
/// Destination is known to be unroutable. This is considered fatal.
Unroutable,
/// The given message cannot be translated into a format that the destination can be expected
/// to interpret.
DestinationUnsupported,
/// Message could not be sent due to its size exceeding the maximum allowed by the transport
/// layer.
ExceedsMaxMessageSize,
/// A needed argument is `None` when it should be `Some`.
MissingArgument,
/// Fees needed to be paid in order to send the message and they were unavailable.
Fees,
}
/// A hash type for identifying messages.
pub type XcmHash = [u8; 32];
/// Result value when attempting to send an XCM message.
pub type SendResult<T> = result::Result<(T, MultiAssets), SendError>;
/// Utility for sending an XCM message to a given location.
///
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each
/// router might return `NotApplicable` to pass the execution to the next sender item. Note that
/// each `NotApplicable` might alter the destination and the XCM message for to the next router.
///
/// # Example
/// ```rust
/// # use codec::Encode;
/// # use staging_xcm::v3::{prelude::*, Weight};
/// # use staging_xcm::VersionedXcm;
/// # use std::convert::Infallible;
///
/// /// A sender that only passes the message through and does nothing.
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// type Ticket = Infallible;
/// fn validate(_: &mut Option<MultiLocation>, _: &mut Option<Xcm<()>>) -> SendResult<Infallible> {
/// Err(SendError::NotApplicable)
/// }
/// fn deliver(_: Infallible) -> Result<XcmHash, SendError> {
/// unreachable!()
/// }
/// }
///
/// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing.
/// struct Sender2;
/// impl SendXcm for Sender2 {
/// type Ticket = ();
/// fn validate(destination: &mut Option<MultiLocation>, message: &mut Option<Xcm<()>>) -> SendResult<()> {
/// match destination.as_ref().ok_or(SendError::MissingArgument)? {
/// MultiLocation { parents: 0, interior: X2(j1, j2) } => Ok(((), MultiAssets::new())),
/// _ => Err(SendError::Unroutable),
/// }
/// }
/// fn deliver(_: ()) -> Result<XcmHash, SendError> {
/// Ok([0; 32])
/// }
/// }
///
/// /// A sender that accepts a message from a parent, passing through otherwise.
/// struct Sender3;
/// impl SendXcm for Sender3 {
/// type Ticket = ();
/// fn validate(destination: &mut Option<MultiLocation>, message: &mut Option<Xcm<()>>) -> SendResult<()> {
/// match destination.as_ref().ok_or(SendError::MissingArgument)? {
/// MultiLocation { parents: 1, interior: Here } => Ok(((), MultiAssets::new())),
/// _ => Err(SendError::NotApplicable),
/// }
/// }
/// fn deliver(_: ()) -> Result<XcmHash, SendError> {
/// Ok([0; 32])
/// }
/// }
///
/// // A call to send via XCM. We don't really care about this.
/// # fn main() {
/// let call: Vec<u8> = ().encode();
/// let message = Xcm(vec![Instruction::Transact {
/// origin_kind: OriginKind::Superuser,
/// require_weight_at_most: Weight::zero(),
/// call: call.into(),
/// }]);
/// let message_hash = message.using_encoded(sp_io::hashing::blake2_256);
///
/// // Sender2 will block this.
/// assert!(send_xcm::<(Sender1, Sender2, Sender3)>(Parent.into(), message.clone()).is_err());
///
/// // Sender3 will catch this.
/// assert!(send_xcm::<(Sender1, Sender3)>(Parent.into(), message.clone()).is_ok());
/// # }
/// ```
pub trait SendXcm {
/// Intermediate value which connects the two phases of the send operation.
type Ticket;
/// Check whether the given `message` is deliverable to the given `destination` and if so
/// determine the cost which will be paid by this chain to do so, returning a `Validated` token
/// which can be used to enact delivery.
///
/// The `destination` and `message` must be `Some` (or else an error will be returned) and they
/// may only be consumed if the `Err` is not `NotApplicable`.
///
/// If it is not a destination which can be reached with this type but possibly could by others,
/// then this *MUST* return `NotApplicable`. Any other error will cause the tuple
/// implementation to exit early without trying other type fields.
fn validate(
destination: &mut Option<MultiLocation>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket>;
/// Actually carry out the delivery operation for a previously validated message sending.
fn deliver(ticket: Self::Ticket) -> result::Result<XcmHash, SendError>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl SendXcm for Tuple {
for_tuples! { type Ticket = (#( Option<Tuple::Ticket> ),* ); }
fn validate(
destination: &mut Option<MultiLocation>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
let mut maybe_cost: Option<MultiAssets> = None;
let one_ticket: Self::Ticket = (for_tuples! { #(
if maybe_cost.is_some() {
None
} else {
match Tuple::validate(destination, message) {
Err(SendError::NotApplicable) => None,
Err(e) => { return Err(e) },
Ok((v, c)) => {
maybe_cost = Some(c);
Some(v)
},
}
}
),* });
if let Some(cost) = maybe_cost {
Ok((one_ticket, cost))
} else {
Err(SendError::NotApplicable)
}
}
fn deliver(one_ticket: Self::Ticket) -> result::Result<XcmHash, SendError> {
for_tuples!( #(
if let Some(validated) = one_ticket.Tuple {
return Tuple::deliver(validated);
}
)* );
Err(SendError::Unroutable)
}
}
/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
/// both in `Some` before passing them as mutable references into `T::send_xcm`.
pub fn validate_send<T: SendXcm>(dest: MultiLocation, msg: Xcm<()>) -> SendResult<T::Ticket> {
T::validate(&mut Some(dest), &mut Some(msg))
}
/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
/// both in `Some` before passing them as mutable references into `T::send_xcm`.
///
/// Returns either `Ok` with the price of the delivery, or `Err` with the reason why the message
/// could not be sent.
///
/// Generally you'll want to validate and get the price first to ensure that the sender can pay it
/// before actually doing the delivery.
pub fn send_xcm<T: SendXcm>(
dest: MultiLocation,
msg: Xcm<()>,
) -> result::Result<(XcmHash, MultiAssets), SendError> {
let (ticket, price) = T::validate(&mut Some(dest), &mut Some(msg))?;
let hash = T::deliver(ticket)?;
Ok((hash, price))
}
File diff suppressed because it is too large Load Diff
+363
View File
@@ -0,0 +1,363 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! Support data structures for `Location`, primarily the `Junction` datatype.
use super::Location;
pub use crate::v3::{BodyId, BodyPart};
use crate::{
v3::{Junction as OldJunction, NetworkId as OldNetworkId},
v5::{Junction as NewJunction, NetworkId as NewNetworkId},
VersionedLocation,
};
use bounded_collections::{BoundedSlice, BoundedVec, ConstU32};
use codec::{self, Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
/// A single item in a path to describe the relative location of a consensus system.
///
/// Each item assumes a pre-existing location as its context and is defined in terms of it.
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
pub enum Junction {
/// An indexed teyrchain belonging to and operated by the context.
///
/// Generally used when the context is a Pezkuwi Relay-chain.
Teyrchain(#[codec(compact)] u32),
/// A 32-byte identifier for an account of a specific network that is respected as a sovereign
/// endpoint within the context.
///
/// Generally used when the context is a Substrate-based chain.
AccountId32 { network: Option<NetworkId>, id: [u8; 32] },
/// An 8-byte index for an account of a specific network that is respected as a sovereign
/// endpoint within the context.
///
/// May be used when the context is a Frame-based chain and includes e.g. an indices pallet.
AccountIndex64 {
network: Option<NetworkId>,
#[codec(compact)]
index: u64,
},
/// A 20-byte identifier for an account of a specific network that is respected as a sovereign
/// endpoint within the context.
///
/// May be used when the context is an Ethereum or Bitcoin chain or smart-contract.
AccountKey20 { network: Option<NetworkId>, key: [u8; 20] },
/// An instanced, indexed pallet that forms a constituent part of the context.
///
/// Generally used when the context is a Frame-based chain.
PalletInstance(u8),
/// A non-descript index within the context location.
///
/// Usage will vary widely owing to its generality.
///
/// NOTE: Try to avoid using this and instead use a more specific item.
GeneralIndex(#[codec(compact)] u128),
/// A nondescript array datum, 32 bytes, acting as a key within the context
/// location.
///
/// Usage will vary widely owing to its generality.
///
/// NOTE: Try to avoid using this and instead use a more specific item.
// Note this is implemented as an array with a length rather than using `BoundedVec` owing to
// the bound for `Copy`.
GeneralKey { length: u8, data: [u8; 32] },
/// The unambiguous child.
///
/// Not currently used except as a fallback when deriving context.
OnlyChild,
/// A pluralistic body existing within consensus.
///
/// Typical to be used to represent a governance origin of a chain, but could in principle be
/// used to represent things such as multisigs also.
Plurality { id: BodyId, part: BodyPart },
/// A global network capable of externalizing its own consensus. This is not generally
/// meaningful outside of the universal level.
GlobalConsensus(NetworkId),
}
impl From<NewNetworkId> for Option<NetworkId> {
fn from(new: NewNetworkId) -> Self {
Some(NetworkId::from(new))
}
}
impl From<NewNetworkId> for NetworkId {
fn from(new: NewNetworkId) -> Self {
use NewNetworkId::*;
match new {
ByGenesis(hash) => Self::ByGenesis(hash),
ByFork { block_number, block_hash } => Self::ByFork { block_number, block_hash },
Pezkuwi => Self::Pezkuwi,
Kusama => Self::Kusama,
Ethereum { chain_id } => Self::Ethereum { chain_id },
BitcoinCore => Self::BitcoinCore,
BitcoinCash => Self::BitcoinCash,
PezkuwiBulletin => Self::PezkuwiBulletin,
}
}
}
/// A global identifier of a data structure existing within consensus.
///
/// Maintenance note: Networks with global consensus and which are practically bridgeable within the
/// Pezkuwi ecosystem are given preference over explicit naming in this enumeration.
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
pub enum NetworkId {
/// Network specified by the first 32 bytes of its genesis block.
ByGenesis([u8; 32]),
/// Network defined by the first 32-bytes of the hash and number of some block it contains.
ByFork { block_number: u64, block_hash: [u8; 32] },
/// The Pezkuwi mainnet Relay-chain.
Pezkuwi,
/// The Kusama canary-net Relay-chain.
Kusama,
/// The Zagros testnet Relay-chain.
Zagros,
/// The Pezkuwichain testnet Relay-chain.
Pezkuwichain,
/// The Wococo testnet Relay-chain.
Wococo,
/// An Ethereum network specified by its chain ID.
Ethereum {
/// The EIP-155 chain ID.
#[codec(compact)]
chain_id: u64,
},
/// The Bitcoin network, including hard-forks supported by Bitcoin Core development team.
BitcoinCore,
/// The Bitcoin network, including hard-forks supported by Bitcoin Cash developers.
BitcoinCash,
/// The Pezkuwi Bulletin chain.
PezkuwiBulletin,
}
impl From<OldNetworkId> for Option<NetworkId> {
fn from(old: OldNetworkId) -> Self {
Some(NetworkId::from(old))
}
}
impl From<OldNetworkId> for NetworkId {
fn from(old: OldNetworkId) -> Self {
use OldNetworkId::*;
match old {
ByGenesis(hash) => Self::ByGenesis(hash),
ByFork { block_number, block_hash } => Self::ByFork { block_number, block_hash },
Pezkuwi => Self::Pezkuwi,
Kusama => Self::Kusama,
Zagros => Self::Zagros,
Pezkuwichain => Self::Pezkuwichain,
Wococo => Self::Wococo,
Ethereum { chain_id } => Self::Ethereum { chain_id },
BitcoinCore => Self::BitcoinCore,
BitcoinCash => Self::BitcoinCash,
PezkuwiBulletin => Self::PezkuwiBulletin,
}
}
}
impl From<NetworkId> for Junction {
fn from(n: NetworkId) -> Self {
Self::GlobalConsensus(n)
}
}
impl From<[u8; 32]> for Junction {
fn from(id: [u8; 32]) -> Self {
Self::AccountId32 { network: None, id }
}
}
impl From<BoundedVec<u8, ConstU32<32>>> for Junction {
fn from(key: BoundedVec<u8, ConstU32<32>>) -> Self {
key.as_bounded_slice().into()
}
}
impl<'a> From<BoundedSlice<'a, u8, ConstU32<32>>> for Junction {
fn from(key: BoundedSlice<'a, u8, ConstU32<32>>) -> Self {
let mut data = [0u8; 32];
data[..key.len()].copy_from_slice(&key[..]);
Self::GeneralKey { length: key.len() as u8, data }
}
}
impl<'a> TryFrom<&'a Junction> for BoundedSlice<'a, u8, ConstU32<32>> {
type Error = ();
fn try_from(key: &'a Junction) -> Result<Self, ()> {
match key {
Junction::GeneralKey { length, data } =>
BoundedSlice::try_from(&data[..data.len().min(*length as usize)]).map_err(|_| ()),
_ => Err(()),
}
}
}
impl From<[u8; 20]> for Junction {
fn from(key: [u8; 20]) -> Self {
Self::AccountKey20 { network: None, key }
}
}
impl From<u64> for Junction {
fn from(index: u64) -> Self {
Self::AccountIndex64 { network: None, index }
}
}
impl From<u128> for Junction {
fn from(id: u128) -> Self {
Self::GeneralIndex(id)
}
}
impl TryFrom<OldJunction> for Junction {
type Error = ();
fn try_from(value: OldJunction) -> Result<Self, ()> {
use OldJunction::*;
Ok(match value {
Teyrchain(id) => Self::Teyrchain(id),
AccountId32 { network: maybe_network, id } =>
Self::AccountId32 { network: maybe_network.map(|network| network.into()), id },
AccountIndex64 { network: maybe_network, index } =>
Self::AccountIndex64 { network: maybe_network.map(|network| network.into()), index },
AccountKey20 { network: maybe_network, key } =>
Self::AccountKey20 { network: maybe_network.map(|network| network.into()), key },
PalletInstance(index) => Self::PalletInstance(index),
GeneralIndex(id) => Self::GeneralIndex(id),
GeneralKey { length, data } => Self::GeneralKey { length, data },
OnlyChild => Self::OnlyChild,
Plurality { id, part } => Self::Plurality { id, part },
GlobalConsensus(network) => Self::GlobalConsensus(network.into()),
})
}
}
impl TryFrom<NewJunction> for Junction {
type Error = ();
fn try_from(value: NewJunction) -> Result<Self, Self::Error> {
use NewJunction::*;
Ok(match value {
Teyrchain(id) => Self::Teyrchain(id),
AccountId32 { network: maybe_network, id } =>
Self::AccountId32 { network: maybe_network.map(|network| network.into()), id },
AccountIndex64 { network: maybe_network, index } =>
Self::AccountIndex64 { network: maybe_network.map(|network| network.into()), index },
AccountKey20 { network: maybe_network, key } =>
Self::AccountKey20 { network: maybe_network.map(|network| network.into()), key },
PalletInstance(index) => Self::PalletInstance(index),
GeneralIndex(id) => Self::GeneralIndex(id),
GeneralKey { length, data } => Self::GeneralKey { length, data },
OnlyChild => Self::OnlyChild,
Plurality { id, part } => Self::Plurality { id, part },
GlobalConsensus(network) => Self::GlobalConsensus(network.into()),
})
}
}
impl Junction {
/// Convert `self` into a `Location` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub fn into_location(self) -> Location {
Location::new(0, [self])
}
/// Convert `self` into a `Location` containing `n` parents.
///
/// Similar to `Self::into_location`, with the added ability to specify the number of parent
/// junctions.
pub fn into_exterior(self, n: u8) -> Location {
Location::new(n, [self])
}
/// Convert `self` into a `VersionedLocation` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub fn into_versioned(self) -> VersionedLocation {
self.into_location().into_versioned()
}
/// Remove the `NetworkId` value.
pub fn remove_network_id(&mut self) {
use Junction::*;
match self {
AccountId32 { ref mut network, .. } |
AccountIndex64 { ref mut network, .. } |
AccountKey20 { ref mut network, .. } => *network = None,
_ => {},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn junction_round_trip_works() {
let j = Junction::GeneralKey { length: 32, data: [1u8; 32] };
let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap();
assert_eq!(j, k);
let j = OldJunction::GeneralKey { length: 32, data: [1u8; 32] };
let k = OldJunction::try_from(Junction::try_from(j).unwrap()).unwrap();
assert_eq!(j, k);
let j = Junction::from(BoundedVec::try_from(vec![1u8, 2, 3, 4]).unwrap());
let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap();
assert_eq!(j, k);
let s: BoundedSlice<_, _> = (&k).try_into().unwrap();
assert_eq!(s, &[1u8, 2, 3, 4][..]);
let j = OldJunction::GeneralKey { length: 32, data: [1u8; 32] };
let k = OldJunction::try_from(Junction::try_from(j).unwrap()).unwrap();
assert_eq!(j, k);
}
}
+724
View File
@@ -0,0 +1,724 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! XCM `Junctions`/`InteriorLocation` datatype.
use super::{Junction, Location, NetworkId};
use alloc::sync::Arc;
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use core::{mem, ops::Range, result};
use scale_info::TypeInfo;
/// Maximum number of `Junction`s that a `Junctions` can contain.
pub(crate) const MAX_JUNCTIONS: usize = 8;
/// Non-parent junctions that can be constructed, up to the length of 8. This specific `Junctions`
/// implementation uses a Rust `enum` in order to make pattern matching easier.
///
/// Parent junctions cannot be constructed with this type. Refer to `Location` for
/// instructions on constructing parent junctions.
#[derive(
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
serde::Serialize,
serde::Deserialize,
)]
pub enum Junctions {
/// The interpreting consensus system.
Here,
/// A relative path comprising 1 junction.
X1(Arc<[Junction; 1]>),
/// A relative path comprising 2 junctions.
X2(Arc<[Junction; 2]>),
/// A relative path comprising 3 junctions.
X3(Arc<[Junction; 3]>),
/// A relative path comprising 4 junctions.
X4(Arc<[Junction; 4]>),
/// A relative path comprising 5 junctions.
X5(Arc<[Junction; 5]>),
/// A relative path comprising 6 junctions.
X6(Arc<[Junction; 6]>),
/// A relative path comprising 7 junctions.
X7(Arc<[Junction; 7]>),
/// A relative path comprising 8 junctions.
X8(Arc<[Junction; 8]>),
}
macro_rules! impl_junctions {
($count:expr, $variant:ident) => {
impl From<[Junction; $count]> for Junctions {
fn from(junctions: [Junction; $count]) -> Self {
Self::$variant(Arc::new(junctions))
}
}
impl PartialEq<[Junction; $count]> for Junctions {
fn eq(&self, rhs: &[Junction; $count]) -> bool {
self.as_slice() == rhs
}
}
};
}
impl_junctions!(1, X1);
impl_junctions!(2, X2);
impl_junctions!(3, X3);
impl_junctions!(4, X4);
impl_junctions!(5, X5);
impl_junctions!(6, X6);
impl_junctions!(7, X7);
impl_junctions!(8, X8);
pub struct JunctionsIterator {
junctions: Junctions,
range: Range<usize>,
}
impl Iterator for JunctionsIterator {
type Item = Junction;
fn next(&mut self) -> Option<Junction> {
self.junctions.at(self.range.next()?).cloned()
}
}
impl DoubleEndedIterator for JunctionsIterator {
fn next_back(&mut self) -> Option<Junction> {
self.junctions.at(self.range.next_back()?).cloned()
}
}
pub struct JunctionsRefIterator<'a> {
junctions: &'a Junctions,
range: Range<usize>,
}
impl<'a> Iterator for JunctionsRefIterator<'a> {
type Item = &'a Junction;
fn next(&mut self) -> Option<&'a Junction> {
self.junctions.at(self.range.next()?)
}
}
impl<'a> DoubleEndedIterator for JunctionsRefIterator<'a> {
fn next_back(&mut self) -> Option<&'a Junction> {
self.junctions.at(self.range.next_back()?)
}
}
impl<'a> IntoIterator for &'a Junctions {
type Item = &'a Junction;
type IntoIter = JunctionsRefIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
JunctionsRefIterator { junctions: self, range: 0..self.len() }
}
}
impl IntoIterator for Junctions {
type Item = Junction;
type IntoIter = JunctionsIterator;
fn into_iter(self) -> Self::IntoIter {
JunctionsIterator { range: 0..self.len(), junctions: self }
}
}
impl Junctions {
/// Convert `self` into a `Location` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub const fn into_location(self) -> Location {
Location { parents: 0, interior: self }
}
/// Convert `self` into a `Location` containing `n` parents.
///
/// Similar to `Self::into_location`, with the added ability to specify the number of parent
/// junctions.
pub const fn into_exterior(self, n: u8) -> Location {
Location { parents: n, interior: self }
}
/// Casts `self` into a slice containing `Junction`s.
pub fn as_slice(&self) -> &[Junction] {
match self {
Junctions::Here => &[],
Junctions::X1(ref a) => &a[..],
Junctions::X2(ref a) => &a[..],
Junctions::X3(ref a) => &a[..],
Junctions::X4(ref a) => &a[..],
Junctions::X5(ref a) => &a[..],
Junctions::X6(ref a) => &a[..],
Junctions::X7(ref a) => &a[..],
Junctions::X8(ref a) => &a[..],
}
}
/// Casts `self` into a mutable slice containing `Junction`s.
pub fn as_slice_mut(&mut self) -> &mut [Junction] {
match self {
Junctions::Here => &mut [],
Junctions::X1(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X2(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X3(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X4(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X5(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X6(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X7(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X8(ref mut a) => &mut Arc::make_mut(a)[..],
}
}
/// Remove the `NetworkId` value in any `Junction`s.
pub fn remove_network_id(&mut self) {
self.for_each_mut(Junction::remove_network_id);
}
/// Treating `self` as the universal context, return the location of the local consensus system
/// from the point of view of the given `target`.
pub fn invert_target(&self, target: &Location) -> Result<Location, ()> {
let mut itself = self.clone();
let mut junctions = Self::Here;
for _ in 0..target.parent_count() {
junctions = junctions
.pushed_front_with(itself.take_last().unwrap_or(Junction::OnlyChild))
.map_err(|_| ())?;
}
let parents = target.interior().len() as u8;
Ok(Location::new(parents, junctions))
}
/// Execute a function `f` on every junction. We use this since we cannot implement a mutable
/// `Iterator` without unsafe code.
pub fn for_each_mut(&mut self, x: impl FnMut(&mut Junction)) {
self.as_slice_mut().iter_mut().for_each(x)
}
/// Extract the network ID treating this value as a universal location.
///
/// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate
/// that this value is not a universal location.
pub fn global_consensus(&self) -> Result<NetworkId, ()> {
if let Some(Junction::GlobalConsensus(network)) = self.first() {
Ok(*network)
} else {
Err(())
}
}
/// Extract the network ID and the interior consensus location, treating this value as a
/// universal location.
///
/// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate
/// that this value is not a universal location.
pub fn split_global(self) -> Result<(NetworkId, Junctions), ()> {
match self.split_first() {
(location, Some(Junction::GlobalConsensus(network))) => Ok((network, location)),
_ => return Err(()),
}
}
/// Treat `self` as a universal location and the context of `relative`, returning the universal
/// location of relative.
///
/// This will return an error if `relative` has as many (or more) parents than there are
/// junctions in `self`, implying that relative refers into a different global consensus.
pub fn within_global(mut self, relative: Location) -> Result<Self, ()> {
if self.len() <= relative.parent_count() as usize {
return Err(());
}
for _ in 0..relative.parent_count() {
self.take_last();
}
for j in relative.interior() {
self.push(*j).map_err(|_| ())?;
}
Ok(self)
}
/// Consumes `self` and returns how `viewer` would address it locally.
pub fn relative_to(mut self, viewer: &Junctions) -> Location {
let mut i = 0;
while match (self.first(), viewer.at(i)) {
(Some(x), Some(y)) => x == y,
_ => false,
} {
self = self.split_first().0;
// NOTE: Cannot overflow as loop can only iterate at most `MAX_JUNCTIONS` times.
i += 1;
}
// AUDIT NOTES:
// - above loop ensures that `i <= viewer.len()`.
// - `viewer.len()` is at most `MAX_JUNCTIONS`, so won't overflow a `u8`.
Location::new((viewer.len() - i) as u8, self)
}
/// Returns first junction, or `None` if the location is empty.
pub fn first(&self) -> Option<&Junction> {
self.as_slice().first()
}
/// Returns last junction, or `None` if the location is empty.
pub fn last(&self) -> Option<&Junction> {
self.as_slice().last()
}
/// Splits off the first junction, returning the remaining suffix (first item in tuple) and the
/// first element (second item in tuple) or `None` if it was empty.
pub fn split_first(self) -> (Junctions, Option<Junction>) {
match self {
Junctions::Here => (Junctions::Here, None),
Junctions::X1(xs) => {
let [a] = *xs;
(Junctions::Here, Some(a))
},
Junctions::X2(xs) => {
let [a, b] = *xs;
([b].into(), Some(a))
},
Junctions::X3(xs) => {
let [a, b, c] = *xs;
([b, c].into(), Some(a))
},
Junctions::X4(xs) => {
let [a, b, c, d] = *xs;
([b, c, d].into(), Some(a))
},
Junctions::X5(xs) => {
let [a, b, c, d, e] = *xs;
([b, c, d, e].into(), Some(a))
},
Junctions::X6(xs) => {
let [a, b, c, d, e, f] = *xs;
([b, c, d, e, f].into(), Some(a))
},
Junctions::X7(xs) => {
let [a, b, c, d, e, f, g] = *xs;
([b, c, d, e, f, g].into(), Some(a))
},
Junctions::X8(xs) => {
let [a, b, c, d, e, f, g, h] = *xs;
([b, c, d, e, f, g, h].into(), Some(a))
},
}
}
/// Splits off the last junction, returning the remaining prefix (first item in tuple) and the
/// last element (second item in tuple) or `None` if it was empty.
pub fn split_last(self) -> (Junctions, Option<Junction>) {
match self {
Junctions::Here => (Junctions::Here, None),
Junctions::X1(xs) => {
let [a] = *xs;
(Junctions::Here, Some(a))
},
Junctions::X2(xs) => {
let [a, b] = *xs;
([a].into(), Some(b))
},
Junctions::X3(xs) => {
let [a, b, c] = *xs;
([a, b].into(), Some(c))
},
Junctions::X4(xs) => {
let [a, b, c, d] = *xs;
([a, b, c].into(), Some(d))
},
Junctions::X5(xs) => {
let [a, b, c, d, e] = *xs;
([a, b, c, d].into(), Some(e))
},
Junctions::X6(xs) => {
let [a, b, c, d, e, f] = *xs;
([a, b, c, d, e].into(), Some(f))
},
Junctions::X7(xs) => {
let [a, b, c, d, e, f, g] = *xs;
([a, b, c, d, e, f].into(), Some(g))
},
Junctions::X8(xs) => {
let [a, b, c, d, e, f, g, h] = *xs;
([a, b, c, d, e, f, g].into(), Some(h))
},
}
}
/// Removes the first element from `self`, returning it (or `None` if it was empty).
pub fn take_first(&mut self) -> Option<Junction> {
let mut d = Junctions::Here;
mem::swap(&mut *self, &mut d);
let (tail, head) = d.split_first();
*self = tail;
head
}
/// Removes the last element from `self`, returning it (or `None` if it was empty).
pub fn take_last(&mut self) -> Option<Junction> {
let mut d = Junctions::Here;
mem::swap(&mut *self, &mut d);
let (head, tail) = d.split_last();
*self = head;
tail
}
/// Mutates `self` to be appended with `new` or returns an `Err` with `new` if would overflow.
pub fn push(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
let new = new.into();
let mut dummy = Junctions::Here;
mem::swap(self, &mut dummy);
match dummy.pushed_with(new) {
Ok(s) => {
*self = s;
Ok(())
},
Err((s, j)) => {
*self = s;
Err(j)
},
}
}
/// Mutates `self` to be prepended with `new` or returns an `Err` with `new` if would overflow.
pub fn push_front(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
let new = new.into();
let mut dummy = Junctions::Here;
mem::swap(self, &mut dummy);
match dummy.pushed_front_with(new) {
Ok(s) => {
*self = s;
Ok(())
},
Err((s, j)) => {
*self = s;
Err(j)
},
}
}
/// Consumes `self` and returns a `Junctions` suffixed with `new`, or an `Err` with the
/// original value of `self` and `new` in case of overflow.
pub fn pushed_with(self, new: impl Into<Junction>) -> result::Result<Self, (Self, Junction)> {
let new = new.into();
Ok(match self {
Junctions::Here => [new].into(),
Junctions::X1(xs) => {
let [a] = *xs;
[a, new].into()
},
Junctions::X2(xs) => {
let [a, b] = *xs;
[a, b, new].into()
},
Junctions::X3(xs) => {
let [a, b, c] = *xs;
[a, b, c, new].into()
},
Junctions::X4(xs) => {
let [a, b, c, d] = *xs;
[a, b, c, d, new].into()
},
Junctions::X5(xs) => {
let [a, b, c, d, e] = *xs;
[a, b, c, d, e, new].into()
},
Junctions::X6(xs) => {
let [a, b, c, d, e, f] = *xs;
[a, b, c, d, e, f, new].into()
},
Junctions::X7(xs) => {
let [a, b, c, d, e, f, g] = *xs;
[a, b, c, d, e, f, g, new].into()
},
s => Err((s, new))?,
})
}
/// Consumes `self` and returns a `Junctions` prefixed with `new`, or an `Err` with the
/// original value of `self` and `new` in case of overflow.
pub fn pushed_front_with(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
let new = new.into();
Ok(match self {
Junctions::Here => [new].into(),
Junctions::X1(xs) => {
let [a] = *xs;
[new, a].into()
},
Junctions::X2(xs) => {
let [a, b] = *xs;
[new, a, b].into()
},
Junctions::X3(xs) => {
let [a, b, c] = *xs;
[new, a, b, c].into()
},
Junctions::X4(xs) => {
let [a, b, c, d] = *xs;
[new, a, b, c, d].into()
},
Junctions::X5(xs) => {
let [a, b, c, d, e] = *xs;
[new, a, b, c, d, e].into()
},
Junctions::X6(xs) => {
let [a, b, c, d, e, f] = *xs;
[new, a, b, c, d, e, f].into()
},
Junctions::X7(xs) => {
let [a, b, c, d, e, f, g] = *xs;
[new, a, b, c, d, e, f, g].into()
},
s => Err((s, new))?,
})
}
/// Mutate `self` so that it is suffixed with `suffix`.
///
/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v4::{Junctions, Junction::*, Location};
/// # fn main() {
/// let mut m = Junctions::from([Teyrchain(21)]);
/// assert_eq!(m.append_with([PalletInstance(3)]), Ok(()));
/// assert_eq!(m, [Teyrchain(21), PalletInstance(3)]);
/// # }
/// ```
pub fn append_with(&mut self, suffix: impl Into<Junctions>) -> Result<(), Junctions> {
let suffix = suffix.into();
if self.len().saturating_add(suffix.len()) > MAX_JUNCTIONS {
return Err(suffix);
}
for j in suffix.into_iter() {
self.push(j).expect("Already checked the sum of the len()s; qed")
}
Ok(())
}
/// Returns the number of junctions in `self`.
pub fn len(&self) -> usize {
self.as_slice().len()
}
/// Returns the junction at index `i`, or `None` if the location doesn't contain that many
/// elements.
pub fn at(&self, i: usize) -> Option<&Junction> {
self.as_slice().get(i)
}
/// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't
/// contain that many elements.
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
self.as_slice_mut().get_mut(i)
}
/// Returns a reference iterator over the junctions.
pub fn iter(&self) -> JunctionsRefIterator<'_> {
JunctionsRefIterator { junctions: self, range: 0..self.len() }
}
/// Ensures that self begins with `prefix` and that it has a single `Junction` item following.
/// If so, returns a reference to this `Junction` item.
///
/// # Example
/// ```rust
/// # use staging_xcm::v4::{Junctions, Junction::*};
/// # fn main() {
/// let mut m = Junctions::from([Teyrchain(2), PalletInstance(3), OnlyChild]);
/// assert_eq!(m.match_and_split(&[Teyrchain(2), PalletInstance(3)].into()), Some(&OnlyChild));
/// assert_eq!(m.match_and_split(&[Teyrchain(2)].into()), None);
/// # }
/// ```
pub fn match_and_split(&self, prefix: &Junctions) -> Option<&Junction> {
if prefix.len() + 1 != self.len() {
return None;
}
for i in 0..prefix.len() {
if prefix.at(i) != self.at(i) {
return None;
}
}
return self.at(prefix.len());
}
pub fn starts_with(&self, prefix: &Junctions) -> bool {
prefix.len() <= self.len() && prefix.iter().zip(self.iter()).all(|(x, y)| x == y)
}
}
impl TryFrom<Location> for Junctions {
type Error = Location;
fn try_from(x: Location) -> result::Result<Self, Location> {
if x.parent_count() > 0 {
Err(x)
} else {
Ok(x.interior().clone())
}
}
}
impl<T: Into<Junction>> From<T> for Junctions {
fn from(x: T) -> Self {
[x.into()].into()
}
}
impl From<[Junction; 0]> for Junctions {
fn from(_: [Junction; 0]) -> Self {
Self::Here
}
}
impl From<()> for Junctions {
fn from(_: ()) -> Self {
Self::Here
}
}
xcm_procedural::impl_conversion_functions_for_junctions_v4!();
#[cfg(test)]
mod tests {
use super::{super::prelude::*, *};
#[test]
fn inverting_works() {
let context: InteriorLocation = (Teyrchain(1000), PalletInstance(42)).into();
let target = (Parent, PalletInstance(69)).into();
let expected = (Parent, PalletInstance(42)).into();
let inverted = context.invert_target(&target).unwrap();
assert_eq!(inverted, expected);
let context: InteriorLocation =
(Teyrchain(1000), PalletInstance(42), GeneralIndex(1)).into();
let target = (Parent, Parent, PalletInstance(69), GeneralIndex(2)).into();
let expected = (Parent, Parent, PalletInstance(42), GeneralIndex(1)).into();
let inverted = context.invert_target(&target).unwrap();
assert_eq!(inverted, expected);
}
#[test]
fn relative_to_works() {
use NetworkId::*;
assert_eq!(
Junctions::from([Pezkuwi.into()]).relative_to(&Junctions::from([Kusama.into()])),
(Parent, Pezkuwi).into()
);
let base = Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(1)]);
// Ancestors.
assert_eq!(Here.relative_to(&base), (Parent, Parent, Parent).into());
assert_eq!(Junctions::from([Kusama.into()]).relative_to(&base), (Parent, Parent).into());
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(1)]).relative_to(&base),
(Parent,).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(1)]).relative_to(&base),
Here.into()
);
// Ancestors with one child.
assert_eq!(
Junctions::from([Pezkuwi.into()]).relative_to(&base),
(Parent, Parent, Parent, Pezkuwi).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(2)]).relative_to(&base),
(Parent, Parent, Teyrchain(2)).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(2)]).relative_to(&base),
(Parent, PalletInstance(2)).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(1), [1u8; 32].into()])
.relative_to(&base),
([1u8; 32],).into()
);
// Ancestors with grandchildren.
assert_eq!(
Junctions::from([Pezkuwi.into(), Teyrchain(1)]).relative_to(&base),
(Parent, Parent, Parent, Pezkuwi, Teyrchain(1)).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(2), PalletInstance(1)]).relative_to(&base),
(Parent, Parent, Teyrchain(2), PalletInstance(1)).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(2), [1u8; 32].into()])
.relative_to(&base),
(Parent, PalletInstance(2), [1u8; 32]).into()
);
assert_eq!(
Junctions::from([
Kusama.into(),
Teyrchain(1),
PalletInstance(1),
[1u8; 32].into(),
1u128.into()
])
.relative_to(&base),
([1u8; 32], 1u128).into()
);
}
#[test]
fn global_consensus_works() {
use NetworkId::*;
assert_eq!(Junctions::from([Pezkuwi.into()]).global_consensus(), Ok(Pezkuwi));
assert_eq!(Junctions::from([Kusama.into(), 1u64.into()]).global_consensus(), Ok(Kusama));
assert_eq!(Here.global_consensus(), Err(()));
assert_eq!(Junctions::from([1u64.into()]).global_consensus(), Err(()));
assert_eq!(Junctions::from([1u64.into(), Kusama.into()]).global_consensus(), Err(()));
}
#[test]
fn test_conversion() {
use super::{Junction::*, NetworkId::*};
let x: Junctions = GlobalConsensus(Pezkuwi).into();
assert_eq!(x, Junctions::from([GlobalConsensus(Pezkuwi)]));
let x: Junctions = Pezkuwi.into();
assert_eq!(x, Junctions::from([GlobalConsensus(Pezkuwi)]));
let x: Junctions = (Pezkuwi, Kusama).into();
assert_eq!(x, Junctions::from([GlobalConsensus(Pezkuwi), GlobalConsensus(Kusama)]));
}
#[test]
fn encode_decode_junctions_works() {
let original = Junctions::from([
Pezkuwi.into(),
Kusama.into(),
1u64.into(),
GlobalConsensus(Pezkuwi),
Teyrchain(123),
PalletInstance(45),
]);
let encoded = original.encode();
assert_eq!(encoded, &[6, 9, 2, 9, 3, 2, 0, 4, 9, 2, 0, 237, 1, 4, 45]);
let decoded = Junctions::decode(&mut &encoded[..]).unwrap();
assert_eq!(decoded, original);
}
}
+770
View File
@@ -0,0 +1,770 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! XCM `Location` datatype.
use super::{traits::Reanchorable, Junction, Junctions};
use crate::{v3::MultiLocation as OldLocation, v5::Location as NewLocation, VersionedLocation};
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use core::result;
use scale_info::TypeInfo;
/// A relative path between state-bearing consensus systems.
///
/// A location in a consensus system is defined as an *isolatable state machine* held within global
/// consensus. The location in question need not have a sophisticated consensus algorithm of its
/// own; a single account within Ethereum, for example, could be considered a location.
///
/// A very-much non-exhaustive list of types of location include:
/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a teyrchain.
/// - A layer-0 super-chain, e.g. the Pezkuwi Relay chain.
/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum.
/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based
/// Substrate chain.
/// - An account.
///
/// A `Location` is a *relative identifier*, meaning that it can only be used to define the
/// relative path between two locations, and cannot generally be used to refer to a location
/// universally. It is comprised of an integer number of parents specifying the number of times to
/// "escape" upwards into the containing consensus system and then a number of *junctions*, each
/// diving down and specifying some interior portion of state (which may be considered a
/// "sub-consensus" system).
///
/// This specific `Location` implementation uses a `Junctions` datatype which is a Rust `enum`
/// in order to make pattern matching easier. There are occasions where it is important to ensure
/// that a value is strictly an interior location, in those cases, `Junctions` may be used.
///
/// The `Location` value of `Null` simply refers to the interpreting consensus system.
#[derive(
Clone,
Decode,
DecodeWithMemTracking,
Encode,
Eq,
PartialEq,
Ord,
PartialOrd,
Debug,
TypeInfo,
MaxEncodedLen,
serde::Serialize,
serde::Deserialize,
)]
pub struct Location {
/// The number of parent junctions at the beginning of this `Location`.
pub parents: u8,
/// The interior (i.e. non-parent) junctions that this `Location` contains.
pub interior: Junctions,
}
impl Default for Location {
fn default() -> Self {
Self { parents: 0, interior: Junctions::Here }
}
}
/// A relative location which is constrained to be an interior location of the context.
///
/// See also `Location`.
pub type InteriorLocation = Junctions;
impl Location {
/// Creates a new `Location` with the given number of parents and interior junctions.
pub fn new(parents: u8, interior: impl Into<Junctions>) -> Location {
Location { parents, interior: interior.into() }
}
/// Consume `self` and return the equivalent `VersionedLocation` value.
pub const fn into_versioned(self) -> VersionedLocation {
VersionedLocation::V4(self)
}
/// Creates a new `Location` with 0 parents and a `Here` interior.
///
/// The resulting `Location` can be interpreted as the "current consensus system".
pub const fn here() -> Location {
Location { parents: 0, interior: Junctions::Here }
}
/// Creates a new `Location` which evaluates to the parent context.
pub const fn parent() -> Location {
Location { parents: 1, interior: Junctions::Here }
}
/// Creates a new `Location` with `parents` and an empty (`Here`) interior.
pub const fn ancestor(parents: u8) -> Location {
Location { parents, interior: Junctions::Here }
}
/// Whether the `Location` has no parents and has a `Here` interior.
pub fn is_here(&self) -> bool {
self.parents == 0 && self.interior.len() == 0
}
/// Remove the `NetworkId` value in any interior `Junction`s.
pub fn remove_network_id(&mut self) {
self.interior.remove_network_id();
}
/// Return a reference to the interior field.
pub fn interior(&self) -> &Junctions {
&self.interior
}
/// Return a mutable reference to the interior field.
pub fn interior_mut(&mut self) -> &mut Junctions {
&mut self.interior
}
/// Returns the number of `Parent` junctions at the beginning of `self`.
pub const fn parent_count(&self) -> u8 {
self.parents
}
/// Returns the parent count and the interior [`Junctions`] as a tuple.
///
/// To be used when pattern matching, for example:
///
/// ```rust
/// # use staging_xcm::v4::{Junctions::*, Junction::*, Location};
/// fn get_teyrchain_id(loc: &Location) -> Option<u32> {
/// match loc.unpack() {
/// (0, [Teyrchain(id)]) => Some(*id),
/// _ => None
/// }
/// }
/// ```
pub fn unpack(&self) -> (u8, &[Junction]) {
(self.parents, self.interior.as_slice())
}
/// Returns boolean indicating whether `self` contains only the specified amount of
/// parents and no interior junctions.
pub const fn contains_parents_only(&self, count: u8) -> bool {
matches!(self.interior, Junctions::Here) && self.parents == count
}
/// Returns the number of parents and junctions in `self`.
pub fn len(&self) -> usize {
self.parent_count() as usize + self.interior.len()
}
/// Returns the first interior junction, or `None` if the location is empty or contains only
/// parents.
pub fn first_interior(&self) -> Option<&Junction> {
self.interior.first()
}
/// Returns last junction, or `None` if the location is empty or contains only parents.
pub fn last(&self) -> Option<&Junction> {
self.interior.last()
}
/// Splits off the first interior junction, returning the remaining suffix (first item in tuple)
/// and the first element (second item in tuple) or `None` if it was empty.
pub fn split_first_interior(self) -> (Location, Option<Junction>) {
let Location { parents, interior: junctions } = self;
let (suffix, first) = junctions.split_first();
let location = Location { parents, interior: suffix };
(location, first)
}
/// Splits off the last interior junction, returning the remaining prefix (first item in tuple)
/// and the last element (second item in tuple) or `None` if it was empty or if `self` only
/// contains parents.
pub fn split_last_interior(self) -> (Location, Option<Junction>) {
let Location { parents, interior: junctions } = self;
let (prefix, last) = junctions.split_last();
let location = Location { parents, interior: prefix };
(location, last)
}
/// Mutates `self`, suffixing its interior junctions with `new`. Returns `Err` with `new` in
/// case of overflow.
pub fn push_interior(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
self.interior.push(new)
}
/// Mutates `self`, prefixing its interior junctions with `new`. Returns `Err` with `new` in
/// case of overflow.
pub fn push_front_interior(
&mut self,
new: impl Into<Junction>,
) -> result::Result<(), Junction> {
self.interior.push_front(new)
}
/// Consumes `self` and returns a `Location` suffixed with `new`, or an `Err` with
/// the original value of `self` in case of overflow.
pub fn pushed_with_interior(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
match self.interior.pushed_with(new) {
Ok(i) => Ok(Location { interior: i, parents: self.parents }),
Err((i, j)) => Err((Location { interior: i, parents: self.parents }, j)),
}
}
/// Consumes `self` and returns a `Location` prefixed with `new`, or an `Err` with the
/// original value of `self` in case of overflow.
pub fn pushed_front_with_interior(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
match self.interior.pushed_front_with(new) {
Ok(i) => Ok(Location { interior: i, parents: self.parents }),
Err((i, j)) => Err((Location { interior: i, parents: self.parents }, j)),
}
}
/// Returns the junction at index `i`, or `None` if the location is a parent or if the location
/// does not contain that many elements.
pub fn at(&self, i: usize) -> Option<&Junction> {
let num_parents = self.parents as usize;
if i < num_parents {
return None;
}
self.interior.at(i - num_parents)
}
/// Returns a mutable reference to the junction at index `i`, or `None` if the location is a
/// parent or if it doesn't contain that many elements.
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
let num_parents = self.parents as usize;
if i < num_parents {
return None;
}
self.interior.at_mut(i - num_parents)
}
/// Decrements the parent count by 1.
pub fn dec_parent(&mut self) {
self.parents = self.parents.saturating_sub(1);
}
/// Removes the first interior junction from `self`, returning it
/// (or `None` if it was empty or if `self` contains only parents).
pub fn take_first_interior(&mut self) -> Option<Junction> {
self.interior.take_first()
}
/// Removes the last element from `interior`, returning it (or `None` if it was empty or if
/// `self` only contains parents).
pub fn take_last(&mut self) -> Option<Junction> {
self.interior.take_last()
}
/// Ensures that `self` has the same number of parents as `prefix`, its junctions begins with
/// the junctions of `prefix` and that it has a single `Junction` item following.
/// If so, returns a reference to this `Junction` item.
///
/// # Example
/// ```rust
/// # use staging_xcm::v4::{Junctions::*, Junction::*, Location};
/// # fn main() {
/// let mut m = Location::new(1, [PalletInstance(3), OnlyChild]);
/// assert_eq!(
/// m.match_and_split(&Location::new(1, [PalletInstance(3)])),
/// Some(&OnlyChild),
/// );
/// assert_eq!(m.match_and_split(&Location::new(1, Here)), None);
/// # }
/// ```
pub fn match_and_split(&self, prefix: &Location) -> Option<&Junction> {
if self.parents != prefix.parents {
return None;
}
self.interior.match_and_split(&prefix.interior)
}
pub fn starts_with(&self, prefix: &Location) -> bool {
self.parents == prefix.parents && self.interior.starts_with(&prefix.interior)
}
/// Mutate `self` so that it is suffixed with `suffix`.
///
/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v4::{Junctions::*, Junction::*, Location, Parent};
/// # fn main() {
/// let mut m: Location = (Parent, Teyrchain(21), 69u64).into();
/// assert_eq!(m.append_with((Parent, PalletInstance(3))), Ok(()));
/// assert_eq!(m, Location::new(1, [Teyrchain(21), PalletInstance(3)]));
/// # }
/// ```
pub fn append_with(&mut self, suffix: impl Into<Self>) -> Result<(), Self> {
let prefix = core::mem::replace(self, suffix.into());
match self.prepend_with(prefix) {
Ok(()) => Ok(()),
Err(prefix) => Err(core::mem::replace(self, prefix)),
}
}
/// Consume `self` and return its value suffixed with `suffix`.
///
/// Returns `Err` with the original value of `self` and `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v4::{Junctions::*, Junction::*, Location, Parent};
/// # fn main() {
/// let mut m: Location = (Parent, Teyrchain(21), 69u64).into();
/// let r = m.appended_with((Parent, PalletInstance(3))).unwrap();
/// assert_eq!(r, Location::new(1, [Teyrchain(21), PalletInstance(3)]));
/// # }
/// ```
pub fn appended_with(mut self, suffix: impl Into<Self>) -> Result<Self, (Self, Self)> {
match self.append_with(suffix) {
Ok(()) => Ok(self),
Err(suffix) => Err((self, suffix)),
}
}
/// Mutate `self` so that it is prefixed with `prefix`.
///
/// Does not modify `self` and returns `Err` with `prefix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v4::{Junctions::*, Junction::*, Location, Parent};
/// # fn main() {
/// let mut m: Location = (Parent, Parent, PalletInstance(3)).into();
/// assert_eq!(m.prepend_with((Parent, Teyrchain(21), OnlyChild)), Ok(()));
/// assert_eq!(m, Location::new(1, [PalletInstance(3)]));
/// # }
/// ```
pub fn prepend_with(&mut self, prefix: impl Into<Self>) -> Result<(), Self> {
// prefix self (suffix)
// P .. P I .. I p .. p i .. i
let mut prefix = prefix.into();
let prepend_interior = prefix.interior.len().saturating_sub(self.parents as usize);
let final_interior = self.interior.len().saturating_add(prepend_interior);
if final_interior > super::junctions::MAX_JUNCTIONS {
return Err(prefix);
}
let suffix_parents = (self.parents as usize).saturating_sub(prefix.interior.len());
let final_parents = (prefix.parents as usize).saturating_add(suffix_parents);
if final_parents > 255 {
return Err(prefix);
}
// cancel out the final item on the prefix interior for one of the suffix's parents.
while self.parents > 0 && prefix.take_last().is_some() {
self.dec_parent();
}
// now we have either removed all suffix's parents or prefix interior.
// this means we can combine the prefix's and suffix's remaining parents/interior since
// we know that with at least one empty, the overall order will be respected:
// prefix self (suffix)
// P .. P (I) p .. p i .. i => P + p .. (no I) i
// -- or --
// P .. P I .. I (p) i .. i => P (no p) .. I + i
self.parents = self.parents.saturating_add(prefix.parents);
for j in prefix.interior.into_iter().rev() {
self.push_front_interior(j)
.expect("final_interior no greater than MAX_JUNCTIONS; qed");
}
Ok(())
}
/// Consume `self` and return its value prefixed with `prefix`.
///
/// Returns `Err` with the original value of `self` and `prefix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v4::{Junctions::*, Junction::*, Location, Parent};
/// # fn main() {
/// let m: Location = (Parent, Parent, PalletInstance(3)).into();
/// let r = m.prepended_with((Parent, Teyrchain(21), OnlyChild)).unwrap();
/// assert_eq!(r, Location::new(1, [PalletInstance(3)]));
/// # }
/// ```
pub fn prepended_with(mut self, prefix: impl Into<Self>) -> Result<Self, (Self, Self)> {
match self.prepend_with(prefix) {
Ok(()) => Ok(self),
Err(prefix) => Err((self, prefix)),
}
}
/// Remove any unneeded parents/junctions in `self` based on the given context it will be
/// interpreted in.
pub fn simplify(&mut self, context: &Junctions) {
if context.len() < self.parents as usize {
// Not enough context
return;
}
while self.parents > 0 {
let maybe = context.at(context.len() - (self.parents as usize));
match (self.interior.first(), maybe) {
(Some(i), Some(j)) if i == j => {
self.interior.take_first();
self.parents -= 1;
},
_ => break,
}
}
}
/// Return the Location subsection identifying the chain that `self` points to.
pub fn chain_location(&self) -> Location {
let mut clone = self.clone();
// start popping junctions until we reach chain identifier
while let Some(j) = clone.last() {
if matches!(j, Junction::Teyrchain(_) | Junction::GlobalConsensus(_)) {
// return chain subsection
return clone;
} else {
(clone, _) = clone.split_last_interior();
}
}
Location::new(clone.parents, Junctions::Here)
}
}
impl Reanchorable for Location {
type Error = Self;
/// Mutate `self` so that it represents the same location from the point of view of `target`.
/// The context of `self` is provided as `context`.
///
/// Does not modify `self` in case of overflow.
fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
// TODO: https://github.com/paritytech/polkadot/issues/4489 Optimize this.
// 1. Use our `context` to figure out how the `target` would address us.
let inverted_target = context.invert_target(target)?;
// 2. Prepend `inverted_target` to `self` to get self's location from the perspective of
// `target`.
self.prepend_with(inverted_target).map_err(|_| ())?;
// 3. Given that we know some of `target` context, ensure that any parents in `self` are
// strictly needed.
self.simplify(target.interior());
Ok(())
}
/// Consume `self` and return a new value representing the same location from the point of view
/// of `target`. The context of `self` is provided as `context`.
///
/// Returns the original `self` in case of overflow.
fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result<Self, Self> {
match self.reanchor(target, context) {
Ok(()) => Ok(self),
Err(()) => Err(self),
}
}
}
impl TryFrom<OldLocation> for Option<Location> {
type Error = ();
fn try_from(value: OldLocation) -> result::Result<Self, Self::Error> {
Ok(Some(Location::try_from(value)?))
}
}
impl TryFrom<OldLocation> for Location {
type Error = ();
fn try_from(x: OldLocation) -> result::Result<Self, ()> {
Ok(Location { parents: x.parents, interior: x.interior.try_into()? })
}
}
impl TryFrom<NewLocation> for Option<Location> {
type Error = ();
fn try_from(new: NewLocation) -> result::Result<Self, Self::Error> {
Ok(Some(Location::try_from(new)?))
}
}
impl TryFrom<NewLocation> for Location {
type Error = ();
fn try_from(new: NewLocation) -> result::Result<Self, ()> {
Ok(Location { parents: new.parent_count(), interior: new.interior().clone().try_into()? })
}
}
/// A unit struct which can be converted into a `Location` of `parents` value 1.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Parent;
impl From<Parent> for Location {
fn from(_: Parent) -> Self {
Location { parents: 1, interior: Junctions::Here }
}
}
/// A tuple struct which can be converted into a `Location` of `parents` value 1 with the inner
/// interior.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct ParentThen(pub Junctions);
impl From<ParentThen> for Location {
fn from(ParentThen(interior): ParentThen) -> Self {
Location { parents: 1, interior }
}
}
/// A unit struct which can be converted into a `Location` of the inner `parents` value.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Ancestor(pub u8);
impl From<Ancestor> for Location {
fn from(Ancestor(parents): Ancestor) -> Self {
Location { parents, interior: Junctions::Here }
}
}
/// A unit struct which can be converted into a `Location` of the inner `parents` value and the
/// inner interior.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct AncestorThen<Interior>(pub u8, pub Interior);
impl<Interior: Into<Junctions>> From<AncestorThen<Interior>> for Location {
fn from(AncestorThen(parents, interior): AncestorThen<Interior>) -> Self {
Location { parents, interior: interior.into() }
}
}
impl From<[u8; 32]> for Location {
fn from(bytes: [u8; 32]) -> Self {
let junction: Junction = bytes.into();
junction.into()
}
}
impl From<sp_runtime::AccountId32> for Location {
fn from(id: sp_runtime::AccountId32) -> Self {
Junction::AccountId32 { network: None, id: id.into() }.into()
}
}
xcm_procedural::impl_conversion_functions_for_location_v4!();
#[cfg(test)]
mod tests {
use crate::v4::prelude::*;
use codec::{Decode, Encode};
#[test]
fn conversion_works() {
let x: Location = Parent.into();
assert_eq!(x, Location { parents: 1, interior: Here });
// let x: Location = (Parent,).into();
// assert_eq!(x, Location { parents: 1, interior: Here });
// let x: Location = (Parent, Parent).into();
// assert_eq!(x, Location { parents: 2, interior: Here });
let x: Location = (Parent, Parent, OnlyChild).into();
assert_eq!(x, Location { parents: 2, interior: OnlyChild.into() });
let x: Location = OnlyChild.into();
assert_eq!(x, Location { parents: 0, interior: OnlyChild.into() });
let x: Location = (OnlyChild,).into();
assert_eq!(x, Location { parents: 0, interior: OnlyChild.into() });
}
#[test]
fn simplify_basic_works() {
let mut location: Location =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [Teyrchain(1000), PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location = (Parent, PalletInstance(42), GeneralIndex(69)).into();
let context = [PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location = (Parent, PalletInstance(42), GeneralIndex(69)).into();
let context = [Teyrchain(1000), PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [OnlyChild, Teyrchain(1000), PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
}
#[test]
fn simplify_incompatible_location_fails() {
let mut location: Location =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [Teyrchain(1000), PalletInstance(42), GeneralIndex(42)].into();
let expected =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [Teyrchain(1000)].into();
let expected =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
}
#[test]
fn reanchor_works() {
let mut id: Location = (Parent, Teyrchain(1000), GeneralIndex(42)).into();
let context = Teyrchain(2000).into();
let target = (Parent, Teyrchain(1000)).into();
let expected = GeneralIndex(42).into();
id.reanchor(&target, &context).unwrap();
assert_eq!(id, expected);
}
#[test]
fn encode_and_decode_works() {
let m = Location {
parents: 1,
interior: [Teyrchain(42), AccountIndex64 { network: None, index: 23 }].into(),
};
let encoded = m.encode();
assert_eq!(encoded, [1, 2, 0, 168, 2, 0, 92].to_vec());
let decoded = Location::decode(&mut &encoded[..]);
assert_eq!(decoded, Ok(m));
}
#[test]
fn match_and_split_works() {
let m = Location {
parents: 1,
interior: [Teyrchain(42), AccountIndex64 { network: None, index: 23 }].into(),
};
assert_eq!(m.match_and_split(&Location { parents: 1, interior: Here }), None);
assert_eq!(
m.match_and_split(&Location { parents: 1, interior: [Teyrchain(42)].into() }),
Some(&AccountIndex64 { network: None, index: 23 })
);
assert_eq!(m.match_and_split(&m), None);
}
#[test]
fn append_with_works() {
let acc = AccountIndex64 { network: None, index: 23 };
let mut m = Location { parents: 1, interior: [Teyrchain(42)].into() };
assert_eq!(m.append_with([PalletInstance(3), acc]), Ok(()));
assert_eq!(
m,
Location { parents: 1, interior: [Teyrchain(42), PalletInstance(3), acc].into() }
);
// cannot append to create overly long location
let acc = AccountIndex64 { network: None, index: 23 };
let m = Location {
parents: 254,
interior: [Teyrchain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild].into(),
};
let suffix: Location = (PalletInstance(3), acc, OnlyChild, OnlyChild).into();
assert_eq!(m.clone().append_with(suffix.clone()), Err(suffix));
}
#[test]
fn prepend_with_works() {
let mut m = Location {
parents: 1,
interior: [Teyrchain(42), AccountIndex64 { network: None, index: 23 }].into(),
};
assert_eq!(m.prepend_with(Location { parents: 1, interior: [OnlyChild].into() }), Ok(()));
assert_eq!(
m,
Location {
parents: 1,
interior: [Teyrchain(42), AccountIndex64 { network: None, index: 23 }].into()
}
);
// cannot prepend to create overly long location
let mut m = Location { parents: 254, interior: [Teyrchain(42)].into() };
let prefix = Location { parents: 2, interior: Here };
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
let prefix = Location { parents: 1, interior: Here };
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, Location { parents: 255, interior: [Teyrchain(42)].into() });
}
#[test]
fn double_ended_ref_iteration_works() {
let m: Junctions = [Teyrchain(1000), Teyrchain(3), PalletInstance(5)].into();
let mut iter = m.iter();
let first = iter.next().unwrap();
assert_eq!(first, &Teyrchain(1000));
let third = iter.next_back().unwrap();
assert_eq!(third, &PalletInstance(5));
let second = iter.next_back().unwrap();
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
assert_eq!(second, &Teyrchain(3));
let res = Here
.pushed_with(*first)
.unwrap()
.pushed_with(*second)
.unwrap()
.pushed_with(*third)
.unwrap();
assert_eq!(m, res);
// make sure there's no funny business with the 0 indexing
let m = Here;
let mut iter = m.iter();
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
}
#[test]
fn conversion_from_other_types_works() {
use crate::v3;
fn takes_location<Arg: Into<Location>>(_arg: Arg) {}
takes_location(Parent);
takes_location(Here);
takes_location([Teyrchain(42)]);
takes_location((Ancestor(255), PalletInstance(8)));
takes_location((Ancestor(5), Teyrchain(1), PalletInstance(3)));
takes_location((Ancestor(2), Here));
takes_location(AncestorThen(
3,
[Teyrchain(43), AccountIndex64 { network: None, index: 155 }],
));
takes_location((Parent, AccountId32 { network: None, id: [0; 32] }));
takes_location((Parent, Here));
takes_location(ParentThen([Teyrchain(75)].into()));
takes_location([Teyrchain(100), PalletInstance(3)]);
assert_eq!(v3::Location::from(v3::Junctions::Here).try_into(), Ok(Location::here()));
assert_eq!(v3::Location::from(v3::Parent).try_into(), Ok(Location::parent()));
assert_eq!(
v3::Location::from((v3::Parent, v3::Parent, v3::Junction::GeneralIndex(42u128),))
.try_into(),
Ok(Location { parents: 2, interior: [GeneralIndex(42u128)].into() }),
);
}
}
File diff suppressed because it is too large Load Diff
+312
View File
@@ -0,0 +1,312 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
pub use crate::v3::{Error, Result, SendError, XcmHash};
use codec::{Decode, Encode};
use core::result;
use scale_info::TypeInfo;
pub use sp_weights::Weight;
use super::*;
/// Outcome of an XCM execution.
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
pub enum Outcome {
/// Execution completed successfully; given weight was used.
Complete { used: Weight },
/// Execution started, but did not complete successfully due to the given error; given weight
/// was used.
Incomplete { used: Weight, error: Error },
/// Execution did not start due to the given error.
Error { error: Error },
}
impl Outcome {
pub fn ensure_complete(self) -> Result {
match self {
Outcome::Complete { .. } => Ok(()),
Outcome::Incomplete { error, .. } => Err(error),
Outcome::Error { error, .. } => Err(error),
}
}
pub fn ensure_execution(self) -> result::Result<Weight, Error> {
match self {
Outcome::Complete { used, .. } => Ok(used),
Outcome::Incomplete { used, .. } => Ok(used),
Outcome::Error { error, .. } => Err(error),
}
}
/// How much weight was used by the XCM execution attempt.
pub fn weight_used(&self) -> Weight {
match self {
Outcome::Complete { used, .. } => *used,
Outcome::Incomplete { used, .. } => *used,
Outcome::Error { .. } => Weight::zero(),
}
}
}
impl From<Error> for Outcome {
fn from(error: Error) -> Self {
Self::Error { error }
}
}
pub trait PreparedMessage {
fn weight_of(&self) -> Weight;
}
/// Type of XCM message executor.
pub trait ExecuteXcm<Call> {
type Prepared: PreparedMessage;
fn prepare(message: Xcm<Call>) -> result::Result<Self::Prepared, Xcm<Call>>;
fn execute(
origin: impl Into<Location>,
pre: Self::Prepared,
id: &mut XcmHash,
weight_credit: Weight,
) -> Outcome;
fn prepare_and_execute(
origin: impl Into<Location>,
message: Xcm<Call>,
id: &mut XcmHash,
weight_limit: Weight,
weight_credit: Weight,
) -> Outcome {
let pre = match Self::prepare(message) {
Ok(x) => x,
Err(_) => return Outcome::Error { error: Error::WeightNotComputable },
};
let xcm_weight = pre.weight_of();
if xcm_weight.any_gt(weight_limit) {
return Outcome::Error { error: Error::WeightLimitReached(xcm_weight) };
}
Self::execute(origin, pre, id, weight_credit)
}
/// Deduct some `fees` to the sovereign account of the given `location` and place them as per
/// the convention for fees.
fn charge_fees(location: impl Into<Location>, fees: Assets) -> Result;
}
pub enum Weightless {}
impl PreparedMessage for Weightless {
fn weight_of(&self) -> Weight {
unreachable!()
}
}
impl<C> ExecuteXcm<C> for () {
type Prepared = Weightless;
fn prepare(message: Xcm<C>) -> result::Result<Self::Prepared, Xcm<C>> {
Err(message)
}
fn execute(_: impl Into<Location>, _: Self::Prepared, _: &mut XcmHash, _: Weight) -> Outcome {
unreachable!()
}
fn charge_fees(_location: impl Into<Location>, _fees: Assets) -> Result {
Err(Error::Unimplemented)
}
}
pub trait Reanchorable: Sized {
/// Type to return in case of an error.
type Error: Debug;
/// Mutate `self` so that it represents the same location from the point of view of `target`.
/// The context of `self` is provided as `context`.
///
/// Does not modify `self` in case of overflow.
fn reanchor(
&mut self,
target: &Location,
context: &InteriorLocation,
) -> core::result::Result<(), ()>;
/// Consume `self` and return a new value representing the same location from the point of view
/// of `target`. The context of `self` is provided as `context`.
///
/// Returns the original `self` in case of overflow.
fn reanchored(
self,
target: &Location,
context: &InteriorLocation,
) -> core::result::Result<Self, Self::Error>;
}
/// Result value when attempting to send an XCM message.
pub type SendResult<T> = result::Result<(T, Assets), SendError>;
/// Utility for sending an XCM message to a given location.
///
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each
/// router might return `NotApplicable` to pass the execution to the next sender item. Note that
/// each `NotApplicable` might alter the destination and the XCM message for to the next router.
///
/// # Example
/// ```rust
/// # use codec::Encode;
/// # use staging_xcm::v4::{prelude::*, Weight};
/// # use staging_xcm::VersionedXcm;
/// # use std::convert::Infallible;
///
/// /// A sender that only passes the message through and does nothing.
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// type Ticket = Infallible;
/// fn validate(_: &mut Option<Location>, _: &mut Option<Xcm<()>>) -> SendResult<Infallible> {
/// Err(SendError::NotApplicable)
/// }
/// fn deliver(_: Infallible) -> Result<XcmHash, SendError> {
/// unreachable!()
/// }
/// }
///
/// /// A sender that accepts a message that has two junctions, otherwise stops the routing.
/// struct Sender2;
/// impl SendXcm for Sender2 {
/// type Ticket = ();
/// fn validate(destination: &mut Option<Location>, message: &mut Option<Xcm<()>>) -> SendResult<()> {
/// match destination.as_ref().ok_or(SendError::MissingArgument)?.unpack() {
/// (0, [j1, j2]) => Ok(((), Assets::new())),
/// _ => Err(SendError::Unroutable),
/// }
/// }
/// fn deliver(_: ()) -> Result<XcmHash, SendError> {
/// Ok([0; 32])
/// }
/// }
///
/// /// A sender that accepts a message from a parent, passing through otherwise.
/// struct Sender3;
/// impl SendXcm for Sender3 {
/// type Ticket = ();
/// fn validate(destination: &mut Option<Location>, message: &mut Option<Xcm<()>>) -> SendResult<()> {
/// match destination.as_ref().ok_or(SendError::MissingArgument)?.unpack() {
/// (1, []) => Ok(((), Assets::new())),
/// _ => Err(SendError::NotApplicable),
/// }
/// }
/// fn deliver(_: ()) -> Result<XcmHash, SendError> {
/// Ok([0; 32])
/// }
/// }
///
/// // A call to send via XCM. We don't really care about this.
/// # fn main() {
/// let call: Vec<u8> = ().encode();
/// let message = Xcm(vec![Instruction::Transact {
/// origin_kind: OriginKind::Superuser,
/// require_weight_at_most: Weight::zero(),
/// call: call.into(),
/// }]);
/// let message_hash = message.using_encoded(sp_io::hashing::blake2_256);
///
/// // Sender2 will block this.
/// assert!(send_xcm::<(Sender1, Sender2, Sender3)>(Parent.into(), message.clone()).is_err());
///
/// // Sender3 will catch this.
/// assert!(send_xcm::<(Sender1, Sender3)>(Parent.into(), message.clone()).is_ok());
/// # }
/// ```
pub trait SendXcm {
/// Intermediate value which connects the two phases of the send operation.
type Ticket;
/// Check whether the given `_message` is deliverable to the given `_destination` and if so
/// determine the cost which will be paid by this chain to do so, returning a `Validated` token
/// which can be used to enact delivery.
///
/// The `destination` and `message` must be `Some` (or else an error will be returned) and they
/// may only be consumed if the `Err` is not `NotApplicable`.
///
/// If it is not a destination which can be reached with this type but possibly could by others,
/// then this *MUST* return `NotApplicable`. Any other error will cause the tuple
/// implementation to exit early without trying other type fields.
fn validate(
destination: &mut Option<Location>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket>;
/// Actually carry out the delivery operation for a previously validated message sending.
fn deliver(ticket: Self::Ticket) -> result::Result<XcmHash, SendError>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl SendXcm for Tuple {
for_tuples! { type Ticket = (#( Option<Tuple::Ticket> ),* ); }
fn validate(
destination: &mut Option<Location>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
let mut maybe_cost: Option<Assets> = None;
let one_ticket: Self::Ticket = (for_tuples! { #(
if maybe_cost.is_some() {
None
} else {
match Tuple::validate(destination, message) {
Err(SendError::NotApplicable) => None,
Err(e) => { return Err(e) },
Ok((v, c)) => {
maybe_cost = Some(c);
Some(v)
},
}
}
),* });
if let Some(cost) = maybe_cost {
Ok((one_ticket, cost))
} else {
Err(SendError::NotApplicable)
}
}
fn deliver(one_ticket: Self::Ticket) -> result::Result<XcmHash, SendError> {
for_tuples!( #(
if let Some(validated) = one_ticket.Tuple {
return Tuple::deliver(validated);
}
)* );
Err(SendError::Unroutable)
}
}
/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
/// both in `Some` before passing them as mutable references into `T::send_xcm`.
pub fn validate_send<T: SendXcm>(dest: Location, msg: Xcm<()>) -> SendResult<T::Ticket> {
T::validate(&mut Some(dest), &mut Some(msg))
}
/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
/// both in `Some` before passing them as mutable references into `T::send_xcm`.
///
/// Returns either `Ok` with the price of the delivery, or `Err` with the reason why the message
/// could not be sent.
///
/// Generally you'll want to validate and get the price first to ensure that the sender can pay it
/// before actually doing the delivery.
pub fn send_xcm<T: SendXcm>(
dest: Location,
msg: Xcm<()>,
) -> result::Result<(XcmHash, Assets), SendError> {
let (ticket, price) = T::validate(&mut Some(dest), &mut Some(msg))?;
let hash = T::deliver(ticket)?;
Ok((hash, price))
}
File diff suppressed because it is too large Load Diff
+327
View File
@@ -0,0 +1,327 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! Support data structures for `Location`, primarily the `Junction` datatype.
use super::Location;
pub use crate::v4::{BodyId, BodyPart};
use crate::{
v4::{Junction as OldJunction, NetworkId as OldNetworkId},
VersionedLocation,
};
use bounded_collections::{BoundedSlice, BoundedVec, ConstU32};
use codec::{self, Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use hex_literal::hex;
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
/// A single item in a path to describe the relative location of a consensus system.
///
/// Each item assumes a pre-existing location as its context and is defined in terms of it.
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
pub enum Junction {
/// An indexed teyrchain belonging to and operated by the context.
///
/// Generally used when the context is a Pezkuwi Relay-chain.
Teyrchain(#[codec(compact)] u32),
/// A 32-byte identifier for an account of a specific network that is respected as a sovereign
/// endpoint within the context.
///
/// Generally used when the context is a Substrate-based chain.
AccountId32 { network: Option<NetworkId>, id: [u8; 32] },
/// An 8-byte index for an account of a specific network that is respected as a sovereign
/// endpoint within the context.
///
/// May be used when the context is a Frame-based chain and includes e.g. an indices pallet.
AccountIndex64 {
network: Option<NetworkId>,
#[codec(compact)]
index: u64,
},
/// A 20-byte identifier for an account of a specific network that is respected as a sovereign
/// endpoint within the context.
///
/// May be used when the context is an Ethereum or Bitcoin chain or smart-contract.
AccountKey20 { network: Option<NetworkId>, key: [u8; 20] },
/// An instanced, indexed pallet that forms a constituent part of the context.
///
/// Generally used when the context is a Frame-based chain.
PalletInstance(u8),
/// A non-descript index within the context location.
///
/// Usage will vary widely owing to its generality.
///
/// NOTE: Try to avoid using this and instead use a more specific item.
GeneralIndex(#[codec(compact)] u128),
/// A nondescript array datum, 32 bytes, acting as a key within the context
/// location.
///
/// Usage will vary widely owing to its generality.
///
/// NOTE: Try to avoid using this and instead use a more specific item.
// Note this is implemented as an array with a length rather than using `BoundedVec` owing to
// the bound for `Copy`.
GeneralKey { length: u8, data: [u8; 32] },
/// The unambiguous child.
///
/// Not currently used except as a fallback when deriving context.
OnlyChild,
/// A pluralistic body existing within consensus.
///
/// Typical to be used to represent a governance origin of a chain, but could in principle be
/// used to represent things such as multisigs also.
Plurality { id: BodyId, part: BodyPart },
/// A global network capable of externalizing its own consensus. This is not generally
/// meaningful outside of the universal level.
GlobalConsensus(NetworkId),
}
/// The genesis hash of the Zagros testnet. Used to identify it.
pub const ZAGROS_GENESIS_HASH: [u8; 32] =
hex!["e143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e"];
/// The genesis hash of the Pezkuwichain testnet. Used to identify it.
pub const PEZKUWICHAIN_GENESIS_HASH: [u8; 32] =
hex!["6408de7737c59c238890533af25896a2c20608d8b380bb01029acb392781063e"];
/// Dummy genesis hash used instead of defunct networks like Wococo (and soon Pezkuwichain).
pub const DUMMY_GENESIS_HASH: [u8; 32] = [0; 32];
/// A global identifier of a data structure existing within consensus.
///
/// Maintenance note: Networks with global consensus and which are practically bridgeable within the
/// Pezkuwi ecosystem are given preference over explicit naming in this enumeration.
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
Serialize,
Deserialize,
)]
pub enum NetworkId {
/// Network specified by the first 32 bytes of its genesis block.
ByGenesis([u8; 32]),
/// Network defined by the first 32-bytes of the hash and number of some block it contains.
ByFork { block_number: u64, block_hash: [u8; 32] },
/// The Pezkuwi mainnet Relay-chain.
Pezkuwi,
/// The Kusama canary-net Relay-chain.
Kusama,
/// An Ethereum network specified by its chain ID.
#[codec(index = 7)]
Ethereum {
/// The EIP-155 chain ID.
#[codec(compact)]
chain_id: u64,
},
/// The Bitcoin network, including hard-forks supported by Bitcoin Core development team.
#[codec(index = 8)]
BitcoinCore,
/// The Bitcoin network, including hard-forks supported by Bitcoin Cash developers.
#[codec(index = 9)]
BitcoinCash,
/// The Pezkuwi Bulletin chain.
#[codec(index = 10)]
PezkuwiBulletin,
}
impl From<OldNetworkId> for Option<NetworkId> {
fn from(old: OldNetworkId) -> Self {
Some(NetworkId::from(old))
}
}
impl From<OldNetworkId> for NetworkId {
fn from(old: OldNetworkId) -> Self {
use OldNetworkId::*;
match old {
ByGenesis(hash) => Self::ByGenesis(hash),
ByFork { block_number, block_hash } => Self::ByFork { block_number, block_hash },
Pezkuwi => Self::Pezkuwi,
Kusama => Self::Kusama,
Zagros => Self::ByGenesis(ZAGROS_GENESIS_HASH),
Pezkuwichain => Self::ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
Wococo => Self::ByGenesis(DUMMY_GENESIS_HASH),
Ethereum { chain_id } => Self::Ethereum { chain_id },
BitcoinCore => Self::BitcoinCore,
BitcoinCash => Self::BitcoinCash,
PezkuwiBulletin => Self::PezkuwiBulletin,
}
}
}
impl From<NetworkId> for Junction {
fn from(n: NetworkId) -> Self {
Self::GlobalConsensus(n)
}
}
impl From<[u8; 32]> for Junction {
fn from(id: [u8; 32]) -> Self {
Self::AccountId32 { network: None, id }
}
}
impl From<BoundedVec<u8, ConstU32<32>>> for Junction {
fn from(key: BoundedVec<u8, ConstU32<32>>) -> Self {
key.as_bounded_slice().into()
}
}
impl<'a> From<BoundedSlice<'a, u8, ConstU32<32>>> for Junction {
fn from(key: BoundedSlice<'a, u8, ConstU32<32>>) -> Self {
let mut data = [0u8; 32];
data[..key.len()].copy_from_slice(&key[..]);
Self::GeneralKey { length: key.len() as u8, data }
}
}
impl<'a> TryFrom<&'a Junction> for BoundedSlice<'a, u8, ConstU32<32>> {
type Error = ();
fn try_from(key: &'a Junction) -> Result<Self, ()> {
match key {
Junction::GeneralKey { length, data } =>
BoundedSlice::try_from(&data[..data.len().min(*length as usize)]).map_err(|_| ()),
_ => Err(()),
}
}
}
impl From<[u8; 20]> for Junction {
fn from(key: [u8; 20]) -> Self {
Self::AccountKey20 { network: None, key }
}
}
impl From<u64> for Junction {
fn from(index: u64) -> Self {
Self::AccountIndex64 { network: None, index }
}
}
impl From<u128> for Junction {
fn from(id: u128) -> Self {
Self::GeneralIndex(id)
}
}
impl TryFrom<OldJunction> for Junction {
type Error = ();
fn try_from(value: OldJunction) -> Result<Self, ()> {
use OldJunction::*;
Ok(match value {
Teyrchain(id) => Self::Teyrchain(id),
AccountId32 { network: maybe_network, id } =>
Self::AccountId32 { network: maybe_network.map(|network| network.into()), id },
AccountIndex64 { network: maybe_network, index } =>
Self::AccountIndex64 { network: maybe_network.map(|network| network.into()), index },
AccountKey20 { network: maybe_network, key } =>
Self::AccountKey20 { network: maybe_network.map(|network| network.into()), key },
PalletInstance(index) => Self::PalletInstance(index),
GeneralIndex(id) => Self::GeneralIndex(id),
GeneralKey { length, data } => Self::GeneralKey { length, data },
OnlyChild => Self::OnlyChild,
Plurality { id, part } => Self::Plurality { id, part },
GlobalConsensus(network) => Self::GlobalConsensus(network.into()),
})
}
}
impl Junction {
/// Convert `self` into a `Location` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub fn into_location(self) -> Location {
Location::new(0, [self])
}
/// Convert `self` into a `Location` containing `n` parents.
///
/// Similar to `Self::into_location`, with the added ability to specify the number of parent
/// junctions.
pub fn into_exterior(self, n: u8) -> Location {
Location::new(n, [self])
}
/// Convert `self` into a `VersionedLocation` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub fn into_versioned(self) -> VersionedLocation {
self.into_location().into_versioned()
}
/// Remove the `NetworkId` value.
pub fn remove_network_id(&mut self) {
use Junction::*;
match self {
AccountId32 { ref mut network, .. } |
AccountIndex64 { ref mut network, .. } |
AccountKey20 { ref mut network, .. } => *network = None,
_ => {},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn junction_round_trip_works() {
let j = Junction::GeneralKey { length: 32, data: [1u8; 32] };
let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap();
assert_eq!(j, k);
let j = OldJunction::GeneralKey { length: 32, data: [1u8; 32] };
let k = OldJunction::try_from(Junction::try_from(j).unwrap()).unwrap();
assert_eq!(j, k);
let j = Junction::from(BoundedVec::try_from(vec![1u8, 2, 3, 4]).unwrap());
let k = Junction::try_from(OldJunction::try_from(j).unwrap()).unwrap();
assert_eq!(j, k);
let s: BoundedSlice<_, _> = (&k).try_into().unwrap();
assert_eq!(s, &[1u8, 2, 3, 4][..]);
let j = OldJunction::GeneralKey { length: 32, data: [1u8; 32] };
let k = OldJunction::try_from(Junction::try_from(j).unwrap()).unwrap();
assert_eq!(j, k);
}
}
+724
View File
@@ -0,0 +1,724 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! XCM `Junctions`/`InteriorLocation` datatype.
use super::{Junction, Location, NetworkId};
use alloc::sync::Arc;
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use core::{mem, ops::Range, result};
use scale_info::TypeInfo;
/// Maximum number of `Junction`s that a `Junctions` can contain.
pub(crate) const MAX_JUNCTIONS: usize = 8;
/// Non-parent junctions that can be constructed, up to the length of 8. This specific `Junctions`
/// implementation uses a Rust `enum` in order to make pattern matching easier.
///
/// Parent junctions cannot be constructed with this type. Refer to `Location` for
/// instructions on constructing parent junctions.
#[derive(
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
serde::Serialize,
serde::Deserialize,
)]
pub enum Junctions {
/// The interpreting consensus system.
Here,
/// A relative path comprising 1 junction.
X1(Arc<[Junction; 1]>),
/// A relative path comprising 2 junctions.
X2(Arc<[Junction; 2]>),
/// A relative path comprising 3 junctions.
X3(Arc<[Junction; 3]>),
/// A relative path comprising 4 junctions.
X4(Arc<[Junction; 4]>),
/// A relative path comprising 5 junctions.
X5(Arc<[Junction; 5]>),
/// A relative path comprising 6 junctions.
X6(Arc<[Junction; 6]>),
/// A relative path comprising 7 junctions.
X7(Arc<[Junction; 7]>),
/// A relative path comprising 8 junctions.
X8(Arc<[Junction; 8]>),
}
macro_rules! impl_junctions {
($count:expr, $variant:ident) => {
impl From<[Junction; $count]> for Junctions {
fn from(junctions: [Junction; $count]) -> Self {
Self::$variant(Arc::new(junctions))
}
}
impl PartialEq<[Junction; $count]> for Junctions {
fn eq(&self, rhs: &[Junction; $count]) -> bool {
self.as_slice() == rhs
}
}
};
}
impl_junctions!(1, X1);
impl_junctions!(2, X2);
impl_junctions!(3, X3);
impl_junctions!(4, X4);
impl_junctions!(5, X5);
impl_junctions!(6, X6);
impl_junctions!(7, X7);
impl_junctions!(8, X8);
pub struct JunctionsIterator {
junctions: Junctions,
range: Range<usize>,
}
impl Iterator for JunctionsIterator {
type Item = Junction;
fn next(&mut self) -> Option<Junction> {
self.junctions.at(self.range.next()?).cloned()
}
}
impl DoubleEndedIterator for JunctionsIterator {
fn next_back(&mut self) -> Option<Junction> {
self.junctions.at(self.range.next_back()?).cloned()
}
}
pub struct JunctionsRefIterator<'a> {
junctions: &'a Junctions,
range: Range<usize>,
}
impl<'a> Iterator for JunctionsRefIterator<'a> {
type Item = &'a Junction;
fn next(&mut self) -> Option<&'a Junction> {
self.junctions.at(self.range.next()?)
}
}
impl<'a> DoubleEndedIterator for JunctionsRefIterator<'a> {
fn next_back(&mut self) -> Option<&'a Junction> {
self.junctions.at(self.range.next_back()?)
}
}
impl<'a> IntoIterator for &'a Junctions {
type Item = &'a Junction;
type IntoIter = JunctionsRefIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
JunctionsRefIterator { junctions: self, range: 0..self.len() }
}
}
impl IntoIterator for Junctions {
type Item = Junction;
type IntoIter = JunctionsIterator;
fn into_iter(self) -> Self::IntoIter {
JunctionsIterator { range: 0..self.len(), junctions: self }
}
}
impl Junctions {
/// Convert `self` into a `Location` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub const fn into_location(self) -> Location {
Location { parents: 0, interior: self }
}
/// Convert `self` into a `Location` containing `n` parents.
///
/// Similar to `Self::into_location`, with the added ability to specify the number of parent
/// junctions.
pub const fn into_exterior(self, n: u8) -> Location {
Location { parents: n, interior: self }
}
/// Casts `self` into a slice containing `Junction`s.
pub fn as_slice(&self) -> &[Junction] {
match self {
Junctions::Here => &[],
Junctions::X1(ref a) => &a[..],
Junctions::X2(ref a) => &a[..],
Junctions::X3(ref a) => &a[..],
Junctions::X4(ref a) => &a[..],
Junctions::X5(ref a) => &a[..],
Junctions::X6(ref a) => &a[..],
Junctions::X7(ref a) => &a[..],
Junctions::X8(ref a) => &a[..],
}
}
/// Casts `self` into a mutable slice containing `Junction`s.
pub fn as_slice_mut(&mut self) -> &mut [Junction] {
match self {
Junctions::Here => &mut [],
Junctions::X1(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X2(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X3(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X4(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X5(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X6(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X7(ref mut a) => &mut Arc::make_mut(a)[..],
Junctions::X8(ref mut a) => &mut Arc::make_mut(a)[..],
}
}
/// Remove the `NetworkId` value in any `Junction`s.
pub fn remove_network_id(&mut self) {
self.for_each_mut(Junction::remove_network_id);
}
/// Treating `self` as the universal context, return the location of the local consensus system
/// from the point of view of the given `target`.
pub fn invert_target(&self, target: &Location) -> Result<Location, ()> {
let mut itself = self.clone();
let mut junctions = Self::Here;
for _ in 0..target.parent_count() {
junctions = junctions
.pushed_front_with(itself.take_last().unwrap_or(Junction::OnlyChild))
.map_err(|_| ())?;
}
let parents = target.interior().len() as u8;
Ok(Location::new(parents, junctions))
}
/// Execute a function `f` on every junction. We use this since we cannot implement a mutable
/// `Iterator` without unsafe code.
pub fn for_each_mut(&mut self, x: impl FnMut(&mut Junction)) {
self.as_slice_mut().iter_mut().for_each(x)
}
/// Extract the network ID treating this value as a universal location.
///
/// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate
/// that this value is not a universal location.
pub fn global_consensus(&self) -> Result<NetworkId, ()> {
if let Some(Junction::GlobalConsensus(network)) = self.first() {
Ok(*network)
} else {
Err(())
}
}
/// Extract the network ID and the interior consensus location, treating this value as a
/// universal location.
///
/// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate
/// that this value is not a universal location.
pub fn split_global(self) -> Result<(NetworkId, Junctions), ()> {
match self.split_first() {
(location, Some(Junction::GlobalConsensus(network))) => Ok((network, location)),
_ => return Err(()),
}
}
/// Treat `self` as a universal location and the context of `relative`, returning the universal
/// location of relative.
///
/// This will return an error if `relative` has as many (or more) parents than there are
/// junctions in `self`, implying that relative refers into a different global consensus.
pub fn within_global(mut self, relative: Location) -> Result<Self, ()> {
if self.len() <= relative.parent_count() as usize {
return Err(());
}
for _ in 0..relative.parent_count() {
self.take_last();
}
for j in relative.interior() {
self.push(*j).map_err(|_| ())?;
}
Ok(self)
}
/// Consumes `self` and returns how `viewer` would address it locally.
pub fn relative_to(mut self, viewer: &Junctions) -> Location {
let mut i = 0;
while match (self.first(), viewer.at(i)) {
(Some(x), Some(y)) => x == y,
_ => false,
} {
self = self.split_first().0;
// NOTE: Cannot overflow as loop can only iterate at most `MAX_JUNCTIONS` times.
i += 1;
}
// AUDIT NOTES:
// - above loop ensures that `i <= viewer.len()`.
// - `viewer.len()` is at most `MAX_JUNCTIONS`, so won't overflow a `u8`.
Location::new((viewer.len() - i) as u8, self)
}
/// Returns first junction, or `None` if the location is empty.
pub fn first(&self) -> Option<&Junction> {
self.as_slice().first()
}
/// Returns last junction, or `None` if the location is empty.
pub fn last(&self) -> Option<&Junction> {
self.as_slice().last()
}
/// Splits off the first junction, returning the remaining suffix (first item in tuple) and the
/// first element (second item in tuple) or `None` if it was empty.
pub fn split_first(self) -> (Junctions, Option<Junction>) {
match self {
Junctions::Here => (Junctions::Here, None),
Junctions::X1(xs) => {
let [a] = *xs;
(Junctions::Here, Some(a))
},
Junctions::X2(xs) => {
let [a, b] = *xs;
([b].into(), Some(a))
},
Junctions::X3(xs) => {
let [a, b, c] = *xs;
([b, c].into(), Some(a))
},
Junctions::X4(xs) => {
let [a, b, c, d] = *xs;
([b, c, d].into(), Some(a))
},
Junctions::X5(xs) => {
let [a, b, c, d, e] = *xs;
([b, c, d, e].into(), Some(a))
},
Junctions::X6(xs) => {
let [a, b, c, d, e, f] = *xs;
([b, c, d, e, f].into(), Some(a))
},
Junctions::X7(xs) => {
let [a, b, c, d, e, f, g] = *xs;
([b, c, d, e, f, g].into(), Some(a))
},
Junctions::X8(xs) => {
let [a, b, c, d, e, f, g, h] = *xs;
([b, c, d, e, f, g, h].into(), Some(a))
},
}
}
/// Splits off the last junction, returning the remaining prefix (first item in tuple) and the
/// last element (second item in tuple) or `None` if it was empty.
pub fn split_last(self) -> (Junctions, Option<Junction>) {
match self {
Junctions::Here => (Junctions::Here, None),
Junctions::X1(xs) => {
let [a] = *xs;
(Junctions::Here, Some(a))
},
Junctions::X2(xs) => {
let [a, b] = *xs;
([a].into(), Some(b))
},
Junctions::X3(xs) => {
let [a, b, c] = *xs;
([a, b].into(), Some(c))
},
Junctions::X4(xs) => {
let [a, b, c, d] = *xs;
([a, b, c].into(), Some(d))
},
Junctions::X5(xs) => {
let [a, b, c, d, e] = *xs;
([a, b, c, d].into(), Some(e))
},
Junctions::X6(xs) => {
let [a, b, c, d, e, f] = *xs;
([a, b, c, d, e].into(), Some(f))
},
Junctions::X7(xs) => {
let [a, b, c, d, e, f, g] = *xs;
([a, b, c, d, e, f].into(), Some(g))
},
Junctions::X8(xs) => {
let [a, b, c, d, e, f, g, h] = *xs;
([a, b, c, d, e, f, g].into(), Some(h))
},
}
}
/// Removes the first element from `self`, returning it (or `None` if it was empty).
pub fn take_first(&mut self) -> Option<Junction> {
let mut d = Junctions::Here;
mem::swap(&mut *self, &mut d);
let (tail, head) = d.split_first();
*self = tail;
head
}
/// Removes the last element from `self`, returning it (or `None` if it was empty).
pub fn take_last(&mut self) -> Option<Junction> {
let mut d = Junctions::Here;
mem::swap(&mut *self, &mut d);
let (head, tail) = d.split_last();
*self = head;
tail
}
/// Mutates `self` to be appended with `new` or returns an `Err` with `new` if would overflow.
pub fn push(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
let new = new.into();
let mut dummy = Junctions::Here;
mem::swap(self, &mut dummy);
match dummy.pushed_with(new) {
Ok(s) => {
*self = s;
Ok(())
},
Err((s, j)) => {
*self = s;
Err(j)
},
}
}
/// Mutates `self` to be prepended with `new` or returns an `Err` with `new` if would overflow.
pub fn push_front(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
let new = new.into();
let mut dummy = Junctions::Here;
mem::swap(self, &mut dummy);
match dummy.pushed_front_with(new) {
Ok(s) => {
*self = s;
Ok(())
},
Err((s, j)) => {
*self = s;
Err(j)
},
}
}
/// Consumes `self` and returns a `Junctions` suffixed with `new`, or an `Err` with the
/// original value of `self` and `new` in case of overflow.
pub fn pushed_with(self, new: impl Into<Junction>) -> result::Result<Self, (Self, Junction)> {
let new = new.into();
Ok(match self {
Junctions::Here => [new].into(),
Junctions::X1(xs) => {
let [a] = *xs;
[a, new].into()
},
Junctions::X2(xs) => {
let [a, b] = *xs;
[a, b, new].into()
},
Junctions::X3(xs) => {
let [a, b, c] = *xs;
[a, b, c, new].into()
},
Junctions::X4(xs) => {
let [a, b, c, d] = *xs;
[a, b, c, d, new].into()
},
Junctions::X5(xs) => {
let [a, b, c, d, e] = *xs;
[a, b, c, d, e, new].into()
},
Junctions::X6(xs) => {
let [a, b, c, d, e, f] = *xs;
[a, b, c, d, e, f, new].into()
},
Junctions::X7(xs) => {
let [a, b, c, d, e, f, g] = *xs;
[a, b, c, d, e, f, g, new].into()
},
s => Err((s, new))?,
})
}
/// Consumes `self` and returns a `Junctions` prefixed with `new`, or an `Err` with the
/// original value of `self` and `new` in case of overflow.
pub fn pushed_front_with(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
let new = new.into();
Ok(match self {
Junctions::Here => [new].into(),
Junctions::X1(xs) => {
let [a] = *xs;
[new, a].into()
},
Junctions::X2(xs) => {
let [a, b] = *xs;
[new, a, b].into()
},
Junctions::X3(xs) => {
let [a, b, c] = *xs;
[new, a, b, c].into()
},
Junctions::X4(xs) => {
let [a, b, c, d] = *xs;
[new, a, b, c, d].into()
},
Junctions::X5(xs) => {
let [a, b, c, d, e] = *xs;
[new, a, b, c, d, e].into()
},
Junctions::X6(xs) => {
let [a, b, c, d, e, f] = *xs;
[new, a, b, c, d, e, f].into()
},
Junctions::X7(xs) => {
let [a, b, c, d, e, f, g] = *xs;
[new, a, b, c, d, e, f, g].into()
},
s => Err((s, new))?,
})
}
/// Mutate `self` so that it is suffixed with `suffix`.
///
/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v5::{Junctions, Junction::*, Location};
/// # fn main() {
/// let mut m = Junctions::from([Teyrchain(21)]);
/// assert_eq!(m.append_with([PalletInstance(3)]), Ok(()));
/// assert_eq!(m, [Teyrchain(21), PalletInstance(3)]);
/// # }
/// ```
pub fn append_with(&mut self, suffix: impl Into<Junctions>) -> Result<(), Junctions> {
let suffix = suffix.into();
if self.len().saturating_add(suffix.len()) > MAX_JUNCTIONS {
return Err(suffix);
}
for j in suffix.into_iter() {
self.push(j).expect("Already checked the sum of the len()s; qed")
}
Ok(())
}
/// Returns the number of junctions in `self`.
pub fn len(&self) -> usize {
self.as_slice().len()
}
/// Returns the junction at index `i`, or `None` if the location doesn't contain that many
/// elements.
pub fn at(&self, i: usize) -> Option<&Junction> {
self.as_slice().get(i)
}
/// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't
/// contain that many elements.
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
self.as_slice_mut().get_mut(i)
}
/// Returns a reference iterator over the junctions.
pub fn iter(&self) -> JunctionsRefIterator<'_> {
JunctionsRefIterator { junctions: self, range: 0..self.len() }
}
/// Ensures that self begins with `prefix` and that it has a single `Junction` item following.
/// If so, returns a reference to this `Junction` item.
///
/// # Example
/// ```rust
/// # use staging_xcm::v5::{Junctions, Junction::*};
/// # fn main() {
/// let mut m = Junctions::from([Teyrchain(2), PalletInstance(3), OnlyChild]);
/// assert_eq!(m.match_and_split(&[Teyrchain(2), PalletInstance(3)].into()), Some(&OnlyChild));
/// assert_eq!(m.match_and_split(&[Teyrchain(2)].into()), None);
/// # }
/// ```
pub fn match_and_split(&self, prefix: &Junctions) -> Option<&Junction> {
if prefix.len() + 1 != self.len() {
return None;
}
for i in 0..prefix.len() {
if prefix.at(i) != self.at(i) {
return None;
}
}
return self.at(prefix.len());
}
pub fn starts_with(&self, prefix: &Junctions) -> bool {
prefix.len() <= self.len() && prefix.iter().zip(self.iter()).all(|(x, y)| x == y)
}
}
impl TryFrom<Location> for Junctions {
type Error = Location;
fn try_from(x: Location) -> result::Result<Self, Location> {
if x.parent_count() > 0 {
Err(x)
} else {
Ok(x.interior().clone())
}
}
}
impl<T: Into<Junction>> From<T> for Junctions {
fn from(x: T) -> Self {
[x.into()].into()
}
}
impl From<[Junction; 0]> for Junctions {
fn from(_: [Junction; 0]) -> Self {
Self::Here
}
}
impl From<()> for Junctions {
fn from(_: ()) -> Self {
Self::Here
}
}
xcm_procedural::impl_conversion_functions_for_junctions_v5!();
#[cfg(test)]
mod tests {
use super::{super::prelude::*, *};
#[test]
fn inverting_works() {
let context: InteriorLocation = (Teyrchain(1000), PalletInstance(42)).into();
let target = (Parent, PalletInstance(69)).into();
let expected = (Parent, PalletInstance(42)).into();
let inverted = context.invert_target(&target).unwrap();
assert_eq!(inverted, expected);
let context: InteriorLocation =
(Teyrchain(1000), PalletInstance(42), GeneralIndex(1)).into();
let target = (Parent, Parent, PalletInstance(69), GeneralIndex(2)).into();
let expected = (Parent, Parent, PalletInstance(42), GeneralIndex(1)).into();
let inverted = context.invert_target(&target).unwrap();
assert_eq!(inverted, expected);
}
#[test]
fn relative_to_works() {
use NetworkId::*;
assert_eq!(
Junctions::from([Pezkuwi.into()]).relative_to(&Junctions::from([Kusama.into()])),
(Parent, Pezkuwi).into()
);
let base = Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(1)]);
// Ancestors.
assert_eq!(Here.relative_to(&base), (Parent, Parent, Parent).into());
assert_eq!(Junctions::from([Kusama.into()]).relative_to(&base), (Parent, Parent).into());
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(1)]).relative_to(&base),
(Parent,).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(1)]).relative_to(&base),
Here.into()
);
// Ancestors with one child.
assert_eq!(
Junctions::from([Pezkuwi.into()]).relative_to(&base),
(Parent, Parent, Parent, Pezkuwi).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(2)]).relative_to(&base),
(Parent, Parent, Teyrchain(2)).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(2)]).relative_to(&base),
(Parent, PalletInstance(2)).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(1), [1u8; 32].into()])
.relative_to(&base),
([1u8; 32],).into()
);
// Ancestors with grandchildren.
assert_eq!(
Junctions::from([Pezkuwi.into(), Teyrchain(1)]).relative_to(&base),
(Parent, Parent, Parent, Pezkuwi, Teyrchain(1)).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(2), PalletInstance(1)]).relative_to(&base),
(Parent, Parent, Teyrchain(2), PalletInstance(1)).into()
);
assert_eq!(
Junctions::from([Kusama.into(), Teyrchain(1), PalletInstance(2), [1u8; 32].into()])
.relative_to(&base),
(Parent, PalletInstance(2), [1u8; 32]).into()
);
assert_eq!(
Junctions::from([
Kusama.into(),
Teyrchain(1),
PalletInstance(1),
[1u8; 32].into(),
1u128.into()
])
.relative_to(&base),
([1u8; 32], 1u128).into()
);
}
#[test]
fn global_consensus_works() {
use NetworkId::*;
assert_eq!(Junctions::from([Pezkuwi.into()]).global_consensus(), Ok(Pezkuwi));
assert_eq!(Junctions::from([Kusama.into(), 1u64.into()]).global_consensus(), Ok(Kusama));
assert_eq!(Here.global_consensus(), Err(()));
assert_eq!(Junctions::from([1u64.into()]).global_consensus(), Err(()));
assert_eq!(Junctions::from([1u64.into(), Kusama.into()]).global_consensus(), Err(()));
}
#[test]
fn test_conversion() {
use super::{Junction::*, NetworkId::*};
let x: Junctions = GlobalConsensus(Pezkuwi).into();
assert_eq!(x, Junctions::from([GlobalConsensus(Pezkuwi)]));
let x: Junctions = Pezkuwi.into();
assert_eq!(x, Junctions::from([GlobalConsensus(Pezkuwi)]));
let x: Junctions = (Pezkuwi, Kusama).into();
assert_eq!(x, Junctions::from([GlobalConsensus(Pezkuwi), GlobalConsensus(Kusama)]));
}
#[test]
fn encode_decode_junctions_works() {
let original = Junctions::from([
Pezkuwi.into(),
Kusama.into(),
1u64.into(),
GlobalConsensus(Pezkuwi),
Teyrchain(123),
PalletInstance(45),
]);
let encoded = original.encode();
assert_eq!(encoded, &[6, 9, 2, 9, 3, 2, 0, 4, 9, 2, 0, 237, 1, 4, 45]);
let decoded = Junctions::decode(&mut &encoded[..]).unwrap();
assert_eq!(decoded, original);
}
}
+756
View File
@@ -0,0 +1,756 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! XCM `Location` datatype.
use super::{traits::Reanchorable, Junction, Junctions};
use crate::{v4::Location as OldLocation, VersionedLocation};
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use core::result;
use scale_info::TypeInfo;
/// A relative path between state-bearing consensus systems.
///
/// A location in a consensus system is defined as an *isolatable state machine* held within global
/// consensus. The location in question need not have a sophisticated consensus algorithm of its
/// own; a single account within Ethereum, for example, could be considered a location.
///
/// A very-much non-exhaustive list of types of location include:
/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a teyrchain.
/// - A layer-0 super-chain, e.g. the Pezkuwi Relay chain.
/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum.
/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based
/// Substrate chain.
/// - An account.
///
/// A `Location` is a *relative identifier*, meaning that it can only be used to define the
/// relative path between two locations, and cannot generally be used to refer to a location
/// universally. It is comprised of an integer number of parents specifying the number of times to
/// "escape" upwards into the containing consensus system and then a number of *junctions*, each
/// diving down and specifying some interior portion of state (which may be considered a
/// "sub-consensus" system).
///
/// This specific `Location` implementation uses a `Junctions` datatype which is a Rust `enum`
/// in order to make pattern matching easier. There are occasions where it is important to ensure
/// that a value is strictly an interior location, in those cases, `Junctions` may be used.
///
/// The `Location` value of `Null` simply refers to the interpreting consensus system.
#[derive(
Clone,
Decode,
Encode,
DecodeWithMemTracking,
Eq,
PartialEq,
Ord,
PartialOrd,
Debug,
TypeInfo,
MaxEncodedLen,
serde::Serialize,
serde::Deserialize,
)]
pub struct Location {
/// The number of parent junctions at the beginning of this `Location`.
pub parents: u8,
/// The interior (i.e. non-parent) junctions that this `Location` contains.
pub interior: Junctions,
}
impl Default for Location {
fn default() -> Self {
Self::here()
}
}
/// A relative location which is constrained to be an interior location of the context.
///
/// See also `Location`.
pub type InteriorLocation = Junctions;
impl Location {
/// Creates a new `Location` with the given number of parents and interior junctions.
pub fn new(parents: u8, interior: impl Into<Junctions>) -> Location {
Location { parents, interior: interior.into() }
}
/// Consume `self` and return the equivalent `VersionedLocation` value.
pub const fn into_versioned(self) -> VersionedLocation {
VersionedLocation::V5(self)
}
/// Creates a new `Location` with 0 parents and a `Here` interior.
///
/// The resulting `Location` can be interpreted as the "current consensus system".
pub const fn here() -> Location {
Location { parents: 0, interior: Junctions::Here }
}
/// Creates a new `Location` which evaluates to the parent context.
pub const fn parent() -> Location {
Location { parents: 1, interior: Junctions::Here }
}
/// Creates a new `Location` with `parents` and an empty (`Here`) interior.
pub const fn ancestor(parents: u8) -> Location {
Location { parents, interior: Junctions::Here }
}
/// Whether the `Location` has no parents and has a `Here` interior.
pub fn is_here(&self) -> bool {
self.parents == 0 && self.interior.len() == 0
}
/// Remove the `NetworkId` value in any interior `Junction`s.
pub fn remove_network_id(&mut self) {
self.interior.remove_network_id();
}
/// Return a reference to the interior field.
pub fn interior(&self) -> &Junctions {
&self.interior
}
/// Return a mutable reference to the interior field.
pub fn interior_mut(&mut self) -> &mut Junctions {
&mut self.interior
}
/// Returns the number of `Parent` junctions at the beginning of `self`.
pub const fn parent_count(&self) -> u8 {
self.parents
}
/// Returns the parent count and the interior [`Junctions`] as a tuple.
///
/// To be used when pattern matching, for example:
///
/// ```rust
/// # use staging_xcm::v5::{Junctions::*, Junction::*, Location};
/// fn get_teyrchain_id(loc: &Location) -> Option<u32> {
/// match loc.unpack() {
/// (0, [Teyrchain(id)]) => Some(*id),
/// _ => None
/// }
/// }
/// ```
pub fn unpack(&self) -> (u8, &[Junction]) {
(self.parents, self.interior.as_slice())
}
/// Returns boolean indicating whether `self` contains only the specified amount of
/// parents and no interior junctions.
pub const fn contains_parents_only(&self, count: u8) -> bool {
matches!(self.interior, Junctions::Here) && self.parents == count
}
/// Returns the number of parents and junctions in `self`.
pub fn len(&self) -> usize {
self.parent_count() as usize + self.interior.len()
}
/// Returns the first interior junction, or `None` if the location is empty or contains only
/// parents.
pub fn first_interior(&self) -> Option<&Junction> {
self.interior.first()
}
/// Returns last junction, or `None` if the location is empty or contains only parents.
pub fn last(&self) -> Option<&Junction> {
self.interior.last()
}
/// Splits off the first interior junction, returning the remaining suffix (first item in tuple)
/// and the first element (second item in tuple) or `None` if it was empty.
pub fn split_first_interior(self) -> (Location, Option<Junction>) {
let Location { parents, interior: junctions } = self;
let (suffix, first) = junctions.split_first();
let location = Location { parents, interior: suffix };
(location, first)
}
/// Splits off the last interior junction, returning the remaining prefix (first item in tuple)
/// and the last element (second item in tuple) or `None` if it was empty or if `self` only
/// contains parents.
pub fn split_last_interior(self) -> (Location, Option<Junction>) {
let Location { parents, interior: junctions } = self;
let (prefix, last) = junctions.split_last();
let location = Location { parents, interior: prefix };
(location, last)
}
/// Mutates `self`, suffixing its interior junctions with `new`. Returns `Err` with `new` in
/// case of overflow.
pub fn push_interior(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
self.interior.push(new)
}
/// Mutates `self`, prefixing its interior junctions with `new`. Returns `Err` with `new` in
/// case of overflow.
pub fn push_front_interior(
&mut self,
new: impl Into<Junction>,
) -> result::Result<(), Junction> {
self.interior.push_front(new)
}
/// Consumes `self` and returns a `Location` suffixed with `new`, or an `Err` with
/// the original value of `self` in case of overflow.
pub fn pushed_with_interior(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
match self.interior.pushed_with(new) {
Ok(i) => Ok(Location { interior: i, parents: self.parents }),
Err((i, j)) => Err((Location { interior: i, parents: self.parents }, j)),
}
}
/// Consumes `self` and returns a `Location` prefixed with `new`, or an `Err` with the
/// original value of `self` in case of overflow.
pub fn pushed_front_with_interior(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
match self.interior.pushed_front_with(new) {
Ok(i) => Ok(Location { interior: i, parents: self.parents }),
Err((i, j)) => Err((Location { interior: i, parents: self.parents }, j)),
}
}
/// Returns the junction at index `i`, or `None` if the location is a parent or if the location
/// does not contain that many elements.
pub fn at(&self, i: usize) -> Option<&Junction> {
let num_parents = self.parents as usize;
if i < num_parents {
return None;
}
self.interior.at(i - num_parents)
}
/// Returns a mutable reference to the junction at index `i`, or `None` if the location is a
/// parent or if it doesn't contain that many elements.
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
let num_parents = self.parents as usize;
if i < num_parents {
return None;
}
self.interior.at_mut(i - num_parents)
}
/// Decrements the parent count by 1.
pub fn dec_parent(&mut self) {
self.parents = self.parents.saturating_sub(1);
}
/// Removes the first interior junction from `self`, returning it
/// (or `None` if it was empty or if `self` contains only parents).
pub fn take_first_interior(&mut self) -> Option<Junction> {
self.interior.take_first()
}
/// Removes the last element from `interior`, returning it (or `None` if it was empty or if
/// `self` only contains parents).
pub fn take_last(&mut self) -> Option<Junction> {
self.interior.take_last()
}
/// Ensures that `self` has the same number of parents as `prefix`, its junctions begins with
/// the junctions of `prefix` and that it has a single `Junction` item following.
/// If so, returns a reference to this `Junction` item.
///
/// # Example
/// ```rust
/// # use staging_xcm::v5::{Junctions::*, Junction::*, Location};
/// # fn main() {
/// let mut m = Location::new(1, [PalletInstance(3), OnlyChild]);
/// assert_eq!(
/// m.match_and_split(&Location::new(1, [PalletInstance(3)])),
/// Some(&OnlyChild),
/// );
/// assert_eq!(m.match_and_split(&Location::new(1, Here)), None);
/// # }
/// ```
pub fn match_and_split(&self, prefix: &Location) -> Option<&Junction> {
if self.parents != prefix.parents {
return None;
}
self.interior.match_and_split(&prefix.interior)
}
pub fn starts_with(&self, prefix: &Location) -> bool {
self.parents == prefix.parents && self.interior.starts_with(&prefix.interior)
}
/// Mutate `self` so that it is suffixed with `suffix`.
///
/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v5::{Junctions::*, Junction::*, Location, Parent};
/// # fn main() {
/// let mut m: Location = (Parent, Teyrchain(21), 69u64).into();
/// assert_eq!(m.append_with((Parent, PalletInstance(3))), Ok(()));
/// assert_eq!(m, Location::new(1, [Teyrchain(21), PalletInstance(3)]));
/// # }
/// ```
pub fn append_with(&mut self, suffix: impl Into<Self>) -> Result<(), Self> {
let prefix = core::mem::replace(self, suffix.into());
match self.prepend_with(prefix) {
Ok(()) => Ok(()),
Err(prefix) => Err(core::mem::replace(self, prefix)),
}
}
/// Consume `self` and return its value suffixed with `suffix`.
///
/// Returns `Err` with the original value of `self` and `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v5::{Junctions::*, Junction::*, Location, Parent};
/// # fn main() {
/// let mut m: Location = (Parent, Teyrchain(21), 69u64).into();
/// let r = m.appended_with((Parent, PalletInstance(3))).unwrap();
/// assert_eq!(r, Location::new(1, [Teyrchain(21), PalletInstance(3)]));
/// # }
/// ```
pub fn appended_with(mut self, suffix: impl Into<Self>) -> Result<Self, (Self, Self)> {
match self.append_with(suffix) {
Ok(()) => Ok(self),
Err(suffix) => Err((self, suffix)),
}
}
/// Mutate `self` so that it is prefixed with `prefix`.
///
/// Does not modify `self` and returns `Err` with `prefix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v5::{Junctions::*, Junction::*, Location, Parent};
/// # fn main() {
/// let mut m: Location = (Parent, Parent, PalletInstance(3)).into();
/// assert_eq!(m.prepend_with((Parent, Teyrchain(21), OnlyChild)), Ok(()));
/// assert_eq!(m, Location::new(1, [PalletInstance(3)]));
/// # }
/// ```
pub fn prepend_with(&mut self, prefix: impl Into<Self>) -> Result<(), Self> {
// prefix self (suffix)
// P .. P I .. I p .. p i .. i
let mut prefix = prefix.into();
let prepend_interior = prefix.interior.len().saturating_sub(self.parents as usize);
let final_interior = self.interior.len().saturating_add(prepend_interior);
if final_interior > super::junctions::MAX_JUNCTIONS {
return Err(prefix);
}
let suffix_parents = (self.parents as usize).saturating_sub(prefix.interior.len());
let final_parents = (prefix.parents as usize).saturating_add(suffix_parents);
if final_parents > 255 {
return Err(prefix);
}
// cancel out the final item on the prefix interior for one of the suffix's parents.
while self.parents > 0 && prefix.take_last().is_some() {
self.dec_parent();
}
// now we have either removed all suffix's parents or prefix interior.
// this means we can combine the prefix's and suffix's remaining parents/interior since
// we know that with at least one empty, the overall order will be respected:
// prefix self (suffix)
// P .. P (I) p .. p i .. i => P + p .. (no I) i
// -- or --
// P .. P I .. I (p) i .. i => P (no p) .. I + i
self.parents = self.parents.saturating_add(prefix.parents);
for j in prefix.interior.into_iter().rev() {
self.push_front_interior(j)
.expect("final_interior no greater than MAX_JUNCTIONS; qed");
}
Ok(())
}
/// Consume `self` and return its value prefixed with `prefix`.
///
/// Returns `Err` with the original value of `self` and `prefix` in case of overflow.
///
/// # Example
/// ```rust
/// # use staging_xcm::v5::{Junctions::*, Junction::*, Location, Parent};
/// # fn main() {
/// let m: Location = (Parent, Parent, PalletInstance(3)).into();
/// let r = m.prepended_with((Parent, Teyrchain(21), OnlyChild)).unwrap();
/// assert_eq!(r, Location::new(1, [PalletInstance(3)]));
/// # }
/// ```
pub fn prepended_with(mut self, prefix: impl Into<Self>) -> Result<Self, (Self, Self)> {
match self.prepend_with(prefix) {
Ok(()) => Ok(self),
Err(prefix) => Err((self, prefix)),
}
}
/// Remove any unneeded parents/junctions in `self` based on the given context it will be
/// interpreted in.
pub fn simplify(&mut self, context: &Junctions) {
if context.len() < self.parents as usize {
// Not enough context
return;
}
while self.parents > 0 {
let maybe = context.at(context.len() - (self.parents as usize));
match (self.interior.first(), maybe) {
(Some(i), Some(j)) if i == j => {
self.interior.take_first();
self.parents -= 1;
},
_ => break,
}
}
}
/// Return the Location subsection identifying the chain that `self` points to.
pub fn chain_location(&self) -> Location {
let mut clone = self.clone();
// start popping junctions until we reach chain identifier
while let Some(j) = clone.last() {
if matches!(j, Junction::Teyrchain(_) | Junction::GlobalConsensus(_)) {
// return chain subsection
return clone;
} else {
(clone, _) = clone.split_last_interior();
}
}
Location::new(clone.parents, Junctions::Here)
}
}
impl Reanchorable for Location {
type Error = Self;
/// Mutate `self` so that it represents the same location from the point of view of `target`.
/// The context of `self` is provided as `context`.
///
/// Does not modify `self` in case of overflow.
fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
// TODO: https://github.com/paritytech/polkadot/issues/4489 Optimize this.
// 1. Use our `context` to figure out how the `target` would address us.
let inverted_target = context.invert_target(target)?;
// 2. Prepend `inverted_target` to `self` to get self's location from the perspective of
// `target`.
self.prepend_with(inverted_target).map_err(|_| ())?;
// 3. Given that we know some of `target` context, ensure that any parents in `self` are
// strictly needed.
self.simplify(target.interior());
Ok(())
}
/// Consume `self` and return a new value representing the same location from the point of view
/// of `target`. The context of `self` is provided as `context`.
///
/// Returns the original `self` in case of overflow.
fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result<Self, Self> {
match self.reanchor(target, context) {
Ok(()) => Ok(self),
Err(()) => Err(self),
}
}
}
impl TryFrom<OldLocation> for Option<Location> {
type Error = ();
fn try_from(value: OldLocation) -> result::Result<Self, Self::Error> {
Ok(Some(Location::try_from(value)?))
}
}
impl TryFrom<OldLocation> for Location {
type Error = ();
fn try_from(x: OldLocation) -> result::Result<Self, ()> {
Ok(Location { parents: x.parents, interior: x.interior.try_into()? })
}
}
/// A unit struct which can be converted into a `Location` of `parents` value 1.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Parent;
impl From<Parent> for Location {
fn from(_: Parent) -> Self {
Location { parents: 1, interior: Junctions::Here }
}
}
/// A tuple struct which can be converted into a `Location` of `parents` value 1 with the inner
/// interior.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct ParentThen(pub Junctions);
impl From<ParentThen> for Location {
fn from(ParentThen(interior): ParentThen) -> Self {
Location { parents: 1, interior }
}
}
/// A unit struct which can be converted into a `Location` of the inner `parents` value.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Ancestor(pub u8);
impl From<Ancestor> for Location {
fn from(Ancestor(parents): Ancestor) -> Self {
Location { parents, interior: Junctions::Here }
}
}
/// A unit struct which can be converted into a `Location` of the inner `parents` value and the
/// inner interior.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct AncestorThen<Interior>(pub u8, pub Interior);
impl<Interior: Into<Junctions>> From<AncestorThen<Interior>> for Location {
fn from(AncestorThen(parents, interior): AncestorThen<Interior>) -> Self {
Location { parents, interior: interior.into() }
}
}
impl From<[u8; 32]> for Location {
fn from(bytes: [u8; 32]) -> Self {
let junction: Junction = bytes.into();
junction.into()
}
}
impl From<sp_runtime::AccountId32> for Location {
fn from(id: sp_runtime::AccountId32) -> Self {
Junction::AccountId32 { network: None, id: id.into() }.into()
}
}
xcm_procedural::impl_conversion_functions_for_location_v5!();
#[cfg(test)]
mod tests {
use crate::v5::prelude::*;
use codec::{Decode, Encode};
#[test]
fn conversion_works() {
let x: Location = Parent.into();
assert_eq!(x, Location { parents: 1, interior: Here });
// let x: Location = (Parent,).into();
// assert_eq!(x, Location { parents: 1, interior: Here });
// let x: Location = (Parent, Parent).into();
// assert_eq!(x, Location { parents: 2, interior: Here });
let x: Location = (Parent, Parent, OnlyChild).into();
assert_eq!(x, Location { parents: 2, interior: OnlyChild.into() });
let x: Location = OnlyChild.into();
assert_eq!(x, Location { parents: 0, interior: OnlyChild.into() });
let x: Location = (OnlyChild,).into();
assert_eq!(x, Location { parents: 0, interior: OnlyChild.into() });
}
#[test]
fn simplify_basic_works() {
let mut location: Location =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [Teyrchain(1000), PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location = (Parent, PalletInstance(42), GeneralIndex(69)).into();
let context = [PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location = (Parent, PalletInstance(42), GeneralIndex(69)).into();
let context = [Teyrchain(1000), PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [OnlyChild, Teyrchain(1000), PalletInstance(42)].into();
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
}
#[test]
fn simplify_incompatible_location_fails() {
let mut location: Location =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [Teyrchain(1000), PalletInstance(42), GeneralIndex(42)].into();
let expected =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: Location =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = [Teyrchain(1000)].into();
let expected =
(Parent, Parent, Teyrchain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
}
#[test]
fn reanchor_works() {
let mut id: Location = (Parent, Teyrchain(1000), GeneralIndex(42)).into();
let context = Teyrchain(2000).into();
let target = (Parent, Teyrchain(1000)).into();
let expected = GeneralIndex(42).into();
id.reanchor(&target, &context).unwrap();
assert_eq!(id, expected);
}
#[test]
fn encode_and_decode_works() {
let m = Location {
parents: 1,
interior: [Teyrchain(42), AccountIndex64 { network: None, index: 23 }].into(),
};
let encoded = m.encode();
assert_eq!(encoded, [1, 2, 0, 168, 2, 0, 92].to_vec());
let decoded = Location::decode(&mut &encoded[..]);
assert_eq!(decoded, Ok(m));
}
#[test]
fn match_and_split_works() {
let m = Location {
parents: 1,
interior: [Teyrchain(42), AccountIndex64 { network: None, index: 23 }].into(),
};
assert_eq!(m.match_and_split(&Location { parents: 1, interior: Here }), None);
assert_eq!(
m.match_and_split(&Location { parents: 1, interior: [Teyrchain(42)].into() }),
Some(&AccountIndex64 { network: None, index: 23 })
);
assert_eq!(m.match_and_split(&m), None);
}
#[test]
fn append_with_works() {
let acc = AccountIndex64 { network: None, index: 23 };
let mut m = Location { parents: 1, interior: [Teyrchain(42)].into() };
assert_eq!(m.append_with([PalletInstance(3), acc]), Ok(()));
assert_eq!(
m,
Location { parents: 1, interior: [Teyrchain(42), PalletInstance(3), acc].into() }
);
// cannot append to create overly long location
let acc = AccountIndex64 { network: None, index: 23 };
let m = Location {
parents: 254,
interior: [Teyrchain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild].into(),
};
let suffix: Location = (PalletInstance(3), acc, OnlyChild, OnlyChild).into();
assert_eq!(m.clone().append_with(suffix.clone()), Err(suffix));
}
#[test]
fn prepend_with_works() {
let mut m = Location {
parents: 1,
interior: [Teyrchain(42), AccountIndex64 { network: None, index: 23 }].into(),
};
assert_eq!(m.prepend_with(Location { parents: 1, interior: [OnlyChild].into() }), Ok(()));
assert_eq!(
m,
Location {
parents: 1,
interior: [Teyrchain(42), AccountIndex64 { network: None, index: 23 }].into()
}
);
// cannot prepend to create overly long location
let mut m = Location { parents: 254, interior: [Teyrchain(42)].into() };
let prefix = Location { parents: 2, interior: Here };
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
let prefix = Location { parents: 1, interior: Here };
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, Location { parents: 255, interior: [Teyrchain(42)].into() });
}
#[test]
fn double_ended_ref_iteration_works() {
let m: Junctions = [Teyrchain(1000), Teyrchain(3), PalletInstance(5)].into();
let mut iter = m.iter();
let first = iter.next().unwrap();
assert_eq!(first, &Teyrchain(1000));
let third = iter.next_back().unwrap();
assert_eq!(third, &PalletInstance(5));
let second = iter.next_back().unwrap();
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
assert_eq!(second, &Teyrchain(3));
let res = Here
.pushed_with(*first)
.unwrap()
.pushed_with(*second)
.unwrap()
.pushed_with(*third)
.unwrap();
assert_eq!(m, res);
// make sure there's no funny business with the 0 indexing
let m = Here;
let mut iter = m.iter();
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
}
#[test]
fn conversion_from_other_types_works() {
use crate::v4;
fn takes_location<Arg: Into<Location>>(_arg: Arg) {}
takes_location(Parent);
takes_location(Here);
takes_location([Teyrchain(42)]);
takes_location((Ancestor(255), PalletInstance(8)));
takes_location((Ancestor(5), Teyrchain(1), PalletInstance(3)));
takes_location((Ancestor(2), Here));
takes_location(AncestorThen(
3,
[Teyrchain(43), AccountIndex64 { network: None, index: 155 }],
));
takes_location((Parent, AccountId32 { network: None, id: [0; 32] }));
takes_location((Parent, Here));
takes_location(ParentThen([Teyrchain(75)].into()));
takes_location([Teyrchain(100), PalletInstance(3)]);
assert_eq!(v4::Location::from(v4::Junctions::Here).try_into(), Ok(Location::here()));
assert_eq!(v4::Location::from(v4::Parent).try_into(), Ok(Location::parent()));
assert_eq!(
v4::Location::from((v4::Parent, v4::Parent, v4::Junction::GeneralIndex(42u128),))
.try_into(),
Ok(Location { parents: 2, interior: [GeneralIndex(42u128)].into() }),
);
}
}
File diff suppressed because it is too large Load Diff
+553
View File
@@ -0,0 +1,553 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi 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.
// Pezkuwi 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 Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
pub use crate::v3::{Error as OldError, SendError, XcmHash};
use codec::{Decode, DecodeWithMemTracking, Encode};
use core::result;
use scale_info::TypeInfo;
pub use sp_weights::Weight;
use super::*;
/// Error codes used in XCM. The first errors codes have explicit indices and are part of the XCM
/// format. Those trailing are merely part of the XCM implementation; there is no expectation that
/// they will retain the same index over time.
#[derive(
Copy,
Clone,
Encode,
Decode,
DecodeWithMemTracking,
Eq,
PartialEq,
Debug,
TypeInfo,
MaxEncodedLen,
)]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub enum Error {
// Errors that happen due to instructions being executed. These alone are defined in the
// XCM specification.
/// An arithmetic overflow happened.
#[codec(index = 0)]
Overflow,
/// The instruction is intentionally unsupported.
#[codec(index = 1)]
Unimplemented,
/// Origin Register does not contain a value value for a reserve transfer notification.
#[codec(index = 2)]
UntrustedReserveLocation,
/// Origin Register does not contain a value value for a teleport notification.
#[codec(index = 3)]
UntrustedTeleportLocation,
/// `MultiLocation` value too large to descend further.
#[codec(index = 4)]
LocationFull,
/// `MultiLocation` value ascend more parents than known ancestors of local location.
#[codec(index = 5)]
LocationNotInvertible,
/// The Origin Register does not contain a valid value for instruction.
#[codec(index = 6)]
BadOrigin,
/// The location parameter is not a valid value for the instruction.
#[codec(index = 7)]
InvalidLocation,
/// The given asset is not handled.
#[codec(index = 8)]
AssetNotFound,
/// An asset transaction (like withdraw or deposit) failed (typically due to type conversions).
#[codec(index = 9)]
FailedToTransactAsset(#[codec(skip)] &'static str),
/// An asset cannot be withdrawn, potentially due to lack of ownership, availability or rights.
#[codec(index = 10)]
NotWithdrawable,
/// An asset cannot be deposited under the ownership of a particular location.
#[codec(index = 11)]
LocationCannotHold,
/// Attempt to send a message greater than the maximum supported by the transport protocol.
#[codec(index = 12)]
ExceedsMaxMessageSize,
/// The given message cannot be translated into a format supported by the destination.
#[codec(index = 13)]
DestinationUnsupported,
/// Destination is routable, but there is some issue with the transport mechanism.
#[codec(index = 14)]
Transport(#[codec(skip)] &'static str),
/// Destination is known to be unroutable.
#[codec(index = 15)]
Unroutable,
/// Used by `ClaimAsset` when the given claim could not be recognized/found.
#[codec(index = 16)]
UnknownClaim,
/// Used by `Transact` when the functor cannot be decoded.
#[codec(index = 17)]
FailedToDecode,
/// Used by `Transact` to indicate that the given weight limit could be breached by the
/// functor.
#[codec(index = 18)]
MaxWeightInvalid,
/// Used by `BuyExecution` when the Holding Register does not contain payable fees.
#[codec(index = 19)]
NotHoldingFees,
/// Used by `BuyExecution` when the fees declared to purchase weight are insufficient.
#[codec(index = 20)]
TooExpensive,
/// Used by the `Trap` instruction to force an error intentionally. Its code is included.
#[codec(index = 21)]
Trap(u64),
/// Used by `ExpectAsset`, `ExpectError` and `ExpectOrigin` when the expectation was not true.
#[codec(index = 22)]
ExpectationFalse,
/// The provided pallet index was not found.
#[codec(index = 23)]
PalletNotFound,
/// The given pallet's name is different to that expected.
#[codec(index = 24)]
NameMismatch,
/// The given pallet's version has an incompatible version to that expected.
#[codec(index = 25)]
VersionIncompatible,
/// The given operation would lead to an overflow of the Holding Register.
#[codec(index = 26)]
HoldingWouldOverflow,
/// The message was unable to be exported.
#[codec(index = 27)]
ExportError,
/// `MultiLocation` value failed to be reanchored.
#[codec(index = 28)]
ReanchorFailed,
/// No deal is possible under the given constraints.
#[codec(index = 29)]
NoDeal,
/// Fees were required which the origin could not pay.
#[codec(index = 30)]
FeesNotMet,
/// Some other error with locking.
#[codec(index = 31)]
LockError,
/// The state was not in a condition where the operation was valid to make.
#[codec(index = 32)]
NoPermission,
/// The universal location of the local consensus is improper.
#[codec(index = 33)]
Unanchored,
/// An asset cannot be deposited, probably because (too much of) it already exists.
#[codec(index = 34)]
NotDepositable,
/// Too many assets matched the given asset filter.
#[codec(index = 35)]
TooManyAssets,
// Errors that happen prior to instructions being executed. These fall outside of the XCM
// spec.
/// XCM version not able to be handled.
UnhandledXcmVersion,
/// Execution of the XCM would potentially result in a greater weight used than weight limit.
WeightLimitReached(Weight),
/// The XCM did not pass the barrier condition for execution.
///
/// The barrier condition differs on different chains and in different circumstances, but
/// generally it means that the conditions surrounding the message were not such that the chain
/// considers the message worth spending time executing. Since most chains lift the barrier to
/// execution on appropriate payment, presentation of an NFT voucher, or based on the message
/// origin, it means that none of those were the case.
Barrier,
/// The weight of an XCM message is not computable ahead of execution.
WeightNotComputable,
/// Recursion stack limit reached
// TODO(https://github.com/pezkuwichain/pezkuwi-sdk/issues/148): This should have a fixed index since
// we use it in `FrameTransactionalProcessor` // which is used in instructions.
// Or we should create a different error for that.
ExceedsStackLimit,
}
impl TryFrom<OldError> for Error {
type Error = ();
fn try_from(old_error: OldError) -> result::Result<Error, ()> {
use OldError::*;
Ok(match old_error {
Overflow => Self::Overflow,
Unimplemented => Self::Unimplemented,
UntrustedReserveLocation => Self::UntrustedReserveLocation,
UntrustedTeleportLocation => Self::UntrustedTeleportLocation,
LocationFull => Self::LocationFull,
LocationNotInvertible => Self::LocationNotInvertible,
BadOrigin => Self::BadOrigin,
InvalidLocation => Self::InvalidLocation,
AssetNotFound => Self::AssetNotFound,
FailedToTransactAsset(s) => Self::FailedToTransactAsset(s),
NotWithdrawable => Self::NotWithdrawable,
LocationCannotHold => Self::LocationCannotHold,
ExceedsMaxMessageSize => Self::ExceedsMaxMessageSize,
DestinationUnsupported => Self::DestinationUnsupported,
Transport(s) => Self::Transport(s),
Unroutable => Self::Unroutable,
UnknownClaim => Self::UnknownClaim,
FailedToDecode => Self::FailedToDecode,
MaxWeightInvalid => Self::MaxWeightInvalid,
NotHoldingFees => Self::NotHoldingFees,
TooExpensive => Self::TooExpensive,
Trap(i) => Self::Trap(i),
ExpectationFalse => Self::ExpectationFalse,
PalletNotFound => Self::PalletNotFound,
NameMismatch => Self::NameMismatch,
VersionIncompatible => Self::VersionIncompatible,
HoldingWouldOverflow => Self::HoldingWouldOverflow,
ExportError => Self::ExportError,
ReanchorFailed => Self::ReanchorFailed,
NoDeal => Self::NoDeal,
FeesNotMet => Self::FeesNotMet,
LockError => Self::LockError,
NoPermission => Self::NoPermission,
Unanchored => Self::Unanchored,
NotDepositable => Self::NotDepositable,
UnhandledXcmVersion => Self::UnhandledXcmVersion,
WeightLimitReached(weight) => Self::WeightLimitReached(weight),
Barrier => Self::Barrier,
WeightNotComputable => Self::WeightNotComputable,
ExceedsStackLimit => Self::ExceedsStackLimit,
})
}
}
impl From<SendError> for Error {
fn from(e: SendError) -> Self {
match e {
SendError::NotApplicable | SendError::Unroutable | SendError::MissingArgument =>
Error::Unroutable,
SendError::Transport(s) => Error::Transport(s),
SendError::DestinationUnsupported => Error::DestinationUnsupported,
SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize,
SendError::Fees => Error::FeesNotMet,
}
}
}
pub type Result = result::Result<(), Error>;
/// Outcome of an XCM execution.
#[derive(Clone, Encode, Decode, DecodeWithMemTracking, Eq, PartialEq, Debug, TypeInfo)]
pub enum Outcome {
/// Execution completed successfully; given weight was used.
Complete { used: Weight },
/// Execution started, but did not complete successfully due to`error` which occurred
/// on the `index`-th (top-level) instruction. Overall, total `weight` was used.
Incomplete { used: Weight, error: InstructionError },
/// Execution did not start due to an error. We use `InstructionError` since it's always
/// possible to isolate the problematic instruction that caused the error.
Error(InstructionError),
}
/// XCM error and the index of the instruction that caused it.
#[derive(Copy, Clone, Encode, Decode, DecodeWithMemTracking, Eq, PartialEq, Debug, TypeInfo)]
pub struct InstructionError {
/// The index of the intruction that caused the error.
pub index: InstructionIndex,
/// The XCM error itself.
pub error: Error,
}
impl Outcome {
pub fn ensure_complete(self) -> result::Result<(), InstructionError> {
match self {
Outcome::Complete { .. } => Ok(()),
Outcome::Incomplete { error, .. } => Err(error),
Outcome::Error(error) => Err(error),
}
}
pub fn ensure_execution(self) -> result::Result<Weight, InstructionError> {
match self {
Outcome::Complete { used, .. } => Ok(used),
Outcome::Incomplete { used, .. } => Ok(used),
Outcome::Error(error) => Err(error),
}
}
/// How much weight was used by the XCM execution attempt.
pub fn weight_used(&self) -> Weight {
match self {
Outcome::Complete { used, .. } => *used,
Outcome::Incomplete { used, .. } => *used,
Outcome::Error(_) => Weight::zero(),
}
}
}
impl From<Error> for Outcome {
fn from(error: Error) -> Self {
Self::Error(InstructionError { error, index: 0 })
}
}
pub trait PreparedMessage {
fn weight_of(&self) -> Weight;
}
/// The index of an instruction in an XCM.
pub type InstructionIndex = u8;
/// Type of XCM message executor.
pub trait ExecuteXcm<Call> {
type Prepared: PreparedMessage;
/// If it fails, returns the index of the problematic instruction.
fn prepare(
message: Xcm<Call>,
weight_limit: Weight,
) -> result::Result<Self::Prepared, InstructionError>;
fn execute(
origin: impl Into<Location>,
pre: Self::Prepared,
id: &mut XcmHash,
weight_credit: Weight,
) -> Outcome;
fn prepare_and_execute(
origin: impl Into<Location>,
message: Xcm<Call>,
id: &mut XcmHash,
weight_limit: Weight,
weight_credit: Weight,
) -> Outcome {
let pre = match Self::prepare(message, weight_limit) {
Ok(x) => x,
Err(error) => return Outcome::Error(error),
};
Self::execute(origin, pre, id, weight_credit)
}
/// Deduct some `fees` to the sovereign account of the given `location` and place them as per
/// the convention for fees.
fn charge_fees(location: impl Into<Location>, fees: Assets) -> Result;
}
pub enum Weightless {}
impl PreparedMessage for Weightless {
fn weight_of(&self) -> Weight {
unreachable!()
}
}
impl<C> ExecuteXcm<C> for () {
type Prepared = Weightless;
fn prepare(_: Xcm<C>, _: Weight) -> result::Result<Self::Prepared, InstructionError> {
Err(InstructionError { index: 0, error: Error::Unimplemented })
}
fn execute(_: impl Into<Location>, _: Self::Prepared, _: &mut XcmHash, _: Weight) -> Outcome {
unreachable!()
}
fn charge_fees(_location: impl Into<Location>, _fees: Assets) -> Result {
Err(Error::Unimplemented)
}
}
pub trait Reanchorable: Sized {
/// Type to return in case of an error.
type Error: Debug;
/// Mutate `self` so that it represents the same location from the point of view of `target`.
/// The context of `self` is provided as `context`.
///
/// Does not modify `self` in case of overflow.
fn reanchor(
&mut self,
target: &Location,
context: &InteriorLocation,
) -> core::result::Result<(), ()>;
/// Consume `self` and return a new value representing the same location from the point of view
/// of `target`. The context of `self` is provided as `context`.
///
/// Returns the original `self` in case of overflow.
fn reanchored(
self,
target: &Location,
context: &InteriorLocation,
) -> core::result::Result<Self, Self::Error>;
}
/// Result value when attempting to send an XCM message.
pub type SendResult<T> = result::Result<(T, Assets), SendError>;
/// Utility for sending an XCM message to a given location.
///
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each
/// router might return `NotApplicable` to pass the execution to the next sender item. Note that
/// each `NotApplicable` might alter the destination and the XCM message for to the next router.
///
/// # Example
/// ```rust
/// # use codec::Encode;
/// # use staging_xcm::v5::{prelude::*, Weight};
/// # use staging_xcm::VersionedXcm;
/// # use std::convert::Infallible;
///
/// /// A sender that only passes the message through and does nothing.
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// type Ticket = Infallible;
/// fn validate(_: &mut Option<Location>, _: &mut Option<Xcm<()>>) -> SendResult<Infallible> {
/// Err(SendError::NotApplicable)
/// }
/// fn deliver(_: Infallible) -> Result<XcmHash, SendError> {
/// unreachable!()
/// }
/// }
///
/// /// A sender that accepts a message that has two junctions, otherwise stops the routing.
/// struct Sender2;
/// impl SendXcm for Sender2 {
/// type Ticket = ();
/// fn validate(destination: &mut Option<Location>, message: &mut Option<Xcm<()>>) -> SendResult<()> {
/// match destination.as_ref().ok_or(SendError::MissingArgument)?.unpack() {
/// (0, [j1, j2]) => Ok(((), Assets::new())),
/// _ => Err(SendError::Unroutable),
/// }
/// }
/// fn deliver(_: ()) -> Result<XcmHash, SendError> {
/// Ok([0; 32])
/// }
/// }
///
/// /// A sender that accepts a message from a parent, passing through otherwise.
/// struct Sender3;
/// impl SendXcm for Sender3 {
/// type Ticket = ();
/// fn validate(destination: &mut Option<Location>, message: &mut Option<Xcm<()>>) -> SendResult<()> {
/// match destination.as_ref().ok_or(SendError::MissingArgument)?.unpack() {
/// (1, []) => Ok(((), Assets::new())),
/// _ => Err(SendError::NotApplicable),
/// }
/// }
/// fn deliver(_: ()) -> Result<XcmHash, SendError> {
/// Ok([0; 32])
/// }
/// }
///
/// // A call to send via XCM. We don't really care about this.
/// # fn main() {
/// let call: Vec<u8> = ().encode();
/// let message = Xcm(vec![Instruction::Transact {
/// origin_kind: OriginKind::Superuser,
/// call: call.into(),
/// fallback_max_weight: None,
/// }]);
/// let message_hash = message.using_encoded(sp_io::hashing::blake2_256);
///
/// // Sender2 will block this.
/// assert!(send_xcm::<(Sender1, Sender2, Sender3)>(Parent.into(), message.clone()).is_err());
///
/// // Sender3 will catch this.
/// assert!(send_xcm::<(Sender1, Sender3)>(Parent.into(), message.clone()).is_ok());
/// # }
/// ```
pub trait SendXcm {
/// Intermediate value which connects the two phases of the send operation.
type Ticket;
/// Check whether the given `_message` is deliverable to the given `_destination` and if so
/// determine the cost which will be paid by this chain to do so, returning a `Validated` token
/// which can be used to enact delivery.
///
/// The `destination` and `message` must be `Some` (or else an error will be returned) and they
/// may only be consumed if the `Err` is not `NotApplicable`.
///
/// If it is not a destination which can be reached with this type but possibly could by others,
/// then this *MUST* return `NotApplicable`. Any other error will cause the tuple
/// implementation to exit early without trying other type fields.
fn validate(
destination: &mut Option<Location>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket>;
/// Actually carry out the delivery operation for a previously validated message sending.
fn deliver(ticket: Self::Ticket) -> result::Result<XcmHash, SendError>;
/// Ensure `[Self::delivery]` is successful for the given `location` when called in benchmarks.
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful_delivery(_location: Option<Location>) {}
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl SendXcm for Tuple {
for_tuples! { type Ticket = (#( Option<Tuple::Ticket> ),* ); }
fn validate(
destination: &mut Option<Location>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
let mut maybe_cost: Option<Assets> = None;
let one_ticket: Self::Ticket = (for_tuples! { #(
if maybe_cost.is_some() {
None
} else {
match Tuple::validate(destination, message) {
Err(SendError::NotApplicable) => None,
Err(e) => { return Err(e) },
Ok((v, c)) => {
maybe_cost = Some(c);
Some(v)
},
}
}
),* });
if let Some(cost) = maybe_cost {
Ok((one_ticket, cost))
} else {
Err(SendError::NotApplicable)
}
}
fn deliver(one_ticket: Self::Ticket) -> result::Result<XcmHash, SendError> {
for_tuples!( #(
if let Some(validated) = one_ticket.Tuple {
return Tuple::deliver(validated);
}
)* );
Err(SendError::Unroutable)
}
#[cfg(feature = "runtime-benchmarks")]
fn ensure_successful_delivery(location: Option<Location>) {
for_tuples!( #(
return Tuple::ensure_successful_delivery(location.clone());
)* );
}
}
/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
/// both in `Some` before passing them as mutable references into `T::send_xcm`.
pub fn validate_send<T: SendXcm>(dest: Location, msg: Xcm<()>) -> SendResult<T::Ticket> {
T::validate(&mut Some(dest), &mut Some(msg))
}
/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
/// both in `Some` before passing them as mutable references into `T::send_xcm`.
///
/// Returns either `Ok` with the price of the delivery, or `Err` with the reason why the message
/// could not be sent.
///
/// Generally you'll want to validate and get the price first to ensure that the sender can pay it
/// before actually doing the delivery.
pub fn send_xcm<T: SendXcm>(
dest: Location,
msg: Xcm<()>,
) -> result::Result<(XcmHash, Assets), SendError> {
let (ticket, price) = T::validate(&mut Some(dest), &mut Some(msg))?;
let hash = T::deliver(ticket)?;
Ok((hash, price))
}