* cargo fmt

* Create benchmarks for XCM instructions introduced in v3 (#4564)

* Create benchmarks for BurnAsset and ExpectAsset

* Add benchmarks for ExpectOrigin and ExpectError

* Add benchmarks for QueryPallet and ExpectPallet

* Add benchmarks for ReportTransactStatus and ClearTransactStatus

* cargo fmt

* Use AllPalletsWithSystem in mocks

* Update XCM generic benchmarks for westend

* Remove default impls for some XCM weight functions

* Fix compilation error

* Add weight_args helper attribute

* Remove manually written XcmWeightInfo

* Parse trailing comma

* Revert "Add weight_args helper attribute"

This reverts commit 3b7c47a6182e1b9227036c38b406d494c3fcf6fd.

* Fixes

* Fixes

* XCM v3: Introduce querier field into `QueryReponse` (#4732)

* Introduce querier field into QueryReponse

* Convert &Option<MultiLocation> to Option<&MultiLocation>

&Option<T> is almost always never quite useful, most of the time it
still gets converted to an Option<&T> via `as_ref`, so we should simply
make functions that accept Option<&T> instead.

* Fix tests

* cargo fmt

* Fix benchmarks

* Appease spellchecker

* Fix test

* Fix tests

* Fix test

* Fix mock

* Fixes

* Fix tests

* Add test for response queriers

* Update xcm/pallet-xcm/src/lib.rs

* Test for non-existence of querier

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* Fixes

* Fixes

* Add `starts_with` function to `MultiLocation` and `Junctions` (#4835)

* add matches_prefix function to MultiLocation and Junctions

* rename matches_prefix to starts_with

* remove unnecessary main in doc comment

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Make use of starts_with in match_and_split

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* XCM v3: Bridge infrastructure (#4681)

* XCM bridge infrastructure

* Missing bit of cherry-pick

* Revamped XCM proc macros; new NetworkIds

* Fixes

* Formatting

* ExportMessage instruction and config type

* Add MessageExporter definitions

* Formatting

* Missing files

* Fixes

* Initial bridging config API

* Allow for two-stage XCM execution

* Update xcm/src/v3/mod.rs

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* XCM crate building again

* Initial bridging primitive

* Docs

* Docs

* More work

* More work

* Merge branch 'gav-xcm-v3' into gav-xcm-v3-bridging

* Make build

* WithComputedOrigin and SovereignPaidRemoteExporter

* Remove TODOs

* Slim bridge API and tests.

* Fixes

* More work

* First bridge test passing

* Formatting

* Another test

* Next round of bridging tests

* Repot tests

* Cleanups

* Paid bridging

* Formatting

* Tests

* Spelling

* Formatting

* Fees and refactoring

* Fixes

* Formatting

* Refactor SendXcm to become two-phase

* Fix tests

* Refactoring of SendXcm and ExportXcm complete

* Formatting

* Rename CannotReachDestination -> NotApplicable

* Remove XCM v0

* Minor grumbles

* Formatting

* Formatting

* Fixes

* Fixes

* Cleanup XCM config

* Fee handling

* Fixes

* Formatting

* Fixes

* Bump

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* Bump Substrate

* XCM v3: `ExchangeAsset` and Remote-locking (#4945)

* Asset Exchange and Locks

* Make sure XCM typers impl MaxEncodedLen

* Basic implementation for locks

* Bump Substrate

* Missing files

* Use new API

* Introduce  instruction

* Big refactor

* Docs

* Remove deprecated struct

* Remove deprecated struct

* Repot XCM builder tests

* ExchangeAsset test

* Exchange tests

* Locking tests

* Locking tests

* Fixes and tests

* Fixes

* Formatting

* Spelling

* Add simulator test for remote locking

* Fix tests

* Bump

* XCM v3: Support for non-fungibles (#4950)

* NFT support and a test

* New files.

* Integration tests for sending NFTs

* Formatting

* Broken Cargo features

* Use 2021 edition

* Fixes

* Formatting

* Formatting

* Update xcm/xcm-builder/src/asset_conversion.rs

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* Update xcm/xcm-builder/src/nonfungibles_adapter.rs

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* Update xcm/xcm-executor/src/lib.rs

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* Fixes

* Fixes

* Fixes

* Formatting

* Fixes

Co-authored-by: Bastian Köcher <info@kchr.de>
Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* XCM v3: Context & ID hash (#4756)

* send_xcm returns message hash

* cargo fmt

* Create topic register and instructions

* Fix weights

* Use tabs

* Sketch out XcmContext

* Fix doc test

* Add the XCM context as a parameter to executor trait fns

* Fixes

* Add XcmContext parameter

* Revert adding context as an arg to SendXcm trait methods

* Revert adding context argument to ConvertOrigin trait methods

* cargo fmt

* Do not change the API of XcmExecutor::execute

* Fixes

* Fixes

* Fixes

* Fixes

* Remove convenience method

* Fixes

* Fixes

* cargo fmt

* Fixes

* Add benchmarks for XCM topic instructions

* cargo run --quiet --profile=production  --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=pallet_xcm_benchmarks::generic --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --template=./xcm/pallet-xcm-benchmarks/template.hbs --output=./runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs

* Remove context argument on FilterAssetLocation

* Fixes

* Remove unused import

* Fixes

* Fixes

* Fixes

* Accept XCM hash parameter in ExecuteXcm trait methods

* cargo fmt

* Properly enable sp-io/std

* Fixes

* default-features = false

* Fixes

* Fixes

* Fixes

* Make XcmContext optional in withdraw_asset

* Fixes

* Fixes

* Fixes

* Modify tests to check for the correct XCM hash

* Small refactor

* cargo fmt

* Check for expected hash in xcm-builder unit tests

* Add doc comment for the optionality of the XCM context in withdraw_asset

* Update xcm/src/v3/traits.rs

* Update xcm/src/v3/traits.rs

* Store XcmContext and avoid rebuilding

* Use ref for XcmContext

* Formatting

* Fix incorrect hash CC @KiChjang

* Refactor and make clear fake hashes

* Fixes

* Fixes

* Fixes

* Fix broken hashing

* Docs

* Fixes

* Fixes

* Fixes

* Formatting

* Fixes

* Fixes

* Fixes

* Remove unknowable hash

* Formatting

* Use message hash for greater identifiability

* Formatting

* Fixes

* Formatting

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
Co-authored-by: Parity Bot <admin@parity.io>

* Fixes

* Fixes

* Fixes

* Fixes

* Formatting

* Fixes

* Formatting

* Fixes

* Fixes

* Formatting

* Formatting

* Remove horrible names

* Bump

* Remove InvertLocation trait (#5092)

* Remove InvertLocation trait

* Remove unneeded functions

* Formatting

* Fixes

* Remove XCMv1 (#5094)

* Remove XCMv1

* Remove XCMv1

* Formatting

* Fixes

* Fixes

* Formatting

* derive serialize/deserialize for xcm primitives (#5036)

* derive serialize/deserialize for xcm primitives

* derive serialize/deserialize for xcm primitives

* update v3

* update v2

Co-authored-by: Gav Wood <gavin@parity.io>

* Update lock

* Fixes

* Add benchmarks for the ExchangeAsset instruction

* `AliasOrigin` instruction stub (#5122)

* AliasOrigin instruction stub

* Fixes

* Fixes

* Update substrate

* Fixes

* Ensure same array length before using copy_from_slice

* Fixes

* Add benchmarks for the UniversalOrigin instruction

* Remove unused import

* Remove unused import

* Add benchmarks for SetFeesMode instruction

* Add benchmarks for asset (un)locking instructions

* Leave AliasOrigin unbenchmarked

* Fixes after merge

* cargo fmt

* Fixes

* Fixes

* Set TrustedReserves to None on both Kusama and Westend

* Remove extraneous reserve_asset_deposited benchmark

* Fix universal_origin benchmark

* cargo run --quiet --profile=production  --features=runtime-benchmarks -- benchmark pallet --chain=westend-dev --steps=50 --repeat=20 --pallet=pallet_xcm_benchmarks::generic --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --template=./xcm/pallet-xcm-benchmarks/template.hbs --output=./runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs

* Don't rely on skipped benchmark functions

* Fixes

* cargo run --quiet --profile=production  --features=runtime-benchmarks -- benchmark pallet --chain=kusama-dev --steps=50 --repeat=20 --pallet=pallet_xcm_benchmarks::generic --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --template=./xcm/pallet-xcm-benchmarks/template.hbs --output=./runtime/kusama/src/weights/xcm/pallet_xcm_benchmarks_generic.rs

* Fix unused variables

* Fixes

* Spelling

* Fixes

* Fix codec index of VersionedXcm

* Allows to customize how calls are dispatched from XCM (#5657)

* CallDispatcher trait

* fmt

* unused import

* fix test-runtime

* remove JustDispatch type

* fix typo in test-runtime

* missing CallDispatcher

* more missing CallDispatcher

* Update comment `NoteAssetLocked` -> `NoteUnlockable`

* Fixes

* Fixes

* Adjust MultiAssets weights based on new wild card variants

* Fixes

* Fixes

* Fixes

* Fixes

* Fixes

* Some late fixes for XCMv3 (#5237)

* Maximise chances that trapped assets can be reclaimed

* Do origin check as part of ExportMessage for security

* Formatting

* Fixes

* Cleanup export XCM APIs

* Formatting

* Update xcm/src/v3/junctions.rs

* UnpaidExecution instruction and associated barrier.

* Tighten barriers (ClearOrigin/QueryResponse)

* Allow only 1 ClearOrigin instruction in AllowTopLevelPaidExecutionFrom

* Bi-directional teleport accounting

* Revert other fix

* Build fixes]

* Tests build

* Benchmark fixes

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* Update Substrate

* Re-export `pub` stuff from universal_exports.rs + removed unecessary clone (#6145)

* Re-export `pub` stuff from universal_exports.rs

* Removed unnecessary clone

* Use 2D weights in XCM v3 (#6134)

* Depend upon sp-core instead of sp-runtime

* Make sp-io a dev-dependency

* Use 2D weights in XCM v3

* cargo fmt

* Add XCM pallet migration to runtimes

* Use from_parts

* cargo fmt

* Fixes

* cargo fmt

* Remove XCMWeight import

* Fixes

* Fixes

* Fixes

* Fixes

* Use translate in migration

* Increase max upward message size in tests

* Fix doc test

* Remove most uses of from_ref_time

* cargo fmt

* Fixes

* Fixes

* Add extrinsic benchmarking to XCM pallet

* cargo fmt

* Fixes

* Use old syntax

* cargo fmt

* Fixes

* Remove hardcoded weights

* Add XCM pallet to benchmarks

* Use successful origin

* Fix weird type parameter compilation issue

* Fixes

* ".git/.scripts/bench-bot.sh" runtime westend-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime rococo-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime kusama-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime polkadot-dev pallet_xcm

* Use benchmarked XCM pallet weights

* Fixes

* Fixes

* Use override instead of skip

* Fixes

* Fixes

* Fixes

* Fixes

* ".git/.scripts/bench-bot.sh" runtime polkadot-dev pallet_xcm

* Fixes

* ".git/.scripts/bench-bot.sh" runtime polkadot-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime westend-dev pallet_xcm

Co-authored-by: command-bot <>

* Replace Weight::MAX with 100b weight units

* Add test to ensure all_gte in barriers is correct

* Update xcm/src/v3/junction.rs

Co-authored-by: asynchronous rob <rphmeier@gmail.com>

* Add more weight tests

* cargo fmt

* Create thread_local in XCM executor to limit recursion depth (#6304)

* Create thread_local in XCM executor to limit recursion depth

* Add unit test for recursion limit

* Fix statefulness in tests

* Remove panic

* Use defer and environmental macro

* Fix the implementation

* Use nicer interface

* Change ThisNetwork to AnyNetwork

* Move recursion check up to top level

* cargo fmt

* Update comment

Co-authored-by: Bastian Köcher <info@kchr.de>

* Add upper limit on the number of overweight messages in the queue (#6298)

* Add upper limit on the number of ovwerweight messages in the queue

* Add newline

* Introduce whitelist for Transact and limit UMP processing to 10 messages per block (#6280)

* Add SafeCallFilter to XcmConfig

* Limit UMP to receive 10 messages every block

* Place 10 message limit on processing instead of receiving

* Always increment the message_processed count whenever a message is processed

* Add as_derivative to the Transact whitelist

* cargo fmt

* Fixes

* Update xcm/xcm-builder/src/universal_exports.rs

Co-authored-by: Branislav Kontur <bkontur@gmail.com>

* Fixes

* Fixes

* Remove topic register and instead use the topic field in XcmContext

* Derive some common traits for DispatchBlobError

* Fixes

* cargo fmt

* Fixes

* Fixes

* Fix comments

* Fixes

* Introduce WithOriginFilter and apply it as the CallDispatcher for runtimes

* Fixes

* Appease clippy and fixes

* Fixes

* Fix more clippy issues

* Fixes

* ".git/.scripts/bench-bot.sh" runtime polkadot-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime westend-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime westend-dev pallet_xcm

* Add benchmark function for ExportMessage

* Fix comment

* Add upper limit to DownwardMessageQueues size

* Add max size check for queue in can_queue_downward_message

* Fixes

* Make Transact runtime call configurable

* Return Weight::MAX when there is no successful send XCM origin

* Update substrate

* Fixes

* Fixes

* Remove ExportMessage benchmark

* Remove assertion on Transact instruction benchmark

* Make reachable destination configurable in XCM pallet benchmarks

* Fixes

* Fixes

* Remove cfg attribute in fuzzer

* Fixes

* Remove cfg attribute for XCM pallet in test runtime

* Fixes

* Use ReachableDest where possible

* Fixes

* Add benchmark for UnpaidExecution

* Update substrate

* Ensure benchmark functions pass filters

* Add runtime-benchmarks feature to fuzzer

* Ensure FixedRateOfFungible accounts for proof size weights

* cargo fmt

* Whitelist remark_with_event when runtime-benchmarks feature is enabled

* Use remark_with_event for Transact benchmarks

* Fix Cargo.lock

* Allow up to 3 DescendOrigin instructions before UnpaidExecution

* cargo fmt

* Edit code comment

* Check check_origin for unpaid execution privilege

* Fixes

* Small nits for xcm-v3 (#6408)

* Add possibility to skip benchmark for export_message

* ".git/.scripts/bench-bot.sh" xcm westend-dev pallet_xcm_benchmarks::generic

* Revert

* ".git/.scripts/bench-bot.sh" xcm westend-dev pallet_xcm_benchmarks::generic

* Add HaulBlobError to `fn haul_blob`

* ".git/.scripts/bench-bot.sh" xcm westend-dev pallet_xcm_benchmarks::generic

Co-authored-by: command-bot <>

* Revert changes to UnpaidExecution

* Change AllowUnpaidExecutionFrom to be explicit

* Fix log text

* cargo fmt

* Add benchmarks for XCM pallet version migration (#6448)

* Add benchmarks for XCM pallet version migration

* cargo fmt

* Fixes

* Fixes

* Fixes

* ".git/.scripts/bench-bot.sh" runtime westend-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime kusama-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime rococo-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime polkadot-dev pallet_xcm

* Fix benchmarks

* Fix benchmarks

* ".git/.scripts/bench-bot.sh" runtime westend-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime kusama-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime rococo-dev pallet_xcm

* ".git/.scripts/bench-bot.sh" runtime polkadot-dev pallet_xcm

Co-authored-by: command-bot <>

* Merge remote-tracking branch 'origin/master' into gav-xcm-v3

* Fixes

* Fix comments (#6470)

* Specify Ethereum networks by their chain id (#6286)

Co-authored-by: Squirrel <gilescope@gmail.com>

* Use  for Kusama

* Use WithComputedOrigin for Polkadot, Rococo and Westend

* Update lock

* Fix warning

* Update xcm/pallet-xcm/src/tests.rs

Co-authored-by: Squirrel <gilescope@gmail.com>

* Update runtime/parachains/src/ump/migration.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update xcm/pallet-xcm/src/migration.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Fixes

* cargo fmt

* Typo

* Update xcm/src/v3/mod.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Docs

* Docs

* Docs

* Docs

* Docs

* Update xcm/src/v3/multiasset.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Add tests for MultiAssets::from_sorted_and_deduplicated

* Fail gracefully when same instance NFTs are detected during push

* Update Substrate to fix benchmarks

* Apply suggestions from code review

* Update runtime/kusama/src/xcm_config.rs

* Rename arguments

* Attempt to fix benchmark

* ".git/.scripts/commands/bench/bench.sh" runtime polkadot-dev runtime_parachains::ump

* Use actual weights for UMP pallet in Polkadot

* ".git/.scripts/commands/bench/bench.sh" runtime kusama-dev runtime_parachains::ump

* ".git/.scripts/commands/bench/bench.sh" runtime westend-dev runtime_parachains::ump

* ".git/.scripts/commands/bench/bench.sh" runtime rococo-dev runtime_parachains::ump

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
Co-authored-by: Bastian Köcher <info@kchr.de>
Co-authored-by: Parity Bot <admin@parity.io>
Co-authored-by: stanly-johnson <stanlyjohnson@outlook.com>
Co-authored-by: nanocryk <6422796+nanocryk@users.noreply.github.com>
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
Co-authored-by: asynchronous rob <rphmeier@gmail.com>
Co-authored-by: command-bot <>
Co-authored-by: Vincent Geddes <vincent.geddes@hey.com>
Co-authored-by: Squirrel <gilescope@gmail.com>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Gavin Wood
2023-01-17 04:04:34 -03:00
committed by GitHub
parent 2952ad6f44
commit 1a1bfd2af9
155 changed files with 19234 additions and 8436 deletions
+320 -299
View File
@@ -23,17 +23,19 @@
#![no_std]
extern crate alloc;
use alloc::vec::Vec;
use derivative::Derivative;
use parity_scale_codec::{Decode, Encode, Error as CodecError, Input};
use parity_scale_codec::{Decode, Encode, Error as CodecError, Input, MaxEncodedLen};
use scale_info::TypeInfo;
pub mod v0;
pub mod v1;
pub mod v2;
pub mod v3;
pub mod lts {
pub use super::v3::*;
}
pub mod latest {
pub use super::v2::*;
pub use super::v3::*;
}
mod double_encoded;
@@ -65,241 +67,284 @@ pub trait IntoVersion: Sized {
}
}
/// A single `MultiLocation` value, together with its version code.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
pub enum VersionedMultiLocation {
V0(v0::MultiLocation),
V1(v1::MultiLocation),
pub trait TryAs<T> {
fn try_as(&self) -> Result<&T, ()>;
}
impl IntoVersion for VersionedMultiLocation {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
0 => Self::V0(self.try_into()?),
1 | 2 => Self::V1(self.try_into()?),
_ => return Err(()),
})
}
}
macro_rules! versioned_type {
($(#[$attr:meta])* pub enum $n:ident {
V3($v3:ty),
}) => {
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(
Clone(bound = ""),
Eq(bound = ""),
PartialEq(bound = ""),
Debug(bound = "")
)]
#[codec(encode_bound())]
#[codec(decode_bound())]
$(#[$attr])*
pub enum $n {
#[codec(index = 0)]
V3($v3),
}
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),
}
}
}
impl IntoVersion for $n {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
3 => Self::V3(self.try_into()?),
_ => return Err(()),
})
}
}
impl<T: Into<$v3>> From<T> for $n {
fn from(x: T) -> Self {
$n::V3(x.into())
}
}
impl TryFrom<$n> for $v3 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V3(x) => Ok(x),
}
}
}
impl MaxEncodedLen for $n {
fn max_encoded_len() -> usize {
<$v3>::max_encoded_len()
}
}
};
impl From<v0::MultiLocation> for VersionedMultiLocation {
fn from(x: v0::MultiLocation) -> Self {
VersionedMultiLocation::V0(x)
}
}
($(#[$attr:meta])* pub enum $n:ident {
V2($v2:ty),
V3($v3:ty),
}) => {
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(
Clone(bound = ""),
Eq(bound = ""),
PartialEq(bound = ""),
Debug(bound = "")
)]
#[codec(encode_bound())]
#[codec(decode_bound())]
$(#[$attr])*
pub enum $n {
#[codec(index = 0)]
V2($v2),
#[codec(index = 1)]
V3($v3),
}
impl $n {
pub fn try_as<T>(&self) -> Result<&T, ()> where Self: TryAs<T> {
<Self as TryAs<T>>::try_as(&self)
}
}
impl TryAs<$v2> for $n {
fn try_as(&self) -> Result<&$v2, ()> {
match &self {
Self::V2(ref x) => Ok(x),
_ => Err(()),
}
}
}
impl TryAs<$v3> for $n {
fn try_as(&self) -> Result<&$v3, ()> {
match &self {
Self::V3(ref x) => Ok(x),
_ => Err(()),
}
}
}
impl IntoVersion for $n {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
1 | 2 => Self::V2(self.try_into()?),
3 => Self::V3(self.try_into()?),
_ => return Err(()),
})
}
}
impl From<$v2> for $n {
fn from(x: $v2) -> Self {
$n::V2(x)
}
}
impl<T: Into<$v3>> From<T> for $n {
fn from(x: T) -> Self {
$n::V3(x.into())
}
}
impl TryFrom<$n> for $v2 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V2(x) => Ok(x),
V3(x) => x.try_into(),
}
}
}
impl TryFrom<$n> for $v3 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V2(x) => x.try_into(),
V3(x) => Ok(x),
}
}
}
impl MaxEncodedLen for $n {
fn max_encoded_len() -> usize {
<$v3>::max_encoded_len()
}
}
};
impl<T: Into<v1::MultiLocation>> From<T> for VersionedMultiLocation {
fn from(x: T) -> Self {
VersionedMultiLocation::V1(x.into())
}
}
impl TryFrom<VersionedMultiLocation> for v0::MultiLocation {
type Error = ();
fn try_from(x: VersionedMultiLocation) -> Result<Self, ()> {
use VersionedMultiLocation::*;
match x {
V0(x) => Ok(x),
V1(x) => x.try_into(),
($(#[$attr:meta])* pub enum $n:ident {
V2($v2:ty),
V3($v3:ty),
}) => {
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
$(#[$attr])*
pub enum $n {
#[codec(index = 1)]
V2($v2),
#[codec(index = 2)]
V3($v3),
}
impl $n {
pub fn try_as<T>(&self) -> Result<&T, ()> where Self: TryAs<T> {
<Self as TryAs<T>>::try_as(&self)
}
}
impl TryAs<$v2> for $n {
fn try_as(&self) -> Result<&$v2, ()> {
match &self {
Self::V2(ref x) => Ok(x),
_ => Err(()),
}
}
}
impl TryAs<$v3> for $n {
fn try_as(&self) -> Result<&$v3, ()> {
match &self {
Self::V3(ref x) => Ok(x),
_ => Err(()),
}
}
}
impl IntoVersion for $n {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
2 => Self::V2(self.try_into()?),
3 => Self::V3(self.try_into()?),
_ => return Err(()),
})
}
}
impl From<$v2> for $n {
fn from(x: $v2) -> Self {
$n::V2(x)
}
}
impl<T: Into<$v3>> From<T> for $n {
fn from(x: T) -> Self {
$n::V3(x.into())
}
}
impl TryFrom<$n> for $v2 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V2(x) => Ok(x),
V3(x) => x.try_into(),
}
}
}
impl TryFrom<$n> for $v3 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V2(x) => x.try_into(),
V3(x) => Ok(x),
}
}
}
impl MaxEncodedLen for $n {
fn max_encoded_len() -> usize {
<$v3>::max_encoded_len()
}
}
}
}
impl TryFrom<VersionedMultiLocation> for v1::MultiLocation {
type Error = ();
fn try_from(x: VersionedMultiLocation) -> Result<Self, ()> {
use VersionedMultiLocation::*;
match x {
V0(x) => x.try_into(),
V1(x) => Ok(x),
}
versioned_type! {
/// A single version's `Response` value, together with its version code.
pub enum VersionedAssetId {
V3(v3::AssetId),
}
}
/// A single `Response` value, together with its version code.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
pub enum VersionedResponse {
V0(v0::Response),
V1(v1::Response),
V2(v2::Response),
}
impl IntoVersion for VersionedResponse {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
0 => Self::V0(self.try_into()?),
1 => Self::V1(self.try_into()?),
2 => Self::V2(self.try_into()?),
_ => return Err(()),
})
versioned_type! {
/// A single version's `Response` value, together with its version code.
pub enum VersionedResponse {
V2(v2::Response),
V3(v3::Response),
}
}
impl From<v0::Response> for VersionedResponse {
fn from(x: v0::Response) -> Self {
VersionedResponse::V0(x)
versioned_type! {
/// A single `MultiLocation` value, together with its version code.
#[derive(Ord, PartialOrd)]
pub enum VersionedMultiLocation {
V2(v2::MultiLocation),
V3(v3::MultiLocation),
}
}
impl From<v1::Response> for VersionedResponse {
fn from(x: v1::Response) -> Self {
VersionedResponse::V1(x)
versioned_type! {
/// A single `InteriorMultiLocation` value, together with its version code.
pub enum VersionedInteriorMultiLocation {
V2(v2::InteriorMultiLocation),
V3(v3::InteriorMultiLocation),
}
}
impl<T: Into<v2::Response>> From<T> for VersionedResponse {
fn from(x: T) -> Self {
VersionedResponse::V2(x.into())
versioned_type! {
/// A single `MultiAsset` value, together with its version code.
pub enum VersionedMultiAsset {
V2(v2::MultiAsset),
V3(v3::MultiAsset),
}
}
impl TryFrom<VersionedResponse> for v0::Response {
type Error = ();
fn try_from(x: VersionedResponse) -> Result<Self, ()> {
use VersionedResponse::*;
match x {
V0(x) => Ok(x),
V1(x) => x.try_into(),
V2(x) => VersionedResponse::V1(x.try_into()?).try_into(),
}
}
}
impl TryFrom<VersionedResponse> for v1::Response {
type Error = ();
fn try_from(x: VersionedResponse) -> Result<Self, ()> {
use VersionedResponse::*;
match x {
V0(x) => x.try_into(),
V1(x) => Ok(x),
V2(x) => x.try_into(),
}
}
}
impl TryFrom<VersionedResponse> for v2::Response {
type Error = ();
fn try_from(x: VersionedResponse) -> Result<Self, ()> {
use VersionedResponse::*;
match x {
V0(x) => VersionedResponse::V1(x.try_into()?).try_into(),
V1(x) => x.try_into(),
V2(x) => Ok(x),
}
}
}
/// A single `MultiAsset` value, together with its version code.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
pub enum VersionedMultiAsset {
V0(v0::MultiAsset),
V1(v1::MultiAsset),
}
impl IntoVersion for VersionedMultiAsset {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
0 => Self::V0(self.try_into()?),
1 | 2 => Self::V1(self.try_into()?),
_ => return Err(()),
})
}
}
impl From<v0::MultiAsset> for VersionedMultiAsset {
fn from(x: v0::MultiAsset) -> Self {
VersionedMultiAsset::V0(x)
}
}
impl<T: Into<v1::MultiAsset>> From<T> for VersionedMultiAsset {
fn from(x: T) -> Self {
VersionedMultiAsset::V1(x.into())
}
}
impl TryFrom<VersionedMultiAsset> for v0::MultiAsset {
type Error = ();
fn try_from(x: VersionedMultiAsset) -> Result<Self, ()> {
use VersionedMultiAsset::*;
match x {
V0(x) => Ok(x),
V1(x) => x.try_into(),
}
}
}
impl TryFrom<VersionedMultiAsset> for v1::MultiAsset {
type Error = ();
fn try_from(x: VersionedMultiAsset) -> Result<Self, ()> {
use VersionedMultiAsset::*;
match x {
V0(x) => x.try_into(),
V1(x) => Ok(x),
}
}
}
/// A single `MultiAssets` value, together with its version code.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
pub enum VersionedMultiAssets {
V0(Vec<v0::MultiAsset>),
V1(v1::MultiAssets),
}
impl IntoVersion for VersionedMultiAssets {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
0 => Self::V0(self.try_into()?),
1 | 2 => Self::V1(self.try_into()?),
_ => return Err(()),
})
}
}
impl From<Vec<v0::MultiAsset>> for VersionedMultiAssets {
fn from(x: Vec<v0::MultiAsset>) -> Self {
VersionedMultiAssets::V0(x)
}
}
impl<T: Into<v1::MultiAssets>> From<T> for VersionedMultiAssets {
fn from(x: T) -> Self {
VersionedMultiAssets::V1(x.into())
}
}
impl TryFrom<VersionedMultiAssets> for Vec<v0::MultiAsset> {
type Error = ();
fn try_from(x: VersionedMultiAssets) -> Result<Self, ()> {
use VersionedMultiAssets::*;
match x {
V0(x) => Ok(x),
V1(x) => x.try_into(),
}
}
}
impl TryFrom<VersionedMultiAssets> for v1::MultiAssets {
type Error = ();
fn try_from(x: VersionedMultiAssets) -> Result<Self, ()> {
use VersionedMultiAssets::*;
match x {
V0(x) => x.try_into(),
V1(x) => Ok(x),
}
versioned_type! {
/// A single `MultiAssets` value, together with its version code.
pub enum VersionedMultiAssets {
V2(v2::MultiAssets),
V3(v3::MultiAssets),
}
}
@@ -310,61 +355,31 @@ impl TryFrom<VersionedMultiAssets> for v1::MultiAssets {
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
pub enum VersionedXcm<RuntimeCall> {
V0(v0::Xcm<RuntimeCall>),
V1(v1::Xcm<RuntimeCall>),
#[codec(index = 2)]
V2(v2::Xcm<RuntimeCall>),
#[codec(index = 3)]
V3(v3::Xcm<RuntimeCall>),
}
impl<C> IntoVersion for VersionedXcm<C> {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
0 => Self::V0(self.try_into()?),
1 => Self::V1(self.try_into()?),
2 => Self::V2(self.try_into()?),
3 => Self::V3(self.try_into()?),
_ => return Err(()),
})
}
}
impl<RuntimeCall> From<v0::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v0::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V0(x)
}
}
impl<RuntimeCall> From<v1::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v1::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V1(x)
}
}
impl<RuntimeCall> From<v2::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v2::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V2(x)
}
}
impl<RuntimeCall> TryFrom<VersionedXcm<RuntimeCall>> for v0::Xcm<RuntimeCall> {
type Error = ();
fn try_from(x: VersionedXcm<RuntimeCall>) -> Result<Self, ()> {
use VersionedXcm::*;
match x {
V0(x) => Ok(x),
V1(x) => x.try_into(),
V2(x) => V1(x.try_into()?).try_into(),
}
}
}
impl<RuntimeCall> TryFrom<VersionedXcm<RuntimeCall>> for v1::Xcm<RuntimeCall> {
type Error = ();
fn try_from(x: VersionedXcm<RuntimeCall>) -> Result<Self, ()> {
use VersionedXcm::*;
match x {
V0(x) => x.try_into(),
V1(x) => Ok(x),
V2(x) => x.try_into(),
}
impl<RuntimeCall> From<v3::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v3::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V3(x)
}
}
@@ -373,9 +388,19 @@ impl<RuntimeCall> TryFrom<VersionedXcm<RuntimeCall>> for v2::Xcm<RuntimeCall> {
fn try_from(x: VersionedXcm<RuntimeCall>) -> Result<Self, ()> {
use VersionedXcm::*;
match x {
V0(x) => V1(x.try_into()?).try_into(),
V1(x) => x.try_into(),
V2(x) => Ok(x),
V3(x) => x.try_into(),
}
}
}
impl<Call> TryFrom<VersionedXcm<Call>> for v3::Xcm<Call> {
type Error = ();
fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
use VersionedXcm::*;
match x {
V2(x) => x.try_into(),
V3(x) => Ok(x),
}
}
}
@@ -398,28 +423,6 @@ impl WrapVersion for () {
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to version 0 before wrapping it.
pub struct AlwaysV0;
impl WrapVersion for AlwaysV0 {
fn wrap_version<RuntimeCall>(
_: &latest::MultiLocation,
xcm: impl Into<VersionedXcm<RuntimeCall>>,
) -> Result<VersionedXcm<RuntimeCall>, ()> {
Ok(VersionedXcm::<RuntimeCall>::V0(xcm.into().try_into()?))
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to version 1 before wrapping it.
pub struct AlwaysV1;
impl WrapVersion for AlwaysV1 {
fn wrap_version<RuntimeCall>(
_: &latest::MultiLocation,
xcm: impl Into<VersionedXcm<RuntimeCall>>,
) -> Result<VersionedXcm<RuntimeCall>, ()> {
Ok(VersionedXcm::<RuntimeCall>::V1(xcm.into().try_into()?))
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to version 2 before wrapping it.
pub struct AlwaysV2;
impl WrapVersion for AlwaysV2 {
@@ -431,42 +434,54 @@ impl WrapVersion for AlwaysV2 {
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to the latest version before wrapping it.
pub type AlwaysLatest = AlwaysV1;
/// `WrapVersion` implementation which attempts to always convert the XCM to version 2 before wrapping it.
pub struct AlwaysV3;
impl WrapVersion for AlwaysV3 {
fn wrap_version<Call>(
_: &latest::MultiLocation,
xcm: impl Into<VersionedXcm<Call>>,
) -> Result<VersionedXcm<Call>, ()> {
Ok(VersionedXcm::<Call>::V3(xcm.into().try_into()?))
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to the release version before wrapping it.
pub type AlwaysRelease = AlwaysV0;
/// `WrapVersion` implementation which attempts to always convert the XCM to the latest version
/// before wrapping it.
pub type AlwaysLatest = AlwaysV3;
/// `WrapVersion` implementation which attempts to always convert the XCM to the most recent Long-
/// Term-Support version before wrapping it.
pub type AlwaysLts = AlwaysV3;
pub mod prelude {
pub use super::{
latest::prelude::*, AlwaysLatest, AlwaysRelease, AlwaysV0, AlwaysV1, AlwaysV2, IntoVersion,
Unsupported, Version as XcmVersion, VersionedMultiAsset, VersionedMultiAssets,
VersionedMultiLocation, VersionedResponse, VersionedXcm, WrapVersion,
latest::prelude::*, AlwaysLatest, AlwaysLts, AlwaysV2, AlwaysV3, IntoVersion, Unsupported,
Version as XcmVersion, VersionedAssetId, VersionedInteriorMultiLocation,
VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation, VersionedResponse,
VersionedXcm, WrapVersion,
};
}
pub mod opaque {
pub mod v0 {
// Everything from v0
pub use crate::v0::*;
// Then override with the opaque types in v0
pub use crate::v0::opaque::{Order, Xcm};
}
pub mod v1 {
// Everything from v1
pub use crate::v1::*;
// Then override with the opaque types in v1
pub use crate::v1::opaque::{Order, Xcm};
}
pub mod v2 {
// Everything from v1
// Everything from v2
pub use crate::v2::*;
// Then override with the opaque types in v2
pub use crate::v2::opaque::{Instruction, Xcm};
}
pub mod v3 {
// Everything from v3
pub use crate::v3::*;
// Then override with the opaque types in v3
pub use crate::v3::opaque::{Instruction, Xcm};
}
pub mod latest {
pub use super::v2::*;
pub use super::v3::*;
}
pub mod lts {
pub use super::v3::*;
}
/// The basic `VersionedXcm` type which just uses the `Vec<u8>` as an encoded call.
@@ -477,3 +492,9 @@ pub mod opaque {
pub trait GetWeight<W> {
fn weight(&self) -> latest::Weight;
}
#[test]
fn conversion_works() {
use latest::prelude::*;
let _: VersionedMultiAssets = (Here, 1u128).into();
}
-214
View File
@@ -1,214 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Support data structures for `MultiLocation`, primarily the `Junction` datatype.
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::{traits::ConstU32, WeakBoundedVec};
/// A global identifier of an account-bearing consensus system.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
pub enum NetworkId {
/// Unidentified/any.
Any,
/// Some named network.
Named(WeakBoundedVec<u8, ConstU32<32>>),
/// The Polkadot Relay chain
Polkadot,
/// Kusama.
Kusama,
}
/// An identifier of a pluralistic body.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
pub enum BodyId {
/// The only body in its context.
Unit,
/// A named body.
Named(WeakBoundedVec<u8, ConstU32<32>>),
/// An indexed body.
Index(#[codec(compact)] u32),
/// The unambiguous executive body (for Polkadot, this would be the Polkadot council).
Executive,
/// The unambiguous technical body (for Polkadot, this would be the Technical Committee).
Technical,
/// The unambiguous legislative body (for Polkadot, this could be considered the opinion of a majority of
/// lock-voters).
Legislative,
/// The unambiguous judicial body (this doesn't exist on Polkadot, but if it were to get a "grand oracle", it
/// may be considered as that).
Judicial,
/// The unambiguous defense body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `staking_admin` track).
Defense,
/// The unambiguous administration body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `general_admin` track).
Administration,
/// The unambiguous treasury body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `treasurer` track).
Treasury,
}
/// A part of a pluralistic body.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
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 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(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
pub enum Junction {
/// The consensus system of which the context is a member and state-wise super-set.
///
/// NOTE: This item is *not* a sub-consensus item: a consensus system may not identify itself trustlessly as
/// a location that includes this junction.
Parent,
/// An indexed parachain belonging to and operated by the context.
///
/// Generally used when the context is a Polkadot Relay-chain.
Parachain(#[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: 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: 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: 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 datum 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.
GeneralKey(WeakBoundedVec<u8, ConstU32<32>>),
/// The unambiguous child.
///
/// Not currently used except as a fallback when deriving ancestry.
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 },
}
impl From<crate::v1::Junction> for Junction {
fn from(v1: crate::v1::Junction) -> Junction {
use crate::v1::Junction::*;
match v1 {
Parachain(id) => Self::Parachain(id),
AccountId32 { network, id } => Self::AccountId32 { network, id },
AccountIndex64 { network, index } => Self::AccountIndex64 { network, index },
AccountKey20 { network, key } => Self::AccountKey20 { network, key },
PalletInstance(index) => Self::PalletInstance(index),
GeneralIndex(index) => Self::GeneralIndex(index),
GeneralKey(key) => Self::GeneralKey(key),
OnlyChild => Self::OnlyChild,
Plurality { id, part } => Self::Plurality { id, part },
}
}
}
impl Junction {
/// Returns true if this junction is a `Parent` item.
pub fn is_parent(&self) -> bool {
match self {
Junction::Parent => true,
_ => false,
}
}
/// Returns true if this junction can be considered an interior part of its context. This is generally `true`,
/// except for the `Parent` item.
pub fn is_interior(&self) -> bool {
match self {
Junction::Parent => false,
Junction::Parachain(..) |
Junction::AccountId32 { .. } |
Junction::AccountIndex64 { .. } |
Junction::AccountKey20 { .. } |
Junction::PalletInstance { .. } |
Junction::GeneralIndex { .. } |
Junction::GeneralKey(..) |
Junction::OnlyChild |
Junction::Plurality { .. } => true,
}
}
}
-389
View File
@@ -1,389 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Version 0 of the Cross-Consensus Message format data structures.
use crate::DoubleEncoded;
use alloc::vec::Vec;
use core::result;
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
use scale_info::TypeInfo;
mod junction;
mod multi_asset;
mod multi_location;
mod order;
mod traits;
use super::v1::{MultiLocation as MultiLocation1, Response as Response1, Xcm as Xcm1};
pub use junction::{BodyId, BodyPart, Junction, NetworkId};
pub use multi_asset::{AssetInstance, MultiAsset};
pub use multi_location::MultiLocation::{self, *};
pub use order::Order;
pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm};
/// A prelude for importing all types typically used when interacting with XCM messages.
pub mod prelude {
pub use super::{
junction::{BodyId, Junction::*},
multi_asset::{
AssetInstance::{self, *},
MultiAsset::{self, *},
},
multi_location::MultiLocation::{self, *},
order::Order::{self, *},
traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm},
Junction::*,
OriginKind,
Xcm::{self, *},
};
}
// TODO: #2841 #XCMENCODE Efficient encodings for MultiAssets, Vec<Order>, using initial byte values 128+ to encode
// the number of items in the vector.
/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`.
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
pub enum OriginKind {
/// Origin should just be the native dispatch origin representation for the sender in the
/// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin
/// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a
/// primary/native dispatch origin form.
Native,
/// Origin should just be the standard account-based origin with the sovereign account of
/// the sender. For Cumulus/Frame chains, this is the `Signed` origin.
SovereignAccount,
/// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin.
/// This will not usually be an available option.
Superuser,
/// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be
/// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be
/// the `pallet_xcm::Origin::Xcm` type.
Xcm,
}
/// Response data to a query.
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
pub enum Response {
/// Some assets.
Assets(Vec<MultiAsset>),
}
/// Cross-Consensus Message: A message from one consensus system to another.
///
/// Consensus systems that may send and receive messages include blockchains and smart contracts.
///
/// All messages are delivered from a known *origin*, expressed as a `MultiLocation`.
///
/// This is the inner XCM format and is version-sensitive. Messages are typically passed using the outer
/// XCM format, known as `VersionedXcm`.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
pub enum Xcm<RuntimeCall> {
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into `holding`. Execute the
/// orders (`effects`).
///
/// - `assets`: The asset(s) to be withdrawn into holding.
/// - `effects`: The order(s) to execute on the holding account.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 0)]
WithdrawAsset { assets: Vec<MultiAsset>, effects: Vec<Order<RuntimeCall>> },
/// Asset(s) (`assets`) have been received into the ownership of this system on the `origin` system.
///
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
/// been placed into `holding`.
///
/// - `assets`: The asset(s) that are minted into holding.
/// - `effects`: The order(s) to execute on the holding account.
///
/// Safety: `origin` must be trusted to have received and be storing `assets` such that they may later be
/// withdrawn should this system send a corresponding message.
///
/// Kind: *Trusted Indication*.
///
/// Errors:
#[codec(index = 1)]
ReserveAssetDeposit { assets: Vec<MultiAsset>, effects: Vec<Order<RuntimeCall>> },
/// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should be
/// created on this system.
///
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
/// been placed into `holding`.
///
/// - `assets`: The asset(s) that are minted into holding.
/// - `effects`: The order(s) to execute on the holding account.
///
/// Safety: `origin` must be trusted to have irrevocably destroyed the `assets` prior as a consequence of
/// sending this message.
///
/// Kind: *Trusted Indication*.
///
/// Errors:
#[codec(index = 2)]
TeleportAsset { assets: Vec<MultiAsset>, effects: Vec<Order<RuntimeCall>> },
/// Indication of the contents of the holding account corresponding to the `QueryHolding` order of `query_id`.
///
/// - `query_id`: The identifier of the query that resulted in this message being sent.
/// - `assets`: The message content.
///
/// Safety: No concerns.
///
/// Kind: *Information*.
///
/// Errors:
#[codec(index = 3)]
QueryResponse {
#[codec(compact)]
query_id: u64,
response: Response,
},
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
/// ownership of `dest` within this consensus system.
///
/// - `assets`: The asset(s) to be withdrawn.
/// - `dest`: The new owner for the assets.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 4)]
TransferAsset { assets: Vec<MultiAsset>, dest: MultiLocation },
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
/// ownership of `dest` within this consensus system.
///
/// Send an onward XCM message to `dest` of `ReserveAssetDeposit` with the given `effects`.
///
/// - `assets`: The asset(s) to be withdrawn.
/// - `dest`: The new owner for the assets.
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposit` which is sent onwards to
/// `dest`.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 5)]
TransferReserveAsset { assets: Vec<MultiAsset>, dest: MultiLocation, effects: Vec<Order<()>> },
/// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed by the kind
/// of origin `origin_type`.
///
/// - `origin_type`: The means of expressing the message origin as a dispatch origin.
/// - `max_weight`: The weight of `call`; this should be at least the chain's calculated weight and will
/// be used in the weight determination arithmetic.
/// - `call`: The encoded transaction to be applied.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 6)]
Transact {
origin_type: OriginKind,
require_weight_at_most: u64,
call: DoubleEncoded<RuntimeCall>,
},
/// A message to notify about a new incoming HRMP channel. This message is meant to be sent by the
/// relay-chain to a para.
///
/// - `sender`: The sender in the to-be opened channel. Also, the initiator of the channel opening.
/// - `max_message_size`: The maximum size of a message proposed by the sender.
/// - `max_capacity`: The maximum number of messages that can be queued in the channel.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
#[codec(index = 7)]
HrmpNewChannelOpenRequest {
#[codec(compact)]
sender: u32,
#[codec(compact)]
max_message_size: u32,
#[codec(compact)]
max_capacity: u32,
},
/// A message to notify about that a previously sent open channel request has been accepted by
/// the recipient. That means that the channel will be opened during the next relay-chain session
/// change. This message is meant to be sent by the relay-chain to a para.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
///
/// Errors:
#[codec(index = 8)]
HrmpChannelAccepted {
#[codec(compact)]
recipient: u32,
},
/// A message to notify that the other party in an open channel decided to close it. In particular,
/// `initiator` is going to close the channel opened from `sender` to the `recipient`. The close
/// will be enacted at the next relay-chain session change. This message is meant to be sent by
/// the relay-chain to a para.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
///
/// Errors:
#[codec(index = 9)]
HrmpChannelClosing {
#[codec(compact)]
initiator: u32,
#[codec(compact)]
sender: u32,
#[codec(compact)]
recipient: u32,
},
/// A message to indicate that the embedded XCM is actually arriving on behalf of some consensus
/// location within the origin.
///
/// Safety: `who` must be an interior location of the context. This basically means that no `Parent`
/// junctions are allowed in it. This should be verified at the time of XCM execution.
///
/// Kind: *Instruction*
///
/// Errors:
#[codec(index = 10)]
RelayedFrom { who: MultiLocation, message: alloc::boxed::Box<Xcm<RuntimeCall>> },
}
impl<RuntimeCall> Xcm<RuntimeCall> {
pub fn into<C>(self) -> Xcm<C> {
Xcm::from(self)
}
pub fn from<C>(xcm: Xcm<C>) -> Self {
use Xcm::*;
match xcm {
WithdrawAsset { assets, effects } =>
WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() },
ReserveAssetDeposit { assets, effects } => ReserveAssetDeposit {
assets,
effects: effects.into_iter().map(Order::into).collect(),
},
TeleportAsset { assets, effects } =>
TeleportAsset { assets, effects: effects.into_iter().map(Order::into).collect() },
QueryResponse { query_id, response } => QueryResponse { query_id, response },
TransferAsset { assets, dest } => TransferAsset { assets, dest },
TransferReserveAsset { assets, dest, effects } =>
TransferReserveAsset { assets, dest, effects },
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call: call.into() },
RelayedFrom { who, message } =>
RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) },
}
}
}
pub mod opaque {
/// The basic concrete type of `generic::Xcm`, which doesn't make any assumptions about the format of a
/// call other than it is pre-encoded.
pub type Xcm = super::Xcm<()>;
pub use super::order::opaque::*;
}
// Convert from a v1 response to a v0 response
impl TryFrom<Response1> for Response {
type Error = ();
fn try_from(new_response: Response1) -> result::Result<Self, ()> {
Ok(match new_response {
Response1::Assets(assets) => Self::Assets(assets.try_into()?),
Response1::Version(..) => return Err(()),
})
}
}
impl<RuntimeCall> TryFrom<Xcm1<RuntimeCall>> for Xcm<RuntimeCall> {
type Error = ();
fn try_from(x: Xcm1<RuntimeCall>) -> result::Result<Xcm<RuntimeCall>, ()> {
use Xcm::*;
Ok(match x {
Xcm1::WithdrawAsset { assets, effects } => WithdrawAsset {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::ReserveAssetDeposited { assets, effects } => ReserveAssetDeposit {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::ReceiveTeleportedAsset { assets, effects } => TeleportAsset {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::QueryResponse { query_id, response } =>
QueryResponse { query_id, response: response.try_into()? },
Xcm1::TransferAsset { assets, beneficiary } =>
TransferAsset { assets: assets.try_into()?, dest: beneficiary.try_into()? },
Xcm1::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset {
assets: assets.try_into()?,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
Xcm1::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
Xcm1::HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
Xcm1::Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call: call.into() },
Xcm1::RelayedFrom { who, message } => RelayedFrom {
who: MultiLocation1 { interior: who, parents: 0 }.try_into()?,
message: alloc::boxed::Box::new((*message).try_into()?),
},
Xcm1::SubscribeVersion { .. } | Xcm1::UnsubscribeVersion => return Err(()),
})
}
}
-407
View File
@@ -1,407 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use super::MultiLocation;
use crate::v1::{MultiAssetFilter, MultiAssets, WildMultiAsset};
use alloc::{vec, vec::Vec};
use core::result;
use parity_scale_codec::{self, Decode, Encode};
use scale_info::TypeInfo;
pub use crate::v1::AssetInstance;
/// A single general identifier for an asset.
///
/// Represents both fungible and non-fungible assets. May only be used to represent a single asset class.
///
/// Wildcards may or may not be allowed by the interpreting context.
///
/// Assets classes may be identified in one of two ways: either an abstract identifier or a concrete identifier.
/// Implementations may support only one of these. A single asset may be referenced from multiple asset identifiers,
/// though will tend to have only a single *preferred* identifier.
///
/// ### Abstract identifiers
///
/// Abstract identifiers are absolute identifiers that represent a notional asset which can exist within multiple
/// consensus systems. These tend to be simpler to deal with since their broad meaning is unchanged regardless stay of
/// the consensus system in which it is interpreted.
///
/// However, in the attempt to provide uniformity across consensus systems, they may conflate different instantiations
/// of some notional asset (e.g. the reserve asset and a local reserve-backed derivative of it) under the same name,
/// leading to confusion. It also implies that one notional asset is accounted for locally in only one way. This may not
/// be the case, e.g. where there are multiple bridge instances each providing a bridged "BTC" token yet none being
/// fungible between the others.
///
/// Since they are meant to be absolute and universal, a global registry is needed to ensure that name collisions do not
/// occur.
///
/// An abstract identifier is represented as a simple variable-size byte string. As of writing, no global registry
/// exists and no proposals have been put forth for asset labeling.
///
/// ### Concrete identifiers
///
/// Concrete identifiers are *relative identifiers* that specifically identify a single asset through its location in a
/// consensus system relative to the context interpreting. Use of a `MultiLocation` ensures that similar but non
/// fungible variants of the same underlying asset can be properly distinguished, and obviates the need for any kind of
/// central registry.
///
/// The limitation is that the asset identifier cannot be trivially copied between consensus systems and must instead be
/// "re-anchored" whenever being moved to a new consensus system, using the two systems' relative paths.
///
/// Throughout XCM, messages are authored such that *when interpreted from the receiver's point of view* they will have
/// the desired meaning/effect. This means that relative paths should always by constructed to be read from the point of
/// view of the receiving system, *which may be have a completely different meaning in the authoring system*.
///
/// Concrete identifiers are the preferred way of identifying an asset since they are entirely unambiguous.
///
/// A concrete identifier is represented by a `MultiLocation`. If a system has an unambiguous primary asset (such as
/// Bitcoin with BTC or Ethereum with ETH), then it will conventionally be identified as the chain itself. Alternative
/// and more specific ways of referring to an asset within a system include:
///
/// - `<chain>/PalletInstance(<id>)` for a Frame chain with a single-asset pallet instance (such as an instance of the
/// Balances pallet).
/// - `<chain>/PalletInstance(<id>)/GeneralIndex(<index>)` for a Frame chain with an indexed multi-asset pallet instance
/// (such as an instance of the Assets pallet).
/// - `<chain>/AccountId32` for an ERC-20-style single-asset smart-contract on a Frame-based contracts chain.
/// - `<chain>/AccountKey20` for an ERC-20-style single-asset smart-contract on an Ethereum-like chain.
///
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)]
pub enum MultiAsset {
/// No assets. Rarely used.
None,
/// All assets. Typically used for the subset of assets to be used for an `Order`, and in that context means
/// "all assets currently in holding".
All,
/// All fungible assets. Typically used for the subset of assets to be used for an `Order`, and in that context
/// means "all fungible assets currently in holding".
AllFungible,
/// All non-fungible assets. Typically used for the subset of assets to be used for an `Order`, and in that
/// context means "all non-fungible assets currently in holding".
AllNonFungible,
/// All fungible assets of a given abstract asset `id`entifier.
AllAbstractFungible { id: Vec<u8> },
/// All non-fungible assets of a given abstract asset `class`.
AllAbstractNonFungible { class: Vec<u8> },
/// All fungible assets of a given concrete asset `id`entifier.
AllConcreteFungible { id: MultiLocation },
/// All non-fungible assets of a given concrete asset `class`.
AllConcreteNonFungible { class: MultiLocation },
/// Some specific `amount` of the fungible asset identified by an abstract `id`.
AbstractFungible {
id: Vec<u8>,
#[codec(compact)]
amount: u128,
},
/// Some specific `instance` of the non-fungible asset whose `class` is identified abstractly.
AbstractNonFungible { class: Vec<u8>, instance: AssetInstance },
/// Some specific `amount` of the fungible asset identified by an concrete `id`.
ConcreteFungible {
id: MultiLocation,
#[codec(compact)]
amount: u128,
},
/// Some specific `instance` of the non-fungible asset whose `class` is identified concretely.
ConcreteNonFungible { class: MultiLocation, instance: AssetInstance },
}
impl MultiAsset {
/// Returns `true` if the `MultiAsset` is a wildcard and can refer to classes of assets, instead of just one.
///
/// Typically can also be inferred by the name starting with `All`.
pub fn is_wildcard(&self) -> bool {
match self {
MultiAsset::None |
MultiAsset::AbstractFungible { .. } |
MultiAsset::AbstractNonFungible { .. } |
MultiAsset::ConcreteFungible { .. } |
MultiAsset::ConcreteNonFungible { .. } => false,
MultiAsset::All |
MultiAsset::AllFungible |
MultiAsset::AllNonFungible |
MultiAsset::AllAbstractFungible { .. } |
MultiAsset::AllConcreteFungible { .. } |
MultiAsset::AllAbstractNonFungible { .. } |
MultiAsset::AllConcreteNonFungible { .. } => true,
}
}
fn is_none(&self) -> bool {
match self {
MultiAsset::None |
MultiAsset::AbstractFungible { amount: 0, .. } |
MultiAsset::ConcreteFungible { amount: 0, .. } => true,
_ => false,
}
}
fn is_fungible(&self) -> bool {
match self {
MultiAsset::All |
MultiAsset::AllFungible |
MultiAsset::AllAbstractFungible { .. } |
MultiAsset::AllConcreteFungible { .. } |
MultiAsset::AbstractFungible { .. } |
MultiAsset::ConcreteFungible { .. } => true,
_ => false,
}
}
fn is_non_fungible(&self) -> bool {
match self {
MultiAsset::All |
MultiAsset::AllNonFungible |
MultiAsset::AllAbstractNonFungible { .. } |
MultiAsset::AllConcreteNonFungible { .. } |
MultiAsset::AbstractNonFungible { .. } |
MultiAsset::ConcreteNonFungible { .. } => true,
_ => false,
}
}
fn is_concrete_fungible(&self, id: &MultiLocation) -> bool {
match self {
MultiAsset::AllFungible => true,
MultiAsset::AllConcreteFungible { id: i } |
MultiAsset::ConcreteFungible { id: i, .. } => i == id,
_ => false,
}
}
fn is_abstract_fungible(&self, id: &[u8]) -> bool {
match self {
MultiAsset::AllFungible => true,
MultiAsset::AllAbstractFungible { id: i } |
MultiAsset::AbstractFungible { id: i, .. } => i == id,
_ => false,
}
}
fn is_concrete_non_fungible(&self, class: &MultiLocation) -> bool {
match self {
MultiAsset::AllNonFungible => true,
MultiAsset::AllConcreteNonFungible { class: i } |
MultiAsset::ConcreteNonFungible { class: i, .. } => i == class,
_ => false,
}
}
fn is_abstract_non_fungible(&self, class: &[u8]) -> bool {
match self {
MultiAsset::AllNonFungible => true,
MultiAsset::AllAbstractNonFungible { class: i } |
MultiAsset::AbstractNonFungible { class: i, .. } => i == class,
_ => false,
}
}
fn is_all(&self) -> bool {
matches!(self, MultiAsset::All)
}
/// Returns true if `self` is a super-set of the given `inner`.
///
/// Typically, any wildcard is never contained in anything else, and a wildcard can contain any other non-wildcard.
/// For more details, see the implementation and tests.
pub fn contains(&self, inner: &MultiAsset) -> bool {
use MultiAsset::*;
// Inner cannot be wild
if inner.is_wildcard() {
return false
}
// Everything contains nothing.
if inner.is_none() {
return true
}
// Everything contains anything.
if self.is_all() {
return true
}
// Nothing contains nothing.
if self.is_none() {
return false
}
match self {
// Anything fungible contains "all fungibles"
AllFungible => inner.is_fungible(),
// Anything non-fungible contains "all non-fungibles"
AllNonFungible => inner.is_non_fungible(),
AllConcreteFungible { id } => inner.is_concrete_fungible(id),
AllAbstractFungible { id } => inner.is_abstract_fungible(id),
AllConcreteNonFungible { class } => inner.is_concrete_non_fungible(class),
AllAbstractNonFungible { class } => inner.is_abstract_non_fungible(class),
ConcreteFungible { id, amount } => matches!(
inner,
ConcreteFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount
),
AbstractFungible { id, amount } => matches!(
inner,
AbstractFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount
),
ConcreteNonFungible { .. } => self == inner,
AbstractNonFungible { .. } => self == inner,
_ => false,
}
}
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
use MultiAsset::*;
match self {
AllConcreteFungible { ref mut id } |
AllConcreteNonFungible { class: ref mut id } |
ConcreteFungible { ref mut id, .. } |
ConcreteNonFungible { class: ref mut id, .. } =>
id.prepend_with(prepend.clone()).map_err(|_| ()),
_ => Ok(()),
}
}
}
impl TryFrom<crate::v1::MultiAsset> for MultiAsset {
type Error = ();
fn try_from(m: crate::v1::MultiAsset) -> result::Result<MultiAsset, ()> {
use crate::v1::{AssetId::*, Fungibility::*};
use MultiAsset::*;
Ok(match (m.id, m.fun) {
(Concrete(id), Fungible(amount)) => ConcreteFungible { id: id.try_into()?, amount },
(Concrete(class), NonFungible(instance)) =>
ConcreteNonFungible { class: class.try_into()?, instance },
(Abstract(id), Fungible(amount)) => AbstractFungible { id, amount },
(Abstract(class), NonFungible(instance)) => AbstractNonFungible { class, instance },
})
}
}
impl TryFrom<MultiAssets> for Vec<MultiAsset> {
type Error = ();
fn try_from(m: MultiAssets) -> result::Result<Vec<MultiAsset>, ()> {
m.drain().into_iter().map(MultiAsset::try_from).collect()
}
}
impl TryFrom<WildMultiAsset> for MultiAsset {
type Error = ();
fn try_from(m: WildMultiAsset) -> result::Result<MultiAsset, ()> {
use crate::v1::{AssetId::*, WildFungibility::*};
use MultiAsset::*;
Ok(match m {
WildMultiAsset::All => All,
WildMultiAsset::AllOf { id, fun } => match (id, fun) {
(Concrete(id), Fungible) => AllConcreteFungible { id: id.try_into()? },
(Concrete(class), NonFungible) =>
AllConcreteNonFungible { class: class.try_into()? },
(Abstract(id), Fungible) => AllAbstractFungible { id },
(Abstract(class), NonFungible) => AllAbstractNonFungible { class },
},
})
}
}
impl TryFrom<WildMultiAsset> for Vec<MultiAsset> {
type Error = ();
fn try_from(m: WildMultiAsset) -> result::Result<Vec<MultiAsset>, ()> {
Ok(vec![m.try_into()?])
}
}
impl TryFrom<MultiAssetFilter> for Vec<MultiAsset> {
type Error = ();
fn try_from(m: MultiAssetFilter) -> result::Result<Vec<MultiAsset>, ()> {
match m {
MultiAssetFilter::Definite(assets) => assets.try_into(),
MultiAssetFilter::Wild(wildcard) => wildcard.try_into(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn contains_works() {
use alloc::vec;
use MultiAsset::*;
// trivial case: all contains any non-wildcard.
assert!(All.contains(&None));
assert!(All.contains(&AbstractFungible { id: alloc::vec![99u8], amount: 1 }));
// trivial case: none contains nothing, except itself.
assert!(None.contains(&None));
assert!(!None.contains(&AllFungible));
assert!(!None.contains(&All));
// A bit more sneaky: Nothing can contain wildcard, even All ir the thing itself.
assert!(!All.contains(&All));
assert!(!All.contains(&AllFungible));
assert!(!AllFungible.contains(&AllFungible));
assert!(!AllNonFungible.contains(&AllNonFungible));
// For fungibles, containing is basically equality, or equal id with higher amount.
assert!(!AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![1u8], amount: 99 }));
assert!(AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![99u8], amount: 99 }));
assert!(AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![99u8], amount: 9 }));
assert!(!AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![99u8], amount: 100 }));
// For non-fungibles, containing is equality.
assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) }
.contains(&AbstractNonFungible {
class: vec![98u8],
instance: AssetInstance::Index(9)
}));
assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(8) }
.contains(&AbstractNonFungible {
class: vec![99u8],
instance: AssetInstance::Index(9)
}));
assert!(AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) }
.contains(&AbstractNonFungible {
class: vec![99u8],
instance: AssetInstance::Index(9)
}));
}
}
-745
View File
@@ -1,745 +0,0 @@
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use super::Junction;
use core::{mem, result};
use parity_scale_codec::{self, Decode, Encode};
/// 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 parachain.
/// - A layer-0 super-chain, e.g. the Polkadot 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 a
/// number of *junctions*, each morphing the previous location, either diving down into one of its internal locations,
/// called a *sub-consensus*, or going up into its parent location. Correct `MultiLocation` values must have all
/// `Parent` junctions as a prefix to all *sub-consensus* junctions.
///
/// This specific `MultiLocation` implementation uses a Rust `enum` in order to make pattern matching easier.
///
/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, scale_info::TypeInfo)]
pub enum MultiLocation {
/// The interpreting consensus system.
Null,
/// 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),
}
/// Maximum number of junctions a `MultiLocation` can contain.
pub const MAX_MULTILOCATION_LENGTH: usize = 8;
xcm_procedural::impl_conversion_functions_for_multilocation_v0!();
pub struct MultiLocationIterator(MultiLocation);
impl Iterator for MultiLocationIterator {
type Item = Junction;
fn next(&mut self) -> Option<Junction> {
self.0.take_first()
}
}
pub struct MultiLocationReverseIterator(MultiLocation);
impl Iterator for MultiLocationReverseIterator {
type Item = Junction;
fn next(&mut self) -> Option<Junction> {
self.0.take_last()
}
}
pub struct MultiLocationRefIterator<'a>(&'a MultiLocation, usize);
impl<'a> Iterator for MultiLocationRefIterator<'a> {
type Item = &'a Junction;
fn next(&mut self) -> Option<&'a Junction> {
let result = self.0.at(self.1);
self.1 += 1;
result
}
}
pub struct MultiLocationReverseRefIterator<'a>(&'a MultiLocation, usize);
impl<'a> Iterator for MultiLocationReverseRefIterator<'a> {
type Item = &'a Junction;
fn next(&mut self) -> Option<&'a Junction> {
self.1 += 1;
self.0.at(self.0.len().checked_sub(self.1)?)
}
}
impl MultiLocation {
/// Returns first junction, or `None` if the location is empty.
pub fn first(&self) -> Option<&Junction> {
match &self {
MultiLocation::Null => None,
MultiLocation::X1(ref a) => Some(a),
MultiLocation::X2(ref a, ..) => Some(a),
MultiLocation::X3(ref a, ..) => Some(a),
MultiLocation::X4(ref a, ..) => Some(a),
MultiLocation::X5(ref a, ..) => Some(a),
MultiLocation::X6(ref a, ..) => Some(a),
MultiLocation::X7(ref a, ..) => Some(a),
MultiLocation::X8(ref a, ..) => Some(a),
}
}
/// Returns last junction, or `None` if the location is empty.
pub fn last(&self) -> Option<&Junction> {
match &self {
MultiLocation::Null => None,
MultiLocation::X1(ref a) => Some(a),
MultiLocation::X2(.., ref a) => Some(a),
MultiLocation::X3(.., ref a) => Some(a),
MultiLocation::X4(.., ref a) => Some(a),
MultiLocation::X5(.., ref a) => Some(a),
MultiLocation::X6(.., ref a) => Some(a),
MultiLocation::X7(.., ref a) => Some(a),
MultiLocation::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) -> (MultiLocation, Option<Junction>) {
match self {
MultiLocation::Null => (MultiLocation::Null, None),
MultiLocation::X1(a) => (MultiLocation::Null, Some(a)),
MultiLocation::X2(a, b) => (MultiLocation::X1(b), Some(a)),
MultiLocation::X3(a, b, c) => (MultiLocation::X2(b, c), Some(a)),
MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(b, c, d), Some(a)),
MultiLocation::X5(a, b, c, d, e) => (MultiLocation::X4(b, c, d, e), Some(a)),
MultiLocation::X6(a, b, c, d, e, f) => (MultiLocation::X5(b, c, d, e, f), Some(a)),
MultiLocation::X7(a, b, c, d, e, f, g) =>
(MultiLocation::X6(b, c, d, e, f, g), Some(a)),
MultiLocation::X8(a, b, c, d, e, f, g, h) =>
(MultiLocation::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) -> (MultiLocation, Option<Junction>) {
match self {
MultiLocation::Null => (MultiLocation::Null, None),
MultiLocation::X1(a) => (MultiLocation::Null, Some(a)),
MultiLocation::X2(a, b) => (MultiLocation::X1(a), Some(b)),
MultiLocation::X3(a, b, c) => (MultiLocation::X2(a, b), Some(c)),
MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(a, b, c), Some(d)),
MultiLocation::X5(a, b, c, d, e) => (MultiLocation::X4(a, b, c, d), Some(e)),
MultiLocation::X6(a, b, c, d, e, f) => (MultiLocation::X5(a, b, c, d, e), Some(f)),
MultiLocation::X7(a, b, c, d, e, f, g) =>
(MultiLocation::X6(a, b, c, d, e, f), Some(g)),
MultiLocation::X8(a, b, c, d, e, f, g, h) =>
(MultiLocation::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 = MultiLocation::Null;
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 = MultiLocation::Null;
mem::swap(&mut *self, &mut d);
let (head, tail) = d.split_last();
*self = head;
tail
}
/// 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(self, new: Junction) -> result::Result<Self, Self> {
Ok(match self {
MultiLocation::Null => MultiLocation::X1(new),
MultiLocation::X1(a) => MultiLocation::X2(a, new),
MultiLocation::X2(a, b) => MultiLocation::X3(a, b, new),
MultiLocation::X3(a, b, c) => MultiLocation::X4(a, b, c, new),
MultiLocation::X4(a, b, c, d) => MultiLocation::X5(a, b, c, d, new),
MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(a, b, c, d, e, new),
MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(a, b, c, d, e, f, new),
MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(a, b, c, d, e, f, g, new),
s => Err(s)?,
})
}
/// 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(self, new: Junction) -> result::Result<Self, Self> {
Ok(match self {
MultiLocation::Null => MultiLocation::X1(new),
MultiLocation::X1(a) => MultiLocation::X2(new, a),
MultiLocation::X2(a, b) => MultiLocation::X3(new, a, b),
MultiLocation::X3(a, b, c) => MultiLocation::X4(new, a, b, c),
MultiLocation::X4(a, b, c, d) => MultiLocation::X5(new, a, b, c, d),
MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(new, a, b, c, d, e),
MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(new, a, b, c, d, e, f),
MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(new, a, b, c, d, e, f, g),
s => Err(s)?,
})
}
/// Returns the number of junctions in `self`.
pub fn len(&self) -> usize {
match &self {
MultiLocation::Null => 0,
MultiLocation::X1(..) => 1,
MultiLocation::X2(..) => 2,
MultiLocation::X3(..) => 3,
MultiLocation::X4(..) => 4,
MultiLocation::X5(..) => 5,
MultiLocation::X6(..) => 6,
MultiLocation::X7(..) => 7,
MultiLocation::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, MultiLocation::X1(ref a)) => a,
(0, MultiLocation::X2(ref a, ..)) => a,
(0, MultiLocation::X3(ref a, ..)) => a,
(0, MultiLocation::X4(ref a, ..)) => a,
(0, MultiLocation::X5(ref a, ..)) => a,
(0, MultiLocation::X6(ref a, ..)) => a,
(0, MultiLocation::X7(ref a, ..)) => a,
(0, MultiLocation::X8(ref a, ..)) => a,
(1, MultiLocation::X2(_, ref a)) => a,
(1, MultiLocation::X3(_, ref a, ..)) => a,
(1, MultiLocation::X4(_, ref a, ..)) => a,
(1, MultiLocation::X5(_, ref a, ..)) => a,
(1, MultiLocation::X6(_, ref a, ..)) => a,
(1, MultiLocation::X7(_, ref a, ..)) => a,
(1, MultiLocation::X8(_, ref a, ..)) => a,
(2, MultiLocation::X3(_, _, ref a)) => a,
(2, MultiLocation::X4(_, _, ref a, ..)) => a,
(2, MultiLocation::X5(_, _, ref a, ..)) => a,
(2, MultiLocation::X6(_, _, ref a, ..)) => a,
(2, MultiLocation::X7(_, _, ref a, ..)) => a,
(2, MultiLocation::X8(_, _, ref a, ..)) => a,
(3, MultiLocation::X4(_, _, _, ref a)) => a,
(3, MultiLocation::X5(_, _, _, ref a, ..)) => a,
(3, MultiLocation::X6(_, _, _, ref a, ..)) => a,
(3, MultiLocation::X7(_, _, _, ref a, ..)) => a,
(3, MultiLocation::X8(_, _, _, ref a, ..)) => a,
(4, MultiLocation::X5(_, _, _, _, ref a)) => a,
(4, MultiLocation::X6(_, _, _, _, ref a, ..)) => a,
(4, MultiLocation::X7(_, _, _, _, ref a, ..)) => a,
(4, MultiLocation::X8(_, _, _, _, ref a, ..)) => a,
(5, MultiLocation::X6(_, _, _, _, _, ref a)) => a,
(5, MultiLocation::X7(_, _, _, _, _, ref a, ..)) => a,
(5, MultiLocation::X8(_, _, _, _, _, ref a, ..)) => a,
(6, MultiLocation::X7(_, _, _, _, _, _, ref a)) => a,
(6, MultiLocation::X8(_, _, _, _, _, _, ref a, ..)) => a,
(7, MultiLocation::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, MultiLocation::X1(ref mut a)) => a,
(0, MultiLocation::X2(ref mut a, ..)) => a,
(0, MultiLocation::X3(ref mut a, ..)) => a,
(0, MultiLocation::X4(ref mut a, ..)) => a,
(0, MultiLocation::X5(ref mut a, ..)) => a,
(0, MultiLocation::X6(ref mut a, ..)) => a,
(0, MultiLocation::X7(ref mut a, ..)) => a,
(0, MultiLocation::X8(ref mut a, ..)) => a,
(1, MultiLocation::X2(_, ref mut a)) => a,
(1, MultiLocation::X3(_, ref mut a, ..)) => a,
(1, MultiLocation::X4(_, ref mut a, ..)) => a,
(1, MultiLocation::X5(_, ref mut a, ..)) => a,
(1, MultiLocation::X6(_, ref mut a, ..)) => a,
(1, MultiLocation::X7(_, ref mut a, ..)) => a,
(1, MultiLocation::X8(_, ref mut a, ..)) => a,
(2, MultiLocation::X3(_, _, ref mut a)) => a,
(2, MultiLocation::X4(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X5(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X6(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X7(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X8(_, _, ref mut a, ..)) => a,
(3, MultiLocation::X4(_, _, _, ref mut a)) => a,
(3, MultiLocation::X5(_, _, _, ref mut a, ..)) => a,
(3, MultiLocation::X6(_, _, _, ref mut a, ..)) => a,
(3, MultiLocation::X7(_, _, _, ref mut a, ..)) => a,
(3, MultiLocation::X8(_, _, _, ref mut a, ..)) => a,
(4, MultiLocation::X5(_, _, _, _, ref mut a)) => a,
(4, MultiLocation::X6(_, _, _, _, ref mut a, ..)) => a,
(4, MultiLocation::X7(_, _, _, _, ref mut a, ..)) => a,
(4, MultiLocation::X8(_, _, _, _, ref mut a, ..)) => a,
(5, MultiLocation::X6(_, _, _, _, _, ref mut a)) => a,
(5, MultiLocation::X7(_, _, _, _, _, ref mut a, ..)) => a,
(5, MultiLocation::X8(_, _, _, _, _, ref mut a, ..)) => a,
(6, MultiLocation::X7(_, _, _, _, _, _, ref mut a)) => a,
(6, MultiLocation::X8(_, _, _, _, _, _, ref mut a, ..)) => a,
(7, MultiLocation::X8(_, _, _, _, _, _, _, ref mut a)) => a,
_ => return None,
})
}
/// Returns a reference iterator over the junctions.
pub fn iter(&self) -> MultiLocationRefIterator {
MultiLocationRefIterator(&self, 0)
}
/// Returns a reference iterator over the junctions in reverse.
pub fn iter_rev(&self) -> MultiLocationReverseRefIterator {
MultiLocationReverseRefIterator(&self, 0)
}
/// Consumes `self` and returns an iterator over the junctions.
pub fn into_iter(self) -> MultiLocationIterator {
MultiLocationIterator(self)
}
/// Consumes `self` and returns an iterator over the junctions in reverse.
pub fn into_iter_rev(self) -> MultiLocationReverseIterator {
MultiLocationReverseIterator(self)
}
/// 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 xcm::v0::{MultiLocation::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parent, PalletInstance(3), OnlyChild);
/// assert_eq!(m.match_and_split(&X2(Parent, PalletInstance(3))), Some(&OnlyChild));
/// assert_eq!(m.match_and_split(&X1(Parent)), None);
/// # }
/// ```
pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> {
if prefix.len() + 1 != self.len() || !self.starts_with(prefix) {
return None
}
return self.at(prefix.len())
}
/// Returns whether `self` begins with or is equal to `prefix`.
///
/// # Example
/// ```rust
/// # use xcm::v0::{Junction::*, MultiLocation::*};
/// let m = X4(Parent, PalletInstance(3), OnlyChild, OnlyChild);
/// assert!(m.starts_with(&X2(Parent, PalletInstance(3))));
/// assert!(m.starts_with(&m));
/// assert!(!m.starts_with(&X2(Parent, GeneralIndex(99))));
/// assert!(!m.starts_with(&X1(PalletInstance(3))));
/// ```
pub fn starts_with(&self, prefix: &MultiLocation) -> bool {
if self.len() < prefix.len() {
return false
}
prefix.iter().zip(self.iter()).all(|(l, r)| l == r)
}
/// Mutates `self`, suffixing it with `new`. Returns `Err` in case of overflow.
pub fn push(&mut self, new: Junction) -> result::Result<(), ()> {
let mut n = MultiLocation::Null;
mem::swap(&mut *self, &mut n);
match n.pushed_with(new) {
Ok(result) => {
*self = result;
Ok(())
},
Err(old) => {
*self = old;
Err(())
},
}
}
/// Mutates `self`, prefixing it with `new`. Returns `Err` in case of overflow.
pub fn push_front(&mut self, new: Junction) -> result::Result<(), ()> {
let mut n = MultiLocation::Null;
mem::swap(&mut *self, &mut n);
match n.pushed_front_with(new) {
Ok(result) => {
*self = result;
Ok(())
},
Err(old) => {
*self = old;
Err(())
},
}
}
/// Returns the number of `Parent` junctions at the beginning of `self`.
pub fn leading_parent_count(&self) -> usize {
use Junction::Parent;
match self {
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 8,
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, ..) => 7,
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 7,
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6,
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6,
MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, Parent) => 6,
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, ..) => 5,
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, ..) => 5,
MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, ..) => 5,
MultiLocation::X5(Parent, Parent, Parent, Parent, Parent) => 5,
MultiLocation::X8(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X7(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X6(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X5(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X4(Parent, Parent, Parent, Parent) => 4,
MultiLocation::X8(Parent, Parent, Parent, ..) => 3,
MultiLocation::X7(Parent, Parent, Parent, ..) => 3,
MultiLocation::X6(Parent, Parent, Parent, ..) => 3,
MultiLocation::X5(Parent, Parent, Parent, ..) => 3,
MultiLocation::X4(Parent, Parent, Parent, ..) => 3,
MultiLocation::X3(Parent, Parent, Parent) => 3,
MultiLocation::X8(Parent, Parent, ..) => 2,
MultiLocation::X7(Parent, Parent, ..) => 2,
MultiLocation::X6(Parent, Parent, ..) => 2,
MultiLocation::X5(Parent, Parent, ..) => 2,
MultiLocation::X4(Parent, Parent, ..) => 2,
MultiLocation::X3(Parent, Parent, ..) => 2,
MultiLocation::X2(Parent, Parent) => 2,
MultiLocation::X8(Parent, ..) => 1,
MultiLocation::X7(Parent, ..) => 1,
MultiLocation::X6(Parent, ..) => 1,
MultiLocation::X5(Parent, ..) => 1,
MultiLocation::X4(Parent, ..) => 1,
MultiLocation::X3(Parent, ..) => 1,
MultiLocation::X2(Parent, ..) => 1,
MultiLocation::X1(Parent) => 1,
_ => 0,
}
}
/// This function ensures a multi-junction is in its canonicalized/normalized form, removing
/// any internal `[Non-Parent, Parent]` combinations.
pub fn canonicalize(&mut self) {
let mut normalized = MultiLocation::Null;
let mut iter = self.iter();
// We build up the the new normalized path by taking items from the original multi-location.
// When the next item we would add is `Parent`, we instead remove the last item assuming
// it is non-parent.
const EXPECT_MESSAGE: &'static str =
"`self` is a well formed multi-location with N junctions; \
this loop iterates over the junctions of `self`; \
the loop can push to the new multi-location at most one time; \
thus the size of the new multi-location is at most N junctions; \
qed";
while let Some(j) = iter.next() {
if j == &Junction::Parent {
match normalized.last() {
None | Some(Junction::Parent) => {},
Some(_) => {
normalized.take_last();
continue
},
}
}
normalized.push(j.clone()).expect(EXPECT_MESSAGE);
}
core::mem::swap(self, &mut normalized);
}
/// Mutate `self` so that it is suffixed with `suffix`. The correct normalized form is returned,
/// removing any internal `[Non-Parent, Parent]` combinations.
///
/// In the case of overflow, `self` is unmodified and we return `Err` with `suffix`.
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parent, Parachain(21), OnlyChild);
/// assert_eq!(m.append_with(X2(Parent, PalletInstance(3))), Ok(()));
/// assert_eq!(m, X3(Parent, Parachain(21), PalletInstance(3)));
/// # }
/// ```
pub fn append_with(&mut self, suffix: MultiLocation) -> Result<(), MultiLocation> {
let mut prefix = suffix;
core::mem::swap(self, &mut prefix);
match self.prepend_with(prefix) {
Ok(()) => Ok(()),
Err(prefix) => {
let mut suffix = prefix;
core::mem::swap(self, &mut suffix);
Err(suffix)
},
}
}
/// Mutate `self` so that it is prefixed with `prefix`. The correct normalized form is returned,
/// removing any internal [Non-Parent, `Parent`] combinations.
///
/// In the case of overflow, `self` is unmodified and we return `Err` with `prefix`.
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation::*, Junction::*, NetworkId::Any};
/// # fn main() {
/// let mut m = X3(Parent, Parent, PalletInstance(3));
/// assert_eq!(m.prepend_with(X3(Parent, Parachain(21), OnlyChild)), Ok(()));
/// assert_eq!(m, X2(Parent, PalletInstance(3)));
/// # }
/// ```
pub fn prepend_with(&mut self, prefix: MultiLocation) -> Result<(), MultiLocation> {
let mut prefix = prefix;
// This will guarantee that all `Parent` junctions in the prefix are leading, which is
// important for calculating the `skipped` items below.
prefix.canonicalize();
let self_leading_parents = self.leading_parent_count();
// These are the number of `non-parent` items in the prefix that we can
// potentially remove if the original location leads with parents.
let prefix_rest = prefix.len() - prefix.leading_parent_count();
// 2 * skipped items will be removed when performing the normalization below.
let skipped = self_leading_parents.min(prefix_rest);
// Pre-pending this prefix would create a multi-location with too many junctions.
if self.len() + prefix.len() - 2 * skipped > MAX_MULTILOCATION_LENGTH {
return Err(prefix)
}
// Here we cancel out `[Non-Parent, Parent]` items (normalization), where
// the non-parent item comes from the end of the prefix, and the parent item
// comes from the front of the original location.
//
// We calculated already how many of these there should be above.
for _ in 0..skipped {
let _non_parent = prefix.take_last();
let _parent = self.take_first();
debug_assert!(
_non_parent.is_some() && _non_parent != Some(Junction::Parent),
"prepend_with should always remove a non-parent from the end of the prefix",
);
debug_assert!(
_parent == Some(Junction::Parent),
"prepend_with should always remove a parent from the front of the location",
);
}
for j in prefix.into_iter_rev() {
self.push_front(j)
.expect("len + prefix minus 2*skipped is less than max length; qed");
}
Ok(())
}
/// Returns true iff `self` is an interior location. For this it may not contain any `Junction`s
/// for which `Junction::is_interior` returns `false`. This is generally true, except for the
/// `Parent` item.
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation::*, Junction::*, NetworkId::Any};
/// # fn main() {
/// let parent = X1(Parent);
/// assert_eq!(parent.is_interior(), false);
/// let m = X2(PalletInstance(12), AccountIndex64 { network: Any, index: 23 });
/// assert_eq!(m.is_interior(), true);
/// # }
/// ```
pub fn is_interior(&self) -> bool {
self.iter().all(Junction::is_interior)
}
}
#[cfg(test)]
mod tests {
use super::MultiLocation::{self, *};
use crate::opaque::v0::{Junction::*, NetworkId::Any};
#[test]
fn match_and_split_works() {
let m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 });
assert_eq!(m.match_and_split(&X1(Parent)), None);
assert_eq!(
m.match_and_split(&X2(Parent, Parachain(42))),
Some(&AccountIndex64 { network: Any, index: 23 })
);
assert_eq!(m.match_and_split(&m), None);
}
#[test]
fn starts_with_works() {
let full = X3(Parent, Parachain(1000), AccountIndex64 { network: Any, index: 23 });
let identity = full.clone();
let prefix = X2(Parent, Parachain(1000));
let wrong_parachain = X2(Parent, Parachain(1001));
let wrong_account = X3(Parent, Parachain(1000), AccountIndex64 { network: Any, index: 24 });
let no_parents = X1(Parachain(1000));
let too_many_parents = X3(Parent, Parent, Parachain(1000));
assert!(full.starts_with(&identity));
assert!(full.starts_with(&prefix));
assert!(!full.starts_with(&wrong_parachain));
assert!(!full.starts_with(&wrong_account));
assert!(!full.starts_with(&no_parents));
assert!(!full.starts_with(&too_many_parents));
}
#[test]
fn append_with_works() {
let acc = AccountIndex64 { network: Any, index: 23 };
let mut m = X2(Parent, Parachain(42));
assert_eq!(m.append_with(X2(PalletInstance(3), acc.clone())), Ok(()));
assert_eq!(m, X4(Parent, Parachain(42), PalletInstance(3), acc.clone()));
// cannot append to create overly long multilocation
let acc = AccountIndex64 { network: Any, index: 23 };
let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42));
let suffix = X2(PalletInstance(3), acc.clone());
assert_eq!(m.append_with(suffix.clone()), Err(suffix));
}
#[test]
fn prepend_with_works() {
let mut m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 });
assert_eq!(m.prepend_with(X2(Parent, OnlyChild)), Ok(()));
assert_eq!(m, X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 }));
// cannot prepend to create overly long multilocation
let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42));
let prefix = X2(Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
// Can handle shared prefix and resizing correctly.
let mut m = X1(Parent);
let prefix = X8(
Parachain(100),
OnlyChild,
OnlyChild,
OnlyChild,
OnlyChild,
OnlyChild,
OnlyChild,
Parent,
);
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, X5(Parachain(100), OnlyChild, OnlyChild, OnlyChild, OnlyChild));
let mut m = X1(Parent);
let prefix = X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
let mut m = X1(Parent);
let prefix = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent));
let mut m = X1(Parent);
let prefix = X8(Parent, Parent, Parent, Parent, OnlyChild, Parent, Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent));
}
#[test]
fn canonicalize_works() {
let mut m = X1(Parent);
m.canonicalize();
assert_eq!(m, X1(Parent));
let mut m = X1(Parachain(1));
m.canonicalize();
assert_eq!(m, X1(Parachain(1)));
let mut m = X6(Parent, Parachain(1), Parent, Parachain(2), Parent, Parachain(3));
m.canonicalize();
assert_eq!(m, X2(Parent, Parachain(3)));
let mut m = X5(Parachain(1), Parent, Parachain(2), Parent, Parachain(3));
m.canonicalize();
assert_eq!(m, X1(Parachain(3)));
let mut m = X6(Parachain(1), Parent, Parachain(2), Parent, Parachain(3), Parent);
m.canonicalize();
assert_eq!(m, Null);
let mut m = X5(Parachain(1), Parent, Parent, Parent, Parachain(3));
m.canonicalize();
assert_eq!(m, X3(Parent, Parent, Parachain(3)));
let mut m = X4(Parachain(1), Parachain(2), Parent, Parent);
m.canonicalize();
assert_eq!(m, Null);
let mut m = X4(Parent, Parent, Parachain(1), Parachain(2));
m.canonicalize();
assert_eq!(m, X4(Parent, Parent, Parachain(1), Parachain(2)));
}
#[test]
fn conversion_from_other_types_works() {
use crate::v1::{self, Junction, Junctions};
fn takes_multilocation<Arg: Into<MultiLocation>>(_arg: Arg) {}
takes_multilocation(Null);
takes_multilocation(Parent);
takes_multilocation([Parent, Parachain(4)]);
assert_eq!(v1::MultiLocation::here().try_into(), Ok(MultiLocation::Null));
assert_eq!(
v1::MultiLocation::new(1, Junctions::X1(Junction::Parachain(8))).try_into(),
Ok(X2(Parent, Parachain(8))),
);
assert_eq!(
v1::MultiLocation::new(24, Junctions::Here).try_into(),
Err::<MultiLocation, ()>(()),
);
}
}
-204
View File
@@ -1,204 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Version 0 of the Cross-Consensus Message format data structures.
use super::{super::v1::Order as Order1, MultiAsset, MultiLocation, Xcm};
use alloc::vec::Vec;
use core::result;
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
/// An instruction to be executed on some or all of the assets in holding, used by asset-related XCM messages.
#[derive(Derivative, Encode, Decode, scale_info::TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
pub enum Order<RuntimeCall> {
/// Do nothing. Not generally used.
#[codec(index = 0)]
Null,
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within
/// this consensus system.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `dest`: The new owner for the assets.
///
/// Errors:
#[codec(index = 1)]
DepositAsset { assets: Vec<MultiAsset>, dest: MultiLocation },
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within
/// this consensus system.
///
/// Send an onward XCM message to `dest` of `ReserveAssetDeposit` with the given `effects`.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `dest`: The new owner for the assets.
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposit` which is sent onwards to
/// `dest`.
///
/// Errors:
#[codec(index = 2)]
DepositReserveAsset { assets: Vec<MultiAsset>, dest: MultiLocation, effects: Vec<Order<()>> },
/// Remove the asset(s) (`give`) from holding and replace them with alternative assets.
///
/// The minimum amount of assets to be received into holding for the order not to fail may be stated.
///
/// - `give`: The asset(s) to remove from holding.
/// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for. The meaning of wildcards
/// is undefined and they should be not be used.
///
/// Errors:
#[codec(index = 3)]
ExchangeAsset { give: Vec<MultiAsset>, receive: Vec<MultiAsset> },
/// Remove the asset(s) (`assets`) from holding and send a `WithdrawAsset` XCM message to a reserve location.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `reserve`: A valid location that acts as a reserve for all asset(s) in `assets`. The sovereign account
/// of this consensus system *on the reserve location* will have appropriate assets withdrawn and `effects` will
/// be executed on them. There will typically be only one valid location on any given asset/chain combination.
/// - `effects`: The orders to execute on the assets once withdrawn *on the reserve location*.
///
/// Errors:
#[codec(index = 4)]
InitiateReserveWithdraw {
assets: Vec<MultiAsset>,
reserve: MultiLocation,
effects: Vec<Order<()>>,
},
/// Remove the asset(s) (`assets`) from holding and send a `TeleportAsset` XCM message to a destination location.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `destination`: A valid location that has a bi-lateral teleportation arrangement.
/// - `effects`: The orders to execute on the assets once arrived *on the destination location*.
///
/// Errors:
#[codec(index = 5)]
InitiateTeleport { assets: Vec<MultiAsset>, dest: MultiLocation, effects: Vec<Order<()>> },
/// Send a `Balances` XCM message with the `assets` value equal to the holding contents, or a portion thereof.
///
/// - `query_id`: An identifier that will be replicated into the returned XCM message.
/// - `dest`: A valid destination for the returned XCM message. This may be limited to the current origin.
/// - `assets`: A filter for the assets that should be reported back. The assets reported back will be, asset-
/// wise, *the lesser of this value and the holding account*. No wildcards will be used when reporting assets
/// back.
///
/// Errors:
#[codec(index = 6)]
QueryHolding {
#[codec(compact)]
query_id: u64,
dest: MultiLocation,
assets: Vec<MultiAsset>,
},
/// Pay for the execution of some XCM with up to `weight` picoseconds of execution time, paying for this with
/// up to `fees` from the holding account.
///
/// Errors:
#[codec(index = 7)]
BuyExecution {
fees: MultiAsset,
weight: u64,
debt: u64,
halt_on_error: bool,
xcm: Vec<Xcm<RuntimeCall>>,
},
}
pub mod opaque {
pub type Order = super::Order<()>;
}
impl<RuntimeCall> Order<RuntimeCall> {
pub fn into<C>(self) -> Order<C> {
Order::from(self)
}
pub fn from<C>(order: Order<C>) -> Self {
use Order::*;
match order {
Null => Null,
DepositAsset { assets, dest } => DepositAsset { assets, dest },
DepositReserveAsset { assets, dest, effects } =>
DepositReserveAsset { assets, dest, effects },
ExchangeAsset { give, receive } => ExchangeAsset { give, receive },
InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw { assets, reserve, effects },
InitiateTeleport { assets, dest, effects } =>
InitiateTeleport { assets, dest, effects },
QueryHolding { query_id, dest, assets } => QueryHolding { query_id, dest, assets },
BuyExecution { fees, weight, debt, halt_on_error, xcm } => {
let xcm = xcm.into_iter().map(Xcm::from).collect();
BuyExecution { fees, weight, debt, halt_on_error, xcm }
},
}
}
}
impl<RuntimeCall> TryFrom<Order1<RuntimeCall>> for Order<RuntimeCall> {
type Error = ();
fn try_from(old: Order1<RuntimeCall>) -> result::Result<Order<RuntimeCall>, ()> {
use Order::*;
Ok(match old {
Order1::Noop => Null,
Order1::DepositAsset { assets, beneficiary, .. } =>
DepositAsset { assets: assets.try_into()?, dest: beneficiary.try_into()? },
Order1::DepositReserveAsset { assets, dest, effects, .. } => DepositReserveAsset {
assets: assets.try_into()?,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order1::ExchangeAsset { give, receive } =>
ExchangeAsset { give: give.try_into()?, receive: receive.try_into()? },
Order1::InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw {
assets: assets.try_into()?,
reserve: reserve.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order1::InitiateTeleport { assets, dest, effects } => InitiateTeleport {
assets: assets.try_into()?,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order1::QueryHolding { query_id, dest, assets } =>
QueryHolding { query_id, dest: dest.try_into()?, assets: assets.try_into()? },
Order1::BuyExecution { fees, weight, debt, halt_on_error, instructions } => {
let xcm = instructions
.into_iter()
.map(Xcm::<RuntimeCall>::try_from)
.collect::<result::Result<_, _>>()?;
BuyExecution { fees: fees.try_into()?, weight, debt, halt_on_error, xcm }
},
})
}
}
-267
View File
@@ -1,267 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use core::result;
use parity_scale_codec::{Decode, Encode};
use super::{MultiLocation, Xcm};
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, scale_info::TypeInfo)]
pub enum Error {
Undefined,
/// An arithmetic overflow happened.
Overflow,
/// The operation is intentionally unsupported.
Unimplemented,
UnhandledXcmVersion,
/// The implementation does not handle a given XCM.
UnhandledXcmMessage,
/// The implementation does not handle an effect present in an XCM.
UnhandledEffect,
EscalationOfPrivilege,
UntrustedReserveLocation,
UntrustedTeleportLocation,
DestinationBufferOverflow,
/// The message and destination was recognized as being reachable but the operation could not be completed.
/// A human-readable explanation of the specific issue is provided.
SendFailed(#[codec(skip)] &'static str),
/// The message and destination combination was not recognized as being reachable.
CannotReachDestination(MultiLocation, Xcm<()>),
MultiLocationFull,
FailedToDecode,
BadOrigin,
ExceedsMaxMessageSize,
/// An asset transaction (like withdraw or deposit) failed.
/// See implementers of the `TransactAsset` trait for sources.
/// Causes can include type conversion failures between id or balance types.
FailedToTransactAsset(#[codec(skip)] &'static str),
/// Execution of the XCM would potentially result in a greater weight used than the pre-specified
/// weight limit. The amount that is potentially required is the parameter.
WeightLimitReached(Weight),
/// An asset wildcard was passed where it was not expected (e.g. as the asset to withdraw in a
/// `WithdrawAsset` XCM).
Wildcard,
/// The case where an XCM message has specified a weight limit on an interior call and this
/// limit is too low.
///
/// Used by:
/// - `Transact`
MaxWeightInvalid,
/// The fees specified by the XCM message were not found in the holding account.
///
/// Used by:
/// - `BuyExecution`
NotHoldingFees,
/// The weight of an XCM message is not computable ahead of execution. This generally means at least part
/// of the message is invalid, which could be due to it containing overly nested structures or an invalid
/// nested data segment (e.g. for the call in `Transact`).
WeightNotComputable,
/// 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,
/// Indicates that it is not possible for a location to have an asset be withdrawn or transferred from its
/// ownership. This probably means it doesn't own (enough of) it, but may also indicate that it is under a
/// lock, hold, freeze or is otherwise unavailable.
NotWithdrawable,
/// Indicates that the consensus system cannot deposit an asset under the ownership of a particular location.
LocationCannotHold,
/// The assets given to purchase weight is are insufficient for the weight desired.
TooExpensive,
/// The given asset is not handled.
AssetNotFound,
/// `execute_xcm` has been called too many times recursively.
RecursionLimitReached,
}
impl From<()> for Error {
fn from(_: ()) -> Self {
Self::Undefined
}
}
pub type Result = result::Result<(), Error>;
/// Local weight type; execution time in picoseconds.
pub type Weight = u64;
/// Outcome of an XCM execution.
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, scale_info::TypeInfo)]
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 {
match self {
Outcome::Complete(_) => Ok(()),
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(_) => 0,
}
}
}
/// Type of XCM message executor.
pub trait ExecuteXcm<RuntimeCall> {
/// Execute some XCM `message` 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: MultiLocation,
message: Xcm<RuntimeCall>,
weight_limit: Weight,
) -> Outcome {
log::debug!(
target: "xcm::execute_xcm",
"origin: {:?}, message: {:?}, weight_limit: {:?}",
origin,
message,
weight_limit,
);
Self::execute_xcm_in_credit(origin, message, weight_limit, 0)
}
/// Execute some XCM `message` 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: MultiLocation,
message: Xcm<RuntimeCall>,
weight_limit: Weight,
weight_credit: Weight,
) -> Outcome;
}
impl<C> ExecuteXcm<C> for () {
fn execute_xcm_in_credit(
_origin: MultiLocation,
_message: Xcm<C>,
_weight_limit: Weight,
_weight_credit: Weight,
) -> Outcome {
Outcome::Error(Error::Unimplemented)
}
}
/// Utility for sending an XCM message.
///
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return
/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination`
/// might alter the destination and the XCM message for to the next router.
///
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation, Xcm, Junction, Error, OriginKind, SendXcm, Result};
/// # use parity_scale_codec::Encode;
///
/// /// A sender that only passes the message through and does nothing.
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// return Err(Error::CannotReachDestination(destination, message))
/// }
/// }
///
/// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing.
/// struct Sender2;
/// impl SendXcm for Sender2 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// if let MultiLocation::X2(j1, j2) = destination {
/// Ok(())
/// } else {
/// Err(Error::Undefined)
/// }
/// }
/// }
///
/// /// A sender that accepts a message from an X1 parent junction, passing through otherwise.
/// struct Sender3;
/// impl SendXcm for Sender3 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// match destination {
/// MultiLocation::X1(j) if j == Junction::Parent => Ok(()),
/// _ => Err(Error::CannotReachDestination(destination, message)),
/// }
/// }
/// }
///
/// // A call to send via XCM. We don't really care about this.
/// # fn main() {
/// let call: Vec<u8> = ().encode();
/// let message = Xcm::Transact { origin_type: OriginKind::Superuser, require_weight_at_most: 0, call: call.into() };
/// let destination = MultiLocation::X1(Junction::Parent);
///
/// assert!(
/// // Sender2 will block this.
/// <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone())
/// .is_err()
/// );
///
/// assert!(
/// // Sender3 will catch this.
/// <(Sender1, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone())
/// .is_ok()
/// );
/// # }
/// ```
pub trait SendXcm {
/// Send an XCM `message` to a given `destination`.
///
/// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST*
/// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without
/// trying other type fields.
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl SendXcm for Tuple {
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
for_tuples!( #(
// we shadow `destination` and `message` in each expansion for the next one.
let (destination, message) = match Tuple::send_xcm(destination, message) {
Err(Error::CannotReachDestination(d, m)) => (d, m),
o @ _ => return o,
};
)* );
Err(Error::CannotReachDestination(destination, message))
}
}
-518
View File
@@ -1,518 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! # XCM Version 1
//! Version 1 of the Cross-Consensus Message format data structures. The comprehensive list of
//! changes can be found in
//! [this PR description](https://github.com/paritytech/polkadot/pull/2815#issue-608567900).
//!
//! ## Changes to be aware of
//! Most changes should automatically be resolved via the conversion traits (i.e. `TryFrom` and
//! `From`). The list here is mostly for incompatible changes that result in an `Err(())` when
//! attempting to convert XCM objects from v0.
//!
//! ### Junction
//! - `v0::Junction::Parent` cannot be converted to v1, because the way we represent parents in v1
//! has changed - instead of being a property of the junction, v1 `MultiLocation`s now have an
//! extra field representing the number of parents that the `MultiLocation` contains.
//!
//! ### `MultiLocation`
//! - The `try_from` conversion method will always canonicalize the v0 `MultiLocation` before
//! attempting to do the proper conversion. Since canonicalization is not a fallible operation,
//! we do not expect v0 `MultiLocation` to ever fail to be upgraded to v1.
//!
//! ### `MultiAsset`
//! - Stronger typing to differentiate between a single class of `MultiAsset` and several classes
//! of `MultiAssets` is introduced. As the name suggests, a `Vec<MultiAsset>` that is used on all
//! APIs will instead be using a new type called `MultiAssets` (note the `s`).
//! - All `MultiAsset` variants whose name contains "All" in it, namely `v0::MultiAsset::All`,
//! `v0::MultiAsset::AllFungible`, `v0::MultiAsset::AllNonFungible`,
//! `v0::MultiAsset::AllAbstractFungible`, `v0::MultiAsset::AllAbstractNonFungible`,
//! `v0::MultiAsset::AllConcreteFungible` and `v0::MultiAsset::AllConcreteNonFungible`, will fail
//! to convert to v1 `MultiAsset`, since v1 does not contain these variants.
//! - Similarly, all `MultiAsset` variants whose name contains "All" in it can be converted into a
//! `WildMultiAsset`.
//! - `v0::MultiAsset::None` is not represented at all in v1.
//!
//! ### XCM
//! - No special attention necessary
//!
//! ### Order
//! - `v1::Order::DepositAsset` and `v1::Order::DepositReserveAsset` both introduced a new
//! `max_asset` field that limits the maximum classes of assets that can be deposited. During
//! conversion from v0, the `max_asset` field defaults to 1.
//! - v1 Orders that contain `MultiAsset` as argument(s) will need to explicitly specify the amount
//! and details of assets. This is to prevent accidental misuse of `All` to possibly transfer,
//! spend or otherwise perform unintended operations on `All` assets.
//! - v1 Orders that do allow the notion of `All` to be used as wildcards, will instead use a new
//! type called `MultiAssetFilter`.
use super::{
v0::{Response as OldResponse, Xcm as OldXcm},
v2::{Instruction, Response as NewResponse, Xcm as NewXcm},
};
use crate::DoubleEncoded;
use alloc::vec::Vec;
use core::{fmt::Debug, result};
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
use scale_info::TypeInfo;
mod junction;
mod multiasset;
mod multilocation;
mod order;
mod traits; // the new multiasset.
pub use junction::Junction;
pub use multiasset::{
AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets,
WildFungibility, WildMultiAsset,
};
pub use multilocation::{
Ancestor, AncestorThen, InteriorMultiLocation, Junctions, MultiLocation, Parent, ParentThen,
};
pub use order::Order;
pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm};
// These parts of XCM v0 have been unchanged in XCM v1, and are re-imported here.
pub use super::v0::{BodyId, BodyPart, NetworkId, OriginKind};
/// A prelude for importing all types typically used when interacting with XCM messages.
pub mod prelude {
pub use super::{
junction::Junction::{self, *},
opaque,
order::Order::{self, *},
Ancestor, AncestorThen,
AssetId::{self, *},
AssetInstance::{self, *},
BodyId, BodyPart, Error as XcmError, ExecuteXcm,
Fungibility::{self, *},
InteriorMultiLocation,
Junctions::{self, *},
MultiAsset,
MultiAssetFilter::{self, *},
MultiAssets, MultiLocation,
NetworkId::{self, *},
OriginKind, Outcome, Parent, ParentThen, Response, Result as XcmResult, SendXcm,
WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
WildMultiAsset::{self, *},
Xcm::{self, *},
};
}
/// Response data to a query.
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
pub enum Response {
/// Some assets.
Assets(MultiAssets),
/// An XCM version.
Version(super::Version),
}
/// Cross-Consensus Message: A message from one consensus system to another.
///
/// Consensus systems that may send and receive messages include blockchains and smart contracts.
///
/// All messages are delivered from a known *origin*, expressed as a `MultiLocation`.
///
/// This is the inner XCM format and is version-sensitive. Messages are typically passed using the outer
/// XCM format, known as `VersionedXcm`.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
pub enum Xcm<RuntimeCall> {
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into `holding`. Execute the
/// orders (`effects`).
///
/// - `assets`: The asset(s) to be withdrawn into holding.
/// - `effects`: The order(s) to execute on the holding register.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 0)]
WithdrawAsset { assets: MultiAssets, effects: Vec<Order<RuntimeCall>> },
/// Asset(s) (`assets`) have been received into the ownership of this system on the `origin` system.
///
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
/// been placed into `holding`.
///
/// - `assets`: The asset(s) that are minted into holding.
/// - `effects`: The order(s) to execute on the holding register.
///
/// Safety: `origin` must be trusted to have received and be storing `assets` such that they may later be
/// withdrawn should this system send a corresponding message.
///
/// Kind: *Trusted Indication*.
///
/// Errors:
#[codec(index = 1)]
ReserveAssetDeposited { assets: MultiAssets, effects: Vec<Order<RuntimeCall>> },
/// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should be
/// created on this system.
///
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
/// been placed into the Holding Register.
///
/// - `assets`: The asset(s) that are minted into the Holding Register.
/// - `effects`: The order(s) to execute on the Holding Register.
///
/// Safety: `origin` must be trusted to have irrevocably destroyed the corresponding `assets` prior as a consequence
/// of sending this message.
///
/// Kind: *Trusted Indication*.
///
/// Errors:
#[codec(index = 2)]
ReceiveTeleportedAsset { assets: MultiAssets, effects: Vec<Order<RuntimeCall>> },
/// Indication of the contents of the holding register corresponding to the `QueryHolding` order of `query_id`.
///
/// - `query_id`: The identifier of the query that resulted in this message being sent.
/// - `assets`: The message content.
///
/// Safety: No concerns.
///
/// Kind: *Information*.
///
/// Errors:
#[codec(index = 3)]
QueryResponse {
#[codec(compact)]
query_id: u64,
response: Response,
},
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
/// ownership of `beneficiary`.
///
/// - `assets`: The asset(s) to be withdrawn.
/// - `beneficiary`: The new owner for the assets.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 4)]
TransferAsset { assets: MultiAssets, beneficiary: MultiLocation },
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
/// ownership of `dest` within this consensus system (i.e. its sovereign account).
///
/// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`.
///
/// - `assets`: The asset(s) to be withdrawn.
/// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the
/// assets and the notification target for the reserve asset deposit message.
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to
/// `dest`.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 5)]
TransferReserveAsset { assets: MultiAssets, dest: MultiLocation, effects: Vec<Order<()>> },
/// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed by the kind
/// of origin `origin_type`.
///
/// - `origin_type`: The means of expressing the message origin as a dispatch origin.
/// - `max_weight`: The weight of `call`; this should be at least the chain's calculated weight and will
/// be used in the weight determination arithmetic.
/// - `call`: The encoded transaction to be applied.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 6)]
Transact {
origin_type: OriginKind,
require_weight_at_most: u64,
call: DoubleEncoded<RuntimeCall>,
},
/// A message to notify about a new incoming HRMP channel. This message is meant to be sent by the
/// relay-chain to a para.
///
/// - `sender`: The sender in the to-be opened channel. Also, the initiator of the channel opening.
/// - `max_message_size`: The maximum size of a message proposed by the sender.
/// - `max_capacity`: The maximum number of messages that can be queued in the channel.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
#[codec(index = 7)]
HrmpNewChannelOpenRequest {
#[codec(compact)]
sender: u32,
#[codec(compact)]
max_message_size: u32,
#[codec(compact)]
max_capacity: u32,
},
/// A message to notify about that a previously sent open channel request has been accepted by
/// the recipient. That means that the channel will be opened during the next relay-chain session
/// change. This message is meant to be sent by the relay-chain to a para.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
///
/// Errors:
#[codec(index = 8)]
HrmpChannelAccepted {
#[codec(compact)]
recipient: u32,
},
/// A message to notify that the other party in an open channel decided to close it. In particular,
/// `initiator` is going to close the channel opened from `sender` to the `recipient`. The close
/// will be enacted at the next relay-chain session change. This message is meant to be sent by
/// the relay-chain to a para.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
///
/// Errors:
#[codec(index = 9)]
HrmpChannelClosing {
#[codec(compact)]
initiator: u32,
#[codec(compact)]
sender: u32,
#[codec(compact)]
recipient: u32,
},
/// A message to indicate that the embedded XCM is actually arriving on behalf of some consensus
/// location within the origin.
///
/// Kind: *Instruction*
///
/// Errors:
#[codec(index = 10)]
RelayedFrom { who: InteriorMultiLocation, message: alloc::boxed::Box<Xcm<RuntimeCall>> },
/// Ask the destination system to respond with the most recent version of XCM that they
/// support in a `QueryResponse` instruction. Any changes to this should also elicit similar
/// responses when they happen.
///
/// Kind: *Instruction*
#[codec(index = 11)]
SubscribeVersion {
#[codec(compact)]
query_id: u64,
#[codec(compact)]
max_response_weight: u64,
},
/// Cancel the effect of a previous `SubscribeVersion` instruction.
///
/// Kind: *Instruction*
#[codec(index = 12)]
UnsubscribeVersion,
}
impl<RuntimeCall> Xcm<RuntimeCall> {
pub fn into<C>(self) -> Xcm<C> {
Xcm::from(self)
}
pub fn from<C>(xcm: Xcm<C>) -> Self {
use Xcm::*;
match xcm {
WithdrawAsset { assets, effects } =>
WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() },
ReserveAssetDeposited { assets, effects } => ReserveAssetDeposited {
assets,
effects: effects.into_iter().map(Order::into).collect(),
},
ReceiveTeleportedAsset { assets, effects } => ReceiveTeleportedAsset {
assets,
effects: effects.into_iter().map(Order::into).collect(),
},
QueryResponse { query_id, response } => QueryResponse { query_id, response },
TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary },
TransferReserveAsset { assets, dest, effects } =>
TransferReserveAsset { assets, dest, effects },
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call: call.into() },
RelayedFrom { who, message } =>
RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) },
SubscribeVersion { query_id, max_response_weight } =>
SubscribeVersion { query_id, max_response_weight },
UnsubscribeVersion => UnsubscribeVersion,
}
}
}
pub mod opaque {
/// The basic concrete type of `generic::Xcm`, which doesn't make any assumptions about the format of a
/// call other than it is pre-encoded.
pub type Xcm = super::Xcm<()>;
pub use super::order::opaque::*;
}
// Convert from a v0 response to a v1 response
impl TryFrom<OldResponse> for Response {
type Error = ();
fn try_from(old_response: OldResponse) -> result::Result<Self, ()> {
match old_response {
OldResponse::Assets(assets) => Ok(Self::Assets(assets.try_into()?)),
}
}
}
impl<RuntimeCall> TryFrom<OldXcm<RuntimeCall>> for Xcm<RuntimeCall> {
type Error = ();
fn try_from(old: OldXcm<RuntimeCall>) -> result::Result<Xcm<RuntimeCall>, ()> {
use Xcm::*;
Ok(match old {
OldXcm::WithdrawAsset { assets, effects } => WithdrawAsset {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
OldXcm::ReserveAssetDeposit { assets, effects } => ReserveAssetDeposited {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
OldXcm::TeleportAsset { assets, effects } => ReceiveTeleportedAsset {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
OldXcm::QueryResponse { query_id, response } =>
QueryResponse { query_id, response: response.try_into()? },
OldXcm::TransferAsset { assets, dest } =>
TransferAsset { assets: assets.try_into()?, beneficiary: dest.try_into()? },
OldXcm::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset {
assets: assets.try_into()?,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
OldXcm::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
OldXcm::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
OldXcm::HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
OldXcm::Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call: call.into() },
OldXcm::RelayedFrom { who, message } => RelayedFrom {
who: MultiLocation::try_from(who)?.try_into()?,
message: alloc::boxed::Box::new((*message).try_into()?),
},
})
}
}
impl<RuntimeCall> TryFrom<NewXcm<RuntimeCall>> for Xcm<RuntimeCall> {
type Error = ();
fn try_from(old: NewXcm<RuntimeCall>) -> result::Result<Xcm<RuntimeCall>, ()> {
use Xcm::*;
let mut iter = old.0.into_iter();
let instruction = iter.next().ok_or(())?;
Ok(match instruction {
Instruction::WithdrawAsset(assets) => {
let effects = iter.map(Order::try_from).collect::<result::Result<_, _>>()?;
WithdrawAsset { assets, effects }
},
Instruction::ReserveAssetDeposited(assets) => {
if !matches!(iter.next(), Some(Instruction::ClearOrigin)) {
return Err(())
}
let effects = iter.map(Order::try_from).collect::<result::Result<_, _>>()?;
ReserveAssetDeposited { assets, effects }
},
Instruction::ReceiveTeleportedAsset(assets) => {
if !matches!(iter.next(), Some(Instruction::ClearOrigin)) {
return Err(())
}
let effects = iter.map(Order::try_from).collect::<result::Result<_, _>>()?;
ReceiveTeleportedAsset { assets, effects }
},
Instruction::QueryResponse { query_id, response, max_weight } => {
// Cannot handle special response weights.
if max_weight > 0 {
return Err(())
}
QueryResponse { query_id, response: response.try_into()? }
},
Instruction::TransferAsset { assets, beneficiary } =>
TransferAsset { assets, beneficiary },
Instruction::TransferReserveAsset { assets, dest, xcm } => TransferReserveAsset {
assets,
dest,
effects: xcm
.0
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Instruction::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
Instruction::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
Instruction::HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
Instruction::Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call },
Instruction::SubscribeVersion { query_id, max_response_weight } =>
SubscribeVersion { query_id, max_response_weight },
Instruction::UnsubscribeVersion => UnsubscribeVersion,
_ => return Err(()),
})
}
}
// Convert from a v1 response to a v2 response
impl TryFrom<NewResponse> for Response {
type Error = ();
fn try_from(response: NewResponse) -> result::Result<Self, ()> {
match response {
NewResponse::Assets(assets) => Ok(Self::Assets(assets)),
NewResponse::Version(version) => Ok(Self::Version(version)),
_ => Err(()),
}
}
}
-292
View File
@@ -1,292 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Version 1 of the Cross-Consensus Message format data structures.
use super::{MultiAsset, MultiAssetFilter, MultiAssets, MultiLocation, Xcm};
use crate::{v0::Order as OldOrder, v2::Instruction};
use alloc::{vec, vec::Vec};
use core::result;
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
use scale_info::TypeInfo;
/// An instruction to be executed on some or all of the assets in holding, used by asset-related XCM messages.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
pub enum Order<RuntimeCall> {
/// Do nothing. Not generally used.
#[codec(index = 0)]
Noop,
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `beneficiary`
/// within this consensus system.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first
/// `max_assets` assets/instances of those matched by `assets` will be removed, prioritized under standard asset
/// ordering. Any others will remain in holding.
/// - `beneficiary`: The new owner for the assets.
///
/// Errors:
#[codec(index = 1)]
DepositAsset { assets: MultiAssetFilter, max_assets: u32, beneficiary: MultiLocation },
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within
/// this consensus system (i.e. its sovereign account).
///
/// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first
/// `max_assets` assets/instances of those matched by `assets` will be removed, prioritized under standard asset
/// ordering. Any others will remain in holding.
/// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the
/// assets and the notification target for the reserve asset deposit message.
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to
/// `dest`.
///
/// Errors:
#[codec(index = 2)]
DepositReserveAsset {
assets: MultiAssetFilter,
max_assets: u32,
dest: MultiLocation,
effects: Vec<Order<()>>,
},
/// Remove the asset(s) (`give`) from holding and replace them with alternative assets.
///
/// The minimum amount of assets to be received into holding for the order not to fail may be stated.
///
/// - `give`: The asset(s) to remove from holding.
/// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for.
///
/// Errors:
#[codec(index = 3)]
ExchangeAsset { give: MultiAssetFilter, receive: MultiAssets },
/// Remove the asset(s) (`assets`) from holding and send a `WithdrawAsset` XCM message to a reserve location.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `reserve`: A valid location that acts as a reserve for all asset(s) in `assets`. The sovereign account
/// of this consensus system *on the reserve location* will have appropriate assets withdrawn and `effects` will
/// be executed on them. There will typically be only one valid location on any given asset/chain combination.
/// - `effects`: The orders to execute on the assets once withdrawn *on the reserve location*.
///
/// Errors:
#[codec(index = 4)]
InitiateReserveWithdraw {
assets: MultiAssetFilter,
reserve: MultiLocation,
effects: Vec<Order<()>>,
},
/// Remove the asset(s) (`assets`) from holding and send a `ReceiveTeleportedAsset` XCM message to a `destination`
/// location.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `destination`: A valid location that has a bi-lateral teleportation arrangement.
/// - `effects`: The orders to execute on the assets once arrived *on the destination location*.
///
/// NOTE: The `destination` location *MUST* respect this origin as a valid teleportation origin for all `assets`.
/// If it does not, then the assets may be lost.
///
/// Errors:
#[codec(index = 5)]
InitiateTeleport { assets: MultiAssetFilter, dest: MultiLocation, effects: Vec<Order<()>> },
/// Send a `Balances` XCM message with the `assets` value equal to the holding contents, or a portion thereof.
///
/// - `query_id`: An identifier that will be replicated into the returned XCM message.
/// - `dest`: A valid destination for the returned XCM message. This may be limited to the current origin.
/// - `assets`: A filter for the assets that should be reported back. The assets reported back will be, asset-
/// wise, *the lesser of this value and the holding register*. No wildcards will be used when reporting assets
/// back.
///
/// Errors:
#[codec(index = 6)]
QueryHolding {
#[codec(compact)]
query_id: u64,
dest: MultiLocation,
assets: MultiAssetFilter,
},
/// Pay for the execution of some XCM `instructions` and `orders` with up to `weight` picoseconds of execution time,
/// paying for this with up to `fees` from the Holding Register.
///
/// - `fees`: The asset(s) to remove from holding to pay for fees.
/// - `weight`: The amount of weight to purchase; this should be at least the shallow weight of `effects` and `xcm`.
/// - `debt`: The amount of weight-debt already incurred to be paid off; this should be equal to the unpaid weight of
/// any surrounding operations/orders.
/// - `halt_on_error`: If `true`, the execution of the `orders` and `operations` will halt on the first failure. If
/// `false`, then execution will continue regardless.
/// - `instructions`: XCM instructions to be executed outside of the context of the current Holding Register;
/// execution of these instructions happens AFTER the execution of the `orders`. The (shallow) weight for these
/// must be paid for with the `weight` purchased.
/// Errors:
#[codec(index = 7)]
BuyExecution {
fees: MultiAsset,
weight: u64,
debt: u64,
halt_on_error: bool,
instructions: Vec<Xcm<RuntimeCall>>,
},
}
pub mod opaque {
pub type Order = super::Order<()>;
}
impl<RuntimeCall> Order<RuntimeCall> {
pub fn into<C>(self) -> Order<C> {
Order::from(self)
}
pub fn from<C>(order: Order<C>) -> Self {
use Order::*;
match order {
Noop => Noop,
DepositAsset { assets, max_assets, beneficiary } =>
DepositAsset { assets, max_assets, beneficiary },
DepositReserveAsset { assets, max_assets, dest, effects } =>
DepositReserveAsset { assets, max_assets, dest, effects },
ExchangeAsset { give, receive } => ExchangeAsset { give, receive },
InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw { assets, reserve, effects },
InitiateTeleport { assets, dest, effects } =>
InitiateTeleport { assets, dest, effects },
QueryHolding { query_id, dest, assets } => QueryHolding { query_id, dest, assets },
BuyExecution { fees, weight, debt, halt_on_error, instructions } => {
let instructions = instructions.into_iter().map(Xcm::from).collect();
BuyExecution { fees, weight, debt, halt_on_error, instructions }
},
}
}
}
impl<RuntimeCall> TryFrom<OldOrder<RuntimeCall>> for Order<RuntimeCall> {
type Error = ();
fn try_from(old: OldOrder<RuntimeCall>) -> result::Result<Order<RuntimeCall>, ()> {
use Order::*;
Ok(match old {
OldOrder::Null => Noop,
OldOrder::DepositAsset { assets, dest } => DepositAsset {
assets: assets.try_into()?,
max_assets: 1,
beneficiary: dest.try_into()?,
},
OldOrder::DepositReserveAsset { assets, dest, effects } => DepositReserveAsset {
assets: assets.try_into()?,
max_assets: 1,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
OldOrder::ExchangeAsset { give, receive } =>
ExchangeAsset { give: give.try_into()?, receive: receive.try_into()? },
OldOrder::InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw {
assets: assets.try_into()?,
reserve: reserve.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
OldOrder::InitiateTeleport { assets, dest, effects } => InitiateTeleport {
assets: assets.try_into()?,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
OldOrder::QueryHolding { query_id, dest, assets } =>
QueryHolding { query_id, dest: dest.try_into()?, assets: assets.try_into()? },
OldOrder::BuyExecution { fees, weight, debt, halt_on_error, xcm } => {
let instructions = xcm
.into_iter()
.map(Xcm::<RuntimeCall>::try_from)
.collect::<result::Result<_, _>>()?;
BuyExecution { fees: fees.try_into()?, weight, debt, halt_on_error, instructions }
},
})
}
}
impl<RuntimeCall> TryFrom<Instruction<RuntimeCall>> for Order<RuntimeCall> {
type Error = ();
fn try_from(old: Instruction<RuntimeCall>) -> result::Result<Order<RuntimeCall>, ()> {
use Order::*;
Ok(match old {
Instruction::DepositAsset { assets, max_assets, beneficiary } =>
DepositAsset { assets, max_assets, beneficiary },
Instruction::DepositReserveAsset { assets, max_assets, dest, xcm } =>
DepositReserveAsset {
assets,
max_assets,
dest,
effects: xcm
.0
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Instruction::ExchangeAsset { give, receive } => ExchangeAsset { give, receive },
Instruction::InitiateReserveWithdraw { assets, reserve, xcm } =>
InitiateReserveWithdraw {
assets,
reserve,
effects: xcm
.0
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Instruction::InitiateTeleport { assets, dest, xcm } => InitiateTeleport {
assets,
dest,
effects: xcm
.0
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Instruction::QueryHolding { query_id, dest, assets, max_response_weight } => {
// Cannot handle special response weights.
if max_response_weight > 0 {
return Err(())
}
QueryHolding { query_id, dest, assets }
},
Instruction::BuyExecution { fees, weight_limit } => {
let instructions = vec![];
let halt_on_error = true;
let weight = 0;
let debt = Option::<u64>::from(weight_limit).ok_or(())?;
BuyExecution { fees, weight, debt, halt_on_error, instructions }
},
_ => return Err(()),
})
}
}
-277
View File
@@ -1,277 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use core::result;
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
use super::{MultiLocation, Xcm};
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
pub enum Error {
Undefined,
/// An arithmetic overflow happened.
Overflow,
/// The operation is intentionally unsupported.
Unimplemented,
UnhandledXcmVersion,
/// The implementation does not handle a given XCM.
UnhandledXcmMessage,
/// The implementation does not handle an effect present in an XCM.
UnhandledEffect,
EscalationOfPrivilege,
UntrustedReserveLocation,
UntrustedTeleportLocation,
DestinationBufferOverflow,
/// The message and destination was recognized as being reachable but the operation could not be completed.
/// A human-readable explanation of the specific issue is provided.
SendFailed(#[codec(skip)] &'static str),
/// The message and destination combination was not recognized as being reachable.
CannotReachDestination(MultiLocation, Xcm<()>),
MultiLocationFull,
FailedToDecode,
BadOrigin,
ExceedsMaxMessageSize,
/// An asset transaction (like withdraw or deposit) failed.
/// See implementers of the `TransactAsset` trait for sources.
/// Causes can include type conversion failures between id or balance types.
FailedToTransactAsset(#[codec(skip)] &'static str),
/// Execution of the XCM would potentially result in a greater weight used than the pre-specified
/// weight limit. The amount that is potentially required is the parameter.
WeightLimitReached(Weight),
/// An asset wildcard was passed where it was not expected (e.g. as the asset to withdraw in a
/// `WithdrawAsset` XCM).
Wildcard,
/// The case where an XCM message has specified a weight limit on an interior call and this
/// limit is too low.
///
/// Used by:
/// - `Transact`
MaxWeightInvalid,
/// The fees specified by the XCM message were not found in the holding register.
///
/// Used by:
/// - `BuyExecution`
NotHoldingFees,
/// The weight of an XCM message is not computable ahead of execution. This generally means at least part
/// of the message is invalid, which could be due to it containing overly nested structures or an invalid
/// nested data segment (e.g. for the call in `Transact`).
WeightNotComputable,
/// 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,
/// Indicates that it is not possible for a location to have an asset be withdrawn or transferred from its
/// ownership. This probably means it doesn't own (enough of) it, but may also indicate that it is under a
/// lock, hold, freeze or is otherwise unavailable.
NotWithdrawable,
/// Indicates that the consensus system cannot deposit an asset under the ownership of a particular location.
LocationCannotHold,
/// The assets given to purchase weight is are insufficient for the weight desired.
TooExpensive,
/// The given asset is not handled.
AssetNotFound,
/// The given message cannot be translated into a format that the destination can be expected to interpret.
DestinationUnsupported,
/// `execute_xcm` has been called too many times recursively.
RecursionLimitReached,
}
impl From<()> for Error {
fn from(_: ()) -> Self {
Self::Undefined
}
}
pub type Result = result::Result<(), Error>;
/// Local weight type; execution time in picoseconds.
pub type Weight = u64;
/// Outcome of an XCM execution.
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
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 {
match self {
Outcome::Complete(_) => Ok(()),
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(_) => 0,
}
}
}
/// Type of XCM message executor.
pub trait ExecuteXcm<RuntimeCall> {
/// Execute some XCM `message` 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<RuntimeCall>,
weight_limit: Weight,
) -> Outcome {
let origin = origin.into();
log::debug!(
target: "xcm::execute_xcm",
"origin: {:?}, message: {:?}, weight_limit: {:?}",
origin,
message,
weight_limit,
);
Self::execute_xcm_in_credit(origin, message, weight_limit, 0)
}
/// Execute some XCM `message` 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<RuntimeCall>,
weight_limit: Weight,
weight_credit: Weight,
) -> Outcome;
}
impl<C> ExecuteXcm<C> for () {
fn execute_xcm_in_credit(
_origin: impl Into<MultiLocation>,
_message: Xcm<C>,
_weight_limit: Weight,
_weight_credit: Weight,
) -> Outcome {
Outcome::Error(Error::Unimplemented)
}
}
/// Utility for sending an XCM message.
///
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return
/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination`
/// might alter the destination and the XCM message for to the next router.
///
///
/// # Example
/// ```rust
/// # use xcm::v1::{MultiLocation, Xcm, Junction, Junctions, Error, OriginKind, SendXcm, Result, Parent};
/// # use parity_scale_codec::Encode;
///
/// /// A sender that only passes the message through and does nothing.
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> Result {
/// return Err(Error::CannotReachDestination(destination.into(), message))
/// }
/// }
///
/// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing.
/// struct Sender2;
/// impl SendXcm for Sender2 {
/// fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> Result {
/// let destination = destination.into();
/// if matches!(destination.interior(), Junctions::X2(j1, j2))
/// && destination.parent_count() == 0
/// {
/// Ok(())
/// } else {
/// Err(Error::Undefined)
/// }
/// }
/// }
///
/// /// A sender that accepts a message from an X1 parent junction, passing through otherwise.
/// struct Sender3;
/// impl SendXcm for Sender3 {
/// fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> Result {
/// let destination = destination.into();
/// if matches!(destination.interior(), Junctions::Here)
/// && destination.parent_count() == 1
/// {
/// Ok(())
/// } else {
/// Err(Error::CannotReachDestination(destination, message))
/// }
/// }
/// }
///
/// // A call to send via XCM. We don't really care about this.
/// # fn main() {
/// let call: Vec<u8> = ().encode();
/// let message = Xcm::Transact { origin_type: OriginKind::Superuser, require_weight_at_most: 0, call: call.into() };
///
/// assert!(
/// // Sender2 will block this.
/// <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(Parent, message.clone())
/// .is_err()
/// );
///
/// assert!(
/// // Sender3 will catch this.
/// <(Sender1, Sender3) as SendXcm>::send_xcm(Parent, message.clone())
/// .is_ok()
/// );
/// # }
/// ```
pub trait SendXcm {
/// Send an XCM `message` to a given `destination`.
///
/// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST*
/// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without
/// trying other type fields.
fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> Result;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl SendXcm for Tuple {
fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> Result {
for_tuples!( #(
// we shadow `destination` and `message` in each expansion for the next one.
let (destination, message) = match Tuple::send_xcm(destination, message) {
Err(Error::CannotReachDestination(d, m)) => (d, m),
o @ _ => return o,
};
)* );
Err(Error::CannotReachDestination(destination.into(), message))
}
}
@@ -17,15 +17,16 @@
//! Support data structures for `MultiLocation`, primarily the `Junction` datatype.
use super::{BodyId, BodyPart, Junctions, MultiLocation, NetworkId};
use crate::v0::Junction as Junction0;
use crate::v3::Junction as NewJunction;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::{traits::ConstU32, WeakBoundedVec};
use sp_core::{bounded::WeakBoundedVec, ConstU32};
/// 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(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum Junction {
/// An indexed parachain belonging to and operated by the context.
///
@@ -77,23 +78,30 @@ pub enum Junction {
Plurality { id: BodyId, part: BodyPart },
}
impl TryFrom<Junction0> for Junction {
impl TryFrom<NewJunction> for Junction {
type Error = ();
fn try_from(value: Junction0) -> Result<Self, Self::Error> {
match value {
Junction0::Parent => Err(()),
Junction0::Parachain(id) => Ok(Self::Parachain(id)),
Junction0::AccountId32 { network, id } => Ok(Self::AccountId32 { network, id }),
Junction0::AccountIndex64 { network, index } =>
Ok(Self::AccountIndex64 { network, index }),
Junction0::AccountKey20 { network, key } => Ok(Self::AccountKey20 { network, key }),
Junction0::PalletInstance(index) => Ok(Self::PalletInstance(index)),
Junction0::GeneralIndex(id) => Ok(Self::GeneralIndex(id)),
Junction0::GeneralKey(key) => Ok(Self::GeneralKey(key)),
Junction0::OnlyChild => Ok(Self::OnlyChild),
Junction0::Plurality { id, part } => Ok(Self::Plurality { id: id.into(), part }),
}
fn try_from(value: NewJunction) -> Result<Self, Self::Error> {
use NewJunction::*;
Ok(match value {
Parachain(id) => Self::Parachain(id),
AccountId32 { network, id } => Self::AccountId32 { network: network.try_into()?, id },
AccountIndex64 { network, index } =>
Self::AccountIndex64 { network: network.try_into()?, index },
AccountKey20 { network, key } =>
Self::AccountKey20 { network: network.try_into()?, key },
PalletInstance(index) => Self::PalletInstance(index),
GeneralIndex(id) => Self::GeneralIndex(id),
GeneralKey(key) => Self::GeneralKey(
key[..]
.to_vec()
.try_into()
.expect("array is of size 32 and so will never be out of bounds; qed"),
),
OnlyChild => Self::OnlyChild,
Plurality { id, part } => Self::Plurality { id: id.into(), part: part.into() },
_ => return Err(()),
})
}
}
+392 -208
View File
@@ -50,23 +50,198 @@
//! `DepositAsset` instructions. Failing that, dispatch calls to `teleport_assets` and
//! `reserve_transfer_assets` will fail with `UnweighableMessage`.
use super::v1::{Order as OldOrder, Response as OldResponse, Xcm as OldXcm};
use crate::{DoubleEncoded, GetWeight};
use super::{
v3::{
BodyId as NewBodyId, BodyPart as NewBodyPart, Instruction as NewInstruction,
NetworkId as NewNetworkId, Response as NewResponse, WeightLimit as NewWeightLimit,
Xcm as NewXcm,
},
DoubleEncoded, GetWeight,
};
use alloc::{vec, vec::Vec};
use core::{fmt::Debug, result};
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
use parity_scale_codec::{self, Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_core::{bounded::WeakBoundedVec, ConstU32};
mod junction;
mod multiasset;
mod multilocation;
mod traits;
pub use traits::{Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm};
// These parts of XCM v1 have been unchanged in XCM v2, and are re-imported here.
pub use super::v1::{
Ancestor, AncestorThen, AssetId, AssetInstance, BodyId, BodyPart, Fungibility,
InteriorMultiLocation, Junction, Junctions, MultiAsset, MultiAssetFilter, MultiAssets,
MultiLocation, NetworkId, OriginKind, Parent, ParentThen, WildFungibility, WildMultiAsset,
pub use junction::Junction;
pub use multiasset::{
AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets,
WildFungibility, WildMultiAsset,
};
pub use multilocation::{
Ancestor, AncestorThen, InteriorMultiLocation, Junctions, MultiLocation, Parent, ParentThen,
};
pub use traits::{Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm};
/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`.
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
pub enum OriginKind {
/// Origin should just be the native dispatch origin representation for the sender in the
/// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin
/// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a
/// primary/native dispatch origin form.
Native,
/// Origin should just be the standard account-based origin with the sovereign account of
/// the sender. For Cumulus/Frame chains, this is the `Signed` origin.
SovereignAccount,
/// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin.
/// This will not usually be an available option.
Superuser,
/// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be
/// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be
/// the `pallet_xcm::Origin::Xcm` type.
Xcm,
}
/// A global identifier of an account-bearing consensus system.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum NetworkId {
/// Unidentified/any.
Any,
/// Some named network.
Named(WeakBoundedVec<u8, ConstU32<32>>),
/// The Polkadot Relay chain
Polkadot,
/// Kusama.
Kusama,
}
impl TryInto<NetworkId> for Option<NewNetworkId> {
type Error = ();
fn try_into(self) -> result::Result<NetworkId, ()> {
use NewNetworkId::*;
Ok(match self {
None => NetworkId::Any,
Some(Polkadot) => NetworkId::Polkadot,
Some(Kusama) => NetworkId::Kusama,
_ => return Err(()),
})
}
}
/// An identifier of a pluralistic body.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum BodyId {
/// The only body in its context.
Unit,
/// A named body.
Named(WeakBoundedVec<u8, ConstU32<32>>),
/// An indexed body.
Index(#[codec(compact)] u32),
/// The unambiguous executive body (for Polkadot, this would be the Polkadot council).
Executive,
/// The unambiguous technical body (for Polkadot, this would be the Technical Committee).
Technical,
/// The unambiguous legislative body (for Polkadot, this could be considered the opinion of a majority of
/// lock-voters).
Legislative,
/// The unambiguous judicial body (this doesn't exist on Polkadot, but if it were to get a "grand oracle", it
/// may be considered as that).
Judicial,
/// The unambiguous defense body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `staking_admin` track).
Defense,
/// The unambiguous administration body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `general_admin` track).
Administration,
/// The unambiguous treasury body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `treasurer` track).
Treasury,
}
impl From<NewBodyId> for BodyId {
fn from(n: NewBodyId) -> Self {
use NewBodyId::*;
match n {
Unit => Self::Unit,
Moniker(n) => Self::Named(
n[..]
.to_vec()
.try_into()
.expect("array size is 4 and so will never be out of bounds; qed"),
),
Index(n) => Self::Index(n),
Executive => Self::Executive,
Technical => Self::Technical,
Legislative => Self::Legislative,
Judicial => Self::Judicial,
Defense => Self::Defense,
Administration => Self::Administration,
Treasury => Self::Treasury,
}
}
}
/// A part of a pluralistic body.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
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 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,
}
}
}
impl From<NewBodyPart> for BodyPart {
fn from(n: NewBodyPart) -> Self {
use NewBodyPart::*;
match n {
Voice => Self::Voice,
Members { count } => Self::Members { count },
Fraction { nom, denom } => Self::Fraction { nom, denom },
AtLeastProportion { nom, denom } => Self::AtLeastProportion { nom, denom },
MoreThanProportion { nom, denom } => Self::MoreThanProportion { nom, denom },
}
}
}
/// This module's XCM version.
pub const VERSION: super::Version = 2;
@@ -218,6 +393,17 @@ impl From<WeightLimit> for Option<u64> {
}
}
impl TryFrom<NewWeightLimit> for WeightLimit {
type Error = ();
fn try_from(x: NewWeightLimit) -> result::Result<Self, Self::Error> {
use NewWeightLimit::*;
match x {
Limited(w) => Ok(Self::Limited(w.ref_time())),
Unlimited => Ok(Self::Unlimited),
}
}
}
/// Local weight type; execution time in picoseconds.
pub type Weight = u64;
@@ -422,6 +608,12 @@ pub enum Instruction<RuntimeCall> {
/// A `QueryResponse` message of type `ExecutionOutcome` is sent to `dest` with the given
/// `query_id` and the outcome of the XCM.
///
/// - `query_id`: An identifier that will be replicated into the returned XCM message.
/// - `dest`: A valid destination for the returned XCM message.
/// - `max_response_weight`: The maximum amount of weight that the `QueryResponse` item which
/// is sent as a reply may take to execute. NOTE: If this is unexpectedly large then the
/// response may not execute at all.
///
/// Kind: *Instruction*
///
/// Errors:
@@ -633,7 +825,14 @@ pub enum Instruction<RuntimeCall> {
/// support in a `QueryResponse` instruction. Any changes to this should also elicit similar
/// responses when they happen.
///
/// - `query_id`: An identifier that will be replicated into the returned XCM message.
/// - `max_response_weight`: The maximum amount of weight that the `QueryResponse` item which
/// is sent as a reply may take to execute. NOTE: If this is unexpectedly large then the
/// response may not execute at all.
///
/// Kind: *Instruction*
///
/// Errors: *Fallible*
SubscribeVersion {
#[codec(compact)]
query_id: QueryId,
@@ -644,6 +843,8 @@ pub enum Instruction<RuntimeCall> {
/// Cancel the effect of a previous `SubscribeVersion` instruction.
///
/// Kind: *Instruction*
///
/// Errors: *Fallible*
UnsubscribeVersion,
}
@@ -708,48 +909,81 @@ impl<RuntimeCall> Instruction<RuntimeCall> {
// TODO: Automate Generation
impl<RuntimeCall, W: XcmWeightInfo<RuntimeCall>> GetWeight<W> for Instruction<RuntimeCall> {
fn weight(&self) -> Weight {
fn weight(&self) -> sp_weights::Weight {
use Instruction::*;
match self {
WithdrawAsset(assets) => W::withdraw_asset(assets),
ReserveAssetDeposited(assets) => W::reserve_asset_deposited(assets),
ReceiveTeleportedAsset(assets) => W::receive_teleported_asset(assets),
WithdrawAsset(assets) => sp_weights::Weight::from_ref_time(W::withdraw_asset(assets)),
ReserveAssetDeposited(assets) =>
sp_weights::Weight::from_ref_time(W::reserve_asset_deposited(assets)),
ReceiveTeleportedAsset(assets) =>
sp_weights::Weight::from_ref_time(W::receive_teleported_asset(assets)),
QueryResponse { query_id, response, max_weight } =>
W::query_response(query_id, response, max_weight),
TransferAsset { assets, beneficiary } => W::transfer_asset(assets, beneficiary),
sp_weights::Weight::from_ref_time(W::query_response(query_id, response, max_weight)),
TransferAsset { assets, beneficiary } =>
sp_weights::Weight::from_ref_time(W::transfer_asset(assets, beneficiary)),
TransferReserveAsset { assets, dest, xcm } =>
W::transfer_reserve_asset(&assets, dest, xcm),
sp_weights::Weight::from_ref_time(W::transfer_reserve_asset(&assets, dest, xcm)),
Transact { origin_type, require_weight_at_most, call } =>
W::transact(origin_type, require_weight_at_most, call),
sp_weights::Weight::from_ref_time(W::transact(
origin_type,
require_weight_at_most,
call,
)),
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
W::hrmp_new_channel_open_request(sender, max_message_size, max_capacity),
HrmpChannelAccepted { recipient } => W::hrmp_channel_accepted(recipient),
sp_weights::Weight::from_ref_time(W::hrmp_new_channel_open_request(
sender,
max_message_size,
max_capacity,
)),
HrmpChannelAccepted { recipient } =>
sp_weights::Weight::from_ref_time(W::hrmp_channel_accepted(recipient)),
HrmpChannelClosing { initiator, sender, recipient } =>
W::hrmp_channel_closing(initiator, sender, recipient),
ClearOrigin => W::clear_origin(),
DescendOrigin(who) => W::descend_origin(who),
sp_weights::Weight::from_ref_time(W::hrmp_channel_closing(
initiator, sender, recipient,
)),
ClearOrigin => sp_weights::Weight::from_ref_time(W::clear_origin()),
DescendOrigin(who) => sp_weights::Weight::from_ref_time(W::descend_origin(who)),
ReportError { query_id, dest, max_response_weight } =>
W::report_error(query_id, dest, max_response_weight),
sp_weights::Weight::from_ref_time(W::report_error(
query_id,
dest,
max_response_weight,
)),
DepositAsset { assets, max_assets, beneficiary } =>
W::deposit_asset(assets, max_assets, beneficiary),
sp_weights::Weight::from_ref_time(W::deposit_asset(assets, max_assets, beneficiary)),
DepositReserveAsset { assets, max_assets, dest, xcm } =>
W::deposit_reserve_asset(assets, max_assets, dest, xcm),
ExchangeAsset { give, receive } => W::exchange_asset(give, receive),
InitiateReserveWithdraw { assets, reserve, xcm } =>
sp_weights::Weight::from_ref_time(W::deposit_reserve_asset(
assets, max_assets, dest, xcm,
)),
ExchangeAsset { give, receive } =>
sp_weights::Weight::from_ref_time(W::exchange_asset(give, receive)),
InitiateReserveWithdraw { assets, reserve, xcm } => sp_weights::Weight::from_ref_time(
W::initiate_reserve_withdraw(assets, reserve, xcm),
InitiateTeleport { assets, dest, xcm } => W::initiate_teleport(assets, dest, xcm),
),
InitiateTeleport { assets, dest, xcm } =>
sp_weights::Weight::from_ref_time(W::initiate_teleport(assets, dest, xcm)),
QueryHolding { query_id, dest, assets, max_response_weight } =>
W::query_holding(query_id, dest, assets, max_response_weight),
BuyExecution { fees, weight_limit } => W::buy_execution(fees, weight_limit),
RefundSurplus => W::refund_surplus(),
SetErrorHandler(xcm) => W::set_error_handler(xcm),
SetAppendix(xcm) => W::set_appendix(xcm),
ClearError => W::clear_error(),
ClaimAsset { assets, ticket } => W::claim_asset(assets, ticket),
Trap(code) => W::trap(code),
sp_weights::Weight::from_ref_time(W::query_holding(
query_id,
dest,
assets,
max_response_weight,
)),
BuyExecution { fees, weight_limit } =>
sp_weights::Weight::from_ref_time(W::buy_execution(fees, weight_limit)),
RefundSurplus => sp_weights::Weight::from_ref_time(W::refund_surplus()),
SetErrorHandler(xcm) => sp_weights::Weight::from_ref_time(W::set_error_handler(xcm)),
SetAppendix(xcm) => sp_weights::Weight::from_ref_time(W::set_appendix(xcm)),
ClearError => sp_weights::Weight::from_ref_time(W::clear_error()),
ClaimAsset { assets, ticket } =>
sp_weights::Weight::from_ref_time(W::claim_asset(assets, ticket)),
Trap(code) => sp_weights::Weight::from_ref_time(W::trap(code)),
SubscribeVersion { query_id, max_response_weight } =>
W::subscribe_version(query_id, max_response_weight),
UnsubscribeVersion => W::unsubscribe_version(),
sp_weights::Weight::from_ref_time(W::subscribe_version(
query_id,
max_response_weight,
)),
UnsubscribeVersion => sp_weights::Weight::from_ref_time(W::unsubscribe_version()),
}
}
}
@@ -764,180 +998,130 @@ pub mod opaque {
pub type Instruction = super::Instruction<()>;
}
// Convert from a v1 response to a v2 response
impl TryFrom<OldResponse> for Response {
// Convert from a v3 response to a v2 response
impl TryFrom<NewResponse> for Response {
type Error = ();
fn try_from(old_response: OldResponse) -> result::Result<Self, ()> {
match old_response {
OldResponse::Assets(assets) => Ok(Self::Assets(assets)),
OldResponse::Version(version) => Ok(Self::Version(version)),
}
}
}
impl<RuntimeCall> TryFrom<OldXcm<RuntimeCall>> for Xcm<RuntimeCall> {
type Error = ();
fn try_from(old: OldXcm<RuntimeCall>) -> result::Result<Xcm<RuntimeCall>, ()> {
use Instruction::*;
Ok(Xcm(match old {
OldXcm::WithdrawAsset { assets, effects } => Some(Ok(WithdrawAsset(assets)))
.into_iter()
.chain(effects.into_iter().map(Instruction::try_from))
.collect::<result::Result<Vec<_>, _>>()?,
OldXcm::ReserveAssetDeposited { assets, effects } =>
Some(Ok(ReserveAssetDeposited(assets)))
.into_iter()
.chain(Some(Ok(ClearOrigin)).into_iter())
.chain(effects.into_iter().map(Instruction::try_from))
.collect::<result::Result<Vec<_>, _>>()?,
OldXcm::ReceiveTeleportedAsset { assets, effects } =>
Some(Ok(ReceiveTeleportedAsset(assets)))
.into_iter()
.chain(Some(Ok(ClearOrigin)).into_iter())
.chain(effects.into_iter().map(Instruction::try_from))
.collect::<result::Result<Vec<_>, _>>()?,
OldXcm::QueryResponse { query_id, response } => vec![QueryResponse {
query_id,
response: response.try_into()?,
max_weight: 50_000_000,
}],
OldXcm::TransferAsset { assets, beneficiary } =>
vec![TransferAsset { assets, beneficiary }],
OldXcm::TransferReserveAsset { assets, dest, effects } => vec![TransferReserveAsset {
assets,
dest,
xcm: Xcm(effects
.into_iter()
.map(Instruction::<()>::try_from)
.collect::<result::Result<_, _>>()?),
}],
OldXcm::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
vec![HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }],
OldXcm::HrmpChannelAccepted { recipient } => vec![HrmpChannelAccepted { recipient }],
OldXcm::HrmpChannelClosing { initiator, sender, recipient } =>
vec![HrmpChannelClosing { initiator, sender, recipient }],
OldXcm::Transact { origin_type, require_weight_at_most, call } =>
vec![Transact { origin_type, require_weight_at_most, call }],
// We don't handle this one at all due to nested XCM.
OldXcm::RelayedFrom { .. } => return Err(()),
OldXcm::SubscribeVersion { query_id, max_response_weight } =>
vec![SubscribeVersion { query_id, max_response_weight }],
OldXcm::UnsubscribeVersion => vec![UnsubscribeVersion],
}))
}
}
impl<RuntimeCall> TryFrom<OldOrder<RuntimeCall>> for Instruction<RuntimeCall> {
type Error = ();
fn try_from(old: OldOrder<RuntimeCall>) -> result::Result<Instruction<RuntimeCall>, ()> {
use Instruction::*;
Ok(match old {
OldOrder::Noop => return Err(()),
OldOrder::DepositAsset { assets, max_assets, beneficiary } =>
DepositAsset { assets, max_assets, beneficiary },
OldOrder::DepositReserveAsset { assets, max_assets, dest, effects } =>
DepositReserveAsset {
assets,
max_assets,
dest,
xcm: Xcm(effects
.into_iter()
.map(Instruction::<()>::try_from)
.collect::<result::Result<_, _>>()?),
},
OldOrder::ExchangeAsset { give, receive } => ExchangeAsset { give, receive },
OldOrder::InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw {
assets,
reserve,
xcm: Xcm(effects
.into_iter()
.map(Instruction::<()>::try_from)
.collect::<result::Result<_, _>>()?),
},
OldOrder::InitiateTeleport { assets, dest, effects } => InitiateTeleport {
assets,
dest,
xcm: Xcm(effects
.into_iter()
.map(Instruction::<()>::try_from)
.collect::<result::Result<_, _>>()?),
},
OldOrder::QueryHolding { query_id, dest, assets } =>
QueryHolding { query_id, dest, assets, max_response_weight: 0 },
OldOrder::BuyExecution { fees, debt, instructions, .. } => {
// We don't handle nested XCM.
if !instructions.is_empty() {
return Err(())
}
BuyExecution { fees, weight_limit: WeightLimit::Limited(debt) }
},
fn try_from(response: NewResponse) -> result::Result<Self, ()> {
Ok(match response {
NewResponse::Assets(assets) => Self::Assets(assets.try_into()?),
NewResponse::Version(version) => Self::Version(version),
NewResponse::ExecutionResult(error) => Self::ExecutionResult(match error {
Some((i, e)) => Some((i, e.try_into()?)),
None => None,
}),
NewResponse::Null => Self::Null,
_ => return Err(()),
})
}
}
#[cfg(test)]
mod tests {
use super::{prelude::*, *};
#[test]
fn basic_roundtrip_works() {
let xcm =
Xcm::<()>(vec![TransferAsset { assets: (Here, 1).into(), beneficiary: Here.into() }]);
let old_xcm =
OldXcm::<()>::TransferAsset { assets: (Here, 1).into(), beneficiary: Here.into() };
assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
assert_eq!(new_xcm, xcm);
}
#[test]
fn teleport_roundtrip_works() {
let xcm = Xcm::<()>(vec![
ReceiveTeleportedAsset((Here, 1).into()),
ClearOrigin,
DepositAsset { assets: Wild(All), max_assets: 1, beneficiary: Here.into() },
]);
let old_xcm: OldXcm<()> = OldXcm::<()>::ReceiveTeleportedAsset {
assets: (Here, 1).into(),
effects: vec![OldOrder::DepositAsset {
assets: Wild(All),
max_assets: 1,
beneficiary: Here.into(),
}],
};
assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
assert_eq!(new_xcm, xcm);
}
#[test]
fn reserve_deposit_roundtrip_works() {
let xcm = Xcm::<()>(vec![
ReserveAssetDeposited((Here, 1).into()),
ClearOrigin,
BuyExecution { fees: (Here, 1).into(), weight_limit: Some(1).into() },
DepositAsset { assets: Wild(All), max_assets: 1, beneficiary: Here.into() },
]);
let old_xcm: OldXcm<()> = OldXcm::<()>::ReserveAssetDeposited {
assets: (Here, 1).into(),
effects: vec![
OldOrder::BuyExecution {
fees: (Here, 1).into(),
debt: 1,
weight: 0,
instructions: vec![],
halt_on_error: true,
},
OldOrder::DepositAsset {
assets: Wild(All),
max_assets: 1,
beneficiary: Here.into(),
},
],
};
assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
assert_eq!(new_xcm, xcm);
// Convert from a v3 XCM to a v2 XCM.
impl<RuntimeCall> TryFrom<NewXcm<RuntimeCall>> for Xcm<RuntimeCall> {
type Error = ();
fn try_from(new_xcm: NewXcm<RuntimeCall>) -> result::Result<Self, ()> {
Ok(Xcm(new_xcm.0.into_iter().map(TryInto::try_into).collect::<result::Result<_, _>>()?))
}
}
// Convert from a v3 instruction to a v2 instruction
impl<RuntimeCall> TryFrom<NewInstruction<RuntimeCall>> for Instruction<RuntimeCall> {
type Error = ();
fn try_from(instruction: NewInstruction<RuntimeCall>) -> result::Result<Self, ()> {
use NewInstruction::*;
Ok(match instruction {
WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?),
ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?),
ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?),
QueryResponse { query_id, response, max_weight, .. } => Self::QueryResponse {
query_id,
response: response.try_into()?,
max_weight: max_weight.ref_time(),
},
TransferAsset { assets, beneficiary } => Self::TransferAsset {
assets: assets.try_into()?,
beneficiary: beneficiary.try_into()?,
},
TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset {
assets: assets.try_into()?,
dest: dest.try_into()?,
xcm: xcm.try_into()?,
},
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient },
HrmpChannelClosing { initiator, sender, recipient } =>
Self::HrmpChannelClosing { initiator, sender, recipient },
Transact { origin_kind, require_weight_at_most, call } => Self::Transact {
origin_type: origin_kind,
require_weight_at_most: require_weight_at_most.ref_time(),
call: call.into(),
},
ReportError(response_info) => Self::ReportError {
query_id: response_info.query_id,
dest: response_info.destination.try_into()?,
max_response_weight: response_info.max_weight.ref_time(),
},
DepositAsset { assets, beneficiary } => {
let max_assets = assets.count().ok_or(())?;
let beneficiary = beneficiary.try_into()?;
let assets = assets.try_into()?;
Self::DepositAsset { assets, max_assets, beneficiary }
},
DepositReserveAsset { assets, dest, xcm } => {
let max_assets = assets.count().ok_or(())?;
let dest = dest.try_into()?;
let xcm = xcm.try_into()?;
let assets = assets.try_into()?;
Self::DepositReserveAsset { assets, max_assets, dest, xcm }
},
ExchangeAsset { give, want, .. } => {
let give = give.try_into()?;
let receive = want.try_into()?;
Self::ExchangeAsset { give, receive }
},
InitiateReserveWithdraw { assets, reserve, xcm } => {
// No `max_assets` here, so if there's a connt, then we cannot translate.
let assets = assets.try_into()?;
let reserve = reserve.try_into()?;
let xcm = xcm.try_into()?;
Self::InitiateReserveWithdraw { assets, reserve, xcm }
},
InitiateTeleport { assets, dest, xcm } => {
// No `max_assets` here, so if there's a connt, then we cannot translate.
let assets = assets.try_into()?;
let dest = dest.try_into()?;
let xcm = xcm.try_into()?;
Self::InitiateTeleport { assets, dest, xcm }
},
ReportHolding { response_info, assets } => Self::QueryHolding {
query_id: response_info.query_id,
dest: response_info.destination.try_into()?,
assets: assets.try_into()?,
max_response_weight: response_info.max_weight.ref_time(),
},
BuyExecution { fees, weight_limit } => {
let fees = fees.try_into()?;
let weight_limit = weight_limit.try_into()?;
Self::BuyExecution { fees, weight_limit }
},
ClearOrigin => Self::ClearOrigin,
DescendOrigin(who) => Self::DescendOrigin(who.try_into()?),
RefundSurplus => Self::RefundSurplus,
SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?),
SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?),
ClearError => Self::ClearError,
ClaimAsset { assets, ticket } => {
let assets = assets.try_into()?;
let ticket = ticket.try_into()?;
Self::ClaimAsset { assets, ticket }
},
Trap(code) => Self::Trap(code),
SubscribeVersion { query_id, max_response_weight } => Self::SubscribeVersion {
query_id,
max_response_weight: max_response_weight.ref_time(),
},
UnsubscribeVersion => Self::UnsubscribeVersion,
_ => return Err(()),
})
}
}
@@ -24,13 +24,20 @@
//! account.
use super::MultiLocation;
use crate::v3::{
AssetId as NewAssetId, AssetInstance as NewAssetInstance, Fungibility as NewFungibility,
MultiAsset as NewMultiAsset, MultiAssetFilter as NewMultiAssetFilter,
MultiAssets as NewMultiAssets, WildFungibility as NewWildFungibility,
WildMultiAsset as NewWildMultiAsset,
};
use alloc::{vec, vec::Vec};
use core::{cmp::Ordering, result};
use core::cmp::Ordering;
use parity_scale_codec::{self as codec, Decode, Encode};
use scale_info::TypeInfo;
/// A general identifier for an instance of a non-fungible asset class.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum AssetInstance {
/// Undefined - used if the non-fungible asset class has only one instance.
Undefined,
@@ -91,8 +98,24 @@ impl From<Vec<u8>> for AssetInstance {
}
}
impl TryFrom<NewAssetInstance> for AssetInstance {
type Error = ();
fn try_from(value: NewAssetInstance) -> Result<Self, Self::Error> {
use NewAssetInstance::*;
Ok(match value {
Undefined => Self::Undefined,
Index(n) => Self::Index(n),
Array4(n) => Self::Array4(n),
Array8(n) => Self::Array8(n),
Array16(n) => Self::Array16(n),
Array32(n) => Self::Array32(n),
})
}
}
/// Classification of an asset being concrete or abstract.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum AssetId {
Concrete(MultiLocation),
Abstract(Vec<u8>),
@@ -110,6 +133,20 @@ impl From<Vec<u8>> for AssetId {
}
}
impl TryFrom<NewAssetId> for AssetId {
type Error = ();
fn try_from(old: NewAssetId) -> Result<Self, ()> {
use NewAssetId::*;
Ok(match old {
Concrete(l) => Self::Concrete(l.try_into()?),
Abstract(v) => {
let zeroes = v.iter().rev().position(|n| *n != 0).unwrap_or(v.len());
Self::Abstract(v[0..(32 - zeroes)].to_vec())
},
})
}
}
impl AssetId {
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
pub fn prepend_with(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
@@ -142,6 +179,7 @@ impl AssetId {
/// Classification of whether an asset is fungible or not, along with a mandatory amount or instance.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum Fungibility {
Fungible(#[codec(compact)] u128),
NonFungible(AssetInstance),
@@ -168,7 +206,19 @@ impl<T: Into<AssetInstance>> From<T> for Fungibility {
}
}
impl TryFrom<NewFungibility> for Fungibility {
type Error = ();
fn try_from(value: NewFungibility) -> Result<Self, Self::Error> {
use NewFungibility::*;
Ok(match value {
Fungible(n) => Self::Fungible(n),
NonFungible(i) => Self::NonFungible(i.try_into()?),
})
}
}
#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiAsset {
pub id: AssetId,
pub fun: Fungibility,
@@ -243,47 +293,16 @@ impl MultiAsset {
}
}
impl TryFrom<super::super::v0::MultiAsset> for MultiAsset {
impl TryFrom<NewMultiAsset> for MultiAsset {
type Error = ();
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<MultiAsset, ()> {
use super::super::v0::MultiAsset as V0;
use AssetId::*;
use Fungibility::*;
let (id, fun) = match old {
V0::ConcreteFungible { id, amount } => (Concrete(id.try_into()?), Fungible(amount)),
V0::ConcreteNonFungible { class, instance } =>
(Concrete(class.try_into()?), NonFungible(instance)),
V0::AbstractFungible { id, amount } => (Abstract(id), Fungible(amount)),
V0::AbstractNonFungible { class, instance } => (Abstract(class), NonFungible(instance)),
_ => return Err(()),
};
Ok(MultiAsset { id, fun })
}
}
impl TryFrom<super::super::v0::MultiAsset> for Option<MultiAsset> {
type Error = ();
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<Option<MultiAsset>, ()> {
match old {
super::super::v0::MultiAsset::None => return Ok(None),
x => return Ok(Some(x.try_into()?)),
}
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAsset {
type Error = ();
fn try_from(mut old: Vec<super::super::v0::MultiAsset>) -> result::Result<MultiAsset, ()> {
if old.len() == 1 {
old.remove(0).try_into()
} else {
Err(())
}
fn try_from(new: NewMultiAsset) -> Result<Self, ()> {
Ok(Self { id: new.id.try_into()?, fun: new.fun.try_into()? })
}
}
/// A `Vec` of `MultiAsset`s. There may be no duplicate fungible items in here and when decoding, they must be sorted.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiAssets(Vec<MultiAsset>);
impl Decode for MultiAssets {
@@ -293,15 +312,15 @@ impl Decode for MultiAssets {
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAssets {
impl TryFrom<NewMultiAssets> for MultiAssets {
type Error = ();
fn try_from(old: Vec<super::super::v0::MultiAsset>) -> result::Result<MultiAssets, ()> {
let v = old
fn try_from(new: NewMultiAssets) -> Result<Self, ()> {
let v = new
.into_inner()
.into_iter()
.map(Option::<MultiAsset>::try_from)
.filter_map(|x| x.transpose())
.collect::<result::Result<Vec<MultiAsset>, ()>>()?;
Ok(v.into())
.map(MultiAsset::try_from)
.collect::<Result<Vec<_>, ()>>()?;
Ok(MultiAssets(v))
}
}
@@ -435,7 +454,8 @@ impl MultiAssets {
self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix))
}
/// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location.
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `ancestry`.
pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> {
self.0.iter_mut().try_for_each(|i| i.reanchor(target, ancestry))
}
@@ -445,15 +465,29 @@ impl MultiAssets {
self.0.get(index)
}
}
/// Classification of whether an asset is fungible or not.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum WildFungibility {
Fungible,
NonFungible,
}
impl TryFrom<NewWildFungibility> for WildFungibility {
type Error = ();
fn try_from(value: NewWildFungibility) -> Result<Self, Self::Error> {
use NewWildFungibility::*;
Ok(match value {
Fungible => Self::Fungible,
NonFungible => Self::NonFungible,
})
}
}
/// A wildcard representing a set of assets.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum WildMultiAsset {
/// All assets in the holding register, up to `usize` individual assets (different instances of non-fungibles could
/// be separate assets).
@@ -463,35 +497,6 @@ pub enum WildMultiAsset {
AllOf { id: AssetId, fun: WildFungibility },
}
impl TryFrom<super::super::v0::MultiAsset> for WildMultiAsset {
type Error = ();
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<WildMultiAsset, ()> {
use super::super::v0::MultiAsset as V0;
use AssetId::*;
use WildFungibility::*;
let (id, fun) = match old {
V0::All => return Ok(WildMultiAsset::All),
V0::AllConcreteFungible { id } => (Concrete(id.try_into()?), Fungible),
V0::AllConcreteNonFungible { class } => (Concrete(class.try_into()?), NonFungible),
V0::AllAbstractFungible { id } => (Abstract(id), Fungible),
V0::AllAbstractNonFungible { class } => (Abstract(class), NonFungible),
_ => return Err(()),
};
Ok(WildMultiAsset::AllOf { id, fun })
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for WildMultiAsset {
type Error = ();
fn try_from(mut old: Vec<super::super::v0::MultiAsset>) -> result::Result<WildMultiAsset, ()> {
if old.len() == 1 {
old.remove(0).try_into()
} else {
Err(())
}
}
}
impl WildMultiAsset {
/// Returns true if `self` is a super-set of the given `inner`.
///
@@ -505,7 +510,8 @@ impl WildMultiAsset {
}
}
/// Prepend a `MultiLocation` to any concrete asset components, giving it a new root location.
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `ancestry`.
pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> {
use WildMultiAsset::*;
match self {
@@ -526,6 +532,7 @@ impl<A: Into<AssetId>, B: Into<WildFungibility>> From<(A, B)> for WildMultiAsset
/// Note: Vectors of wildcards whose encoding is supported in XCM v0 are unsupported
/// in this implementation and will result in a decode error.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum MultiAssetFilter {
Definite(MultiAssets),
Wild(WildMultiAsset),
@@ -567,7 +574,8 @@ impl MultiAssetFilter {
}
}
/// Prepend a `MultiLocation` to any concrete asset components, giving it a new root location.
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `ancestry`.
pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> {
match self {
MultiAssetFilter::Definite(ref mut assets) => assets.reanchor(target, ancestry),
@@ -576,15 +584,25 @@ impl MultiAssetFilter {
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAssetFilter {
impl TryFrom<NewWildMultiAsset> for WildMultiAsset {
type Error = ();
fn try_from(
mut old: Vec<super::super::v0::MultiAsset>,
) -> result::Result<MultiAssetFilter, ()> {
if old.len() == 1 && old[0].is_wildcard() {
Ok(MultiAssetFilter::Wild(old.remove(0).try_into()?))
} else {
Ok(MultiAssetFilter::Definite(old.try_into()?))
}
fn try_from(new: NewWildMultiAsset) -> Result<Self, ()> {
use NewWildMultiAsset::*;
Ok(match new {
AllOf { id, fun } | AllOfCounted { id, fun, .. } =>
Self::AllOf { id: id.try_into()?, fun: fun.try_into()? },
All | AllCounted(_) => Self::All,
})
}
}
impl TryFrom<NewMultiAssetFilter> for MultiAssetFilter {
type Error = ();
fn try_from(old: NewMultiAssetFilter) -> Result<Self, ()> {
use NewMultiAssetFilter::*;
Ok(match old {
Definite(x) => Self::Definite(x.try_into()?),
Wild(x) => Self::Wild(x.try_into()?),
})
}
}
@@ -17,6 +17,7 @@
//! Cross-Consensus Message format data structures.
use super::Junction;
use crate::v3::MultiLocation as NewMultiLocation;
use core::{mem, result};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
@@ -48,6 +49,7 @@ use scale_info::TypeInfo;
///
/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system.
#[derive(Clone, Decode, Encode, Eq, PartialEq, Ord, PartialOrd, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiLocation {
/// The number of parent junctions at the beginning of this `MultiLocation`.
pub parents: u8,
@@ -236,7 +238,7 @@ impl MultiLocation {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation};
/// # use xcm::v2::{Junctions::*, Junction::*, MultiLocation};
/// # fn main() {
/// let mut m = MultiLocation::new(1, X2(PalletInstance(3), OnlyChild));
/// assert_eq!(
@@ -258,7 +260,7 @@ impl MultiLocation {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation};
/// # use xcm::v2::{Junctions::*, Junction::*, MultiLocation};
/// let m = MultiLocation::new(1, X3(PalletInstance(3), OnlyChild, OnlyChild));
/// assert!(m.starts_with(&MultiLocation::new(1, X1(PalletInstance(3)))));
/// assert!(!m.starts_with(&MultiLocation::new(1, X1(GeneralIndex(99)))));
@@ -277,7 +279,7 @@ impl MultiLocation {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation};
/// # use xcm::v2::{Junctions::*, Junction::*, MultiLocation};
/// # fn main() {
/// let mut m = MultiLocation::new(1, X1(Parachain(21)));
/// assert_eq!(m.append_with(X1(PalletInstance(3))), Ok(()));
@@ -300,7 +302,7 @@ impl MultiLocation {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation};
/// # use xcm::v2::{Junctions::*, Junction::*, MultiLocation};
/// # fn main() {
/// let mut m = MultiLocation::new(2, X1(PalletInstance(3)));
/// assert_eq!(m.prepend_with(MultiLocation::new(1, X2(Parachain(21), OnlyChild))), Ok(()));
@@ -342,6 +344,21 @@ impl MultiLocation {
Ok(())
}
/// Consume `self` and return the value representing the same location from the point of view
/// of `target`. The context of `self` is provided as `ancestry`.
///
/// Returns an `Err` with the unmodified `self` in the case of error.
pub fn reanchored(
mut self,
target: &MultiLocation,
ancestry: &MultiLocation,
) -> Result<Self, Self> {
match self.reanchor(target, ancestry) {
Ok(()) => Ok(self),
Err(()) => Err(self),
}
}
/// Mutate `self` so that it represents the same location from the point of view of `target`.
/// The context of `self` is provided as `ancestry`.
///
@@ -397,6 +414,13 @@ impl MultiLocation {
}
}
impl TryFrom<NewMultiLocation> for MultiLocation {
type Error = ();
fn try_from(x: NewMultiLocation) -> result::Result<Self, ()> {
Ok(MultiLocation { parents: x.parents, interior: x.interior.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;
@@ -408,7 +432,7 @@ impl From<Parent> for MultiLocation {
/// 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(Junctions);
pub struct ParentThen(pub Junctions);
impl From<ParentThen> for MultiLocation {
fn from(ParentThen(interior): ParentThen) -> Self {
MultiLocation { parents: 1, interior }
@@ -417,7 +441,7 @@ impl From<ParentThen> for MultiLocation {
/// 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(u8);
pub struct Ancestor(pub u8);
impl From<Ancestor> for MultiLocation {
fn from(Ancestor(parents): Ancestor) -> Self {
MultiLocation { parents, interior: Junctions::Here }
@@ -426,14 +450,14 @@ impl From<Ancestor> for MultiLocation {
/// 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(u8, Junctions);
impl From<AncestorThen> for MultiLocation {
fn from(AncestorThen(parents, interior): AncestorThen) -> Self {
MultiLocation { parents, interior }
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_v1!();
xcm_procedural::impl_conversion_functions_for_multilocation_v2!();
/// Maximum number of `Junction`s that a `Junctions` can contain.
const MAX_JUNCTIONS: usize = 8;
@@ -444,6 +468,7 @@ const MAX_JUNCTIONS: usize = 8;
/// Parent junctions cannot be constructed with this type. Refer to `MultiLocation` for
/// instructions on constructing parent junctions.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum Junctions {
/// The interpreting consensus system.
Here,
@@ -811,7 +836,7 @@ impl Junctions {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*};
/// # use xcm::v2::{Junctions::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parachain(2), PalletInstance(3), OnlyChild);
/// assert_eq!(m.match_and_split(&X2(Parachain(2), PalletInstance(3))), Some(&OnlyChild));
@@ -829,7 +854,7 @@ impl Junctions {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*};
/// # use xcm::v2::{Junctions::*, Junction::*};
/// let mut j = X3(Parachain(2), PalletInstance(3), OnlyChild);
/// assert!(j.starts_with(&X2(Parachain(2), PalletInstance(3))));
/// assert!(j.starts_with(&j));
@@ -859,7 +884,7 @@ impl TryFrom<MultiLocation> for Junctions {
#[cfg(test)]
mod tests {
use super::{Ancestor, AncestorThen, Junctions::*, MultiLocation, Parent, ParentThen};
use crate::opaque::v1::{Junction::*, NetworkId::*};
use crate::opaque::v2::{Junction::*, NetworkId::*};
use parity_scale_codec::{Decode, Encode};
#[test]
@@ -1062,7 +1087,6 @@ mod tests {
#[test]
fn conversion_from_other_types_works() {
use crate::v0;
fn takes_multilocation<Arg: Into<MultiLocation>>(_arg: Arg) {}
takes_multilocation(Parent);
@@ -1079,27 +1103,5 @@ mod tests {
takes_multilocation((Parent, Here));
takes_multilocation(ParentThen(X1(Parachain(75))));
takes_multilocation([Parachain(100), PalletInstance(3)]);
assert_eq!(v0::MultiLocation::Null.try_into(), Ok(MultiLocation::here()));
assert_eq!(
v0::MultiLocation::X1(v0::Junction::Parent).try_into(),
Ok(MultiLocation::parent())
);
assert_eq!(
v0::MultiLocation::X2(v0::Junction::Parachain(88), v0::Junction::Parent).try_into(),
Ok(MultiLocation::here()),
);
assert_eq!(
v0::MultiLocation::X3(
v0::Junction::Parent,
v0::Junction::Parent,
v0::Junction::GeneralKey(b"foo".to_vec().try_into().unwrap()),
)
.try_into(),
Ok(MultiLocation {
parents: 2,
interior: X1(GeneralKey(b"foo".to_vec().try_into().unwrap()))
}),
);
}
}
+41 -8
View File
@@ -16,6 +16,7 @@
//! Cross-Consensus Message format data structures.
use crate::v3::Error as NewError;
use core::result;
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
@@ -110,10 +111,42 @@ pub enum Error {
WeightNotComputable,
}
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::MultiLocationFull,
LocationNotInvertible => Self::MultiLocationNotInvertible,
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),
_ => return Err(()),
})
}
}
impl From<SendError> for Error {
fn from(e: SendError) -> Self {
match e {
SendError::CannotReachDestination(..) | SendError::Unroutable => Error::Unroutable,
SendError::NotApplicable(..) | SendError::Unroutable => Error::Unroutable,
SendError::Transport(s) => Error::Transport(s),
SendError::DestinationUnsupported => Error::DestinationUnsupported,
SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize,
@@ -210,7 +243,7 @@ pub enum SendError {
///
/// This is not considered fatal: if there are alternative transport routes available, then
/// they may be attempted. For this reason, the destination and message are contained.
CannotReachDestination(MultiLocation, Xcm<()>),
NotApplicable(MultiLocation, Xcm<()>),
/// 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.
@@ -231,7 +264,7 @@ pub type SendResult = result::Result<(), SendError>;
/// Utility for sending an XCM message.
///
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return
/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination`
/// `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.
///
///
@@ -244,7 +277,7 @@ pub type SendResult = result::Result<(), SendError>;
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> SendResult {
/// return Err(SendError::CannotReachDestination(destination.into(), message))
/// return Err(SendError::NotApplicable(destination.into(), message))
/// }
/// }
///
@@ -267,7 +300,7 @@ pub type SendResult = result::Result<(), SendError>;
/// let destination = destination.into();
/// match destination {
/// MultiLocation { parents: 1, interior: Here } => Ok(()),
/// _ => Err(SendError::CannotReachDestination(destination, message)),
/// _ => Err(SendError::NotApplicable(destination, message)),
/// }
/// }
/// }
@@ -298,7 +331,7 @@ pub trait SendXcm {
/// Send an XCM `message` to a given `destination`.
///
/// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST*
/// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without
/// return `NotApplicable`. Any other error will cause the tuple implementation to exit early without
/// trying other type fields.
fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> SendResult;
}
@@ -309,10 +342,10 @@ impl SendXcm for Tuple {
for_tuples!( #(
// we shadow `destination` and `message` in each expansion for the next one.
let (destination, message) = match Tuple::send_xcm(destination, message) {
Err(SendError::CannotReachDestination(d, m)) => (d, m),
Err(SendError::NotApplicable(d, m)) => (d, m),
o @ _ => return o,
};
)* );
Err(SendError::CannotReachDestination(destination.into(), message))
Err(SendError::NotApplicable(destination.into(), message))
}
}
+342
View File
@@ -0,0 +1,342 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Support data structures for `MultiLocation`, primarily the `Junction` datatype.
use super::{Junctions, MultiLocation};
use crate::{
v2::{
BodyId as OldBodyId, BodyPart as OldBodyPart, Junction as OldJunction,
NetworkId as OldNetworkId,
},
VersionedMultiLocation,
};
use core::convert::{TryFrom, TryInto};
use parity_scale_codec::{self, Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
/// A global identifier of a data structure existing within consensus.
///
/// Maintenance note: Networks with global consensus and which are practically bridgeable within the
/// Polkadot ecosystem are given preference over explicit naming in this enumeration.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::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 Polkadot mainnet Relay-chain.
Polkadot,
/// The Kusama canary-net Relay-chain.
Kusama,
/// The Westend testnet Relay-chain.
Westend,
/// The Rococo testnet Relay-chain.
Rococo,
/// 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,
}
impl From<OldNetworkId> for Option<NetworkId> {
fn from(old: OldNetworkId) -> Option<NetworkId> {
use OldNetworkId::*;
match old {
Any => None,
Named(_) => None,
Polkadot => Some(NetworkId::Polkadot),
Kusama => Some(NetworkId::Kusama),
}
}
}
/// An identifier of a pluralistic body.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
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 Polkadot, this would be the Polkadot council).
Executive,
/// The unambiguous technical body (for Polkadot, this would be the Technical Committee).
Technical,
/// The unambiguous legislative body (for Polkadot, this could be considered the opinion of a majority of
/// lock-voters).
Legislative,
/// The unambiguous judicial body (this doesn't exist on Polkadot, but if it were to get a "grand oracle", it
/// may be considered as that).
Judicial,
/// The unambiguous defense body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `staking_admin` track).
Defense,
/// The unambiguous administration body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `general_admin` track).
Administration,
/// The unambiguous treasury body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `treasurer` track).
Treasury,
}
impl TryFrom<OldBodyId> for BodyId {
type Error = ();
fn try_from(value: OldBodyId) -> Result<Self, ()> {
use OldBodyId::*;
Ok(match value {
Unit => Self::Unit,
Named(n) =>
if n.len() == 4 {
let mut r = [0u8; 4];
r.copy_from_slice(&n[..]);
Self::Moniker(r)
} else {
return Err(())
},
Index(n) => Self::Index(n),
Executive => Self::Executive,
Technical => Self::Technical,
Legislative => Self::Legislative,
Judicial => Self::Judicial,
Defense => Self::Defense,
Administration => Self::Administration,
Treasury => Self::Treasury,
})
}
}
/// A part of a pluralistic body.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
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 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,
}
}
}
impl TryFrom<OldBodyPart> for BodyPart {
type Error = ();
fn try_from(value: OldBodyPart) -> Result<Self, ()> {
use OldBodyPart::*;
Ok(match value {
Voice => Self::Voice,
Members { count } => Self::Members { count },
Fraction { nom, denom } => Self::Fraction { nom, denom },
AtLeastProportion { nom, denom } => Self::AtLeastProportion { nom, denom },
MoreThanProportion { nom, denom } => Self::MoreThanProportion { nom, denom },
})
}
}
/// 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, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum Junction {
/// An indexed parachain belonging to and operated by the context.
///
/// Generally used when the context is a Polkadot Relay-chain.
Parachain(#[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 128-byte datum 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.
GeneralKey([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<[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 {
Parachain(id) => Self::Parachain(id),
AccountId32 { network, id } => Self::AccountId32 { network: network.into(), id },
AccountIndex64 { network, index } =>
Self::AccountIndex64 { network: network.into(), index },
AccountKey20 { network, key } => Self::AccountKey20 { network: network.into(), key },
PalletInstance(index) => Self::PalletInstance(index),
GeneralIndex(id) => Self::GeneralIndex(id),
GeneralKey(_key) => return Err(()),
OnlyChild => Self::OnlyChild,
Plurality { id, part } =>
Self::Plurality { id: id.try_into()?, part: part.try_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 `VersionedMultiLocation` 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) -> VersionedMultiLocation {
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,
_ => {},
}
}
}
+708
View File
@@ -0,0 +1,708 @@
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! XCM `Junctions`/`InteriorMultiLocation` datatype.
use super::{Junction, MultiLocation, NetworkId};
use core::{convert::TryFrom, mem, result};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
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, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
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),
}
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 xcm::v3::{Junctions::*, Junction::*, MultiLocation};
/// # fn main() {
/// let mut m = X1(Parachain(21));
/// assert_eq!(m.append_with(X1(PalletInstance(3))), Ok(()));
/// assert_eq!(m, X2(Parachain(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 xcm::v3::{Junctions::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parachain(2), PalletInstance(3), OnlyChild);
/// assert_eq!(m.match_and_split(&X2(Parachain(2), PalletInstance(3))), Some(&OnlyChild));
/// assert_eq!(m.match_and_split(&X1(Parachain(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 = (Parachain(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 =
(Parachain(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(Polkadot.into()).relative_to(&X1(Kusama.into())), (Parent, Polkadot).into());
let base = X3(Kusama.into(), Parachain(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(), Parachain(1)).relative_to(&base), (Parent,).into());
assert_eq!(
X3(Kusama.into(), Parachain(1), PalletInstance(1)).relative_to(&base),
Here.into()
);
// Ancestors with one child.
assert_eq!(
X1(Polkadot.into()).relative_to(&base),
(Parent, Parent, Parent, Polkadot).into()
);
assert_eq!(
X2(Kusama.into(), Parachain(2)).relative_to(&base),
(Parent, Parent, Parachain(2)).into()
);
assert_eq!(
X3(Kusama.into(), Parachain(1), PalletInstance(2)).relative_to(&base),
(Parent, PalletInstance(2)).into()
);
assert_eq!(
X4(Kusama.into(), Parachain(1), PalletInstance(1), [1u8; 32].into()).relative_to(&base),
([1u8; 32],).into()
);
// Ancestors with grandchildren.
assert_eq!(
X2(Polkadot.into(), Parachain(1)).relative_to(&base),
(Parent, Parent, Parent, Polkadot, Parachain(1)).into()
);
assert_eq!(
X3(Kusama.into(), Parachain(2), PalletInstance(1)).relative_to(&base),
(Parent, Parent, Parachain(2), PalletInstance(1)).into()
);
assert_eq!(
X4(Kusama.into(), Parachain(1), PalletInstance(2), [1u8; 32].into()).relative_to(&base),
(Parent, PalletInstance(2), [1u8; 32]).into()
);
assert_eq!(
X5(Kusama.into(), Parachain(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(Polkadot.into()).global_consensus(), Ok(Polkadot));
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(Polkadot).into();
assert_eq!(x, X1(GlobalConsensus(Polkadot)));
let x: Junctions = Polkadot.into();
assert_eq!(x, X1(GlobalConsensus(Polkadot)));
let x: Junctions = (Polkadot, Kusama).into();
assert_eq!(x, X2(GlobalConsensus(Polkadot), GlobalConsensus(Kusama)));
}
}
File diff suppressed because it is too large Load Diff
+951
View File
@@ -0,0 +1,951 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format asset data structures.
//!
//! This encompasses four types for representing assets:
//! - `MultiAsset`: A description of a single asset, either an instance of a non-fungible or some amount of a fungible.
//! - `MultiAssets`: A collection of `MultiAsset`s. These are stored in a `Vec` and sorted with fungibles first.
//! - `Wild`: A single asset wildcard, this can either be "all" assets, or all assets of a specific kind.
//! - `MultiAssetFilter`: A combination of `Wild` and `MultiAssets` designed for efficiently filtering an XCM holding
//! account.
use super::{InteriorMultiLocation, MultiLocation};
use crate::v2::{
AssetId as OldAssetId, AssetInstance as OldAssetInstance, Fungibility as OldFungibility,
MultiAsset as OldMultiAsset, MultiAssetFilter as OldMultiAssetFilter,
MultiAssets as OldMultiAssets, WildFungibility as OldWildFungibility,
WildMultiAsset as OldWildMultiAsset,
};
use alloc::{vec, vec::Vec};
use core::{
cmp::Ordering,
convert::{TryFrom, TryInto},
};
use parity_scale_codec::{self as codec, Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
/// A general identifier for an instance of a non-fungible asset class.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum AssetInstance {
/// Undefined - used if the non-fungible asset class has only one instance.
Undefined,
/// A compact index. Technically this could be greater than `u128`, but this implementation supports only
/// values up to `2**128 - 1`.
Index(#[codec(compact)] u128),
/// A 4-byte fixed-length datum.
Array4([u8; 4]),
/// An 8-byte fixed-length datum.
Array8([u8; 8]),
/// A 16-byte fixed-length datum.
Array16([u8; 16]),
/// A 32-byte fixed-length datum.
Array32([u8; 32]),
}
impl TryFrom<OldAssetInstance> for AssetInstance {
type Error = ();
fn try_from(value: OldAssetInstance) -> Result<Self, Self::Error> {
use OldAssetInstance::*;
Ok(match value {
Undefined => Self::Undefined,
Index(n) => Self::Index(n),
Array4(n) => Self::Array4(n),
Array8(n) => Self::Array8(n),
Array16(n) => Self::Array16(n),
Array32(n) => Self::Array32(n),
Blob(_) => return Err(()),
})
}
}
impl From<()> for AssetInstance {
fn from(_: ()) -> Self {
Self::Undefined
}
}
impl From<[u8; 4]> for AssetInstance {
fn from(x: [u8; 4]) -> Self {
Self::Array4(x)
}
}
impl From<[u8; 8]> for AssetInstance {
fn from(x: [u8; 8]) -> Self {
Self::Array8(x)
}
}
impl From<[u8; 16]> for AssetInstance {
fn from(x: [u8; 16]) -> Self {
Self::Array16(x)
}
}
impl From<[u8; 32]> for AssetInstance {
fn from(x: [u8; 32]) -> Self {
Self::Array32(x)
}
}
impl From<u8> for AssetInstance {
fn from(x: u8) -> Self {
Self::Index(x as u128)
}
}
impl From<u16> for AssetInstance {
fn from(x: u16) -> Self {
Self::Index(x as u128)
}
}
impl From<u32> for AssetInstance {
fn from(x: u32) -> Self {
Self::Index(x as u128)
}
}
impl From<u64> for AssetInstance {
fn from(x: u64) -> Self {
Self::Index(x as u128)
}
}
impl TryFrom<AssetInstance> for () {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Undefined => Ok(()),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for [u8; 4] {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Array4(x) => Ok(x),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for [u8; 8] {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Array8(x) => Ok(x),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for [u8; 16] {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Array16(x) => Ok(x),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for [u8; 32] {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Array32(x) => Ok(x),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for u8 {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for u16 {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for u32 {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for u64 {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for u128 {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Index(x) => Ok(x),
_ => Err(()),
}
}
}
/// Classification of whether an asset is fungible or not, along with a mandatory amount or instance.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum Fungibility {
/// A fungible asset; we record a number of units, as a `u128` in the inner item.
Fungible(#[codec(compact)] u128),
/// A non-fungible asset. We record the instance identifier in the inner item. Only one asset
/// of each instance identifier may ever be in existence at once.
NonFungible(AssetInstance),
}
impl Fungibility {
pub fn is_kind(&self, w: WildFungibility) -> bool {
use Fungibility::*;
use WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible};
matches!((self, w), (Fungible(_), WildFungible) | (NonFungible(_), WildNonFungible))
}
}
impl From<i32> for Fungibility {
fn from(amount: i32) -> Fungibility {
debug_assert_ne!(amount, 0);
Fungibility::Fungible(amount as u128)
}
}
impl From<u128> for Fungibility {
fn from(amount: u128) -> Fungibility {
debug_assert_ne!(amount, 0);
Fungibility::Fungible(amount)
}
}
impl<T: Into<AssetInstance>> From<T> for Fungibility {
fn from(instance: T) -> Fungibility {
Fungibility::NonFungible(instance.into())
}
}
impl TryFrom<OldFungibility> for Fungibility {
type Error = ();
fn try_from(value: OldFungibility) -> Result<Self, Self::Error> {
use OldFungibility::*;
Ok(match value {
Fungible(n) => Self::Fungible(n),
NonFungible(i) => Self::NonFungible(i.try_into()?),
})
}
}
/// Classification of whether an asset is fungible or not.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum WildFungibility {
/// The asset is fungible.
Fungible,
/// The asset is not fungible.
NonFungible,
}
impl TryFrom<OldWildFungibility> for WildFungibility {
type Error = ();
fn try_from(value: OldWildFungibility) -> Result<Self, Self::Error> {
use OldWildFungibility::*;
Ok(match value {
Fungible => Self::Fungible,
NonFungible => Self::NonFungible,
})
}
}
/// Classification of an asset being concrete or abstract.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum AssetId {
/// A specific location identifying an asset.
Concrete(MultiLocation),
/// An abstract location; this is a name which may mean different specific locations on
/// different chains at different times.
Abstract([u8; 32]),
}
impl<T: Into<MultiLocation>> From<T> for AssetId {
fn from(x: T) -> Self {
Self::Concrete(x.into())
}
}
impl From<[u8; 32]> for AssetId {
fn from(x: [u8; 32]) -> Self {
Self::Abstract(x)
}
}
impl TryFrom<OldAssetId> for AssetId {
type Error = ();
fn try_from(old: OldAssetId) -> Result<Self, ()> {
use OldAssetId::*;
Ok(match old {
Concrete(l) => Self::Concrete(l.try_into()?),
Abstract(v) if v.len() <= 32 => {
let mut r = [0u8; 32];
r[..v.len()].copy_from_slice(&v[..]);
Self::Abstract(r)
},
_ => return Err(()),
})
}
}
impl AssetId {
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
pub fn prepend_with(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
if let AssetId::Concrete(ref mut l) = self {
l.prepend_with(*prepend).map_err(|_| ())?;
}
Ok(())
}
/// Mutate the asset to represent the same value from the perspective of a new `target`
/// location. The local chain's location is provided in `context`.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
if let AssetId::Concrete(ref mut l) = self {
l.reanchor(target, context)?;
}
Ok(())
}
/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `MultiAsset` value.
pub fn into_multiasset(self, fun: Fungibility) -> MultiAsset {
MultiAsset { fun, id: self }
}
/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `WildMultiAsset`
/// wildcard (`AllOf`) value.
pub fn into_wild(self, fun: WildFungibility) -> WildMultiAsset {
WildMultiAsset::AllOf { fun, id: self }
}
}
/// Either an amount of a single fungible asset, or a single well-identified non-fungible asset.
#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiAsset {
/// The overall asset identity (aka *class*, in the case of a non-fungible).
pub id: AssetId,
/// The fungibility of the asset, which contains either the amount (in the case of a fungible
/// asset) or the *insance ID`, the secondary asset identifier.
pub fun: Fungibility,
}
impl PartialOrd for MultiAsset {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for MultiAsset {
fn cmp(&self, other: &Self) -> Ordering {
match (&self.fun, &other.fun) {
(Fungibility::Fungible(..), Fungibility::NonFungible(..)) => Ordering::Less,
(Fungibility::NonFungible(..), Fungibility::Fungible(..)) => Ordering::Greater,
_ => (&self.id, &self.fun).cmp(&(&other.id, &other.fun)),
}
}
}
impl<A: Into<AssetId>, B: Into<Fungibility>> From<(A, B)> for MultiAsset {
fn from((id, fun): (A, B)) -> MultiAsset {
MultiAsset { fun: fun.into(), id: id.into() }
}
}
impl MultiAsset {
pub fn is_fungible(&self, maybe_id: Option<AssetId>) -> bool {
use Fungibility::*;
matches!(self.fun, Fungible(..)) && maybe_id.map_or(true, |i| i == self.id)
}
pub fn is_non_fungible(&self, maybe_id: Option<AssetId>) -> bool {
use Fungibility::*;
matches!(self.fun, NonFungible(..)) && maybe_id.map_or(true, |i| i == self.id)
}
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
pub fn prepend_with(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
self.id.prepend_with(prepend)
}
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `context`.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
self.id.reanchor(target, context)
}
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `context`.
pub fn reanchored(
mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<Self, ()> {
self.id.reanchor(target, context)?;
Ok(self)
}
/// Returns true if `self` is a super-set of the given `inner` asset.
pub fn contains(&self, inner: &MultiAsset) -> bool {
use Fungibility::*;
if self.id == inner.id {
match (&self.fun, &inner.fun) {
(Fungible(a), Fungible(i)) if a >= i => return true,
(NonFungible(a), NonFungible(i)) if a == i => return true,
_ => (),
}
}
false
}
}
impl TryFrom<OldMultiAsset> for MultiAsset {
type Error = ();
fn try_from(old: OldMultiAsset) -> Result<Self, ()> {
Ok(Self { id: old.id.try_into()?, fun: old.fun.try_into()? })
}
}
/// A `Vec` of `MultiAsset`s.
///
/// There are a number of invariants which the construction and mutation functions must ensure are
/// maintained:
/// - It may contain no items of duplicate asset class;
/// - All items must be ordered;
/// - The number of items should grow no larger than `MAX_ITEMS_IN_MULTIASSETS`.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo, Default)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiAssets(Vec<MultiAsset>);
/// Maximum number of items we expect in a single `MultiAssets` value. Note this is not (yet)
/// enforced, and just serves to provide a sensible `max_encoded_len` for `MultiAssets`.
const MAX_ITEMS_IN_MULTIASSETS: usize = 20;
impl MaxEncodedLen for MultiAssets {
fn max_encoded_len() -> usize {
MultiAsset::max_encoded_len() * MAX_ITEMS_IN_MULTIASSETS
}
}
impl Decode for MultiAssets {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, parity_scale_codec::Error> {
Self::from_sorted_and_deduplicated(Vec::<MultiAsset>::decode(input)?)
.map_err(|()| "Out of order".into())
}
}
impl TryFrom<OldMultiAssets> for MultiAssets {
type Error = ();
fn try_from(old: OldMultiAssets) -> Result<Self, ()> {
let v = old
.drain()
.into_iter()
.map(MultiAsset::try_from)
.collect::<Result<Vec<_>, ()>>()?;
Ok(MultiAssets(v))
}
}
impl From<Vec<MultiAsset>> for MultiAssets {
fn from(mut assets: Vec<MultiAsset>) -> Self {
let mut res = Vec::with_capacity(assets.len());
if !assets.is_empty() {
assets.sort();
let mut iter = assets.into_iter();
if let Some(first) = iter.next() {
let last = iter.fold(first, |a, b| -> MultiAsset {
match (a, b) {
(
MultiAsset { fun: Fungibility::Fungible(a_amount), id: a_id },
MultiAsset { fun: Fungibility::Fungible(b_amount), id: b_id },
) if a_id == b_id => MultiAsset {
id: a_id,
fun: Fungibility::Fungible(a_amount.saturating_add(b_amount)),
},
(
MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id },
MultiAsset { fun: Fungibility::NonFungible(b_instance), id: b_id },
) if a_id == b_id && a_instance == b_instance =>
MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id },
(to_push, to_remember) => {
res.push(to_push);
to_remember
},
}
});
res.push(last);
}
}
Self(res)
}
}
impl<T: Into<MultiAsset>> From<T> for MultiAssets {
fn from(x: T) -> Self {
Self(vec![x.into()])
}
}
impl MultiAssets {
/// A new (empty) value.
pub fn new() -> Self {
Self(Vec::new())
}
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
/// which contain no duplicates.
///
/// Returns `Ok` if the operation succeeds and `Err` if `r` is out of order or had duplicates. If you can't
/// guarantee that `r` is sorted and deduplicated, then use `From::<Vec<MultiAsset>>::from` which is infallible.
pub fn from_sorted_and_deduplicated(r: Vec<MultiAsset>) -> Result<Self, ()> {
if r.is_empty() {
return Ok(Self(Vec::new()))
}
r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&MultiAsset, ()> {
if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) {
Ok(b)
} else {
Err(())
}
})?;
Ok(Self(r))
}
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
/// which contain no duplicates.
///
/// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation.
/// Generally though you should avoid using it unless you have a strict proof that `r` is valid.
#[cfg(test)]
pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<MultiAsset>) -> Self {
Self::from_sorted_and_deduplicated(r).expect("Invalid input r is not sorted/deduped")
}
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
/// which contain no duplicates.
///
/// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation.
/// Generally though you should avoid using it unless you have a strict proof that `r` is valid.
///
/// In test mode, this checks anyway and panics on fail.
#[cfg(not(test))]
pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<MultiAsset>) -> Self {
Self(r)
}
/// Add some asset onto the list, saturating. This is quite a laborious operation since it maintains the ordering.
pub fn push(&mut self, a: MultiAsset) {
for asset in self.0.iter_mut().filter(|x| x.id == a.id) {
match (&a.fun, &mut asset.fun) {
(Fungibility::Fungible(amount), Fungibility::Fungible(balance)) => {
*balance = balance.saturating_add(*amount);
return
},
(Fungibility::NonFungible(inst1), Fungibility::NonFungible(inst2))
if inst1 == inst2 =>
return,
_ => (),
}
}
self.0.push(a);
self.0.sort();
}
/// Returns `true` if this definitely represents no asset.
pub fn is_none(&self) -> bool {
self.0.is_empty()
}
/// Returns true if `self` is a super-set of the given `inner` asset.
pub fn contains(&self, inner: &MultiAsset) -> bool {
self.0.iter().any(|i| i.contains(inner))
}
/// Consume `self` and return the inner vec.
#[deprecated = "Use `into_inner()` instead"]
pub fn drain(self) -> Vec<MultiAsset> {
self.0
}
/// Consume `self` and return the inner vec.
pub fn into_inner(self) -> Vec<MultiAsset> {
self.0
}
/// Return a reference to the inner vec.
pub fn inner(&self) -> &Vec<MultiAsset> {
&self.0
}
/// Return the number of distinct asset instances contained.
pub fn len(&self) -> usize {
self.0.len()
}
/// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location.
pub fn prepend_with(&mut self, prefix: &MultiLocation) -> Result<(), ()> {
self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix))
}
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `context`.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
self.0.iter_mut().try_for_each(|i| i.reanchor(target, context))
}
/// Return a reference to an item at a specific index or `None` if it doesn't exist.
pub fn get(&self, index: usize) -> Option<&MultiAsset> {
self.0.get(index)
}
}
/// A wildcard representing a set of assets.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum WildMultiAsset {
/// All assets in Holding.
All,
/// All assets in Holding of a given fungibility and ID.
AllOf { id: AssetId, fun: WildFungibility },
/// All assets in Holding, up to `u32` individual assets (different instances of non-fungibles
/// are separate assets).
AllCounted(#[codec(compact)] u32),
/// All assets in Holding of a given fungibility and ID up to `count` individual assets
/// (different instances of non-fungibles are separate assets).
AllOfCounted {
id: AssetId,
fun: WildFungibility,
#[codec(compact)]
count: u32,
},
}
impl TryFrom<OldWildMultiAsset> for WildMultiAsset {
type Error = ();
fn try_from(old: OldWildMultiAsset) -> Result<WildMultiAsset, ()> {
use OldWildMultiAsset::*;
Ok(match old {
AllOf { id, fun } => Self::AllOf { id: id.try_into()?, fun: fun.try_into()? },
All => Self::All,
})
}
}
impl TryFrom<(OldWildMultiAsset, u32)> for WildMultiAsset {
type Error = ();
fn try_from(old: (OldWildMultiAsset, u32)) -> Result<WildMultiAsset, ()> {
use OldWildMultiAsset::*;
let count = old.1;
Ok(match old.0 {
AllOf { id, fun } =>
Self::AllOfCounted { id: id.try_into()?, fun: fun.try_into()?, count },
All => Self::AllCounted(count),
})
}
}
impl WildMultiAsset {
/// Returns true if `self` is a super-set of the given `inner` asset.
pub fn contains(&self, inner: &MultiAsset) -> bool {
use WildMultiAsset::*;
match self {
AllOfCounted { count: 0, .. } | AllCounted(0) => false,
AllOf { fun, id } | AllOfCounted { id, fun, .. } =>
inner.fun.is_kind(*fun) && &inner.id == id,
All | AllCounted(_) => true,
}
}
/// Returns true if the wild element of `self` matches `inner`.
///
/// Note that for `Counted` variants of wildcards, then it will disregard the count except for
/// always returning `false` when equal to 0.
#[deprecated = "Use `contains` instead"]
pub fn matches(&self, inner: &MultiAsset) -> bool {
self.contains(inner)
}
/// Mutate the asset to represent the same value from the perspective of a new `target`
/// location. The local chain's location is provided in `context`.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
use WildMultiAsset::*;
match self {
AllOf { ref mut id, .. } | AllOfCounted { ref mut id, .. } =>
id.reanchor(target, context),
All | AllCounted(_) => Ok(()),
}
}
/// Maximum count of assets allowed to match, if any.
pub fn count(&self) -> Option<u32> {
use WildMultiAsset::*;
match self {
AllOfCounted { count, .. } | AllCounted(count) => Some(*count),
All | AllOf { .. } => None,
}
}
/// Explicit limit on number of assets allowed to match, if any.
pub fn limit(&self) -> Option<u32> {
self.count()
}
/// Consume self and return the equivalent version but counted and with the `count` set to the
/// given parameter.
pub fn counted(self, count: u32) -> Self {
use WildMultiAsset::*;
match self {
AllOfCounted { fun, id, .. } | AllOf { fun, id } => AllOfCounted { fun, id, count },
All | AllCounted(_) => AllCounted(count),
}
}
}
impl<A: Into<AssetId>, B: Into<WildFungibility>> From<(A, B)> for WildMultiAsset {
fn from((id, fun): (A, B)) -> WildMultiAsset {
WildMultiAsset::AllOf { fun: fun.into(), id: id.into() }
}
}
/// `MultiAsset` collection, defined either by a number of `MultiAssets` or a single wildcard.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum MultiAssetFilter {
/// Specify the filter as being everything contained by the given `MultiAssets` inner.
Definite(MultiAssets),
/// Specify the filter as the given `WildMultiAsset` wildcard.
Wild(WildMultiAsset),
}
impl<T: Into<WildMultiAsset>> From<T> for MultiAssetFilter {
fn from(x: T) -> Self {
Self::Wild(x.into())
}
}
impl From<MultiAsset> for MultiAssetFilter {
fn from(x: MultiAsset) -> Self {
Self::Definite(vec![x].into())
}
}
impl From<Vec<MultiAsset>> for MultiAssetFilter {
fn from(x: Vec<MultiAsset>) -> Self {
Self::Definite(x.into())
}
}
impl From<MultiAssets> for MultiAssetFilter {
fn from(x: MultiAssets) -> Self {
Self::Definite(x)
}
}
impl MultiAssetFilter {
/// Returns true if `inner` would be matched by `self`.
///
/// Note that for `Counted` variants of wildcards, then it will disregard the count except for
/// always returning `false` when equal to 0.
pub fn matches(&self, inner: &MultiAsset) -> bool {
match self {
MultiAssetFilter::Definite(ref assets) => assets.contains(inner),
MultiAssetFilter::Wild(ref wild) => wild.contains(inner),
}
}
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `context`.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
match self {
MultiAssetFilter::Definite(ref mut assets) => assets.reanchor(target, context),
MultiAssetFilter::Wild(ref mut wild) => wild.reanchor(target, context),
}
}
/// Maximum count of assets it is possible to match, if known.
pub fn count(&self) -> Option<u32> {
use MultiAssetFilter::*;
match self {
Definite(x) => Some(x.len() as u32),
Wild(x) => x.count(),
}
}
/// Explicit limit placed on the number of items, if any.
pub fn limit(&self) -> Option<u32> {
use MultiAssetFilter::*;
match self {
Definite(_) => None,
Wild(x) => x.limit(),
}
}
}
impl TryFrom<OldMultiAssetFilter> for MultiAssetFilter {
type Error = ();
fn try_from(old: OldMultiAssetFilter) -> Result<MultiAssetFilter, ()> {
Ok(match old {
OldMultiAssetFilter::Definite(x) => Self::Definite(x.try_into()?),
OldMultiAssetFilter::Wild(x) => Self::Wild(x.try_into()?),
})
}
}
impl TryFrom<(OldMultiAssetFilter, u32)> for MultiAssetFilter {
type Error = ();
fn try_from(old: (OldMultiAssetFilter, u32)) -> Result<MultiAssetFilter, ()> {
let count = old.1;
Ok(match old.0 {
OldMultiAssetFilter::Definite(x) if count >= x.len() as u32 =>
Self::Definite(x.try_into()?),
OldMultiAssetFilter::Wild(x) => Self::Wild((x, count).try_into()?),
_ => return Err(()),
})
}
}
#[cfg(test)]
mod tests {
use super::super::prelude::*;
#[test]
fn conversion_works() {
let _: MultiAssets = (Here, 1u128).into();
}
#[test]
fn from_sorted_and_deduplicated_works() {
use super::*;
use alloc::vec;
let empty = vec![];
let r = MultiAssets::from_sorted_and_deduplicated(empty);
assert_eq!(r, Ok(MultiAssets(vec![])));
let dup_fun = vec![(Here, 100).into(), (Here, 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(dup_fun);
assert!(r.is_err());
let dup_nft = vec![(Here, *b"notgood!").into(), (Here, *b"notgood!").into()];
let r = MultiAssets::from_sorted_and_deduplicated(dup_nft);
assert!(r.is_err());
let good_fun = vec![(Here, 10).into(), (Parent, 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(good_fun.clone());
assert_eq!(r, Ok(MultiAssets(good_fun)));
let bad_fun = vec![(Parent, 10).into(), (Here, 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(bad_fun);
assert!(r.is_err());
let good_abstract_fun = vec![(Here, 100).into(), ([0u8; 32], 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(good_abstract_fun.clone());
assert_eq!(r, Ok(MultiAssets(good_abstract_fun)));
let bad_abstract_fun = vec![([0u8; 32], 10).into(), (Here, 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(bad_abstract_fun);
assert!(r.is_err());
let good_nft = vec![(Here, ()).into(), (Here, *b"good").into()];
let r = MultiAssets::from_sorted_and_deduplicated(good_nft.clone());
assert_eq!(r, Ok(MultiAssets(good_nft)));
let bad_nft = vec![(Here, *b"bad!").into(), (Here, ()).into()];
let r = MultiAssets::from_sorted_and_deduplicated(bad_nft);
assert!(r.is_err());
let good_abstract_nft = vec![(Here, ()).into(), ([0u8; 32], ()).into()];
let r = MultiAssets::from_sorted_and_deduplicated(good_abstract_nft.clone());
assert_eq!(r, Ok(MultiAssets(good_abstract_nft)));
let bad_abstract_nft = vec![([0u8; 32], ()).into(), (Here, ()).into()];
let r = MultiAssets::from_sorted_and_deduplicated(bad_abstract_nft);
assert!(r.is_err());
let mixed_good = vec![(Here, 10).into(), (Here, *b"good").into()];
let r = MultiAssets::from_sorted_and_deduplicated(mixed_good.clone());
assert_eq!(r, Ok(MultiAssets(mixed_good)));
let mixed_bad = vec![(Here, *b"bad!").into(), (Here, 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(mixed_bad);
assert!(r.is_err());
}
}
+710
View File
@@ -0,0 +1,710 @@
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! XCM `MultiLocation` datatype.
use super::{Junction, Junctions};
use crate::{v2::MultiLocation as OldMultiLocation, VersionedMultiLocation};
use core::{
convert::{TryFrom, TryInto},
result,
};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
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 parachain.
/// - A layer-0 super-chain, e.g. the Polkadot 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, Eq, PartialEq, Ord, PartialOrd, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
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,
}
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 `VersionedMultiLocation` value.
pub const fn into_versioned(self) -> VersionedMultiLocation {
VersionedMultiLocation::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 theoriginal 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 xcm::v3::{Junctions::*, Junction::*, MultiLocation};
/// # fn main() {
/// 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 xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// # fn main() {
/// let mut m: MultiLocation = (Parent, Parachain(21), 69u64).into();
/// assert_eq!(m.append_with((Parent, PalletInstance(3))), Ok(()));
/// assert_eq!(m, MultiLocation::new(1, X2(Parachain(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 xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// # fn main() {
/// let mut m: MultiLocation = (Parent, Parachain(21), 69u64).into();
/// let r = m.appended_with((Parent, PalletInstance(3))).unwrap();
/// assert_eq!(r, MultiLocation::new(1, X2(Parachain(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 xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// # fn main() {
/// let mut m: MultiLocation = (Parent, Parent, PalletInstance(3)).into();
/// assert_eq!(m.prepend_with((Parent, Parachain(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 xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// # fn main() {
/// let m: MultiLocation = (Parent, Parent, PalletInstance(3)).into();
/// let r = m.prepended_with((Parent, Parachain(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,
}
}
}
}
impl TryFrom<OldMultiLocation> for MultiLocation {
type Error = ();
fn try_from(x: OldMultiLocation) -> result::Result<Self, ()> {
Ok(MultiLocation { parents: x.parents, interior: x.interior.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 parity_scale_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, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X2(Parachain(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(Parachain(1000), PalletInstance(42));
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X3(OnlyChild, Parachain(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, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X3(Parachain(1000), PalletInstance(42), GeneralIndex(42));
let expected =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X1(Parachain(1000));
let expected =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
}
#[test]
fn reanchor_works() {
let mut id: MultiLocation = (Parent, Parachain(1000), GeneralIndex(42)).into();
let context = Parachain(2000).into();
let target = (Parent, Parachain(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(Parachain(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(Parachain(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(Parachain(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(Parachain(42)) };
assert_eq!(m.append_with(X2(PalletInstance(3), acc.clone())), Ok(()));
assert_eq!(
m,
MultiLocation {
parents: 1,
interior: X3(Parachain(42), PalletInstance(3), acc.clone())
}
);
// cannot append to create overly long multilocation
let acc = AccountIndex64 { network: None, index: 23 };
let m = MultiLocation {
parents: 254,
interior: X5(Parachain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild),
};
let suffix: MultiLocation = (PalletInstance(3), acc.clone(), OnlyChild, OnlyChild).into();
assert_eq!(m.clone().append_with(suffix.clone()), Err(suffix));
}
#[test]
fn prepend_with_works() {
let mut m = MultiLocation {
parents: 1,
interior: X2(Parachain(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(Parachain(42), AccountIndex64 { network: None, index: 23 })
}
);
// cannot prepend to create overly long multilocation
let mut m = MultiLocation { parents: 254, interior: X1(Parachain(42)) };
let prefix = MultiLocation { parents: 2, interior: Here };
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
let prefix = MultiLocation { parents: 1, interior: Here };
assert_eq!(m.prepend_with(prefix), Ok(()));
assert_eq!(m, MultiLocation { parents: 255, interior: X1(Parachain(42)) });
}
#[test]
fn double_ended_ref_iteration_works() {
let m = X3(Parachain(1000), Parachain(3), PalletInstance(5));
let mut iter = m.iter();
let first = iter.next().unwrap();
assert_eq!(first, &Parachain(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, &Parachain(3));
let res = Here
.pushed_with(first.clone())
.unwrap()
.pushed_with(second.clone())
.unwrap()
.pushed_with(third.clone())
.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::v2;
use core::convert::TryInto;
fn takes_multilocation<Arg: Into<MultiLocation>>(_arg: Arg) {}
takes_multilocation(Parent);
takes_multilocation(Here);
takes_multilocation(X1(Parachain(42)));
takes_multilocation((Ancestor(255), PalletInstance(8)));
takes_multilocation((Ancestor(5), Parachain(1), PalletInstance(3)));
takes_multilocation((Ancestor(2), Here));
takes_multilocation(AncestorThen(
3,
X2(Parachain(43), AccountIndex64 { network: None, index: 155 }),
));
takes_multilocation((Parent, AccountId32 { network: None, id: [0; 32] }));
takes_multilocation((Parent, Here));
takes_multilocation(ParentThen(X1(Parachain(75))));
takes_multilocation([Parachain(100), PalletInstance(3)]);
assert_eq!(
v2::MultiLocation::from(v2::Junctions::Here).try_into(),
Ok(MultiLocation::here())
);
assert_eq!(v2::MultiLocation::from(v2::Parent).try_into(), Ok(MultiLocation::parent()));
assert_eq!(
v2::MultiLocation::from((v2::Parent, v2::Parent, v2::Junction::GeneralIndex(42u128),))
.try_into(),
Ok(MultiLocation { parents: 2, interior: X1(GeneralIndex(42u128)) }),
);
}
}
+541
View File
@@ -0,0 +1,541 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use crate::v2::Error as OldError;
use core::result;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
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, Eq, PartialEq, Debug, TypeInfo)]
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 MaxEncodedLen for Error {
fn max_encoded_len() -> usize {
// TODO: max_encoded_len doesn't quite work here as it tries to take notice of the fields
// marked `codec(skip)`. We can hard-code it with the right answer for now.
1
}
}
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,
MultiLocationFull => Self::LocationFull,
MultiLocationNotInvertible => 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),
_ => 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)]
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 {
match self {
Outcome::Complete(_) => Ok(()),
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,
hash: XcmHash,
weight_credit: Weight,
) -> Outcome;
/// 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();
log::debug!(
target: "xcm::execute_xcm",
"origin: {:?}, message: {:?}, weight_limit: {:?}",
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>,
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, 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, _: 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, Eq, PartialEq, Debug, scale_info::TypeInfo)]
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>;
pub trait Unwrappable {
type Inner;
fn none() -> Self;
fn some(i: Self::Inner) -> Self;
fn take(self) -> Option<Self::Inner>;
}
impl<T> Unwrappable for Option<T> {
type Inner = T;
fn none() -> Self {
None
}
fn some(i: Self::Inner) -> Self {
Some(i)
}
fn take(self) -> Option<Self::Inner> {
self
}
}
/// 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 parity_scale_codec::Encode;
/// # use xcm::v3::{prelude::*, Weight};
/// # use 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 phaases 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 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 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))
}