mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-19 19:15:41 +00:00
XCM v3 (#4097)
* 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:
+320
-299
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -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, ()>(()),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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()))
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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)) }),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
Reference in New Issue
Block a user