feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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"]);
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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))
|
||||
}
|
||||
Reference in New Issue
Block a user