* cargo fmt

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

* Create benchmarks for BurnAsset and ExpectAsset

* Add benchmarks for ExpectOrigin and ExpectError

* Add benchmarks for QueryPallet and ExpectPallet

* Add benchmarks for ReportTransactStatus and ClearTransactStatus

* cargo fmt

* Use AllPalletsWithSystem in mocks

* Update XCM generic benchmarks for westend

* Remove default impls for some XCM weight functions

* Fix compilation error

* Add weight_args helper attribute

* Remove manually written XcmWeightInfo

* Parse trailing comma

* Revert "Add weight_args helper attribute"

This reverts commit 3b7c47a6182e1b9227036c38b406d494c3fcf6fd.

* Fixes

* Fixes

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

* Introduce querier field into QueryReponse

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

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

* Fix tests

* cargo fmt

* Fix benchmarks

* Appease spellchecker

* Fix test

* Fix tests

* Fix test

* Fix mock

* Fixes

* Fix tests

* Add test for response queriers

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

* Test for non-existence of querier

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

* Fixes

* Fixes

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

* add matches_prefix function to MultiLocation and Junctions

* rename matches_prefix to starts_with

* remove unnecessary main in doc comment

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

* Make use of starts_with in match_and_split

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

* XCM v3: Bridge infrastructure (#4681)

* XCM bridge infrastructure

* Missing bit of cherry-pick

* Revamped XCM proc macros; new NetworkIds

* Fixes

* Formatting

* ExportMessage instruction and config type

* Add MessageExporter definitions

* Formatting

* Missing files

* Fixes

* Initial bridging config API

* Allow for two-stage XCM execution

* Update xcm/src/v3/mod.rs

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

* XCM crate building again

* Initial bridging primitive

* Docs

* Docs

* More work

* More work

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

* Make build

* WithComputedOrigin and SovereignPaidRemoteExporter

* Remove TODOs

* Slim bridge API and tests.

* Fixes

* More work

* First bridge test passing

* Formatting

* Another test

* Next round of bridging tests

* Repot tests

* Cleanups

* Paid bridging

* Formatting

* Tests

* Spelling

* Formatting

* Fees and refactoring

* Fixes

* Formatting

* Refactor SendXcm to become two-phase

* Fix tests

* Refactoring of SendXcm and ExportXcm complete

* Formatting

* Rename CannotReachDestination -> NotApplicable

* Remove XCM v0

* Minor grumbles

* Formatting

* Formatting

* Fixes

* Fixes

* Cleanup XCM config

* Fee handling

* Fixes

* Formatting

* Fixes

* Bump

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

* Bump Substrate

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

* Asset Exchange and Locks

* Make sure XCM typers impl MaxEncodedLen

* Basic implementation for locks

* Bump Substrate

* Missing files

* Use new API

* Introduce  instruction

* Big refactor

* Docs

* Remove deprecated struct

* Remove deprecated struct

* Repot XCM builder tests

* ExchangeAsset test

* Exchange tests

* Locking tests

* Locking tests

* Fixes and tests

* Fixes

* Formatting

* Spelling

* Add simulator test for remote locking

* Fix tests

* Bump

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

* NFT support and a test

* New files.

* Integration tests for sending NFTs

* Formatting

* Broken Cargo features

* Use 2021 edition

* Fixes

* Formatting

* Formatting

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

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

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

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

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

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

* Fixes

* Fixes

* Fixes

* Formatting

* Fixes

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

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

* send_xcm returns message hash

* cargo fmt

* Create topic register and instructions

* Fix weights

* Use tabs

* Sketch out XcmContext

* Fix doc test

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

* Fixes

* Add XcmContext parameter

* Revert adding context as an arg to SendXcm trait methods

* Revert adding context argument to ConvertOrigin trait methods

* cargo fmt

* Do not change the API of XcmExecutor::execute

* Fixes

* Fixes

* Fixes

* Fixes

* Remove convenience method

* Fixes

* Fixes

* cargo fmt

* Fixes

* Add benchmarks for XCM topic instructions

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

* Remove context argument on FilterAssetLocation

* Fixes

* Remove unused import

* Fixes

* Fixes

* Fixes

* Accept XCM hash parameter in ExecuteXcm trait methods

* cargo fmt

* Properly enable sp-io/std

* Fixes

* default-features = false

* Fixes

* Fixes

* Fixes

* Make XcmContext optional in withdraw_asset

* Fixes

* Fixes

* Fixes

* Modify tests to check for the correct XCM hash

* Small refactor

* cargo fmt

* Check for expected hash in xcm-builder unit tests

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

* Update xcm/src/v3/traits.rs

* Update xcm/src/v3/traits.rs

* Store XcmContext and avoid rebuilding

* Use ref for XcmContext

* Formatting

* Fix incorrect hash CC @KiChjang

* Refactor and make clear fake hashes

* Fixes

* Fixes

* Fixes

* Fix broken hashing

* Docs

* Fixes

* Fixes

* Fixes

* Formatting

* Fixes

* Fixes

* Fixes

* Remove unknowable hash

* Formatting

* Use message hash for greater identifiability

* Formatting

* Fixes

* Formatting

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

* Fixes

* Fixes

* Fixes

* Fixes

* Formatting

* Fixes

* Formatting

* Fixes

* Fixes

* Formatting

* Formatting

* Remove horrible names

* Bump

* Remove InvertLocation trait (#5092)

* Remove InvertLocation trait

* Remove unneeded functions

* Formatting

* Fixes

* Remove XCMv1 (#5094)

* Remove XCMv1

* Remove XCMv1

* Formatting

* Fixes

* Fixes

* Formatting

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

* derive serialize/deserialize for xcm primitives

* derive serialize/deserialize for xcm primitives

* update v3

* update v2

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

* Update lock

* Fixes

* Add benchmarks for the ExchangeAsset instruction

* `AliasOrigin` instruction stub (#5122)

* AliasOrigin instruction stub

* Fixes

* Fixes

* Update substrate

* Fixes

* Ensure same array length before using copy_from_slice

* Fixes

* Add benchmarks for the UniversalOrigin instruction

* Remove unused import

* Remove unused import

* Add benchmarks for SetFeesMode instruction

* Add benchmarks for asset (un)locking instructions

* Leave AliasOrigin unbenchmarked

* Fixes after merge

* cargo fmt

* Fixes

* Fixes

* Set TrustedReserves to None on both Kusama and Westend

* Remove extraneous reserve_asset_deposited benchmark

* Fix universal_origin benchmark

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

* Don't rely on skipped benchmark functions

* Fixes

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

* Fix unused variables

* Fixes

* Spelling

* Fixes

* Fix codec index of VersionedXcm

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

* CallDispatcher trait

* fmt

* unused import

* fix test-runtime

* remove JustDispatch type

* fix typo in test-runtime

* missing CallDispatcher

* more missing CallDispatcher

* Update comment `NoteAssetLocked` -> `NoteUnlockable`

* Fixes

* Fixes

* Adjust MultiAssets weights based on new wild card variants

* Fixes

* Fixes

* Fixes

* Fixes

* Fixes

* Some late fixes for XCMv3 (#5237)

* Maximise chances that trapped assets can be reclaimed

* Do origin check as part of ExportMessage for security

* Formatting

* Fixes

* Cleanup export XCM APIs

* Formatting

* Update xcm/src/v3/junctions.rs

* UnpaidExecution instruction and associated barrier.

* Tighten barriers (ClearOrigin/QueryResponse)

* Allow only 1 ClearOrigin instruction in AllowTopLevelPaidExecutionFrom

* Bi-directional teleport accounting

* Revert other fix

* Build fixes]

* Tests build

* Benchmark fixes

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

* Update Substrate

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

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

* Removed unnecessary clone

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

* Depend upon sp-core instead of sp-runtime

* Make sp-io a dev-dependency

* Use 2D weights in XCM v3

* cargo fmt

* Add XCM pallet migration to runtimes

* Use from_parts

* cargo fmt

* Fixes

* cargo fmt

* Remove XCMWeight import

* Fixes

* Fixes

* Fixes

* Fixes

* Use translate in migration

* Increase max upward message size in tests

* Fix doc test

* Remove most uses of from_ref_time

* cargo fmt

* Fixes

* Fixes

* Add extrinsic benchmarking to XCM pallet

* cargo fmt

* Fixes

* Use old syntax

* cargo fmt

* Fixes

* Remove hardcoded weights

* Add XCM pallet to benchmarks

* Use successful origin

* Fix weird type parameter compilation issue

* Fixes

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

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

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

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

* Use benchmarked XCM pallet weights

* Fixes

* Fixes

* Use override instead of skip

* Fixes

* Fixes

* Fixes

* Fixes

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

* Fixes

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

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

Co-authored-by: command-bot <>

* Replace Weight::MAX with 100b weight units

* Add test to ensure all_gte in barriers is correct

* Update xcm/src/v3/junction.rs

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

* Add more weight tests

* cargo fmt

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

* Create thread_local in XCM executor to limit recursion depth

* Add unit test for recursion limit

* Fix statefulness in tests

* Remove panic

* Use defer and environmental macro

* Fix the implementation

* Use nicer interface

* Change ThisNetwork to AnyNetwork

* Move recursion check up to top level

* cargo fmt

* Update comment

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

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

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

* Add newline

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

* Add SafeCallFilter to XcmConfig

* Limit UMP to receive 10 messages every block

* Place 10 message limit on processing instead of receiving

* Always increment the message_processed count whenever a message is processed

* Add as_derivative to the Transact whitelist

* cargo fmt

* Fixes

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

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

* Fixes

* Fixes

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

* Derive some common traits for DispatchBlobError

* Fixes

* cargo fmt

* Fixes

* Fixes

* Fix comments

* Fixes

* Introduce WithOriginFilter and apply it as the CallDispatcher for runtimes

* Fixes

* Appease clippy and fixes

* Fixes

* Fix more clippy issues

* Fixes

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

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

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

* Add benchmark function for ExportMessage

* Fix comment

* Add upper limit to DownwardMessageQueues size

* Add max size check for queue in can_queue_downward_message

* Fixes

* Make Transact runtime call configurable

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

* Update substrate

* Fixes

* Fixes

* Remove ExportMessage benchmark

* Remove assertion on Transact instruction benchmark

* Make reachable destination configurable in XCM pallet benchmarks

* Fixes

* Fixes

* Remove cfg attribute in fuzzer

* Fixes

* Remove cfg attribute for XCM pallet in test runtime

* Fixes

* Use ReachableDest where possible

* Fixes

* Add benchmark for UnpaidExecution

* Update substrate

* Ensure benchmark functions pass filters

* Add runtime-benchmarks feature to fuzzer

* Ensure FixedRateOfFungible accounts for proof size weights

* cargo fmt

* Whitelist remark_with_event when runtime-benchmarks feature is enabled

* Use remark_with_event for Transact benchmarks

* Fix Cargo.lock

* Allow up to 3 DescendOrigin instructions before UnpaidExecution

* cargo fmt

* Edit code comment

* Check check_origin for unpaid execution privilege

* Fixes

* Small nits for xcm-v3 (#6408)

* Add possibility to skip benchmark for export_message

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

* Revert

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

* Add HaulBlobError to `fn haul_blob`

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

Co-authored-by: command-bot <>

* Revert changes to UnpaidExecution

* Change AllowUnpaidExecutionFrom to be explicit

* Fix log text

* cargo fmt

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

* Add benchmarks for XCM pallet version migration

* cargo fmt

* Fixes

* Fixes

* Fixes

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

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

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

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

* Fix benchmarks

* Fix benchmarks

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

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

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

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

Co-authored-by: command-bot <>

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

* Fixes

* Fix comments (#6470)

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

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

* Use  for Kusama

* Use WithComputedOrigin for Polkadot, Rococo and Westend

* Update lock

* Fix warning

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

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

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

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

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

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

* Fixes

* cargo fmt

* Typo

* Update xcm/src/v3/mod.rs

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

* Docs

* Docs

* Docs

* Docs

* Docs

* Update xcm/src/v3/multiasset.rs

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

* Add tests for MultiAssets::from_sorted_and_deduplicated

* Fail gracefully when same instance NFTs are detected during push

* Update Substrate to fix benchmarks

* Apply suggestions from code review

* Update runtime/kusama/src/xcm_config.rs

* Rename arguments

* Attempt to fix benchmark

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

* Use actual weights for UMP pallet in Polkadot

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

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

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

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
Co-authored-by: Bastian Köcher <info@kchr.de>
Co-authored-by: Parity Bot <admin@parity.io>
Co-authored-by: stanly-johnson <stanlyjohnson@outlook.com>
Co-authored-by: nanocryk <6422796+nanocryk@users.noreply.github.com>
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
Co-authored-by: asynchronous rob <rphmeier@gmail.com>
Co-authored-by: command-bot <>
Co-authored-by: Vincent Geddes <vincent.geddes@hey.com>
Co-authored-by: Squirrel <gilescope@gmail.com>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Gavin Wood
2023-01-17 04:04:34 -03:00
committed by GitHub
parent 2952ad6f44
commit 1a1bfd2af9
155 changed files with 19234 additions and 8436 deletions
+218 -191
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -354,7 +354,7 @@ async fn run(
futures::select_biased! {
() = cleanup_pulse.select_next_some() => {
// `select_next_some` because we don't expect this to fail, but if it does, we
// still don't fail. The tradeoff is that the compiled cache will start growing
// still don't fail. The trade-off is that the compiled cache will start growing
// in size. That is, however, rather a slow process and hopefully the operator
// will notice it.
@@ -104,7 +104,7 @@ const STATEMENTS_TIMEOUT: Duration = Duration::from_secs(1);
/// We don't want a slow peer to slow down all the others, at the same time we want to get out the
/// data quickly in full to at least some peers (as this will reduce load on us as they then can
/// start serving the data). So this value is a tradeoff. 3 seems to be sensible. So we would need
/// start serving the data). So this value is a trade-off. 3 seems to be sensible. So we would need
/// to have 3 slow nodes connected, to delay transfer for others by `STATEMENTS_TIMEOUT`.
pub const MAX_PARALLEL_STATEMENT_REQUESTS: u32 = 3;
+1 -2
View File
@@ -189,8 +189,7 @@ fn default_parachains_host_configuration(
max_upward_queue_count: 8,
max_upward_queue_size: 1024 * 1024,
max_downward_message_size: 1024 * 1024,
ump_service_total_weight: Weight::from_ref_time(100_000_000_000)
.set_proof_size(MAX_POV_SIZE as u64),
ump_service_total_weight: Weight::from_parts(100_000_000_000, MAX_POV_SIZE as u64),
max_upward_message_size: 50 * 1024,
max_upward_message_num_per_candidate: 5,
hrmp_sender_deposit: 0,
+1 -1
View File
@@ -138,7 +138,7 @@ mod tests {
weight.base_extrinsic = Weight::from_ref_time(100);
})
.for_class(DispatchClass::non_mandatory(), |weight| {
weight.max_total = Some(Weight::from_ref_time(1024).set_proof_size(u64::MAX));
weight.max_total = Some(Weight::from_parts(1024, u64::MAX));
})
.build_or_panic();
pub BlockLength: limits::BlockLength = limits::BlockLength::max(2 * 1024);
@@ -103,7 +103,7 @@ parameter_types! {
pub const BlockHashCount: u32 = 250;
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(
Weight::from_ref_time(4 * 1024 * 1024).set_proof_size(u64::MAX),
Weight::from_parts(4 * 1024 * 1024, u64::MAX),
);
}
@@ -705,9 +705,7 @@ mod tests {
parameter_types! {
pub const BlockHashCount: u32 = 250;
pub BlockWeights: limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(
Weight::from_ref_time(1024).set_proof_size(u64::MAX),
);
frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, u64::MAX));
pub BlockLength: limits::BlockLength =
limits::BlockLength::max_with_normal_ratio(4 * 1024 * 1024, NORMAL_RATIO);
}
+68 -27
View File
@@ -16,34 +16,75 @@
//! XCM sender for relay chain.
use frame_support::traits::Get;
use parity_scale_codec::Encode;
use runtime_parachains::{configuration, dmp};
use sp_std::marker::PhantomData;
use xcm::latest::prelude::*;
use primitives::v2::Id as ParaId;
use runtime_parachains::{
configuration::{self, HostConfiguration},
dmp,
};
use sp_std::{marker::PhantomData, prelude::*};
use xcm::prelude::*;
use SendError::*;
/// XCM sender for relay chain. It only sends downward message.
pub struct ChildParachainRouter<T, W>(PhantomData<(T, W)>);
impl<T: configuration::Config + dmp::Config, W: xcm::WrapVersion> SendXcm
for ChildParachainRouter<T, W>
{
fn send_xcm(dest: impl Into<MultiLocation>, msg: Xcm<()>) -> SendResult {
let dest = dest.into();
match dest {
MultiLocation { parents: 0, interior: X1(Parachain(id)) } => {
// Downward message passing.
let versioned_xcm =
W::wrap_version(&dest, msg).map_err(|()| SendError::DestinationUnsupported)?;
let config = <configuration::Pallet<T>>::config();
<dmp::Pallet<T>>::queue_downward_message(
&config,
id.into(),
versioned_xcm.encode(),
)
.map_err(Into::<SendError>::into)?;
Ok(())
},
dest => Err(SendError::CannotReachDestination(dest, msg)),
}
/// Simple value-bearing trait for determining/expressing the assets required to be paid for a
/// messages to be delivered to a parachain.
pub trait PriceForParachainDelivery {
/// Return the assets required to deliver `message` to the given `para` destination.
fn price_for_parachain_delivery(para: ParaId, message: &Xcm<()>) -> MultiAssets;
}
impl PriceForParachainDelivery for () {
fn price_for_parachain_delivery(_: ParaId, _: &Xcm<()>) -> MultiAssets {
MultiAssets::new()
}
}
/// Implementation of `PriceForParachainDelivery` which returns a fixed price.
pub struct ConstantPrice<T>(sp_std::marker::PhantomData<T>);
impl<T: Get<MultiAssets>> PriceForParachainDelivery for ConstantPrice<T> {
fn price_for_parachain_delivery(_: ParaId, _: &Xcm<()>) -> MultiAssets {
T::get()
}
}
/// XCM sender for relay chain. It only sends downward message.
pub struct ChildParachainRouter<T, W, P>(PhantomData<(T, W, P)>);
impl<T: configuration::Config + dmp::Config, W: xcm::WrapVersion, P: PriceForParachainDelivery>
SendXcm for ChildParachainRouter<T, W, P>
{
type Ticket = (HostConfiguration<T::BlockNumber>, ParaId, Vec<u8>);
fn validate(
dest: &mut Option<MultiLocation>,
msg: &mut Option<Xcm<()>>,
) -> SendResult<(HostConfiguration<T::BlockNumber>, ParaId, Vec<u8>)> {
let d = dest.take().ok_or(MissingArgument)?;
let id = if let MultiLocation { parents: 0, interior: X1(Parachain(id)) } = &d {
*id
} else {
*dest = Some(d);
return Err(NotApplicable)
};
// Downward message passing.
let xcm = msg.take().ok_or(MissingArgument)?;
let config = <configuration::Pallet<T>>::config();
let para = id.into();
let price = P::price_for_parachain_delivery(para, &xcm);
let blob = W::wrap_version(&d, xcm).map_err(|()| DestinationUnsupported)?.encode();
<dmp::Pallet<T>>::can_queue_downward_message(&config, &para, &blob)
.map_err(Into::<SendError>::into)?;
Ok(((config, para, blob), price))
}
fn deliver(
(config, para, blob): (HostConfiguration<T::BlockNumber>, ParaId, Vec<u8>),
) -> Result<XcmHash, SendError> {
let hash = sp_io::hashing::blake2_256(&blob[..]);
<dmp::Pallet<T>>::queue_downward_message(&config, para, blob)
.map(|()| hash)
.map_err(|_| SendError::Transport(&"Error placing into DMP queue"))
}
}
+30 -14
View File
@@ -1497,6 +1497,9 @@ pub type Migrations = (
pallet_staking::migrations::v13::MigrateToV13<Runtime>,
parachains_disputes::migration::v1::MigrateToV1<Runtime>,
parachains_configuration::migration::v4::MigrateToV4<Runtime>,
// "Use 2D weights in XCM v3" <https://github.com/paritytech/polkadot/pull/6134>
pallet_xcm::migration::v1::MigrateToV1<Runtime>,
parachains_ump::migration::v1::MigrateToV1<Runtime>,
);
/// Unchecked extrinsic type as expected by this runtime.
@@ -1576,6 +1579,7 @@ mod benches {
[pallet_vesting, Vesting]
[pallet_whitelist, Whitelist]
// XCM
[pallet_xcm, XcmPallet]
[pallet_xcm_benchmarks::fungible, pallet_xcm_benchmarks::fungible::Pallet::<Runtime>]
[pallet_xcm_benchmarks::generic, pallet_xcm_benchmarks::generic::Pallet::<Runtime>]
);
@@ -1978,7 +1982,9 @@ sp_api::impl_runtime_apis! {
use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench;
use frame_benchmarking::baseline::Pallet as Baseline;
use xcm::latest::prelude::*;
use xcm_config::{CheckAccount, KsmLocation, SovereignAccountOf, Statemine, XcmConfig};
use xcm_config::{
LocalCheckAccount, SovereignAccountOf, Statemine, TokenLocation, XcmConfig,
};
impl pallet_session_benchmarking::Config for Runtime {}
impl pallet_offences_benchmarking::Config for Runtime {}
@@ -1993,10 +1999,10 @@ sp_api::impl_runtime_apis! {
fn valid_destination() -> Result<MultiLocation, BenchmarkError> {
Ok(Statemine::get())
}
fn worst_case_holding() -> MultiAssets {
fn worst_case_holding(_depositable_count: u32) -> MultiAssets {
// Kusama only knows about KSM.
vec![MultiAsset{
id: Concrete(KsmLocation::get()),
id: Concrete(TokenLocation::get()),
fun: Fungible(1_000_000 * UNITS),
}].into()
}
@@ -2005,24 +2011,19 @@ sp_api::impl_runtime_apis! {
parameter_types! {
pub const TrustedTeleporter: Option<(MultiLocation, MultiAsset)> = Some((
Statemine::get(),
MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(KsmLocation::get()) },
));
pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = Some((
Statemine::get(),
MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(KsmLocation::get()) },
MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(TokenLocation::get()) },
));
}
impl pallet_xcm_benchmarks::fungible::Config for Runtime {
type TransactAsset = Balances;
type CheckedAccount = CheckAccount;
type CheckedAccount = LocalCheckAccount;
type TrustedTeleporter = TrustedTeleporter;
type TrustedReserve = TrustedReserve;
fn get_multi_asset() -> MultiAsset {
MultiAsset {
id: Concrete(KsmLocation::get()),
id: Concrete(TokenLocation::get()),
fun: Fungible(1 * UNITS),
}
}
@@ -2035,8 +2036,18 @@ sp_api::impl_runtime_apis! {
(0u64, Response::Version(Default::default()))
}
fn transact_origin() -> Result<MultiLocation, BenchmarkError> {
Ok(Statemine::get())
fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError> {
// Kusama doesn't support asset exchanges
Err(BenchmarkError::Skip)
}
fn universal_alias() -> Result<Junction, BenchmarkError> {
// The XCM executor of Kusama doesn't have a configured `UniversalAliases`
Err(BenchmarkError::Skip)
}
fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> {
Ok((Statemine::get(), frame_system::Call::remark_with_event { remark: vec![] }.into()))
}
fn subscribe_origin() -> Result<MultiLocation, BenchmarkError> {
@@ -2045,10 +2056,15 @@ sp_api::impl_runtime_apis! {
fn claimable_asset() -> Result<(MultiLocation, MultiLocation, MultiAssets), BenchmarkError> {
let origin = Statemine::get();
let assets: MultiAssets = (Concrete(KsmLocation::get()), 1_000 * UNITS).into();
let assets: MultiAssets = (Concrete(TokenLocation::get()), 1_000 * UNITS).into();
let ticket = MultiLocation { parents: 0, interior: Here };
Ok((origin, ticket, assets))
}
fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> {
// Kusama doesn't support asset locking
Err(BenchmarkError::Skip)
}
}
let whitelist: Vec<TrackedStorageKey> = vec![
@@ -50,6 +50,7 @@ pub mod pallet_treasury;
pub mod pallet_utility;
pub mod pallet_vesting;
pub mod pallet_whitelist;
pub mod pallet_xcm;
pub mod runtime_common_auctions;
pub mod runtime_common_claims;
pub mod runtime_common_crowdloan;
@@ -0,0 +1,168 @@
// Copyright 2017-2022 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/>.
//! Autogenerated weights for `pallet_xcm`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-12-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("kusama-dev"), DB CACHE: 1024
// Executed Command:
// /home/benchbot/cargo_target_dir/production/polkadot
// benchmark
// pallet
// --steps=50
// --repeat=20
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/polkadot/.git/.artifacts/bench.json
// --pallet=pallet_xcm
// --chain=kusama-dev
// --header=./file_header.txt
// --output=./runtime/kusama/src/weights/
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
use frame_support::{traits::Get, weights::Weight};
use sp_std::marker::PhantomData;
/// Weight functions for `pallet_xcm`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn send() -> Weight {
// Minimum execution time: 36_474 nanoseconds.
Weight::from_ref_time(37_030_000)
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(3))
}
fn teleport_assets() -> Weight {
// Minimum execution time: 28_147 nanoseconds.
Weight::from_ref_time(28_836_000)
}
fn reserve_transfer_assets() -> Weight {
// Minimum execution time: 28_469 nanoseconds.
Weight::from_ref_time(29_002_000)
}
fn execute() -> Weight {
// Minimum execution time: 15_637 nanoseconds.
Weight::from_ref_time(15_880_000)
}
// Storage: XcmPallet SupportedVersion (r:0 w:1)
fn force_xcm_version() -> Weight {
// Minimum execution time: 15_330 nanoseconds.
Weight::from_ref_time(15_817_000)
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: XcmPallet SafeXcmVersion (r:0 w:1)
fn force_default_xcm_version() -> Weight {
// Minimum execution time: 4_104 nanoseconds.
Weight::from_ref_time(4_365_000)
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: XcmPallet VersionNotifiers (r:1 w:1)
// Storage: XcmPallet QueryCounter (r:1 w:1)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: XcmPallet Queries (r:0 w:1)
fn force_subscribe_version_notify() -> Weight {
// Minimum execution time: 42_177 nanoseconds.
Weight::from_ref_time(42_657_000)
.saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(6))
}
// Storage: XcmPallet VersionNotifiers (r:1 w:1)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: XcmPallet Queries (r:0 w:1)
fn force_unsubscribe_version_notify() -> Weight {
// Minimum execution time: 45_481 nanoseconds.
Weight::from_ref_time(45_960_000)
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(5))
}
// Storage: XcmPallet SupportedVersion (r:4 w:2)
fn migrate_supported_version() -> Weight {
// Minimum execution time: 14_899 nanoseconds.
Weight::from_ref_time(15_452_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifiers (r:4 w:2)
fn migrate_version_notifiers() -> Weight {
// Minimum execution time: 14_759 nanoseconds.
Weight::from_ref_time(15_176_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifyTargets (r:5 w:0)
fn already_notified_target() -> Weight {
// Minimum execution time: 17_022 nanoseconds.
Weight::from_ref_time(17_468_000)
.saturating_add(T::DbWeight::get().reads(5))
}
// Storage: XcmPallet VersionNotifyTargets (r:2 w:1)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn notify_current_targets() -> Weight {
// Minimum execution time: 37_810 nanoseconds.
Weight::from_ref_time(38_198_000)
.saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(4))
}
// Storage: XcmPallet VersionNotifyTargets (r:3 w:0)
fn notify_target_migration_fail() -> Weight {
// Minimum execution time: 7_440 nanoseconds.
Weight::from_ref_time(7_659_000)
.saturating_add(T::DbWeight::get().reads(3))
}
// Storage: XcmPallet VersionNotifyTargets (r:4 w:2)
fn migrate_version_notify_targets() -> Weight {
// Minimum execution time: 14_975 nanoseconds.
Weight::from_ref_time(15_479_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifyTargets (r:4 w:2)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn migrate_and_notify_old_targets() -> Weight {
// Minimum execution time: 43_328 nanoseconds.
Weight::from_ref_time(44_054_000)
.saturating_add(T::DbWeight::get().reads(9))
.saturating_add(T::DbWeight::get().writes(5))
}
}
@@ -16,23 +16,25 @@
//! Autogenerated weights for `runtime_parachains::ump`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-11-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm5`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! DATE: 2023-01-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("kusama-dev"), DB CACHE: 1024
// Executed Command:
// ./target/production/polkadot
// /home/benchbot/cargo_target_dir/production/polkadot
// benchmark
// pallet
// --chain=kusama-dev
// --steps=50
// --repeat=20
// --pallet=runtime_parachains::ump
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/polkadot/.git/.artifacts/bench.json
// --pallet=runtime_parachains::ump
// --chain=kusama-dev
// --header=./file_header.txt
// --output=./runtime/kusama/src/weights/runtime_parachains_ump.rs
// --output=./runtime/kusama/src/weights/
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
@@ -46,26 +48,27 @@ pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> runtime_parachains::ump::WeightInfo for WeightInfo<T> {
/// The range of component `s` is `[0, 51200]`.
fn process_upward_message(s: u32, ) -> Weight {
// Minimum execution time: 10_348 nanoseconds.
Weight::from_ref_time(5_121_205 as u64)
// Standard Error: 12
.saturating_add(Weight::from_ref_time(1_934 as u64).saturating_mul(s as u64))
// Minimum execution time: 10_393 nanoseconds.
Weight::from_ref_time(2_845_995)
// Standard Error: 21
.saturating_add(Weight::from_ref_time(2_016).saturating_mul(s.into()))
}
// Storage: Ump NeedsDispatch (r:1 w:1)
// Storage: Ump NextDispatchRoundStartWith (r:1 w:1)
// Storage: Ump RelayDispatchQueues (r:0 w:1)
// Storage: Ump RelayDispatchQueueSize (r:0 w:1)
fn clean_ump_after_outgoing() -> Weight {
// Minimum execution time: 9_800 nanoseconds.
Weight::from_ref_time(10_025_000 as u64)
.saturating_add(T::DbWeight::get().reads(2 as u64))
.saturating_add(T::DbWeight::get().writes(4 as u64))
// Minimum execution time: 9_686 nanoseconds.
Weight::from_ref_time(9_920_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(4))
}
// Storage: Ump Overweight (r:1 w:1)
// Storage: Ump CounterForOverweight (r:1 w:1)
fn service_overweight() -> Weight {
// Minimum execution time: 26_272 nanoseconds.
Weight::from_ref_time(26_790_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
// Minimum execution time: 28_502 nanoseconds.
Weight::from_ref_time(28_900_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
}
+135 -80
View File
@@ -4,10 +4,7 @@ mod pallet_xcm_benchmarks_generic;
use crate::Runtime;
use frame_support::weights::Weight;
use sp_std::prelude::*;
use xcm::{
latest::{prelude::*, Weight as XCMWeight},
DoubleEncoded,
};
use xcm::{latest::prelude::*, DoubleEncoded};
use pallet_xcm_benchmarks_fungible::WeightInfo as XcmBalancesWeight;
use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
@@ -31,15 +28,15 @@ impl From<&MultiAsset> for AssetTypes {
}
trait WeighMultiAssets {
fn weigh_multi_assets(&self, balances_weight: Weight) -> XCMWeight;
fn weigh_multi_assets(&self, balances_weight: Weight) -> Weight;
}
// Kusama only knows about one asset, the balances pallet.
const MAX_ASSETS: u32 = 1;
impl WeighMultiAssets for MultiAssetFilter {
fn weigh_multi_assets(&self, balances_weight: Weight) -> XCMWeight {
let weight = match self {
fn weigh_multi_assets(&self, balances_weight: Weight) -> Weight {
match self {
Self::Definite(assets) => assets
.inner()
.into_iter()
@@ -49,155 +46,213 @@ impl WeighMultiAssets for MultiAssetFilter {
AssetTypes::Unknown => Weight::MAX,
})
.fold(Weight::zero(), |acc, x| acc.saturating_add(x)),
Self::Wild(_) => balances_weight.saturating_mul(MAX_ASSETS as u64),
};
weight.ref_time()
Self::Wild(AllOf { .. } | AllOfCounted { .. }) => balances_weight,
Self::Wild(AllCounted(count)) => balances_weight.saturating_mul(*count as u64),
Self::Wild(All) => balances_weight.saturating_mul(MAX_ASSETS as u64),
}
}
}
impl WeighMultiAssets for MultiAssets {
fn weigh_multi_assets(&self, balances_weight: Weight) -> XCMWeight {
let weight = self
.inner()
fn weigh_multi_assets(&self, balances_weight: Weight) -> Weight {
self.inner()
.into_iter()
.map(|m| <AssetTypes as From<&MultiAsset>>::from(m))
.map(|t| match t {
AssetTypes::Balances => balances_weight,
AssetTypes::Unknown => Weight::MAX,
})
.fold(Weight::zero(), |acc, x| acc.saturating_add(x));
weight.ref_time()
.fold(Weight::zero(), |acc, x| acc.saturating_add(x))
}
}
pub struct KusamaXcmWeight<RuntimeCall>(core::marker::PhantomData<RuntimeCall>);
impl<RuntimeCall> XcmWeightInfo<RuntimeCall> for KusamaXcmWeight<RuntimeCall> {
fn withdraw_asset(assets: &MultiAssets) -> XCMWeight {
fn withdraw_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::withdraw_asset())
}
fn reserve_asset_deposited(assets: &MultiAssets) -> XCMWeight {
fn reserve_asset_deposited(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::reserve_asset_deposited())
}
fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight {
fn receive_teleported_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::receive_teleported_asset())
}
fn query_response(_query_id: &u64, _response: &Response, _max_weight: &u64) -> XCMWeight {
XcmGeneric::<Runtime>::query_response().ref_time()
fn query_response(
_query_id: &u64,
_response: &Response,
_max_weight: &Weight,
_querier: &Option<MultiLocation>,
) -> Weight {
XcmGeneric::<Runtime>::query_response()
}
fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight {
fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::transfer_asset())
}
fn transfer_reserve_asset(
assets: &MultiAssets,
_dest: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::transfer_reserve_asset())
}
fn transact(
_origin_type: &OriginKind,
_require_weight_at_most: &u64,
_origin_kind: &OriginKind,
_require_weight_at_most: &Weight,
_call: &DoubleEncoded<RuntimeCall>,
) -> XCMWeight {
XcmGeneric::<Runtime>::transact().ref_time()
) -> Weight {
XcmGeneric::<Runtime>::transact()
}
fn hrmp_new_channel_open_request(
_sender: &u32,
_max_message_size: &u32,
_max_capacity: &u32,
) -> XCMWeight {
) -> Weight {
// XCM Executor does not currently support HRMP channel operations
Weight::MAX.ref_time()
Weight::MAX
}
fn hrmp_channel_accepted(_recipient: &u32) -> XCMWeight {
fn hrmp_channel_accepted(_recipient: &u32) -> Weight {
// XCM Executor does not currently support HRMP channel operations
Weight::MAX.ref_time()
Weight::MAX
}
fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> XCMWeight {
fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> Weight {
// XCM Executor does not currently support HRMP channel operations
Weight::MAX.ref_time()
Weight::MAX
}
fn clear_origin() -> XCMWeight {
XcmGeneric::<Runtime>::clear_origin().ref_time()
fn clear_origin() -> Weight {
XcmGeneric::<Runtime>::clear_origin()
}
fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight {
XcmGeneric::<Runtime>::descend_origin().ref_time()
fn descend_origin(_who: &InteriorMultiLocation) -> Weight {
XcmGeneric::<Runtime>::descend_origin()
}
fn report_error(
_query_id: &QueryId,
_dest: &MultiLocation,
_max_response_weight: &u64,
) -> XCMWeight {
XcmGeneric::<Runtime>::report_error().ref_time()
fn report_error(_query_response_info: &QueryResponseInfo) -> Weight {
XcmGeneric::<Runtime>::report_error()
}
fn deposit_asset(
assets: &MultiAssetFilter,
_max_assets: &u32, // TODO use max assets?
_dest: &MultiLocation,
) -> XCMWeight {
fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::deposit_asset())
}
fn deposit_reserve_asset(
assets: &MultiAssetFilter,
_max_assets: &u32, // TODO use max assets?
_dest: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::deposit_reserve_asset())
}
fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets) -> XCMWeight {
Weight::MAX.ref_time() // todo fix
fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets, _maximal: &bool) -> Weight {
// Kusama does not currently support exchange asset operations
Weight::MAX
}
fn initiate_reserve_withdraw(
assets: &MultiAssetFilter,
_reserve: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmGeneric::<Runtime>::initiate_reserve_withdraw())
}
fn initiate_teleport(
assets: &MultiAssetFilter,
_dest: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::initiate_teleport())
}
fn query_holding(
_query_id: &u64,
_dest: &MultiLocation,
_assets: &MultiAssetFilter,
_max_response_weight: &u64,
) -> XCMWeight {
XcmGeneric::<Runtime>::query_holding().ref_time()
fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> Weight {
XcmGeneric::<Runtime>::report_holding()
}
fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight {
XcmGeneric::<Runtime>::buy_execution().ref_time()
fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> Weight {
XcmGeneric::<Runtime>::buy_execution()
}
fn refund_surplus() -> XCMWeight {
XcmGeneric::<Runtime>::refund_surplus().ref_time()
fn refund_surplus() -> Weight {
XcmGeneric::<Runtime>::refund_surplus()
}
fn set_error_handler(_xcm: &Xcm<RuntimeCall>) -> XCMWeight {
XcmGeneric::<Runtime>::set_error_handler().ref_time()
fn set_error_handler(_xcm: &Xcm<RuntimeCall>) -> Weight {
XcmGeneric::<Runtime>::set_error_handler()
}
fn set_appendix(_xcm: &Xcm<RuntimeCall>) -> XCMWeight {
XcmGeneric::<Runtime>::set_appendix().ref_time()
fn set_appendix(_xcm: &Xcm<RuntimeCall>) -> Weight {
XcmGeneric::<Runtime>::set_appendix()
}
fn clear_error() -> XCMWeight {
XcmGeneric::<Runtime>::clear_error().ref_time()
fn clear_error() -> Weight {
XcmGeneric::<Runtime>::clear_error()
}
fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> XCMWeight {
XcmGeneric::<Runtime>::claim_asset().ref_time()
fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> Weight {
XcmGeneric::<Runtime>::claim_asset()
}
fn trap(_code: &u64) -> XCMWeight {
XcmGeneric::<Runtime>::trap().ref_time()
fn trap(_code: &u64) -> Weight {
XcmGeneric::<Runtime>::trap()
}
fn subscribe_version(_query_id: &QueryId, _max_response_weight: &u64) -> XCMWeight {
XcmGeneric::<Runtime>::subscribe_version().ref_time()
fn subscribe_version(_query_id: &QueryId, _max_response_weight: &Weight) -> Weight {
XcmGeneric::<Runtime>::subscribe_version()
}
fn unsubscribe_version() -> XCMWeight {
XcmGeneric::<Runtime>::unsubscribe_version().ref_time()
fn unsubscribe_version() -> Weight {
XcmGeneric::<Runtime>::unsubscribe_version()
}
fn burn_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmGeneric::<Runtime>::burn_asset())
}
fn expect_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmGeneric::<Runtime>::expect_asset())
}
fn expect_origin(_origin: &Option<MultiLocation>) -> Weight {
XcmGeneric::<Runtime>::expect_origin()
}
fn expect_error(_error: &Option<(u32, XcmError)>) -> Weight {
XcmGeneric::<Runtime>::expect_error()
}
fn query_pallet(_module_name: &Vec<u8>, _response_info: &QueryResponseInfo) -> Weight {
XcmGeneric::<Runtime>::query_pallet()
}
fn expect_pallet(
_index: &u32,
_name: &Vec<u8>,
_module_name: &Vec<u8>,
_crate_major: &u32,
_min_crate_minor: &u32,
) -> Weight {
XcmGeneric::<Runtime>::expect_pallet()
}
fn report_transact_status(_response_info: &QueryResponseInfo) -> Weight {
XcmGeneric::<Runtime>::report_transact_status()
}
fn clear_transact_status() -> Weight {
XcmGeneric::<Runtime>::clear_transact_status()
}
fn universal_origin(_: &Junction) -> Weight {
// Kusama does not currently support universal origin operations
Weight::MAX
}
fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight {
Weight::MAX // todo fix
}
fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Kusama does not currently support asset locking operations
Weight::MAX
}
fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Kusama does not currently support asset locking operations
Weight::MAX
}
fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Kusama does not currently support asset locking operations
Weight::MAX
}
fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Kusama does not currently support asset locking operations
Weight::MAX
}
fn set_fees_mode(_: &bool) -> Weight {
XcmGeneric::<Runtime>::set_fees_mode()
}
fn set_topic(_topic: &[u8; 32]) -> Weight {
XcmGeneric::<Runtime>::set_topic()
}
fn clear_topic() -> Weight {
XcmGeneric::<Runtime>::clear_topic()
}
fn alias_origin(_: &MultiLocation) -> Weight {
// XCM Executor does not currently support alias origin operations
Weight::MAX
}
fn unpaid_execution(_: &WeightLimit, _: &Option<MultiLocation>) -> Weight {
XcmGeneric::<Runtime>::unpaid_execution()
}
}
@@ -17,12 +17,13 @@
//! Autogenerated weights for `pallet_xcm_benchmarks::generic`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-03-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2022-04-18, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("kusama-dev"), DB CACHE: 1024
// Executed Command:
// target/production/polkadot
// benchmark
// pallet
// --chain=kusama-dev
// --steps=50
// --repeat=20
@@ -48,89 +49,135 @@ impl<T: frame_system::Config> WeightInfo<T> {
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
pub(crate) fn query_holding() -> Weight {
Weight::from_ref_time(21_822_000 as u64)
.saturating_add(T::DbWeight::get().reads(6 as u64))
pub(crate) fn report_holding() -> Weight {
Weight::from_ref_time(25_878_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
pub(crate) fn buy_execution() -> Weight {
Weight::from_ref_time(3_109_000 as u64)
Weight::from_ref_time(3_697_000 as u64)
}
// Storage: XcmPallet Queries (r:1 w:0)
pub(crate) fn query_response() -> Weight {
Weight::from_ref_time(12_087_000 as u64)
Weight::from_ref_time(13_458_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
}
pub(crate) fn transact() -> Weight {
Weight::from_ref_time(12_398_000 as u64)
Weight::from_ref_time(13_597_000 as u64)
}
pub(crate) fn refund_surplus() -> Weight {
Weight::from_ref_time(3_247_000 as u64)
Weight::from_ref_time(3_839_000 as u64)
}
pub(crate) fn set_error_handler() -> Weight {
Weight::from_ref_time(3_086_000 as u64)
Weight::from_ref_time(3_657_000 as u64)
}
pub(crate) fn set_appendix() -> Weight {
Weight::from_ref_time(3_112_000 as u64)
Weight::from_ref_time(3_757_000 as u64)
}
pub(crate) fn clear_error() -> Weight {
Weight::from_ref_time(3_118_000 as u64)
Weight::from_ref_time(3_651_000 as u64)
}
pub(crate) fn descend_origin() -> Weight {
Weight::from_ref_time(4_054_000 as u64)
Weight::from_ref_time(4_589_000 as u64)
}
pub(crate) fn clear_origin() -> Weight {
Weight::from_ref_time(3_111_000 as u64)
Weight::from_ref_time(3_661_000 as u64)
}
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
pub(crate) fn report_error() -> Weight {
Weight::from_ref_time(18_425_000 as u64)
.saturating_add(T::DbWeight::get().reads(6 as u64))
Weight::from_ref_time(21_351_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
// Storage: XcmPallet AssetTraps (r:1 w:1)
pub(crate) fn claim_asset() -> Weight {
Weight::from_ref_time(7_144_000 as u64)
Weight::from_ref_time(7_674_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
pub(crate) fn trap() -> Weight {
Weight::from_ref_time(3_060_000 as u64)
Weight::from_ref_time(3_606_000 as u64)
}
// Storage: XcmPallet VersionNotifyTargets (r:1 w:1)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
pub(crate) fn subscribe_version() -> Weight {
Weight::from_ref_time(21_642_000 as u64)
.saturating_add(T::DbWeight::get().reads(7 as u64))
Weight::from_ref_time(30_453_000 as u64)
.saturating_add(T::DbWeight::get().reads(6 as u64))
.saturating_add(T::DbWeight::get().writes(4 as u64))
}
// Storage: XcmPallet VersionNotifyTargets (r:0 w:1)
pub(crate) fn unsubscribe_version() -> Weight {
Weight::from_ref_time(4_873_000 as u64)
Weight::from_ref_time(5_543_000 as u64)
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
pub(crate) fn initiate_reserve_withdraw() -> Weight {
Weight::from_ref_time(22_809_000 as u64)
.saturating_add(T::DbWeight::get().reads(6 as u64))
Weight::from_ref_time(25_464_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
pub(crate) fn burn_asset() -> Weight {
Weight::from_ref_time(5_194_000 as u64)
}
pub(crate) fn expect_asset() -> Weight {
Weight::from_ref_time(3_698_000 as u64)
}
pub(crate) fn expect_origin() -> Weight {
Weight::from_ref_time(3_786_000 as u64)
}
pub(crate) fn expect_error() -> Weight {
Weight::from_ref_time(3_645_000 as u64)
}
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
pub(crate) fn query_pallet() -> Weight {
Weight::from_ref_time(22_993_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
pub(crate) fn expect_pallet() -> Weight {
Weight::from_ref_time(4_043_000 as u64)
}
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
pub(crate) fn report_transact_status() -> Weight {
Weight::from_ref_time(21_668_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
pub(crate) fn clear_transact_status() -> Weight {
Weight::from_ref_time(3_673_000 as u64)
}
pub(crate) fn set_topic() -> Weight {
Weight::from_ref_time(3_661_000 as u64)
}
pub(crate) fn clear_topic() -> Weight {
Weight::from_ref_time(3_647_000 as u64)
}
pub(crate) fn set_fees_mode() -> Weight {
Weight::from_ref_time(3_599_000 as u64)
}
pub(crate) fn unpaid_execution() -> Weight {
Weight::from_ref_time(3_111_000 as u64)
}
}
+264 -35
View File
@@ -17,33 +17,44 @@
//! XCM configurations for the Kusama runtime.
use super::{
parachains_origin, AccountId, Balances, CouncilCollective, Fellows, ParaId, Runtime,
RuntimeCall, RuntimeEvent, RuntimeOrigin, StakingAdmin, WeightToFee, XcmPallet,
parachains_origin, AccountId, AllPalletsWithSystem, Balances, CouncilCollective, Fellows,
ParaId, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, StakingAdmin, WeightToFee,
XcmPallet,
};
use frame_support::{
match_types, parameter_types,
traits::{Contains, Everything, Nothing},
weights::Weight,
};
use frame_support::{match_types, parameter_types, traits::Everything};
use runtime_common::{xcm_sender, ToAuthor};
use sp_core::ConstU32;
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, BackingToPlurality,
AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses,
AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, BackingToPlurality,
ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds, IsChildSystemParachain, IsConcrete,
LocationInverter, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32,
MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds,
WithComputedOrigin,
};
use xcm_executor::traits::WithOriginFilter;
parameter_types! {
/// The location of the KSM token, from the context of this chain. Since this token is native to this
/// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to
/// the context".
pub const KsmLocation: MultiLocation = Here.into();
pub const TokenLocation: MultiLocation = Here.into_location();
/// The Kusama network ID. This is named.
pub const KusamaNetwork: NetworkId = NetworkId::Kusama;
/// Our XCM location ancestry - i.e. what, if anything, `Parent` means evaluated in our context. Since
/// Kusama is a top-level relay-chain, there is no ancestry.
pub const Ancestry: MultiLocation = Here.into();
pub const ThisNetwork: NetworkId = Kusama;
/// Our XCM location ancestry - i.e. our location within the Consensus Universe.
///
/// Since Kusama is a top-level relay-chain with its own consensus, it's just our network ID.
pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into();
/// The check account, which holds any native assets that have been teleported out and not back in (yet).
pub CheckAccount: AccountId = XcmPallet::check_account();
/// The check account that is allowed to mint assets locally.
pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local);
}
/// The canonical means of converting a `MultiLocation` into an `AccountId`, used when we want to determine
@@ -52,41 +63,41 @@ pub type SovereignAccountOf = (
// We can convert a child parachain using the standard `AccountId` conversion.
ChildParachainConvertsVia<ParaId, AccountId>,
// We can directly alias an `AccountId32` into a local account.
AccountId32Aliases<KusamaNetwork, AccountId>,
AccountId32Aliases<ThisNetwork, AccountId>,
);
/// Our asset transactor. This is what allows us to interest with the runtime facilities from the point of
/// view of XCM-only concepts like `MultiLocation` and `MultiAsset`.
///
/// Ours is only aware of the Balances pallet, which is mapped to `KsmLocation`.
/// Ours is only aware of the Balances pallet, which is mapped to `TokenLocation`.
pub type LocalAssetTransactor = XcmCurrencyAdapter<
// Use this currency:
Balances,
// Use this currency when it is a fungible asset matching the given location or name:
IsConcrete<KsmLocation>,
IsConcrete<TokenLocation>,
// We can convert the MultiLocations with our converter above:
SovereignAccountOf,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
AccountId,
// We track our teleports in/out to keep total issuance correct.
CheckAccount,
LocalCheckAccount,
>;
/// The means that we convert an the XCM message origin location into a local dispatch origin.
/// The means that we convert the XCM message origin location into a local dispatch origin.
type LocalOriginConverter = (
// A `Signed` origin of the sovereign account that the original location controls.
SovereignSignedViaLocation<SovereignAccountOf, RuntimeOrigin>,
// A child parachain, natively expressed, has the `Parachain` origin.
ChildParachainAsNative<parachains_origin::Origin, RuntimeOrigin>,
// The AccountId32 location type can be expressed natively as a `Signed` origin.
SignedAccountId32AsNative<KusamaNetwork, RuntimeOrigin>,
SignedAccountId32AsNative<ThisNetwork, RuntimeOrigin>,
// A system child parachain, expressed as a Superuser, converts to the `Root` origin.
ChildSystemParachainAsSuperuser<ParaId, RuntimeOrigin>,
);
parameter_types! {
/// The amount of weight an XCM operation takes. This is a safe overestimate.
pub const BaseXcmWeight: u64 = 1_000_000_000;
pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000, 64 * 1024);
/// Maximum number of instructions in a single XCM fragment. A sanity check against weight
/// calculations getting too crazy.
pub const MaxInstructions: u32 = 100;
@@ -96,18 +107,19 @@ parameter_types! {
/// individual routers.
pub type XcmRouter = (
// Only one router so far - use DMP to communicate with child parachains.
xcm_sender::ChildParachainRouter<Runtime, XcmPallet>,
xcm_sender::ChildParachainRouter<Runtime, XcmPallet, ()>,
);
parameter_types! {
pub const Kusama: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(KsmLocation::get()) });
pub const Statemine: MultiLocation = Parachain(1000).into();
pub const Encointer: MultiLocation = Parachain(1001).into();
pub const KusamaForStatemine: (MultiAssetFilter, MultiLocation) = (Kusama::get(), Statemine::get());
pub const KusamaForEncointer: (MultiAssetFilter, MultiLocation) = (Kusama::get(), Encointer::get());
pub const Ksm: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) });
pub const Statemine: MultiLocation = Parachain(1000).into_location();
pub const Encointer: MultiLocation = Parachain(1001).into_location();
pub const KsmForStatemine: (MultiAssetFilter, MultiLocation) = (Ksm::get(), Statemine::get());
pub const KsmForEncointer: (MultiAssetFilter, MultiLocation) = (Ksm::get(), Encointer::get());
pub const MaxAssetsIntoHolding: u32 = 64;
}
pub type TrustedTeleporters =
(xcm_builder::Case<KusamaForStatemine>, xcm_builder::Case<KusamaForEncointer>);
(xcm_builder::Case<KsmForStatemine>, xcm_builder::Case<KsmForEncointer>);
match_types! {
pub type OnlyParachains: impl Contains<MultiLocation> = {
@@ -119,16 +131,209 @@ match_types! {
pub type Barrier = (
// Weight that is paid for may be consumed.
TakeWeightCredit,
// If the message is one that immediately attemps to pay for execution, then allow it.
AllowTopLevelPaidExecutionFrom<Everything>,
// Messages coming from system parachains need not pay for execution.
AllowUnpaidExecutionFrom<IsChildSystemParachain<ParaId>>,
// Expected responses are OK.
AllowKnownQueryResponses<XcmPallet>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom<OnlyParachains>,
WithComputedOrigin<
(
// If the message is one that immediately attempts to pay for execution, then allow it.
AllowTopLevelPaidExecutionFrom<Everything>,
// Messages coming from system parachains need not pay for execution.
AllowExplicitUnpaidExecutionFrom<IsChildSystemParachain<ParaId>>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom<OnlyParachains>,
),
UniversalLocation,
ConstU32<8>,
>,
);
/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly
/// account for proof size weights.
///
/// Calls that are allowed through this filter must:
/// 1. Have a fixed weight;
/// 2. Cannot lead to another call being made;
/// 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters.
pub struct SafeCallFilter;
impl Contains<RuntimeCall> for SafeCallFilter {
fn contains(call: &RuntimeCall) -> bool {
#[cfg(feature = "runtime-benchmarks")]
{
if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event { .. })) {
return true
}
}
match call {
RuntimeCall::System(
frame_system::Call::kill_prefix { .. } | frame_system::Call::set_heap_pages { .. },
) |
RuntimeCall::Babe(..) |
RuntimeCall::Timestamp(..) |
RuntimeCall::Indices(..) |
RuntimeCall::Balances(..) |
RuntimeCall::Staking(
pallet_staking::Call::bond { .. } |
pallet_staking::Call::bond_extra { .. } |
pallet_staking::Call::unbond { .. } |
pallet_staking::Call::withdraw_unbonded { .. } |
pallet_staking::Call::validate { .. } |
pallet_staking::Call::nominate { .. } |
pallet_staking::Call::chill { .. } |
pallet_staking::Call::set_payee { .. } |
pallet_staking::Call::set_controller { .. } |
pallet_staking::Call::set_validator_count { .. } |
pallet_staking::Call::increase_validator_count { .. } |
pallet_staking::Call::scale_validator_count { .. } |
pallet_staking::Call::force_no_eras { .. } |
pallet_staking::Call::force_new_era { .. } |
pallet_staking::Call::set_invulnerables { .. } |
pallet_staking::Call::force_unstake { .. } |
pallet_staking::Call::force_new_era_always { .. } |
pallet_staking::Call::payout_stakers { .. } |
pallet_staking::Call::rebond { .. } |
pallet_staking::Call::reap_stash { .. } |
pallet_staking::Call::set_staking_configs { .. } |
pallet_staking::Call::chill_other { .. } |
pallet_staking::Call::force_apply_min_commission { .. },
) |
RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) |
RuntimeCall::Grandpa(..) |
RuntimeCall::ImOnline(..) |
RuntimeCall::Democracy(
pallet_democracy::Call::second { .. } |
pallet_democracy::Call::vote { .. } |
pallet_democracy::Call::emergency_cancel { .. } |
pallet_democracy::Call::fast_track { .. } |
pallet_democracy::Call::veto_external { .. } |
pallet_democracy::Call::cancel_referendum { .. } |
pallet_democracy::Call::delegate { .. } |
pallet_democracy::Call::undelegate { .. } |
pallet_democracy::Call::clear_public_proposals { .. } |
pallet_democracy::Call::unlock { .. } |
pallet_democracy::Call::remove_vote { .. } |
pallet_democracy::Call::remove_other_vote { .. } |
pallet_democracy::Call::blacklist { .. } |
pallet_democracy::Call::cancel_proposal { .. },
) |
RuntimeCall::Council(
pallet_collective::Call::vote { .. } |
pallet_collective::Call::close_old_weight { .. } |
pallet_collective::Call::disapprove_proposal { .. } |
pallet_collective::Call::close { .. },
) |
RuntimeCall::TechnicalCommittee(
pallet_collective::Call::vote { .. } |
pallet_collective::Call::close_old_weight { .. } |
pallet_collective::Call::disapprove_proposal { .. } |
pallet_collective::Call::close { .. },
) |
RuntimeCall::PhragmenElection(
pallet_elections_phragmen::Call::remove_voter { .. } |
pallet_elections_phragmen::Call::submit_candidacy { .. } |
pallet_elections_phragmen::Call::renounce_candidacy { .. } |
pallet_elections_phragmen::Call::remove_member { .. } |
pallet_elections_phragmen::Call::clean_defunct_voters { .. },
) |
RuntimeCall::TechnicalMembership(
pallet_membership::Call::add_member { .. } |
pallet_membership::Call::remove_member { .. } |
pallet_membership::Call::swap_member { .. } |
pallet_membership::Call::change_key { .. } |
pallet_membership::Call::set_prime { .. } |
pallet_membership::Call::clear_prime { .. },
) |
RuntimeCall::Treasury(..) |
RuntimeCall::ConvictionVoting(..) |
RuntimeCall::Referenda(
pallet_referenda::Call::place_decision_deposit { .. } |
pallet_referenda::Call::refund_decision_deposit { .. } |
pallet_referenda::Call::cancel { .. } |
pallet_referenda::Call::kill { .. } |
pallet_referenda::Call::nudge_referendum { .. } |
pallet_referenda::Call::one_fewer_deciding { .. },
) |
RuntimeCall::FellowshipCollective(..) |
RuntimeCall::FellowshipReferenda(
pallet_referenda::Call::place_decision_deposit { .. } |
pallet_referenda::Call::refund_decision_deposit { .. } |
pallet_referenda::Call::cancel { .. } |
pallet_referenda::Call::kill { .. } |
pallet_referenda::Call::nudge_referendum { .. } |
pallet_referenda::Call::one_fewer_deciding { .. },
) |
RuntimeCall::Claims(
super::claims::Call::claim { .. } |
super::claims::Call::mint_claim { .. } |
super::claims::Call::move_claim { .. },
) |
RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) |
RuntimeCall::Identity(
pallet_identity::Call::add_registrar { .. } |
pallet_identity::Call::set_identity { .. } |
pallet_identity::Call::clear_identity { .. } |
pallet_identity::Call::request_judgement { .. } |
pallet_identity::Call::cancel_request { .. } |
pallet_identity::Call::set_fee { .. } |
pallet_identity::Call::set_account_id { .. } |
pallet_identity::Call::set_fields { .. } |
pallet_identity::Call::provide_judgement { .. } |
pallet_identity::Call::kill_identity { .. } |
pallet_identity::Call::add_sub { .. } |
pallet_identity::Call::rename_sub { .. } |
pallet_identity::Call::remove_sub { .. } |
pallet_identity::Call::quit_sub { .. },
) |
RuntimeCall::Society(
pallet_society::Call::bid { .. } |
pallet_society::Call::unbid { .. } |
pallet_society::Call::vouch { .. } |
pallet_society::Call::unvouch { .. } |
pallet_society::Call::vote { .. } |
pallet_society::Call::defender_vote { .. } |
pallet_society::Call::payout { .. } |
pallet_society::Call::unfound { .. } |
pallet_society::Call::judge_suspended_member { .. } |
pallet_society::Call::judge_suspended_candidate { .. } |
pallet_society::Call::set_max_members { .. },
) |
RuntimeCall::Recovery(..) |
RuntimeCall::Vesting(..) |
RuntimeCall::Bounties(
pallet_bounties::Call::propose_bounty { .. } |
pallet_bounties::Call::approve_bounty { .. } |
pallet_bounties::Call::propose_curator { .. } |
pallet_bounties::Call::unassign_curator { .. } |
pallet_bounties::Call::accept_curator { .. } |
pallet_bounties::Call::award_bounty { .. } |
pallet_bounties::Call::claim_bounty { .. } |
pallet_bounties::Call::close_bounty { .. },
) |
RuntimeCall::ChildBounties(..) |
RuntimeCall::ElectionProviderMultiPhase(..) |
RuntimeCall::VoterList(..) |
RuntimeCall::NominationPools(
pallet_nomination_pools::Call::join { .. } |
pallet_nomination_pools::Call::bond_extra { .. } |
pallet_nomination_pools::Call::claim_payout { .. } |
pallet_nomination_pools::Call::unbond { .. } |
pallet_nomination_pools::Call::pool_withdraw_unbonded { .. } |
pallet_nomination_pools::Call::withdraw_unbonded { .. } |
pallet_nomination_pools::Call::create { .. } |
pallet_nomination_pools::Call::create_with_pool_id { .. } |
pallet_nomination_pools::Call::set_state { .. } |
pallet_nomination_pools::Call::set_configs { .. } |
pallet_nomination_pools::Call::update_roles { .. } |
pallet_nomination_pools::Call::chill { .. },
) |
RuntimeCall::XcmPallet(pallet_xcm::Call::limited_reserve_transfer_assets {
..
}) => true,
_ => false,
}
}
}
pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
@@ -137,7 +342,7 @@ impl xcm_executor::Config for XcmConfig {
type OriginConverter = LocalOriginConverter;
type IsReserve = ();
type IsTeleporter = TrustedTeleporters;
type LocationInverter = LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
type Weigher = WeightInfoBounds<
crate::weights::xcm::KusamaXcmWeight<RuntimeCall>,
@@ -145,11 +350,22 @@ impl xcm_executor::Config for XcmConfig {
MaxInstructions,
>;
// The weight trader piggybacks on the existing transaction-fee conversion logic.
type Trader = UsingComponents<WeightToFee, KsmLocation, AccountId, Balances, ToAuthor<Runtime>>;
type Trader =
UsingComponents<WeightToFee, TokenLocation, AccountId, Balances, ToAuthor<Runtime>>;
type ResponseHandler = XcmPallet;
type AssetTrap = XcmPallet;
type AssetLocker = ();
type AssetExchanger = ();
type AssetClaims = XcmPallet;
type SubscriptionService = XcmPallet;
type PalletInstancesInfo = AllPalletsWithSystem;
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = ();
// No bridges yet...
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = WithOriginFilter<SafeCallFilter>;
type SafeCallFilter = SafeCallFilter;
}
parameter_types! {
@@ -160,6 +376,11 @@ parameter_types! {
pub const FellowsBodyId: BodyId = BodyId::Technical;
}
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parachain(1000).into());
}
/// Type to convert the council origin to a Plurality `MultiLocation` value.
pub type CouncilToPlurality = BackingToPlurality<
RuntimeOrigin,
@@ -174,7 +395,7 @@ pub type LocalOriginToLocation = (
// `Unit` body.
CouncilToPlurality,
// And a usual Signed origin to be used in XCM as a corresponding AccountId32
SignedToAccountId32<RuntimeOrigin, AccountId, KusamaNetwork>,
SignedToAccountId32<RuntimeOrigin, AccountId, ThisNetwork>,
);
/// Type to convert the `StakingAdmin` origin to a Plurality `MultiLocation` value.
@@ -214,9 +435,17 @@ impl pallet_xcm::Config for Runtime {
// transfer.
type XcmReserveTransferFilter = Everything;
type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
type LocationInverter = LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
type Currency = Balances;
type CurrencyMatcher = ();
type TrustedLockers = ();
type SovereignAccountOf = SovereignAccountOf;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
}
@@ -227,7 +227,6 @@ ump_max_individual_weight : pre.ump_max_individual_weight,
pvf_checking_enabled : pre.pvf_checking_enabled,
pvf_voting_ttl : pre.pvf_voting_ttl,
minimum_validation_upgrade_delay : pre.minimum_validation_upgrade_delay,
}
};
@@ -318,7 +318,7 @@ fn setting_pending_config_members() {
max_upward_queue_count: 1337,
max_upward_queue_size: 228,
max_downward_message_size: 2048,
ump_service_total_weight: Weight::from_ref_time(20000),
ump_service_total_weight: Weight::from_parts(20000, 20000),
max_upward_message_size: 448,
max_upward_message_num_per_candidate: 5,
hrmp_sender_deposit: 22,
@@ -331,7 +331,7 @@ fn setting_pending_config_members() {
hrmp_max_parachain_outbound_channels: 10,
hrmp_max_parathread_outbound_channels: 20,
hrmp_max_message_num_per_candidate: 20,
ump_max_individual_weight: Weight::from_ref_time(909),
ump_max_individual_weight: Weight::from_parts(909, 909),
pvf_checking_enabled: true,
pvf_voting_ttl: 3,
minimum_validation_upgrade_delay: 20,
+1 -1
View File
@@ -973,7 +973,7 @@ impl<T: Config> Pallet<T> {
/// Handle a set of dispute statements corresponding to a single candidate.
///
/// Fails if the dispute data is invalid. Returns a boolean indicating whether the
/// Fails if the dispute data is invalid. Returns a Boolean indicating whether the
/// dispute is fresh.
fn process_checked_dispute_data(
set: CheckedDisputeStatementSet,
+30
View File
@@ -29,6 +29,8 @@ pub use pallet::*;
#[cfg(test)]
mod tests;
pub const MAX_MESSAGE_QUEUE_SIZE: usize = 1024;
/// An error sending a downward message.
#[cfg_attr(test, derive(Debug))]
pub enum QueueDownwardMessageError {
@@ -137,6 +139,28 @@ impl<T: Config> Pallet<T> {
<Self as Store>::DownwardMessageQueueHeads::remove(outgoing_para);
}
/// Determine whether enqueuing a downward message to a specific recipient para would result
/// in an error. If this returns `Ok(())` the caller can be certain that a call to
/// `queue_downward_message` with the same parameters will be successful.
pub fn can_queue_downward_message(
config: &HostConfiguration<T::BlockNumber>,
para: &ParaId,
msg: &DownwardMessage,
) -> Result<(), QueueDownwardMessageError> {
let serialized_len = msg.len() as u32;
if serialized_len > config.max_downward_message_size {
return Err(QueueDownwardMessageError::ExceedsMaxMessageSize)
}
if <Self as Store>::DownwardMessageQueues::decode_len(para).unwrap_or(0) >
MAX_MESSAGE_QUEUE_SIZE
{
return Err(QueueDownwardMessageError::ExceedsMaxMessageSize)
}
Ok(())
}
/// Enqueue a downward message to a specific recipient para.
///
/// When encoded, the message should not exceed the `config.max_downward_message_size`.
@@ -155,6 +179,12 @@ impl<T: Config> Pallet<T> {
return Err(QueueDownwardMessageError::ExceedsMaxMessageSize)
}
if <Self as Store>::DownwardMessageQueues::decode_len(para).unwrap_or(0) >
MAX_MESSAGE_QUEUE_SIZE
{
return Err(QueueDownwardMessageError::ExceedsMaxMessageSize)
}
let inbound =
InboundDownwardMessage { msg, sent_at: <frame_system::Pallet<T>>::block_number() };
+2 -2
View File
@@ -83,7 +83,7 @@ parameter_types! {
pub const BlockHashCount: u32 = 250;
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(
Weight::from_ref_time(4 * 1024 * 1024).set_proof_size(u64::MAX),
Weight::from_parts(4 * 1024 * 1024, u64::MAX),
);
}
@@ -394,7 +394,7 @@ impl UmpSink for TestUmpSink {
max_weight: Weight,
) -> Result<Weight, (MessageId, Weight)> {
let weight = match u32::decode(&mut &actual_msg[..]) {
Ok(w) => Weight::from_ref_time(w as u64),
Ok(w) => Weight::from_parts(w as u64, w as u64),
Err(_) => return Ok(Weight::zero()), // same as the real `UmpSink`
};
if weight.any_gt(max_weight) {
+2 -2
View File
@@ -159,7 +159,7 @@ pub struct ReplacementTimes<N> {
#[cfg_attr(test, derive(Debug, Clone, PartialEq))]
pub struct ParaPastCodeMeta<N> {
/// Block numbers where the code was expected to be replaced and where the code
/// was actually replaced, respectively. The first is used to do accurate lookups
/// was actually replaced, respectively. The first is used to do accurate look-ups
/// of historic code in historic contexts, whereas the second is used to do
/// pruning on an accurate timeframe. These can be used as indices
/// into the `PastCodeHash` map along with the `ParaId` to fetch the code itself.
@@ -520,7 +520,7 @@ impl WeightInfo for TestWeightInfo {
}
fn include_pvf_check_statement() -> Weight {
// This special value is to distinguish from the finalizing variants above in tests.
Weight::MAX - Weight::from_ref_time(1)
Weight::MAX - Weight::from_parts(1, 1)
}
}
+20 -8
View File
@@ -31,9 +31,12 @@ pub use pallet::*;
/// This is used for benchmarking sanely bounding relevant storate items. It is expected from the `configurations`
/// pallet to check these values before setting.
pub const MAX_UPWARD_MESSAGE_SIZE_BOUND: u32 = 50 * 1024;
/// Maximum amount of overweight messages that can exist in the queue at any given time.
pub const MAX_OVERWEIGHT_MESSAGES: u32 = 1000;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod migration;
#[cfg(test)]
pub(crate) mod tests;
@@ -133,13 +136,11 @@ impl<XcmExecutor: xcm::latest::ExecuteXcm<C::RuntimeCall>, C: Config> UmpSink
},
Ok((Ok(xcm_message), weight_used)) => {
let xcm_junction = Junction::Parachain(origin.into());
let outcome =
XcmExecutor::execute_xcm(xcm_junction, xcm_message, max_weight.ref_time());
let outcome = XcmExecutor::execute_xcm(xcm_junction, xcm_message, id, max_weight);
match outcome {
Outcome::Error(XcmError::WeightLimitReached(required)) =>
Err((id, Weight::from_ref_time(required))),
Outcome::Error(XcmError::WeightLimitReached(required)) => Err((id, required)),
outcome => {
let outcome_weight = Weight::from_ref_time(outcome.weight_used());
let outcome_weight = outcome.weight_used();
Pallet::<C>::deposit_event(Event::ExecutedUpward(id, outcome));
Ok(weight_used.saturating_add(outcome_weight))
},
@@ -215,6 +216,7 @@ pub mod pallet {
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::without_storage_info]
#[pallet::storage_version(migration::STORAGE_VERSION)]
pub struct Pallet<T>(_);
#[pallet::config]
@@ -328,7 +330,7 @@ pub mod pallet {
/// These messages stay there until manually dispatched.
#[pallet::storage]
pub type Overweight<T: Config> =
StorageMap<_, Twox64Concat, OverweightIndex, (ParaId, Vec<u8>), OptionQuery>;
CountedStorageMap<_, Twox64Concat, OverweightIndex, (ParaId, Vec<u8>), OptionQuery>;
/// The number of overweight messages ever recorded in `Overweight` (and thus the lowest free
/// index).
@@ -508,6 +510,8 @@ impl<T: Config> Pallet<T> {
/// Devote some time into dispatching pending upward messages.
pub(crate) fn process_pending_upward_messages() -> Weight {
const MAX_MESSAGES_PER_BLOCK: u8 = 10;
let mut messages_processed = 0;
let mut weight_used = Weight::zero();
let config = <configuration::Pallet<T>>::config();
@@ -515,7 +519,12 @@ impl<T: Config> Pallet<T> {
let mut queue_cache = QueueCache::new();
while let Some(dispatchee) = cursor.peek() {
if weight_used.any_gte(config.ump_service_total_weight) {
if weight_used.any_gte(config.ump_service_total_weight) ||
messages_processed >= MAX_MESSAGES_PER_BLOCK
{
// Temporarily allow for processing of a max of 10 messages per block, until we
// properly account for proof size weights.
//
// Then check whether we've reached or overshoot the
// preferred weight for the dispatching stage.
//
@@ -537,13 +546,16 @@ impl<T: Config> Pallet<T> {
// our remaining weight limit, then consume it.
let maybe_next = queue_cache.peek_front::<T>(dispatchee);
if let Some(upward_message) = maybe_next {
messages_processed += 1;
match T::UmpSink::process_upward_message(dispatchee, upward_message, max_weight) {
Ok(used) => {
weight_used += used;
let _ = queue_cache.consume_front::<T>(dispatchee);
},
Err((id, required)) => {
if required.any_gt(config.ump_max_individual_weight) {
let is_under_limit = Overweight::<T>::count() < MAX_OVERWEIGHT_MESSAGES;
weight_used.saturating_accrue(T::DbWeight::get().reads(1));
if required.any_gt(config.ump_max_individual_weight) && is_under_limit {
// overweight - add to overweight queue and continue with message
// execution consuming the message.
let upward_message = queue_cache.consume_front::<T>(dispatchee).expect(
@@ -42,8 +42,8 @@ fn queue_upward_msg<T: Config>(
fn create_message_min_size<T: Config>(size: u32) -> Vec<u8> {
// Create a message with an empty remark call to determine the encoding overhead
let msg_size_empty_transact = VersionedXcm::<T>::from(Xcm::<T>(vec![Transact {
origin_type: OriginKind::SovereignAccount,
require_weight_at_most: Weight::MAX.ref_time(),
origin_kind: OriginKind::SovereignAccount,
require_weight_at_most: Weight::MAX,
call: frame_system::Call::<T>::remark_with_event { remark: vec![] }.encode().into(),
}]))
.encode()
@@ -54,8 +54,8 @@ fn create_message_min_size<T: Config>(size: u32) -> Vec<u8> {
let mut remark = Vec::new();
remark.resize(size, 0u8);
let msg = VersionedXcm::<T>::from(Xcm::<T>(vec![Transact {
origin_type: OriginKind::SovereignAccount,
require_weight_at_most: Weight::MAX.ref_time(),
origin_kind: OriginKind::SovereignAccount,
require_weight_at_most: Weight::MAX,
call: frame_system::Call::<T>::remark_with_event { remark }.encode().into(),
}]))
.encode();
@@ -65,12 +65,11 @@ fn create_message_min_size<T: Config>(size: u32) -> Vec<u8> {
}
fn create_message_overweight<T: Config>() -> Vec<u8> {
let max_block_weight = T::BlockWeights::get().max_block;
// We use a `set_code` Call because it
let call = frame_system::Call::<T>::set_code { code: vec![] };
VersionedXcm::<T>::from(Xcm::<T>(vec![Transact {
origin_type: OriginKind::Superuser,
require_weight_at_most: max_block_weight.ref_time(),
origin_kind: OriginKind::Superuser,
require_weight_at_most: Weight::MAX / 2,
call: call.encode().into(),
}]))
.encode()
@@ -107,7 +106,6 @@ frame_benchmarking::benchmarks! {
service_overweight {
let host_conf = configuration::ActiveConfig::<T>::get();
let weight = host_conf.ump_max_individual_weight + host_conf.ump_max_individual_weight + Weight::from_ref_time(1000000);
let para = ParaId::from(1978);
// The message's weight does not really matter here, as we add service_overweight's
// max_weight parameter to the extrinsic's weight in the weight calculation.
@@ -117,7 +115,7 @@ frame_benchmarking::benchmarks! {
let msg = create_message_overweight::<T>();
// This just makes sure that 0 is not a valid index and we can use it later on.
let _ = Ump::<T>::service_overweight(RawOrigin::Root.into(), 0, Weight::from_ref_time(1000).set_proof_size(u64::MAX));
let _ = Ump::<T>::service_overweight(RawOrigin::Root.into(), 0, Weight::from_parts(1000, 1000));
// Start with the block number 1. This is needed because should an event be
// emitted during the genesis block they will be implicitly wiped.
frame_system::Pallet::<T>::set_block_number(1u32.into());
@@ -0,0 +1,49 @@
// Copyright 2022 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/>.
use crate::ump::{Config, Overweight, Pallet};
use frame_support::{
pallet_prelude::*,
traits::{OnRuntimeUpgrade, StorageVersion},
weights::Weight,
};
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
pub mod v1 {
use super::*;
pub struct MigrateToV1<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
fn on_runtime_upgrade() -> Weight {
if StorageVersion::get::<Pallet<T>>() == 0 {
let mut weight = T::DbWeight::get().reads(1);
let overweight_messages = Overweight::<T>::initialize_counter() as u64;
log::info!("Initialized Overweight to {}", overweight_messages);
weight.saturating_accrue(T::DbWeight::get().reads_writes(overweight_messages, 1));
StorageVersion::new(1).put::<Pallet<T>>();
weight.saturating_add(T::DbWeight::get().writes(1))
} else {
log::warn!("skipping v1, should be removed");
T::DbWeight::get().reads(1)
}
}
}
}
+15 -15
View File
@@ -34,12 +34,12 @@ pub(super) struct GenesisConfigBuilder {
impl Default for GenesisConfigBuilder {
fn default() -> Self {
Self {
max_upward_message_size: 16,
max_upward_message_size: 32,
max_upward_message_num_per_candidate: 2,
max_upward_queue_count: 4,
max_upward_queue_size: 64,
ump_service_total_weight: Weight::from_ref_time(1000).set_proof_size(1000),
ump_max_individual_weight: Weight::from_ref_time(100).set_proof_size(100),
ump_service_total_weight: Weight::from_parts(1000, 1000),
ump_max_individual_weight: Weight::from_parts(100, 100),
}
}
}
@@ -156,7 +156,7 @@ fn dispatch_resume_after_exceeding_dispatch_stage_weight() {
new_test_ext(
GenesisConfigBuilder {
ump_service_total_weight: Weight::from_ref_time(500).set_proof_size(500),
ump_service_total_weight: Weight::from_parts(500, 500),
..Default::default()
}
.build(),
@@ -203,8 +203,8 @@ fn dispatch_keeps_message_after_weight_exhausted() {
new_test_ext(
GenesisConfigBuilder {
ump_service_total_weight: Weight::from_ref_time(500).set_proof_size(500),
ump_max_individual_weight: Weight::from_ref_time(300).set_proof_size(300),
ump_service_total_weight: Weight::from_parts(500, 500),
ump_max_individual_weight: Weight::from_parts(300, 300),
..Default::default()
}
.build(),
@@ -243,7 +243,7 @@ fn dispatch_correctly_handle_remove_of_latest() {
new_test_ext(
GenesisConfigBuilder {
ump_service_total_weight: Weight::from_ref_time(900).set_proof_size(900),
ump_service_total_weight: Weight::from_parts(900, 900),
..Default::default()
}
.build(),
@@ -296,7 +296,7 @@ fn service_overweight_unknown() {
// the next test.
new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| {
assert_noop!(
Ump::service_overweight(RuntimeOrigin::root(), 0, Weight::from_ref_time(1000)),
Ump::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(1000, 1000)),
Error::<Test>::UnknownMessageIndex
);
});
@@ -312,8 +312,8 @@ fn overweight_queue_works() {
new_test_ext(
GenesisConfigBuilder {
ump_service_total_weight: Weight::from_ref_time(900).set_proof_size(900),
ump_max_individual_weight: Weight::from_ref_time(300).set_proof_size(300),
ump_service_total_weight: Weight::from_parts(900, 900),
ump_max_individual_weight: Weight::from_parts(300, 300),
..Default::default()
}
.build(),
@@ -338,7 +338,7 @@ fn overweight_queue_works() {
para_a,
upward_message_id(&a_msg_3[..]),
0,
Weight::from_ref_time(500),
Weight::from_parts(500, 500),
)
.into(),
);
@@ -346,18 +346,18 @@ fn overweight_queue_works() {
// Now verify that if we wanted to service this overweight message with less than enough
// weight it will fail.
assert_noop!(
Ump::service_overweight(RuntimeOrigin::root(), 0, Weight::from_ref_time(499)),
Ump::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(499, 499)),
Error::<Test>::WeightOverLimit
);
// ... and if we try to service it with just enough weight it will succeed as well.
assert_ok!(Ump::service_overweight(RuntimeOrigin::root(), 0, Weight::from_ref_time(500)));
assert_last_event(Event::OverweightServiced(0, Weight::from_ref_time(500)).into());
assert_ok!(Ump::service_overweight(RuntimeOrigin::root(), 0, Weight::from_parts(500, 500)));
assert_last_event(Event::OverweightServiced(0, Weight::from_parts(500, 500)).into());
// ... and if we try to service a message with index that doesn't exist it will error
// out.
assert_noop!(
Ump::service_overweight(RuntimeOrigin::root(), 1, Weight::from_ref_time(1000)),
Ump::service_overweight(RuntimeOrigin::root(), 1, Weight::from_parts(1000, 1000)),
Error::<Test>::UnknownMessageIndex
);
});
+6 -1
View File
@@ -1303,7 +1303,7 @@ impl parachains_ump::Config for Runtime {
crate::parachains_ump::XcmSink<xcm_executor::XcmExecutor<xcm_config::XcmConfig>, Runtime>;
type FirstMessageFactorPercent = FirstMessageFactorPercent;
type ExecuteOverweightOrigin = EnsureRoot<AccountId>;
type WeightInfo = parachains_ump::TestWeightInfo;
type WeightInfo = weights::runtime_parachains_ump::WeightInfo<Self>;
}
impl parachains_dmp::Config for Runtime {}
@@ -1609,6 +1609,9 @@ pub type Migrations = (
pallet_staking::migrations::v13::MigrateToV13<Runtime>,
parachains_disputes::migration::v1::MigrateToV1<Runtime>,
parachains_configuration::migration::v4::MigrateToV4<Runtime>,
// "Use 2D weights in XCM v3" <https://github.com/paritytech/polkadot/pull/6134>
pallet_xcm::migration::v1::MigrateToV1<Runtime>,
parachains_ump::migration::v1::MigrateToV1<Runtime>,
);
/// Unchecked extrinsic type as expected by this runtime.
@@ -1680,6 +1683,8 @@ mod benches {
[pallet_treasury, Treasury]
[pallet_utility, Utility]
[pallet_vesting, Vesting]
// XCM
[pallet_xcm, XcmPallet]
);
}
@@ -43,6 +43,7 @@ pub mod pallet_tips;
pub mod pallet_treasury;
pub mod pallet_utility;
pub mod pallet_vesting;
pub mod pallet_xcm;
pub mod runtime_common_auctions;
pub mod runtime_common_claims;
pub mod runtime_common_crowdloan;
@@ -54,3 +55,4 @@ pub mod runtime_parachains_hrmp;
pub mod runtime_parachains_initializer;
pub mod runtime_parachains_paras;
pub mod runtime_parachains_paras_inherent;
pub mod runtime_parachains_ump;
@@ -0,0 +1,174 @@
// Copyright 2017-2022 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/>.
//! Autogenerated weights for `pallet_xcm`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-12-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("polkadot-dev"), DB CACHE: 1024
// Executed Command:
// /home/benchbot/cargo_target_dir/production/polkadot
// benchmark
// pallet
// --steps=50
// --repeat=20
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/polkadot/.git/.artifacts/bench.json
// --pallet=pallet_xcm
// --chain=polkadot-dev
// --header=./file_header.txt
// --output=./runtime/polkadot/src/weights/
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
use frame_support::{traits::Get, weights::Weight};
use sp_std::marker::PhantomData;
/// Weight functions for `pallet_xcm`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn send() -> Weight {
// Minimum execution time: 35_717 nanoseconds.
Weight::from_ref_time(36_278_000)
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(3))
}
fn teleport_assets() -> Weight {
// Minimum execution time: 28_865 nanoseconds.
Weight::from_ref_time(29_336_000)
}
fn reserve_transfer_assets() -> Weight {
// Minimum execution time: 27_531 nanoseconds.
Weight::from_ref_time(28_248_000)
}
// Storage: Benchmark Override (r:0 w:0)
fn execute() -> Weight {
// Minimum execution time: 18_446_744_073_709_551 nanoseconds.
Weight::from_ref_time(18_446_744_073_709_551_000)
}
// Storage: XcmPallet SupportedVersion (r:0 w:1)
fn force_xcm_version() -> Weight {
// Minimum execution time: 15_205 nanoseconds.
Weight::from_ref_time(15_526_000)
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: XcmPallet SafeXcmVersion (r:0 w:1)
fn force_default_xcm_version() -> Weight {
// Minimum execution time: 4_336 nanoseconds.
Weight::from_ref_time(4_518_000)
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: XcmPallet VersionNotifiers (r:1 w:1)
// Storage: XcmPallet QueryCounter (r:1 w:1)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: XcmPallet Queries (r:0 w:1)
fn force_subscribe_version_notify() -> Weight {
// Minimum execution time: 41_446 nanoseconds.
Weight::from_ref_time(42_152_000)
.saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(6))
}
// Storage: XcmPallet VersionNotifiers (r:1 w:1)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: XcmPallet Queries (r:0 w:1)
fn force_unsubscribe_version_notify() -> Weight {
// Minimum execution time: 44_944 nanoseconds.
Weight::from_ref_time(45_519_000)
.saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(5))
}
// Storage: XcmPallet SupportedVersion (r:4 w:2)
fn migrate_supported_version() -> Weight {
// Minimum execution time: 15_254 nanoseconds.
Weight::from_ref_time(15_491_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifiers (r:4 w:2)
fn migrate_version_notifiers() -> Weight {
// Minimum execution time: 15_083 nanoseconds.
Weight::from_ref_time(15_298_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifyTargets (r:5 w:0)
fn already_notified_target() -> Weight {
// Minimum execution time: 17_889 nanoseconds.
Weight::from_ref_time(18_144_000)
.saturating_add(T::DbWeight::get().reads(5))
}
// Storage: XcmPallet VersionNotifyTargets (r:2 w:1)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn notify_current_targets() -> Weight {
// Minimum execution time: 37_255 nanoseconds.
Weight::from_ref_time(37_893_000)
.saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(4))
}
// Storage: XcmPallet VersionNotifyTargets (r:3 w:0)
fn notify_target_migration_fail() -> Weight {
// Minimum execution time: 7_884 nanoseconds.
Weight::from_ref_time(8_111_000)
.saturating_add(T::DbWeight::get().reads(3))
}
// Storage: XcmPallet VersionNotifyTargets (r:4 w:2)
fn migrate_version_notify_targets() -> Weight {
// Minimum execution time: 15_853 nanoseconds.
Weight::from_ref_time(16_220_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifyTargets (r:4 w:2)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn migrate_and_notify_old_targets() -> Weight {
// Minimum execution time: 43_836 nanoseconds.
Weight::from_ref_time(44_836_000)
.saturating_add(T::DbWeight::get().reads(10))
.saturating_add(T::DbWeight::get().writes(5))
}
}
@@ -0,0 +1,74 @@
// Copyright 2017-2022 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/>.
//! Autogenerated weights for `runtime_parachains::ump`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-01-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("polkadot-dev"), DB CACHE: 1024
// Executed Command:
// /home/benchbot/cargo_target_dir/production/polkadot
// benchmark
// pallet
// --steps=50
// --repeat=20
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/polkadot/.git/.artifacts/bench.json
// --pallet=runtime_parachains::ump
// --chain=polkadot-dev
// --header=./file_header.txt
// --output=./runtime/polkadot/src/weights/
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
use frame_support::{traits::Get, weights::Weight};
use sp_std::marker::PhantomData;
/// Weight functions for `runtime_parachains::ump`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> runtime_parachains::ump::WeightInfo for WeightInfo<T> {
/// The range of component `s` is `[0, 51200]`.
fn process_upward_message(s: u32, ) -> Weight {
// Minimum execution time: 10_291 nanoseconds.
Weight::from_ref_time(4_272_368)
// Standard Error: 12
.saturating_add(Weight::from_ref_time(1_872).saturating_mul(s.into()))
}
// Storage: Ump NeedsDispatch (r:1 w:1)
// Storage: Ump NextDispatchRoundStartWith (r:1 w:1)
// Storage: Ump RelayDispatchQueues (r:0 w:1)
// Storage: Ump RelayDispatchQueueSize (r:0 w:1)
fn clean_ump_after_outgoing() -> Weight {
// Minimum execution time: 9_837 nanoseconds.
Weight::from_ref_time(9_951_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(4))
}
// Storage: Ump Overweight (r:1 w:1)
// Storage: Ump CounterForOverweight (r:1 w:1)
fn service_overweight() -> Weight {
// Minimum execution time: 29_540 nanoseconds.
Weight::from_ref_time(29_889_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
}
+222 -34
View File
@@ -17,35 +17,39 @@
//! XCM configuration for Polkadot.
use super::{
parachains_origin, AccountId, Balances, CouncilCollective, ParaId, Runtime, RuntimeCall,
RuntimeEvent, RuntimeOrigin, WeightToFee, XcmPallet,
parachains_origin, AccountId, AllPalletsWithSystem, Balances, CouncilCollective, ParaId,
Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmPallet,
};
use frame_support::{
match_types, parameter_types,
traits::{Everything, Nothing},
traits::{Contains, Everything, Nothing},
weights::Weight,
};
use runtime_common::{xcm_sender, ToAuthor};
use sp_core::ConstU32;
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, BackingToPlurality, ChildParachainAsNative,
ChildParachainConvertsVia, CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds,
IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, UsingComponents,
IsConcrete, MintLocation, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WithComputedOrigin,
};
use xcm_executor::traits::WithOriginFilter;
parameter_types! {
/// The location of the DOT token, from the context of this chain. Since this token is native to this
/// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to
/// the context".
pub const DotLocation: MultiLocation = Here.into();
pub const TokenLocation: MultiLocation = Here.into_location();
/// The Polkadot network ID. This is named.
pub const PolkadotNetwork: NetworkId = NetworkId::Polkadot;
/// Our XCM location ancestry - i.e. what, if anything, `Parent` means evaluated in our context. Since
/// Polkadot is a top-level relay-chain, there is no ancestry.
pub const Ancestry: MultiLocation = Here.into();
/// The check account, which holds any native assets that have been teleported out and not back in (yet).
pub const ThisNetwork: NetworkId = NetworkId::Polkadot;
/// Our location in the universe of consensus systems.
pub const UniversalLocation: InteriorMultiLocation = X1(GlobalConsensus(ThisNetwork::get()));
/// The Checking Account, which holds any native assets that have been teleported out and not back in (yet).
pub CheckAccount: AccountId = XcmPallet::check_account();
/// The Checking Account along with the indication that the local chain is able to mint tokens.
pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local);
}
/// The canonical means of converting a `MultiLocation` into an `AccountId`, used when we want to determine
@@ -54,24 +58,24 @@ pub type SovereignAccountOf = (
// We can convert a child parachain using the standard `AccountId` conversion.
ChildParachainConvertsVia<ParaId, AccountId>,
// We can directly alias an `AccountId32` into a local account.
AccountId32Aliases<PolkadotNetwork, AccountId>,
AccountId32Aliases<ThisNetwork, AccountId>,
);
/// Our asset transactor. This is what allows us to interact with the runtime assets from the point of
/// view of XCM-only concepts like `MultiLocation` and `MultiAsset`.
///
/// Ours is only aware of the Balances pallet, which is mapped to `DotLocation`.
/// Ours is only aware of the Balances pallet, which is mapped to `TokenLocation`.
pub type LocalAssetTransactor = XcmCurrencyAdapter<
// Use this currency:
Balances,
// Use this currency when it is a fungible asset matching the given location or name:
IsConcrete<DotLocation>,
IsConcrete<TokenLocation>,
// We can convert the MultiLocations with our converter above:
SovereignAccountOf,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
AccountId,
// We track our teleports in/out to keep total issuance correct.
CheckAccount,
LocalCheckAccount,
>;
/// The means that we convert an XCM origin `MultiLocation` into the runtime's `Origin` type for
@@ -86,12 +90,12 @@ type LocalOriginConverter = (
ChildParachainAsNative<parachains_origin::Origin, RuntimeOrigin>,
// If the origin kind is `Native` and the XCM origin is the `AccountId32` location, then it can
// be expressed using the `Signed` origin variant.
SignedAccountId32AsNative<PolkadotNetwork, RuntimeOrigin>,
SignedAccountId32AsNative<ThisNetwork, RuntimeOrigin>,
);
parameter_types! {
/// The amount of weight an XCM operation takes. This is a safe overestimate.
pub const BaseXcmWeight: u64 = 1_000_000_000;
pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000, 64 * 1024);
/// Maximum number of instructions in a single XCM fragment. A sanity check against weight
/// calculations getting too crazy.
pub const MaxInstructions: u32 = 100;
@@ -101,18 +105,19 @@ parameter_types! {
/// individual routers.
pub type XcmRouter = (
// Only one router so far - use DMP to communicate with child parachains.
xcm_sender::ChildParachainRouter<Runtime, XcmPallet>,
xcm_sender::ChildParachainRouter<Runtime, XcmPallet, ()>,
);
parameter_types! {
pub const Polkadot: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(DotLocation::get()) });
pub const PolkadotForStatemint: (MultiAssetFilter, MultiLocation) = (Polkadot::get(), Parachain(1000).into());
pub const PolkadotForCollectives: (MultiAssetFilter, MultiLocation) = (Polkadot::get(), Parachain(1001).into());
pub const Dot: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) });
pub const DotForStatemint: (MultiAssetFilter, MultiLocation) = (Dot::get(), Parachain(1000).into_location());
pub const DotForCollectives: (MultiAssetFilter, MultiLocation) = (Dot::get(), Parachain(1001).into_location());
pub const MaxAssetsIntoHolding: u32 = 64;
}
/// Polkadot Relay recognizes/respects System parachains as teleporters.
/// Polkadot Relay recognizes/respects the Statemint chain as a teleporter.
pub type TrustedTeleporters =
(xcm_builder::Case<PolkadotForStatemint>, xcm_builder::Case<PolkadotForCollectives>);
(xcm_builder::Case<DotForStatemint>, xcm_builder::Case<DotForCollectives>);
match_types! {
pub type OnlyParachains: impl Contains<MultiLocation> = {
@@ -124,14 +129,175 @@ match_types! {
pub type Barrier = (
// Weight that is paid for may be consumed.
TakeWeightCredit,
// If the message is one that immediately attemps to pay for execution, then allow it.
AllowTopLevelPaidExecutionFrom<Everything>,
// Expected responses are OK.
AllowKnownQueryResponses<XcmPallet>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom<OnlyParachains>,
WithComputedOrigin<
(
// If the message is one that immediately attemps to pay for execution, then allow it.
AllowTopLevelPaidExecutionFrom<Everything>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom<OnlyParachains>,
),
UniversalLocation,
ConstU32<8>,
>,
);
/// A call filter for the XCM Transact instruction. This is a temporary measure until we
/// properly account for proof size weights.
///
/// Calls that are allowed through this filter must:
/// 1. Have a fixed weight;
/// 2. Cannot lead to another call being made;
/// 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters.
pub struct SafeCallFilter;
impl Contains<RuntimeCall> for SafeCallFilter {
fn contains(call: &RuntimeCall) -> bool {
#[cfg(feature = "runtime-benchmarks")]
{
if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event { .. })) {
return true
}
}
match call {
RuntimeCall::System(
frame_system::Call::kill_prefix { .. } | frame_system::Call::set_heap_pages { .. },
) |
RuntimeCall::Babe(..) |
RuntimeCall::Timestamp(..) |
RuntimeCall::Indices(..) |
RuntimeCall::Balances(..) |
RuntimeCall::Staking(
pallet_staking::Call::bond { .. } |
pallet_staking::Call::bond_extra { .. } |
pallet_staking::Call::unbond { .. } |
pallet_staking::Call::withdraw_unbonded { .. } |
pallet_staking::Call::validate { .. } |
pallet_staking::Call::nominate { .. } |
pallet_staking::Call::chill { .. } |
pallet_staking::Call::set_payee { .. } |
pallet_staking::Call::set_controller { .. } |
pallet_staking::Call::set_validator_count { .. } |
pallet_staking::Call::increase_validator_count { .. } |
pallet_staking::Call::scale_validator_count { .. } |
pallet_staking::Call::force_no_eras { .. } |
pallet_staking::Call::force_new_era { .. } |
pallet_staking::Call::set_invulnerables { .. } |
pallet_staking::Call::force_unstake { .. } |
pallet_staking::Call::force_new_era_always { .. } |
pallet_staking::Call::payout_stakers { .. } |
pallet_staking::Call::rebond { .. } |
pallet_staking::Call::reap_stash { .. } |
pallet_staking::Call::set_staking_configs { .. } |
pallet_staking::Call::chill_other { .. } |
pallet_staking::Call::force_apply_min_commission { .. },
) |
RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) |
RuntimeCall::Grandpa(..) |
RuntimeCall::ImOnline(..) |
RuntimeCall::Democracy(
pallet_democracy::Call::second { .. } |
pallet_democracy::Call::vote { .. } |
pallet_democracy::Call::emergency_cancel { .. } |
pallet_democracy::Call::fast_track { .. } |
pallet_democracy::Call::veto_external { .. } |
pallet_democracy::Call::cancel_referendum { .. } |
pallet_democracy::Call::delegate { .. } |
pallet_democracy::Call::undelegate { .. } |
pallet_democracy::Call::clear_public_proposals { .. } |
pallet_democracy::Call::unlock { .. } |
pallet_democracy::Call::remove_vote { .. } |
pallet_democracy::Call::remove_other_vote { .. } |
pallet_democracy::Call::blacklist { .. } |
pallet_democracy::Call::cancel_proposal { .. },
) |
RuntimeCall::Council(
pallet_collective::Call::vote { .. } |
pallet_collective::Call::close_old_weight { .. } |
pallet_collective::Call::disapprove_proposal { .. } |
pallet_collective::Call::close { .. },
) |
RuntimeCall::TechnicalCommittee(
pallet_collective::Call::vote { .. } |
pallet_collective::Call::close_old_weight { .. } |
pallet_collective::Call::disapprove_proposal { .. } |
pallet_collective::Call::close { .. },
) |
RuntimeCall::PhragmenElection(
pallet_elections_phragmen::Call::remove_voter { .. } |
pallet_elections_phragmen::Call::submit_candidacy { .. } |
pallet_elections_phragmen::Call::renounce_candidacy { .. } |
pallet_elections_phragmen::Call::remove_member { .. } |
pallet_elections_phragmen::Call::clean_defunct_voters { .. },
) |
RuntimeCall::TechnicalMembership(
pallet_membership::Call::add_member { .. } |
pallet_membership::Call::remove_member { .. } |
pallet_membership::Call::swap_member { .. } |
pallet_membership::Call::change_key { .. } |
pallet_membership::Call::set_prime { .. } |
pallet_membership::Call::clear_prime { .. },
) |
RuntimeCall::Treasury(..) |
RuntimeCall::Claims(
super::claims::Call::claim { .. } |
super::claims::Call::mint_claim { .. } |
super::claims::Call::move_claim { .. },
) |
RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) |
RuntimeCall::Identity(
pallet_identity::Call::add_registrar { .. } |
pallet_identity::Call::set_identity { .. } |
pallet_identity::Call::clear_identity { .. } |
pallet_identity::Call::request_judgement { .. } |
pallet_identity::Call::cancel_request { .. } |
pallet_identity::Call::set_fee { .. } |
pallet_identity::Call::set_account_id { .. } |
pallet_identity::Call::set_fields { .. } |
pallet_identity::Call::provide_judgement { .. } |
pallet_identity::Call::kill_identity { .. } |
pallet_identity::Call::add_sub { .. } |
pallet_identity::Call::rename_sub { .. } |
pallet_identity::Call::remove_sub { .. } |
pallet_identity::Call::quit_sub { .. },
) |
RuntimeCall::Vesting(..) |
RuntimeCall::Bounties(
pallet_bounties::Call::propose_bounty { .. } |
pallet_bounties::Call::approve_bounty { .. } |
pallet_bounties::Call::propose_curator { .. } |
pallet_bounties::Call::unassign_curator { .. } |
pallet_bounties::Call::accept_curator { .. } |
pallet_bounties::Call::award_bounty { .. } |
pallet_bounties::Call::claim_bounty { .. } |
pallet_bounties::Call::close_bounty { .. },
) |
RuntimeCall::ChildBounties(..) |
RuntimeCall::ElectionProviderMultiPhase(..) |
RuntimeCall::VoterList(..) |
RuntimeCall::NominationPools(
pallet_nomination_pools::Call::join { .. } |
pallet_nomination_pools::Call::bond_extra { .. } |
pallet_nomination_pools::Call::claim_payout { .. } |
pallet_nomination_pools::Call::unbond { .. } |
pallet_nomination_pools::Call::pool_withdraw_unbonded { .. } |
pallet_nomination_pools::Call::withdraw_unbonded { .. } |
pallet_nomination_pools::Call::create { .. } |
pallet_nomination_pools::Call::create_with_pool_id { .. } |
pallet_nomination_pools::Call::set_state { .. } |
pallet_nomination_pools::Call::set_configs { .. } |
pallet_nomination_pools::Call::update_roles { .. } |
pallet_nomination_pools::Call::chill { .. },
) |
RuntimeCall::XcmPallet(pallet_xcm::Call::limited_reserve_transfer_assets {
..
}) => true,
_ => false,
}
}
}
pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
@@ -141,21 +307,35 @@ impl xcm_executor::Config for XcmConfig {
// Polkadot Relay recognises no chains which act as reserves.
type IsReserve = ();
type IsTeleporter = TrustedTeleporters;
type LocationInverter = LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
// The weight trader piggybacks on the existing transaction-fee conversion logic.
type Trader = UsingComponents<WeightToFee, DotLocation, AccountId, Balances, ToAuthor<Runtime>>;
type Trader =
UsingComponents<WeightToFee, TokenLocation, AccountId, Balances, ToAuthor<Runtime>>;
type ResponseHandler = XcmPallet;
type AssetTrap = XcmPallet;
type AssetLocker = ();
type AssetExchanger = ();
type AssetClaims = XcmPallet;
type SubscriptionService = XcmPallet;
type PalletInstancesInfo = AllPalletsWithSystem;
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = ();
// No bridges yet...
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = WithOriginFilter<SafeCallFilter>;
type SafeCallFilter = SafeCallFilter;
}
parameter_types! {
pub const CouncilBodyId: BodyId = BodyId::Executive;
// We are conservative with the XCM version we advertize.
pub const AdvertisedXcmVersion: u32 = 2;
}
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parachain(1000).into());
}
/// Type to convert a council origin to a Plurality `MultiLocation` value.
@@ -172,7 +352,7 @@ pub type LocalOriginToLocation = (
// `Unit` body.
CouncilToPlurality,
// And a usual Signed origin to be used in XCM as a corresponding AccountId32
SignedToAccountId32<RuntimeOrigin, AccountId, PolkadotNetwork>,
SignedToAccountId32<RuntimeOrigin, AccountId, ThisNetwork>,
);
impl pallet_xcm::Config for Runtime {
@@ -188,9 +368,17 @@ impl pallet_xcm::Config for Runtime {
type XcmTeleportFilter = Everything; // == Allow All
type XcmReserveTransferFilter = Everything; // == Allow All
type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
type LocationInverter = LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = AdvertisedXcmVersion;
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
type Currency = Balances;
type CurrencyMatcher = ();
type TrustedLockers = ();
type SovereignAccountOf = SovereignAccountOf;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
}
+32 -12
View File
@@ -1488,6 +1488,9 @@ pub type Migrations = (
pallet_scheduler::migration::v4::CleanupAgendas<Runtime>,
parachains_disputes::migration::v1::MigrateToV1<Runtime>,
parachains_configuration::migration::v4::MigrateToV4<Runtime>,
// "Use 2D weights in XCM v3" <https://github.com/paritytech/polkadot/pull/6134>
pallet_xcm::migration::v1::MigrateToV1<Runtime>,
parachains_ump::migration::v1::MigrateToV1<Runtime>,
);
/// Executive: handles dispatch to the various modules.
@@ -1576,6 +1579,7 @@ mod benches {
[pallet_utility, Utility]
[pallet_vesting, Vesting]
// XCM
[pallet_xcm, XcmPallet]
[pallet_xcm_benchmarks::fungible, pallet_xcm_benchmarks::fungible::Pallet::<Runtime>]
[pallet_xcm_benchmarks::generic, pallet_xcm_benchmarks::generic::Pallet::<Runtime>]
);
@@ -1979,20 +1983,22 @@ sp_api::impl_runtime_apis! {
use frame_system_benchmarking::Pallet as SystemBench;
use frame_benchmarking::baseline::Pallet as Baseline;
use xcm::latest::prelude::*;
use xcm_config::{CheckAccount, RocLocation, SovereignAccountOf, Statemine, XcmConfig};
use xcm_config::{
LocalCheckAccount, LocationConverter, Statemine, TokenLocation, XcmConfig,
};
impl frame_system_benchmarking::Config for Runtime {}
impl frame_benchmarking::baseline::Config for Runtime {}
impl pallet_xcm_benchmarks::Config for Runtime {
type XcmConfig = XcmConfig;
type AccountIdConverter = SovereignAccountOf;
type AccountIdConverter = LocationConverter;
fn valid_destination() -> Result<MultiLocation, BenchmarkError> {
Ok(Statemine::get())
}
fn worst_case_holding() -> MultiAssets {
fn worst_case_holding(_depositable_count: u32) -> MultiAssets {
// Rococo only knows about ROC
vec![MultiAsset{
id: Concrete(RocLocation::get()),
id: Concrete(TokenLocation::get()),
fun: Fungible(1_000_000 * UNITS),
}].into()
}
@@ -2001,24 +2007,23 @@ sp_api::impl_runtime_apis! {
parameter_types! {
pub const TrustedTeleporter: Option<(MultiLocation, MultiAsset)> = Some((
Statemine::get(),
MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(RocLocation::get()) },
MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(TokenLocation::get()) },
));
pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = Some((
Statemine::get(),
MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(RocLocation::get()) },
MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(TokenLocation::get()) },
));
}
impl pallet_xcm_benchmarks::fungible::Config for Runtime {
type TransactAsset = Balances;
type CheckedAccount = CheckAccount;
type CheckedAccount = LocalCheckAccount;
type TrustedTeleporter = TrustedTeleporter;
type TrustedReserve = TrustedReserve;
fn get_multi_asset() -> MultiAsset {
MultiAsset {
id: Concrete(RocLocation::get()),
id: Concrete(TokenLocation::get()),
fun: Fungible(1 * UNITS),
}
}
@@ -2031,8 +2036,18 @@ sp_api::impl_runtime_apis! {
(0u64, Response::Version(Default::default()))
}
fn transact_origin() -> Result<MultiLocation, BenchmarkError> {
Ok(Statemine::get())
fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError> {
// Rococo doesn't support asset exchanges
Err(BenchmarkError::Skip)
}
fn universal_alias() -> Result<Junction, BenchmarkError> {
// The XCM executor of Rococo doesn't have a configured `UniversalAliases`
Err(BenchmarkError::Skip)
}
fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> {
Ok((Statemine::get(), frame_system::Call::remark_with_event { remark: vec![] }.into()))
}
fn subscribe_origin() -> Result<MultiLocation, BenchmarkError> {
@@ -2041,10 +2056,15 @@ sp_api::impl_runtime_apis! {
fn claimable_asset() -> Result<(MultiLocation, MultiLocation, MultiAssets), BenchmarkError> {
let origin = Statemine::get();
let assets: MultiAssets = (Concrete(RocLocation::get()), 1_000 * UNITS).into();
let assets: MultiAssets = (Concrete(TokenLocation::get()), 1_000 * UNITS).into();
let ticket = MultiLocation { parents: 0, interior: Here };
Ok((origin, ticket, assets))
}
fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> {
// Rococo doesn't support asset locking
Err(BenchmarkError::Skip)
}
}
let whitelist: Vec<TrackedStorageKey> = vec![
@@ -39,6 +39,7 @@ pub mod pallet_tips;
pub mod pallet_treasury;
pub mod pallet_utility;
pub mod pallet_vesting;
pub mod pallet_xcm;
pub mod runtime_common_auctions;
pub mod runtime_common_claims;
pub mod runtime_common_crowdloan;
@@ -0,0 +1,173 @@
// Copyright 2017-2022 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/>.
//! Autogenerated weights for `pallet_xcm`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-12-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024
// Executed Command:
// /home/benchbot/cargo_target_dir/production/polkadot
// benchmark
// pallet
// --steps=50
// --repeat=20
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/polkadot/.git/.artifacts/bench.json
// --pallet=pallet_xcm
// --chain=rococo-dev
// --header=./file_header.txt
// --output=./runtime/rococo/src/weights/
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
use frame_support::{traits::Get, weights::Weight};
use sp_std::marker::PhantomData;
/// Weight functions for `pallet_xcm`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn send() -> Weight {
// Minimum execution time: 36_707 nanoseconds.
Weight::from_ref_time(37_718_000)
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(3))
}
fn teleport_assets() -> Weight {
// Minimum execution time: 28_720 nanoseconds.
Weight::from_ref_time(29_098_000)
}
fn reserve_transfer_assets() -> Weight {
// Minimum execution time: 27_702 nanoseconds.
Weight::from_ref_time(28_517_000)
}
fn execute() -> Weight {
// Minimum execution time: 14_527 nanoseconds.
Weight::from_ref_time(14_823_000)
}
// Storage: XcmPallet SupportedVersion (r:0 w:1)
fn force_xcm_version() -> Weight {
// Minimum execution time: 16_306 nanoseconds.
Weight::from_ref_time(16_619_000)
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: XcmPallet SafeXcmVersion (r:0 w:1)
fn force_default_xcm_version() -> Weight {
// Minimum execution time: 4_911 nanoseconds.
Weight::from_ref_time(5_080_000)
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: XcmPallet VersionNotifiers (r:1 w:1)
// Storage: XcmPallet QueryCounter (r:1 w:1)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: XcmPallet Queries (r:0 w:1)
fn force_subscribe_version_notify() -> Weight {
// Minimum execution time: 48_258 nanoseconds.
Weight::from_ref_time(49_130_000)
.saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(6))
}
// Storage: XcmPallet VersionNotifiers (r:1 w:1)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: XcmPallet Queries (r:0 w:1)
fn force_unsubscribe_version_notify() -> Weight {
// Minimum execution time: 52_381 nanoseconds.
Weight::from_ref_time(53_183_000)
.saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(5))
}
// Storage: XcmPallet SupportedVersion (r:4 w:2)
fn migrate_supported_version() -> Weight {
// Minimum execution time: 16_915 nanoseconds.
Weight::from_ref_time(17_479_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifiers (r:4 w:2)
fn migrate_version_notifiers() -> Weight {
// Minimum execution time: 17_012 nanoseconds.
Weight::from_ref_time(17_319_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifyTargets (r:5 w:0)
fn already_notified_target() -> Weight {
// Minimum execution time: 19_489 nanoseconds.
Weight::from_ref_time(19_995_000)
.saturating_add(T::DbWeight::get().reads(5))
}
// Storage: XcmPallet VersionNotifyTargets (r:2 w:1)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn notify_current_targets() -> Weight {
// Minimum execution time: 43_334 nanoseconds.
Weight::from_ref_time(43_983_000)
.saturating_add(T::DbWeight::get().reads(8))
.saturating_add(T::DbWeight::get().writes(4))
}
// Storage: XcmPallet VersionNotifyTargets (r:3 w:0)
fn notify_target_migration_fail() -> Weight {
// Minimum execution time: 8_627 nanoseconds.
Weight::from_ref_time(8_860_000)
.saturating_add(T::DbWeight::get().reads(3))
}
// Storage: XcmPallet VersionNotifyTargets (r:4 w:2)
fn migrate_version_notify_targets() -> Weight {
// Minimum execution time: 17_679 nanoseconds.
Weight::from_ref_time(18_042_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifyTargets (r:4 w:2)
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn migrate_and_notify_old_targets() -> Weight {
// Minimum execution time: 45_445 nanoseconds.
Weight::from_ref_time(48_369_000)
.saturating_add(T::DbWeight::get().reads(10))
.saturating_add(T::DbWeight::get().writes(5))
}
}
@@ -16,23 +16,25 @@
//! Autogenerated weights for `runtime_parachains::ump`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-11-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm6`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! DATE: 2023-01-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 1024
// Executed Command:
// ./target/production/polkadot
// /home/benchbot/cargo_target_dir/production/polkadot
// benchmark
// pallet
// --chain=rococo-dev
// --steps=50
// --repeat=20
// --pallet=runtime_parachains::ump
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/polkadot/.git/.artifacts/bench.json
// --pallet=runtime_parachains::ump
// --chain=rococo-dev
// --header=./file_header.txt
// --output=./runtime/rococo/src/weights/runtime_parachains_ump.rs
// --output=./runtime/rococo/src/weights/
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
@@ -46,26 +48,27 @@ pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> runtime_parachains::ump::WeightInfo for WeightInfo<T> {
/// The range of component `s` is `[0, 51200]`.
fn process_upward_message(s: u32, ) -> Weight {
// Minimum execution time: 10_433 nanoseconds.
Weight::from_ref_time(6_809_084 as u64)
// Standard Error: 12
.saturating_add(Weight::from_ref_time(1_973 as u64).saturating_mul(s as u64))
// Minimum execution time: 10_224 nanoseconds.
Weight::from_ref_time(4_699_572)
// Standard Error: 11
.saturating_add(Weight::from_ref_time(1_907).saturating_mul(s.into()))
}
// Storage: Ump NeedsDispatch (r:1 w:1)
// Storage: Ump NextDispatchRoundStartWith (r:1 w:1)
// Storage: Ump RelayDispatchQueues (r:0 w:1)
// Storage: Ump RelayDispatchQueueSize (r:0 w:1)
fn clean_ump_after_outgoing() -> Weight {
// Minimum execution time: 8_932 nanoseconds.
Weight::from_ref_time(9_171_000 as u64)
.saturating_add(T::DbWeight::get().reads(2 as u64))
.saturating_add(T::DbWeight::get().writes(4 as u64))
// Minimum execution time: 9_180 nanoseconds.
Weight::from_ref_time(9_354_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(4))
}
// Storage: Ump Overweight (r:1 w:1)
// Storage: Ump CounterForOverweight (r:1 w:1)
fn service_overweight() -> Weight {
// Minimum execution time: 25_129 nanoseconds.
Weight::from_ref_time(25_441_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
// Minimum execution time: 28_704 nanoseconds.
Weight::from_ref_time(29_057_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
}
+138 -83
View File
@@ -4,10 +4,7 @@ mod pallet_xcm_benchmarks_generic;
use crate::Runtime;
use frame_support::weights::Weight;
use sp_std::prelude::*;
use xcm::{
latest::{prelude::*, Weight as XCMWeight},
DoubleEncoded,
};
use xcm::{latest::prelude::*, DoubleEncoded};
use pallet_xcm_benchmarks_fungible::WeightInfo as XcmBalancesWeight;
use pallet_xcm_benchmarks_generic::WeightInfo as XcmGeneric;
@@ -31,15 +28,15 @@ impl From<&MultiAsset> for AssetTypes {
}
trait WeighMultiAssets {
fn weigh_multi_assets(&self, balances_weight: Weight) -> XCMWeight;
fn weigh_multi_assets(&self, balances_weight: Weight) -> Weight;
}
// Rococo only knows about one asset, the balances pallet.
const MAX_ASSETS: u32 = 1;
impl WeighMultiAssets for MultiAssetFilter {
fn weigh_multi_assets(&self, balances_weight: Weight) -> XCMWeight {
let weight = match self {
fn weigh_multi_assets(&self, balances_weight: Weight) -> Weight {
match self {
Self::Definite(assets) => assets
.inner()
.into_iter()
@@ -49,155 +46,213 @@ impl WeighMultiAssets for MultiAssetFilter {
AssetTypes::Unknown => Weight::MAX,
})
.fold(Weight::zero(), |acc, x| acc.saturating_add(x)),
Self::Wild(_) => balances_weight.saturating_mul(MAX_ASSETS as u64),
};
weight.ref_time()
Self::Wild(AllOf { .. } | AllOfCounted { .. }) => balances_weight,
Self::Wild(AllCounted(count)) => balances_weight.saturating_mul(*count as u64),
Self::Wild(All) => balances_weight.saturating_mul(MAX_ASSETS as u64),
}
}
}
impl WeighMultiAssets for MultiAssets {
fn weigh_multi_assets(&self, balances_weight: Weight) -> XCMWeight {
let weight = self
.inner()
fn weigh_multi_assets(&self, balances_weight: Weight) -> Weight {
self.inner()
.into_iter()
.map(|m| <AssetTypes as From<&MultiAsset>>::from(m))
.map(|t| match t {
AssetTypes::Balances => balances_weight,
AssetTypes::Unknown => Weight::MAX,
})
.fold(Weight::zero(), |acc, x| acc.saturating_add(x));
weight.ref_time()
.fold(Weight::zero(), |acc, x| acc.saturating_add(x))
}
}
pub struct RococoXcmWeight<Call>(core::marker::PhantomData<Call>);
impl<Call> XcmWeightInfo<Call> for RococoXcmWeight<Call> {
fn withdraw_asset(assets: &MultiAssets) -> XCMWeight {
pub struct RococoXcmWeight<RuntimeCall>(core::marker::PhantomData<RuntimeCall>);
impl<RuntimeCall> XcmWeightInfo<RuntimeCall> for RococoXcmWeight<RuntimeCall> {
fn withdraw_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::withdraw_asset())
}
fn reserve_asset_deposited(assets: &MultiAssets) -> XCMWeight {
fn reserve_asset_deposited(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::reserve_asset_deposited())
}
fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight {
fn receive_teleported_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::receive_teleported_asset())
}
fn query_response(_query_id: &u64, _response: &Response, _max_weight: &u64) -> XCMWeight {
XcmGeneric::<Runtime>::query_response().ref_time()
fn query_response(
_query_id: &u64,
_response: &Response,
_max_weight: &Weight,
_querier: &Option<MultiLocation>,
) -> Weight {
XcmGeneric::<Runtime>::query_response()
}
fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight {
fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::transfer_asset())
}
fn transfer_reserve_asset(
assets: &MultiAssets,
_dest: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::transfer_reserve_asset())
}
fn transact(
_origin_type: &OriginKind,
_require_weight_at_most: &u64,
_call: &DoubleEncoded<Call>,
) -> XCMWeight {
XcmGeneric::<Runtime>::transact().ref_time()
_origin_kind: &OriginKind,
_require_weight_at_most: &Weight,
_call: &DoubleEncoded<RuntimeCall>,
) -> Weight {
XcmGeneric::<Runtime>::transact()
}
fn hrmp_new_channel_open_request(
_sender: &u32,
_max_message_size: &u32,
_max_capacity: &u32,
) -> XCMWeight {
) -> Weight {
// XCM Executor does not currently support HRMP channel operations
Weight::MAX.ref_time()
Weight::MAX
}
fn hrmp_channel_accepted(_recipient: &u32) -> XCMWeight {
fn hrmp_channel_accepted(_recipient: &u32) -> Weight {
// XCM Executor does not currently support HRMP channel operations
Weight::MAX.ref_time()
Weight::MAX
}
fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> XCMWeight {
fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> Weight {
// XCM Executor does not currently support HRMP channel operations
Weight::MAX.ref_time()
Weight::MAX
}
fn clear_origin() -> XCMWeight {
XcmGeneric::<Runtime>::clear_origin().ref_time()
fn clear_origin() -> Weight {
XcmGeneric::<Runtime>::clear_origin()
}
fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight {
XcmGeneric::<Runtime>::descend_origin().ref_time()
fn descend_origin(_who: &InteriorMultiLocation) -> Weight {
XcmGeneric::<Runtime>::descend_origin()
}
fn report_error(
_query_id: &QueryId,
_dest: &MultiLocation,
_max_response_weight: &u64,
) -> XCMWeight {
XcmGeneric::<Runtime>::report_error().ref_time()
fn report_error(_query_response_info: &QueryResponseInfo) -> Weight {
XcmGeneric::<Runtime>::report_error()
}
fn deposit_asset(
assets: &MultiAssetFilter,
_max_assets: &u32, // TODO use max assets?
_dest: &MultiLocation,
) -> XCMWeight {
fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::deposit_asset())
}
fn deposit_reserve_asset(
assets: &MultiAssetFilter,
_max_assets: &u32, // TODO use max assets?
_dest: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::deposit_reserve_asset())
}
fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets) -> XCMWeight {
Weight::MAX.ref_time() // todo fix
fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets, _maximal: &bool) -> Weight {
// Rococo does not currently support exchange asset operations
Weight::MAX
}
fn initiate_reserve_withdraw(
assets: &MultiAssetFilter,
_reserve: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmGeneric::<Runtime>::initiate_reserve_withdraw())
}
fn initiate_teleport(
assets: &MultiAssetFilter,
_dest: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::initiate_teleport())
}
fn query_holding(
_query_id: &u64,
_dest: &MultiLocation,
_assets: &MultiAssetFilter,
_max_response_weight: &u64,
) -> XCMWeight {
XcmGeneric::<Runtime>::query_holding().ref_time()
fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> Weight {
XcmGeneric::<Runtime>::report_holding()
}
fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight {
XcmGeneric::<Runtime>::buy_execution().ref_time()
fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> Weight {
XcmGeneric::<Runtime>::buy_execution()
}
fn refund_surplus() -> XCMWeight {
XcmGeneric::<Runtime>::refund_surplus().ref_time()
fn refund_surplus() -> Weight {
XcmGeneric::<Runtime>::refund_surplus()
}
fn set_error_handler(_xcm: &Xcm<Call>) -> XCMWeight {
XcmGeneric::<Runtime>::set_error_handler().ref_time()
fn set_error_handler(_xcm: &Xcm<RuntimeCall>) -> Weight {
XcmGeneric::<Runtime>::set_error_handler()
}
fn set_appendix(_xcm: &Xcm<Call>) -> XCMWeight {
XcmGeneric::<Runtime>::set_appendix().ref_time()
fn set_appendix(_xcm: &Xcm<RuntimeCall>) -> Weight {
XcmGeneric::<Runtime>::set_appendix()
}
fn clear_error() -> XCMWeight {
XcmGeneric::<Runtime>::clear_error().ref_time()
fn clear_error() -> Weight {
XcmGeneric::<Runtime>::clear_error()
}
fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> XCMWeight {
XcmGeneric::<Runtime>::claim_asset().ref_time()
fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> Weight {
XcmGeneric::<Runtime>::claim_asset()
}
fn trap(_code: &u64) -> XCMWeight {
XcmGeneric::<Runtime>::trap().ref_time()
fn trap(_code: &u64) -> Weight {
XcmGeneric::<Runtime>::trap()
}
fn subscribe_version(_query_id: &QueryId, _max_response_weight: &u64) -> XCMWeight {
XcmGeneric::<Runtime>::subscribe_version().ref_time()
fn subscribe_version(_query_id: &QueryId, _max_response_weight: &Weight) -> Weight {
XcmGeneric::<Runtime>::subscribe_version()
}
fn unsubscribe_version() -> XCMWeight {
XcmGeneric::<Runtime>::unsubscribe_version().ref_time()
fn unsubscribe_version() -> Weight {
XcmGeneric::<Runtime>::unsubscribe_version()
}
fn burn_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmGeneric::<Runtime>::burn_asset())
}
fn expect_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmGeneric::<Runtime>::expect_asset())
}
fn expect_origin(_origin: &Option<MultiLocation>) -> Weight {
XcmGeneric::<Runtime>::expect_origin()
}
fn expect_error(_error: &Option<(u32, XcmError)>) -> Weight {
XcmGeneric::<Runtime>::expect_error()
}
fn query_pallet(_module_name: &Vec<u8>, _response_info: &QueryResponseInfo) -> Weight {
XcmGeneric::<Runtime>::query_pallet()
}
fn expect_pallet(
_index: &u32,
_name: &Vec<u8>,
_module_name: &Vec<u8>,
_crate_major: &u32,
_min_crate_minor: &u32,
) -> Weight {
XcmGeneric::<Runtime>::expect_pallet()
}
fn report_transact_status(_response_info: &QueryResponseInfo) -> Weight {
XcmGeneric::<Runtime>::report_transact_status()
}
fn clear_transact_status() -> Weight {
XcmGeneric::<Runtime>::clear_transact_status()
}
fn universal_origin(_: &Junction) -> Weight {
// Rococo does not currently support universal origin operations
Weight::MAX
}
fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight {
Weight::MAX // todo fix
}
fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Rococo does not currently support asset locking operations
Weight::MAX
}
fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Rococo does not currently support asset locking operations
Weight::MAX
}
fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Rococo does not currently support asset locking operations
Weight::MAX
}
fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Rococo does not currently support asset locking operations
Weight::MAX
}
fn set_fees_mode(_: &bool) -> Weight {
XcmGeneric::<Runtime>::set_fees_mode()
}
fn set_topic(_topic: &[u8; 32]) -> Weight {
XcmGeneric::<Runtime>::set_topic()
}
fn clear_topic() -> Weight {
XcmGeneric::<Runtime>::clear_topic()
}
fn alias_origin(_: &MultiLocation) -> Weight {
// XCM Executor does not currently support alias origin operations
Weight::MAX
}
fn unpaid_execution(_: &WeightLimit, _: &Option<MultiLocation>) -> Weight {
XcmGeneric::<Runtime>::unpaid_execution()
}
}
@@ -51,7 +51,7 @@ impl<T: frame_system::Config> WeightInfo<T> {
// Storage: Configuration ActiveConfig (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
pub(crate) fn query_holding() -> Weight {
pub(crate) fn report_holding() -> Weight {
Weight::from_ref_time(21_822_000 as u64)
.saturating_add(T::DbWeight::get().reads(6 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
@@ -133,4 +133,54 @@ impl<T: frame_system::Config> WeightInfo<T> {
.saturating_add(T::DbWeight::get().reads(6 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
pub(crate) fn burn_asset() -> Weight {
Weight::from_ref_time(5_259_000 as u64)
}
pub(crate) fn expect_asset() -> Weight {
Weight::from_ref_time(3_745_000 as u64)
}
pub(crate) fn expect_origin() -> Weight {
Weight::from_ref_time(3_847_000 as u64)
}
pub(crate) fn expect_error() -> Weight {
Weight::from_ref_time(3_633_000 as u64)
}
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
pub(crate) fn query_pallet() -> Weight {
Weight::from_ref_time(21_645_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
pub(crate) fn expect_pallet() -> Weight {
Weight::from_ref_time(4_017_000 as u64)
}
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
pub(crate) fn report_transact_status() -> Weight {
Weight::from_ref_time(20_465_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
pub(crate) fn clear_transact_status() -> Weight {
Weight::from_ref_time(3_723_000 as u64)
}
pub(crate) fn set_topic() -> Weight {
Weight::from_ref_time(3_687_000 as u64)
}
pub(crate) fn clear_topic() -> Weight {
Weight::from_ref_time(3_654_000 as u64)
}
pub(crate) fn set_fees_mode() -> Weight {
Weight::from_ref_time(3_721_000 as u64)
}
pub(crate) fn unpaid_execution() -> Weight {
Weight::from_ref_time(3_111_000 as u64)
}
}
+215 -68
View File
@@ -17,44 +17,37 @@
//! XCM configuration for Rococo.
use super::{
parachains_origin, AccountId, Balances, CouncilCollective, ParaId, Runtime, RuntimeCall,
RuntimeEvent, RuntimeOrigin, WeightToFee, XcmPallet,
parachains_origin, AccountId, AllPalletsWithSystem, Balances, CouncilCollective, ParaId,
Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmPallet,
};
use frame_support::{
match_types, parameter_types,
traits::{Contains, Everything, Nothing},
weights::Weight,
};
use frame_support::{match_types, parameter_types, traits::Everything};
use runtime_common::{xcm_sender, ToAuthor};
use sp_core::ConstU32;
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, BackingToPlurality,
AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses,
AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, BackingToPlurality,
ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
CurrencyAdapter as XcmCurrencyAdapter, FixedWeightBounds, IsChildSystemParachain, IsConcrete,
LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
TakeWeightCredit, UsingComponents, WeightInfoBounds,
MintLocation, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation,
TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin,
};
use xcm_executor::{traits::WithOriginFilter, XcmExecutor};
parameter_types! {
/// The location of the ROC token, from the context of this chain. Since this token is native to this
/// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to
/// the context".
pub const RocLocation: MultiLocation = Here.into();
/// The Rococo network ID. This is named.
pub RococoNetwork: NetworkId =
NetworkId::Named(b"Rococo".to_vec().try_into().expect("shorter than length limit; qed"));
/// Our XCM location ancestry - i.e. what, if anything, `Parent` means evaluated in our context. Since
/// Rococo is a top-level relay-chain, there is no ancestry.
pub const Ancestry: MultiLocation = Here.into();
/// The check account, which holds any native assets that have been teleported out and not back in (yet).
pub const TokenLocation: MultiLocation = Here.into_location();
pub const ThisNetwork: NetworkId = NetworkId::Rococo;
pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into();
pub CheckAccount: AccountId = XcmPallet::check_account();
pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local);
}
/// The canonical means of converting a `MultiLocation` into an `AccountId`, used when we want to determine
/// the sovereign account controlled by a location.
pub type SovereignAccountOf = (
// We can convert a child parachain using the standard `AccountId` conversion.
ChildParachainConvertsVia<ParaId, AccountId>,
// We can directly alias an `AccountId32` into a local account.
AccountId32Aliases<RococoNetwork, AccountId>,
);
pub type LocationConverter =
(ChildParachainConvertsVia<ParaId, AccountId>, AccountId32Aliases<ThisNetwork, AccountId>);
/// Our asset transactor. This is what allows us to interest with the runtime facilities from the point of
/// view of XCM-only concepts like `MultiLocation` and `MultiAsset`.
@@ -64,63 +57,62 @@ pub type LocalAssetTransactor = XcmCurrencyAdapter<
// Use this currency:
Balances,
// Use this currency when it is a fungible asset matching the given location or name:
IsConcrete<RocLocation>,
IsConcrete<TokenLocation>,
// We can convert the MultiLocations with our converter above:
SovereignAccountOf,
LocationConverter,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
AccountId,
// We track our teleports in/out to keep total issuance correct.
CheckAccount,
LocalCheckAccount,
>;
/// The means that we convert an the XCM message origin location into a local dispatch origin.
type LocalOriginConverter = (
// A `Signed` origin of the sovereign account that the original location controls.
SovereignSignedViaLocation<SovereignAccountOf, RuntimeOrigin>,
SovereignSignedViaLocation<LocationConverter, RuntimeOrigin>,
// A child parachain, natively expressed, has the `Parachain` origin.
ChildParachainAsNative<parachains_origin::Origin, RuntimeOrigin>,
// The AccountId32 location type can be expressed natively as a `Signed` origin.
SignedAccountId32AsNative<RococoNetwork, RuntimeOrigin>,
SignedAccountId32AsNative<ThisNetwork, RuntimeOrigin>,
// A system child parachain, expressed as a Superuser, converts to the `Root` origin.
ChildSystemParachainAsSuperuser<ParaId, RuntimeOrigin>,
);
parameter_types! {
/// The amount of weight an XCM operation takes. This is a safe overestimate.
pub const BaseXcmWeight: u64 = 1_000_000_000;
/// Maximum number of instructions in a single XCM fragment. A sanity check against weight
/// calculations getting too crazy.
pub const MaxInstructions: u32 = 100;
pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000, 64 * 1024);
}
/// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our
/// individual routers.
pub type XcmRouter = (
// Only one router so far - use DMP to communicate with child parachains.
xcm_sender::ChildParachainRouter<Runtime, XcmPallet>,
xcm_sender::ChildParachainRouter<Runtime, XcmPallet, ()>,
);
parameter_types! {
pub const Rococo: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(RocLocation::get()) });
pub const Statemine: MultiLocation = Parachain(1000).into();
pub const Contracts: MultiLocation = Parachain(1002).into();
pub const Encointer: MultiLocation = Parachain(1003).into();
pub const Tick: MultiLocation = Parachain(100).into();
pub const Trick: MultiLocation = Parachain(110).into();
pub const Track: MultiLocation = Parachain(120).into();
pub const RococoForTick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Tick::get());
pub const RococoForTrick: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Trick::get());
pub const RococoForTrack: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Track::get());
pub const RococoForStatemine: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Statemine::get());
pub const RococoForContracts: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Contracts::get());
pub const RococoForEncointer: (MultiAssetFilter, MultiLocation) = (Rococo::get(), Encointer::get());
pub const Roc: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) });
pub const Statemine: MultiLocation = Parachain(1000).into_location();
pub const Contracts: MultiLocation = Parachain(1002).into_location();
pub const Encointer: MultiLocation = Parachain(1003).into_location();
pub const Tick: MultiLocation = Parachain(100).into_location();
pub const Trick: MultiLocation = Parachain(110).into_location();
pub const Track: MultiLocation = Parachain(120).into_location();
pub const RocForTick: (MultiAssetFilter, MultiLocation) = (Roc::get(), Tick::get());
pub const RocForTrick: (MultiAssetFilter, MultiLocation) = (Roc::get(), Trick::get());
pub const RocForTrack: (MultiAssetFilter, MultiLocation) = (Roc::get(), Track::get());
pub const RocForStatemine: (MultiAssetFilter, MultiLocation) = (Roc::get(), Statemine::get());
pub const RocForContracts: (MultiAssetFilter, MultiLocation) = (Roc::get(), Contracts::get());
pub const RocForEncointer: (MultiAssetFilter, MultiLocation) = (Roc::get(), Encointer::get());
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 64;
}
pub type TrustedTeleporters = (
xcm_builder::Case<RococoForTick>,
xcm_builder::Case<RococoForTrick>,
xcm_builder::Case<RococoForTrack>,
xcm_builder::Case<RococoForStatemine>,
xcm_builder::Case<RococoForContracts>,
xcm_builder::Case<RococoForEncointer>,
xcm_builder::Case<RocForTick>,
xcm_builder::Case<RocForTrick>,
xcm_builder::Case<RocForTrack>,
xcm_builder::Case<RocForStatemine>,
xcm_builder::Case<RocForContracts>,
xcm_builder::Case<RocForEncointer>,
);
match_types! {
@@ -133,16 +125,150 @@ match_types! {
pub type Barrier = (
// Weight that is paid for may be consumed.
TakeWeightCredit,
// If the message is one that immediately attemps to pay for execution, then allow it.
AllowTopLevelPaidExecutionFrom<Everything>,
// Messages coming from system parachains need not pay for execution.
AllowUnpaidExecutionFrom<IsChildSystemParachain<ParaId>>,
// Expected responses are OK.
AllowKnownQueryResponses<XcmPallet>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom<OnlyParachains>,
WithComputedOrigin<
(
// If the message is one that immediately attemps to pay for execution, then allow it.
AllowTopLevelPaidExecutionFrom<Everything>,
// Messages coming from system parachains need not pay for execution.
AllowExplicitUnpaidExecutionFrom<IsChildSystemParachain<ParaId>>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom<OnlyParachains>,
),
UniversalLocation,
ConstU32<8>,
>,
);
/// A call filter for the XCM Transact instruction. This is a temporary measure until we
/// properly account for proof size weights.
///
/// Calls that are allowed through this filter must:
/// 1. Have a fixed weight;
/// 2. Cannot lead to another call being made;
/// 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters.
pub struct SafeCallFilter;
impl Contains<RuntimeCall> for SafeCallFilter {
fn contains(call: &RuntimeCall) -> bool {
#[cfg(feature = "runtime-benchmarks")]
{
if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event { .. })) {
return true
}
}
match call {
RuntimeCall::System(
frame_system::Call::kill_prefix { .. } | frame_system::Call::set_heap_pages { .. },
) |
RuntimeCall::Babe(..) |
RuntimeCall::Timestamp(..) |
RuntimeCall::Indices(..) |
RuntimeCall::Balances(..) |
RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) |
RuntimeCall::Grandpa(..) |
RuntimeCall::ImOnline(..) |
RuntimeCall::Democracy(
pallet_democracy::Call::second { .. } |
pallet_democracy::Call::vote { .. } |
pallet_democracy::Call::emergency_cancel { .. } |
pallet_democracy::Call::fast_track { .. } |
pallet_democracy::Call::veto_external { .. } |
pallet_democracy::Call::cancel_referendum { .. } |
pallet_democracy::Call::delegate { .. } |
pallet_democracy::Call::undelegate { .. } |
pallet_democracy::Call::clear_public_proposals { .. } |
pallet_democracy::Call::unlock { .. } |
pallet_democracy::Call::remove_vote { .. } |
pallet_democracy::Call::remove_other_vote { .. } |
pallet_democracy::Call::blacklist { .. } |
pallet_democracy::Call::cancel_proposal { .. },
) |
RuntimeCall::Council(
pallet_collective::Call::vote { .. } |
pallet_collective::Call::close_old_weight { .. } |
pallet_collective::Call::disapprove_proposal { .. } |
pallet_collective::Call::close { .. },
) |
RuntimeCall::TechnicalCommittee(
pallet_collective::Call::vote { .. } |
pallet_collective::Call::close_old_weight { .. } |
pallet_collective::Call::disapprove_proposal { .. } |
pallet_collective::Call::close { .. },
) |
RuntimeCall::PhragmenElection(
pallet_elections_phragmen::Call::remove_voter { .. } |
pallet_elections_phragmen::Call::submit_candidacy { .. } |
pallet_elections_phragmen::Call::renounce_candidacy { .. } |
pallet_elections_phragmen::Call::remove_member { .. } |
pallet_elections_phragmen::Call::clean_defunct_voters { .. },
) |
RuntimeCall::TechnicalMembership(
pallet_membership::Call::add_member { .. } |
pallet_membership::Call::remove_member { .. } |
pallet_membership::Call::swap_member { .. } |
pallet_membership::Call::change_key { .. } |
pallet_membership::Call::set_prime { .. } |
pallet_membership::Call::clear_prime { .. },
) |
RuntimeCall::Treasury(..) |
RuntimeCall::Claims(
super::claims::Call::claim { .. } |
super::claims::Call::mint_claim { .. } |
super::claims::Call::move_claim { .. },
) |
RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) |
RuntimeCall::Identity(
pallet_identity::Call::add_registrar { .. } |
pallet_identity::Call::set_identity { .. } |
pallet_identity::Call::clear_identity { .. } |
pallet_identity::Call::request_judgement { .. } |
pallet_identity::Call::cancel_request { .. } |
pallet_identity::Call::set_fee { .. } |
pallet_identity::Call::set_account_id { .. } |
pallet_identity::Call::set_fields { .. } |
pallet_identity::Call::provide_judgement { .. } |
pallet_identity::Call::kill_identity { .. } |
pallet_identity::Call::add_sub { .. } |
pallet_identity::Call::rename_sub { .. } |
pallet_identity::Call::remove_sub { .. } |
pallet_identity::Call::quit_sub { .. },
) |
RuntimeCall::Society(
pallet_society::Call::bid { .. } |
pallet_society::Call::unbid { .. } |
pallet_society::Call::vouch { .. } |
pallet_society::Call::unvouch { .. } |
pallet_society::Call::vote { .. } |
pallet_society::Call::defender_vote { .. } |
pallet_society::Call::payout { .. } |
pallet_society::Call::unfound { .. } |
pallet_society::Call::judge_suspended_member { .. } |
pallet_society::Call::judge_suspended_candidate { .. } |
pallet_society::Call::set_max_members { .. },
) |
RuntimeCall::Recovery(..) |
RuntimeCall::Vesting(..) |
RuntimeCall::Bounties(
pallet_bounties::Call::propose_bounty { .. } |
pallet_bounties::Call::approve_bounty { .. } |
pallet_bounties::Call::propose_curator { .. } |
pallet_bounties::Call::unassign_curator { .. } |
pallet_bounties::Call::accept_curator { .. } |
pallet_bounties::Call::award_bounty { .. } |
pallet_bounties::Call::claim_bounty { .. } |
pallet_bounties::Call::close_bounty { .. },
) |
RuntimeCall::ChildBounties(..) |
RuntimeCall::XcmPallet(pallet_xcm::Call::limited_reserve_transfer_assets {
..
}) => true,
_ => false,
}
}
}
pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
@@ -151,19 +277,28 @@ impl xcm_executor::Config for XcmConfig {
type OriginConverter = LocalOriginConverter;
type IsReserve = ();
type IsTeleporter = TrustedTeleporters;
type LocationInverter = LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
type Weigher = WeightInfoBounds<
crate::weights::xcm::RococoXcmWeight<RuntimeCall>,
RuntimeCall,
MaxInstructions,
>;
// The weight trader piggybacks on the existing transaction-fee conversion logic.
type Trader = UsingComponents<WeightToFee, RocLocation, AccountId, Balances, ToAuthor<Runtime>>;
type Trader =
UsingComponents<WeightToFee, TokenLocation, AccountId, Balances, ToAuthor<Runtime>>;
type ResponseHandler = XcmPallet;
type AssetTrap = XcmPallet;
type AssetLocker = ();
type AssetExchanger = ();
type AssetClaims = XcmPallet;
type SubscriptionService = XcmPallet;
type PalletInstancesInfo = AllPalletsWithSystem;
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = ();
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = WithOriginFilter<SafeCallFilter>;
type SafeCallFilter = SafeCallFilter;
}
parameter_types! {
@@ -174,6 +309,11 @@ parameter_types! {
pub const CouncilBodyId: BodyId = BodyId::Executive;
}
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parachain(1000).into());
}
/// Type to convert the council origin to a Plurality `MultiLocation` value.
pub type CouncilToPlurality = BackingToPlurality<
RuntimeOrigin,
@@ -188,7 +328,7 @@ pub type LocalOriginToLocation = (
// `Unit` body.
CouncilToPlurality,
// And a usual Signed origin to be used in XCM as a corresponding AccountId32
SignedToAccountId32<RuntimeOrigin, AccountId, RococoNetwork>,
SignedToAccountId32<RuntimeOrigin, AccountId, ThisNetwork>,
);
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
@@ -197,16 +337,23 @@ impl pallet_xcm::Config for Runtime {
// Anyone can execute XCM messages locally.
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type XcmExecuteFilter = Everything;
type XcmExecutor = xcm_executor::XcmExecutor<XcmConfig>;
// Anyone is able to use teleportation regardless of who they are and what they want to teleport.
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = Everything;
// Anyone is able to use reserve transfers regardless of who they are and what they want to
// transfer.
type XcmReserveTransferFilter = Everything;
type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
type LocationInverter = LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
type Currency = Balances;
type CurrencyMatcher = IsConcrete<TokenLocation>;
type TrustedLockers = ();
type SovereignAccountOf = LocationConverter;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
}
+4
View File
@@ -129,3 +129,7 @@ std = [
"frame-election-provider-support/std",
"pallet-sudo/std",
]
runtime-benchmarks = [
"pallet-xcm/runtime-benchmarks",
]
+5 -31
View File
@@ -131,7 +131,7 @@ parameter_types! {
}
impl frame_system::Config for Runtime {
type BaseCallFilter = frame_support::traits::Everything;
type BaseCallFilter = Everything;
type BlockWeights = BlockWeights;
type BlockLength = BlockLength;
type DbWeight = ();
@@ -537,34 +537,6 @@ impl parachains_ump::Config for Runtime {
type WeightInfo = parachains_ump::TestWeightInfo;
}
parameter_types! {
pub const BaseXcmWeight: xcm::latest::Weight = 1_000;
pub const AnyNetwork: xcm::latest::NetworkId = xcm::latest::NetworkId::Any;
pub const MaxInstructions: u32 = 100;
}
pub type LocalOriginToLocation =
xcm_builder::SignedToAccountId32<RuntimeOrigin, AccountId, AnyNetwork>;
impl pallet_xcm::Config for Runtime {
// The config types here are entirely configurable, since the only one that is sorely needed
// is `XcmExecutor`, which will be used in unit tests located in xcm-executor.
type RuntimeEvent = RuntimeEvent;
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type LocationInverter = xcm_config::InvertNothing;
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type Weigher = xcm_builder::FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
type XcmRouter = xcm_config::DoNothingRouter;
type XcmExecuteFilter = Everything;
type XcmExecutor = xcm_executor::XcmExecutor<xcm_config::XcmConfig>;
type XcmTeleportFilter = Everything;
type XcmReserveTransferFilter = Everything;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
}
impl parachains_hrmp::Config for Runtime {
type RuntimeOrigin = RuntimeOrigin;
type RuntimeEvent = RuntimeEvent;
@@ -628,8 +600,9 @@ pub mod pallet_test_notifier {
.using_encoded(|mut d| <[u8; 32]>::decode(&mut d))
.map_err(|_| Error::<T>::BadAccountFormat)?;
let qid = pallet_xcm::Pallet::<T>::new_query(
Junction::AccountId32 { network: Any, id }.into(),
Junction::AccountId32 { network: None, id },
100u32.into(),
Here,
);
Self::deposit_event(Event::<T>::QueryPrepared(qid));
Ok(())
@@ -645,9 +618,10 @@ pub mod pallet_test_notifier {
let call =
Call::<T>::notification_received { query_id: 0, response: Default::default() };
let qid = pallet_xcm::Pallet::<T>::new_notify_query(
Junction::AccountId32 { network: Any, id }.into(),
Junction::AccountId32 { network: None, id },
<T as Config>::RuntimeCall::from(call),
100u32.into(),
Here,
);
Self::deposit_event(Event::<T>::NotifyQueryPrepared(qid));
Ok(())
+77 -23
View File
@@ -14,30 +14,44 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use frame_support::{parameter_types, traits::Everything};
use xcm::latest::{prelude::*, Weight as XCMWeight};
use xcm_builder::{AllowUnpaidExecutionFrom, FixedWeightBounds, SignedToAccountId32};
use frame_support::{
parameter_types,
traits::{Everything, Nothing},
weights::Weight,
};
use xcm::latest::prelude::*;
use xcm_builder::{
AllowUnpaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, SignedAccountId32AsNative,
SignedToAccountId32,
};
use xcm_executor::{
traits::{InvertLocation, TransactAsset, WeightTrader},
traits::{TransactAsset, WeightTrader},
Assets,
};
parameter_types! {
pub const OurNetwork: NetworkId = NetworkId::Polkadot;
pub const BaseXcmWeight: xcm::latest::Weight = Weight::from_parts(1_000, 1_000);
pub const AnyNetwork: Option<NetworkId> = None;
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 16;
pub const UniversalLocation: xcm::latest::InteriorMultiLocation = xcm::latest::Junctions::Here;
}
/// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior location
/// of this chain.
pub type LocalOriginToLocation = (
// And a usual Signed origin to be used in XCM as a corresponding AccountId32
SignedToAccountId32<crate::RuntimeOrigin, crate::AccountId, OurNetwork>,
SignedToAccountId32<crate::RuntimeOrigin, crate::AccountId, AnyNetwork>,
);
pub struct DoNothingRouter;
impl SendXcm for DoNothingRouter {
fn send_xcm(_dest: impl Into<MultiLocation>, _msg: Xcm<()>) -> SendResult {
Ok(())
type Ticket = ();
fn validate(_dest: &mut Option<MultiLocation>, _msg: &mut Option<Xcm<()>>) -> SendResult<()> {
Ok(((), MultiAssets::new()))
}
fn deliver(_: ()) -> Result<XcmHash, SendError> {
Ok([0; 32])
}
}
@@ -45,11 +59,15 @@ pub type Barrier = AllowUnpaidExecutionFrom<Everything>;
pub struct DummyAssetTransactor;
impl TransactAsset for DummyAssetTransactor {
fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation) -> XcmResult {
fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation, _context: &XcmContext) -> XcmResult {
Ok(())
}
fn withdraw_asset(_what: &MultiAsset, _who: &MultiLocation) -> Result<Assets, XcmError> {
fn withdraw_asset(
_what: &MultiAsset,
_who: &MultiLocation,
_maybe_context: Option<&XcmContext>,
) -> Result<Assets, XcmError> {
let asset: MultiAsset = (Parent, 100_000).into();
Ok(asset.into())
}
@@ -61,35 +79,71 @@ impl WeightTrader for DummyWeightTrader {
DummyWeightTrader
}
fn buy_weight(&mut self, _weight: XCMWeight, _payment: Assets) -> Result<Assets, XcmError> {
fn buy_weight(&mut self, _weight: Weight, _payment: Assets) -> Result<Assets, XcmError> {
Ok(Assets::default())
}
}
pub struct InvertNothing;
impl InvertLocation for InvertNothing {
fn invert_location(_: &MultiLocation) -> sp_std::result::Result<MultiLocation, ()> {
Ok(Here.into())
}
fn ancestry() -> MultiLocation {
Here.into()
}
}
type OriginConverter = (
pallet_xcm::XcmPassthrough<super::RuntimeOrigin>,
SignedAccountId32AsNative<AnyNetwork, super::RuntimeOrigin>,
);
pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = super::RuntimeCall;
type XcmSender = DoNothingRouter;
type AssetTransactor = DummyAssetTransactor;
type OriginConverter = pallet_xcm::XcmPassthrough<super::RuntimeOrigin>;
type OriginConverter = OriginConverter;
type IsReserve = ();
type IsTeleporter = ();
type LocationInverter = InvertNothing;
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<super::BaseXcmWeight, super::RuntimeCall, MaxInstructions>;
type Weigher = FixedWeightBounds<BaseXcmWeight, super::RuntimeCall, MaxInstructions>;
type Trader = DummyWeightTrader;
type ResponseHandler = super::Xcm;
type AssetTrap = super::Xcm;
type AssetLocker = ();
type AssetExchanger = ();
type AssetClaims = super::Xcm;
type SubscriptionService = super::Xcm;
type PalletInstancesInfo = ();
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = ();
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = super::RuntimeCall;
type SafeCallFilter = Everything;
}
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(xcm::latest::Junctions::Here.into());
}
impl pallet_xcm::Config for crate::Runtime {
// The config types here are entirely configurable, since the only one that is sorely needed
// is `XcmExecutor`, which will be used in unit tests located in xcm-executor.
type RuntimeEvent = crate::RuntimeEvent;
type ExecuteXcmOrigin = EnsureXcmOrigin<crate::RuntimeOrigin, LocalOriginToLocation>;
type UniversalLocation = UniversalLocation;
type SendXcmOrigin = EnsureXcmOrigin<crate::RuntimeOrigin, LocalOriginToLocation>;
type Weigher = FixedWeightBounds<BaseXcmWeight, crate::RuntimeCall, MaxInstructions>;
type XcmRouter = DoNothingRouter;
type XcmExecuteFilter = Everything;
type XcmExecutor = xcm_executor::XcmExecutor<XcmConfig>;
type XcmTeleportFilter = Everything;
type XcmReserveTransferFilter = Everything;
type RuntimeOrigin = crate::RuntimeOrigin;
type RuntimeCall = crate::RuntimeCall;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
type Currency = crate::Balances;
type CurrencyMatcher = ();
type TrustedLockers = ();
type SovereignAccountOf = ();
type MaxLockers = frame_support::traits::ConstU32<8>;
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
}
+30 -16
View File
@@ -1247,6 +1247,9 @@ pub type Migrations = (
parachains_disputes::migration::v1::MigrateToV1<Runtime>,
parachains_configuration::migration::v4::MigrateToV4<Runtime>,
init_state_migration::InitMigrate,
// "Use 2D weights in XCM v3" <https://github.com/paritytech/polkadot/pull/6134>
pallet_xcm::migration::v1::MigrateToV1<Runtime>,
parachains_ump::migration::v1::MigrateToV1<Runtime>,
);
/// Unchecked extrinsic type as expected by this runtime.
@@ -1309,6 +1312,7 @@ mod benches {
[pallet_utility, Utility]
[pallet_vesting, Vesting]
// XCM
[pallet_xcm, XcmPallet]
// NOTE: Make sure you point to the individual modules below.
[pallet_xcm_benchmarks::fungible, XcmBalances]
[pallet_xcm_benchmarks::generic, XcmGeneric]
@@ -1728,10 +1732,10 @@ sp_api::impl_runtime_apis! {
impl runtime_parachains::disputes::slashing::benchmarking::Config for Runtime {}
use xcm::latest::{
AssetId::*, Fungibility::*, Junctions::*, MultiAsset, MultiAssets, MultiLocation,
Response,
AssetId::*, Fungibility::*, Junction, Junctions::*, MultiAsset, MultiAssets,
MultiLocation, Response,
};
use xcm_config::{Westmint, WndLocation};
use xcm_config::{Westmint, TokenLocation};
impl pallet_xcm_benchmarks::Config for Runtime {
type XcmConfig = xcm_config::XcmConfig;
@@ -1739,10 +1743,10 @@ sp_api::impl_runtime_apis! {
fn valid_destination() -> Result<MultiLocation, BenchmarkError> {
Ok(Westmint::get())
}
fn worst_case_holding() -> MultiAssets {
fn worst_case_holding(_depositable_count: u32) -> MultiAssets {
// Westend only knows about WND.
vec![MultiAsset{
id: Concrete(WndLocation::get()),
id: Concrete(TokenLocation::get()),
fun: Fungible(1_000_000 * UNITS),
}].into()
}
@@ -1751,24 +1755,19 @@ sp_api::impl_runtime_apis! {
parameter_types! {
pub const TrustedTeleporter: Option<(MultiLocation, MultiAsset)> = Some((
Westmint::get(),
MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(WndLocation::get()) },
));
pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = Some((
Westmint::get(),
MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(WndLocation::get()) },
MultiAsset { fun: Fungible(1 * UNITS), id: Concrete(TokenLocation::get()) },
));
}
impl pallet_xcm_benchmarks::fungible::Config for Runtime {
type TransactAsset = Balances;
type CheckedAccount = xcm_config::CheckAccount;
type CheckedAccount = xcm_config::LocalCheckAccount;
type TrustedTeleporter = TrustedTeleporter;
type TrustedReserve = TrustedReserve;
fn get_multi_asset() -> MultiAsset {
MultiAsset {
id: Concrete(WndLocation::get()),
id: Concrete(TokenLocation::get()),
fun: Fungible(1 * UNITS),
}
}
@@ -1781,8 +1780,18 @@ sp_api::impl_runtime_apis! {
(0u64, Response::Version(Default::default()))
}
fn transact_origin() -> Result<MultiLocation, BenchmarkError> {
Ok(Westmint::get())
fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError> {
// Westend doesn't support asset exchanges
Err(BenchmarkError::Skip)
}
fn universal_alias() -> Result<Junction, BenchmarkError> {
// The XCM executor of Westend doesn't have a configured `UniversalAliases`
Err(BenchmarkError::Skip)
}
fn transact_origin_and_runtime_call() -> Result<(MultiLocation, RuntimeCall), BenchmarkError> {
Ok((Westmint::get(), frame_system::Call::remark_with_event { remark: vec![] }.into()))
}
fn subscribe_origin() -> Result<MultiLocation, BenchmarkError> {
@@ -1791,10 +1800,15 @@ sp_api::impl_runtime_apis! {
fn claimable_asset() -> Result<(MultiLocation, MultiLocation, MultiAssets), BenchmarkError> {
let origin = Westmint::get();
let assets: MultiAssets = (Concrete(WndLocation::get()), 1_000 * UNITS).into();
let assets: MultiAssets = (Concrete(TokenLocation::get()), 1_000 * UNITS).into();
let ticket = MultiLocation { parents: 0, interior: Here };
Ok((origin, ticket, assets))
}
fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> {
// Westend doesn't support asset locking
Err(BenchmarkError::Skip)
}
}
type XcmBalances = pallet_xcm_benchmarks::fungible::Pallet::<Runtime>;
+4 -4
View File
@@ -17,7 +17,7 @@
//! Tests for the Westend Runtime Configuration
use crate::*;
use xcm::latest::{AssetId::*, Fungibility::*, MultiLocation};
use xcm::latest::prelude::*;
#[test]
fn remove_keys_weight_is_sensible() {
@@ -57,9 +57,9 @@ fn sanity_check_teleport_assets_weight() {
// so this test will certainly ensure that this problem does not occur.
use frame_support::dispatch::GetDispatchInfo;
let weight = pallet_xcm::Call::<Runtime>::teleport_assets {
dest: Box::new(xcm::VersionedMultiLocation::V1(MultiLocation::here())),
beneficiary: Box::new(xcm::VersionedMultiLocation::V1(MultiLocation::here())),
assets: Box::new((Concrete(MultiLocation::here()), Fungible(200_000)).into()),
dest: Box::new(Here.into()),
beneficiary: Box::new(Here.into()),
assets: Box::new((Here, 200_000).into()),
fee_asset_item: 0,
}
.get_dispatch_info()
@@ -34,6 +34,7 @@ pub mod pallet_staking;
pub mod pallet_timestamp;
pub mod pallet_utility;
pub mod pallet_vesting;
pub mod pallet_xcm;
pub mod runtime_common_auctions;
pub mod runtime_common_crowdloan;
pub mod runtime_common_paras_registrar;
@@ -0,0 +1,169 @@
// Copyright 2017-2022 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/>.
//! Autogenerated weights for `pallet_xcm`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-12-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024
// Executed Command:
// /home/benchbot/cargo_target_dir/production/polkadot
// benchmark
// pallet
// --steps=50
// --repeat=20
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/polkadot/.git/.artifacts/bench.json
// --pallet=pallet_xcm
// --chain=westend-dev
// --header=./file_header.txt
// --output=./runtime/westend/src/weights/
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
use frame_support::{traits::Get, weights::Weight};
use sp_std::marker::PhantomData;
/// Weight functions for `pallet_xcm`.
pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn send() -> Weight {
// Minimum execution time: 36_453 nanoseconds.
Weight::from_ref_time(37_511_000)
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(3))
}
fn teleport_assets() -> Weight {
// Minimum execution time: 28_144 nanoseconds.
Weight::from_ref_time(28_952_000)
}
fn reserve_transfer_assets() -> Weight {
// Minimum execution time: 28_245 nanoseconds.
Weight::from_ref_time(28_710_000)
}
// Storage: Benchmark Override (r:0 w:0)
fn execute() -> Weight {
// Minimum execution time: 18_446_744_073_709_551 nanoseconds.
Weight::from_ref_time(18_446_744_073_709_551_000)
}
// Storage: XcmPallet SupportedVersion (r:0 w:1)
fn force_xcm_version() -> Weight {
// Minimum execution time: 15_350 nanoseconds.
Weight::from_ref_time(15_829_000)
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: XcmPallet SafeXcmVersion (r:0 w:1)
fn force_default_xcm_version() -> Weight {
// Minimum execution time: 4_482 nanoseconds.
Weight::from_ref_time(4_588_000)
.saturating_add(T::DbWeight::get().writes(1))
}
// Storage: XcmPallet VersionNotifiers (r:1 w:1)
// Storage: XcmPallet QueryCounter (r:1 w:1)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: XcmPallet Queries (r:0 w:1)
fn force_subscribe_version_notify() -> Weight {
// Minimum execution time: 41_818 nanoseconds.
Weight::from_ref_time(42_824_000)
.saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(6))
}
// Storage: XcmPallet VersionNotifiers (r:1 w:1)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: XcmPallet Queries (r:0 w:1)
fn force_unsubscribe_version_notify() -> Weight {
// Minimum execution time: 45_488 nanoseconds.
Weight::from_ref_time(46_295_000)
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(5))
}
// Storage: XcmPallet SupportedVersion (r:4 w:2)
fn migrate_supported_version() -> Weight {
// Minimum execution time: 14_614 nanoseconds.
Weight::from_ref_time(14_829_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifiers (r:4 w:2)
fn migrate_version_notifiers() -> Weight {
// Minimum execution time: 14_724 nanoseconds.
Weight::from_ref_time(14_915_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifyTargets (r:5 w:0)
fn already_notified_target() -> Weight {
// Minimum execution time: 16_814 nanoseconds.
Weight::from_ref_time(17_007_000)
.saturating_add(T::DbWeight::get().reads(5))
}
// Storage: XcmPallet VersionNotifyTargets (r:2 w:1)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn notify_current_targets() -> Weight {
// Minimum execution time: 37_273 nanoseconds.
Weight::from_ref_time(37_869_000)
.saturating_add(T::DbWeight::get().reads(7))
.saturating_add(T::DbWeight::get().writes(4))
}
// Storage: XcmPallet VersionNotifyTargets (r:3 w:0)
fn notify_target_migration_fail() -> Weight {
// Minimum execution time: 7_517 nanoseconds.
Weight::from_ref_time(7_682_000)
.saturating_add(T::DbWeight::get().reads(3))
}
// Storage: XcmPallet VersionNotifyTargets (r:4 w:2)
fn migrate_version_notify_targets() -> Weight {
// Minimum execution time: 15_088 nanoseconds.
Weight::from_ref_time(15_464_000)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
// Storage: XcmPallet VersionNotifyTargets (r:4 w:2)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
fn migrate_and_notify_old_targets() -> Weight {
// Minimum execution time: 42_829 nanoseconds.
Weight::from_ref_time(43_814_000)
.saturating_add(T::DbWeight::get().reads(9))
.saturating_add(T::DbWeight::get().writes(5))
}
}
@@ -16,23 +16,25 @@
//! Autogenerated weights for `runtime_parachains::ump`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2022-11-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm6`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! DATE: 2023-01-16, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024
// Executed Command:
// ./target/production/polkadot
// /home/benchbot/cargo_target_dir/production/polkadot
// benchmark
// pallet
// --chain=westend-dev
// --steps=50
// --repeat=20
// --pallet=runtime_parachains::ump
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/polkadot/.git/.artifacts/bench.json
// --pallet=runtime_parachains::ump
// --chain=westend-dev
// --header=./file_header.txt
// --output=./runtime/westend/src/weights/runtime_parachains_ump.rs
// --output=./runtime/westend/src/weights/
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
@@ -46,26 +48,27 @@ pub struct WeightInfo<T>(PhantomData<T>);
impl<T: frame_system::Config> runtime_parachains::ump::WeightInfo for WeightInfo<T> {
/// The range of component `s` is `[0, 51200]`.
fn process_upward_message(s: u32, ) -> Weight {
// Minimum execution time: 10_921 nanoseconds.
Weight::from_ref_time(5_417_303 as u64)
// Standard Error: 13
.saturating_add(Weight::from_ref_time(1_939 as u64).saturating_mul(s as u64))
// Minimum execution time: 10_155 nanoseconds.
Weight::from_ref_time(4_325_532)
// Standard Error: 11
.saturating_add(Weight::from_ref_time(1_926).saturating_mul(s.into()))
}
// Storage: Ump NeedsDispatch (r:1 w:1)
// Storage: Ump NextDispatchRoundStartWith (r:1 w:1)
// Storage: Ump RelayDispatchQueues (r:0 w:1)
// Storage: Ump RelayDispatchQueueSize (r:0 w:1)
fn clean_ump_after_outgoing() -> Weight {
// Minimum execution time: 9_499 nanoseconds.
Weight::from_ref_time(9_800_000 as u64)
.saturating_add(T::DbWeight::get().reads(2 as u64))
.saturating_add(T::DbWeight::get().writes(4 as u64))
// Minimum execution time: 9_674 nanoseconds.
Weight::from_ref_time(9_964_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(4))
}
// Storage: Ump Overweight (r:1 w:1)
// Storage: Ump CounterForOverweight (r:1 w:1)
fn service_overweight() -> Weight {
// Minimum execution time: 26_656 nanoseconds.
Weight::from_ref_time(27_122_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
// Minimum execution time: 29_605 nanoseconds.
Weight::from_ref_time(30_173_000)
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
}
}
+136 -77
View File
@@ -5,7 +5,7 @@ use crate::Runtime;
use frame_support::weights::Weight;
use sp_std::prelude::*;
use xcm::{
latest::{prelude::*, Weight as XCMWeight},
latest::{prelude::*, QueryResponseInfo},
DoubleEncoded,
};
@@ -31,15 +31,15 @@ impl From<&MultiAsset> for AssetTypes {
}
trait WeighMultiAssets {
fn weigh_multi_assets(&self, balances_weight: Weight) -> XCMWeight;
fn weigh_multi_assets(&self, balances_weight: Weight) -> Weight;
}
// Westend only knows about one asset, the balances pallet.
const MAX_ASSETS: u32 = 1;
impl WeighMultiAssets for MultiAssetFilter {
fn weigh_multi_assets(&self, balances_weight: Weight) -> XCMWeight {
let weight = match self {
fn weigh_multi_assets(&self, balances_weight: Weight) -> Weight {
match self {
Self::Definite(assets) => assets
.inner()
.into_iter()
@@ -49,155 +49,214 @@ impl WeighMultiAssets for MultiAssetFilter {
AssetTypes::Unknown => Weight::MAX,
})
.fold(Weight::zero(), |acc, x| acc.saturating_add(x)),
Self::Wild(_) => balances_weight.saturating_mul(MAX_ASSETS as u64),
};
weight.ref_time()
Self::Wild(AllOf { .. } | AllOfCounted { .. }) => balances_weight,
Self::Wild(AllCounted(count)) => balances_weight.saturating_mul(*count as u64),
Self::Wild(All) => balances_weight.saturating_mul(MAX_ASSETS as u64),
}
}
}
impl WeighMultiAssets for MultiAssets {
fn weigh_multi_assets(&self, balances_weight: Weight) -> XCMWeight {
let weight = self
.inner()
fn weigh_multi_assets(&self, balances_weight: Weight) -> Weight {
self.inner()
.into_iter()
.map(|m| <AssetTypes as From<&MultiAsset>>::from(m))
.map(|t| match t {
AssetTypes::Balances => balances_weight,
AssetTypes::Unknown => Weight::MAX,
})
.fold(Weight::zero(), |acc, x| acc.saturating_add(x));
weight.ref_time()
.fold(Weight::zero(), |acc, x| acc.saturating_add(x))
}
}
pub struct WestendXcmWeight<RuntimeCall>(core::marker::PhantomData<RuntimeCall>);
impl<RuntimeCall> XcmWeightInfo<RuntimeCall> for WestendXcmWeight<RuntimeCall> {
fn withdraw_asset(assets: &MultiAssets) -> XCMWeight {
fn withdraw_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::withdraw_asset())
}
fn reserve_asset_deposited(assets: &MultiAssets) -> XCMWeight {
fn reserve_asset_deposited(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::reserve_asset_deposited())
}
fn receive_teleported_asset(assets: &MultiAssets) -> XCMWeight {
fn receive_teleported_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::receive_teleported_asset())
}
fn query_response(_query_id: &u64, _response: &Response, _max_weight: &u64) -> XCMWeight {
XcmGeneric::<Runtime>::query_response().ref_time()
fn query_response(
_query_id: &u64,
_response: &Response,
_max_weight: &Weight,
_querier: &Option<MultiLocation>,
) -> Weight {
XcmGeneric::<Runtime>::query_response()
}
fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> XCMWeight {
fn transfer_asset(assets: &MultiAssets, _dest: &MultiLocation) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::transfer_asset())
}
fn transfer_reserve_asset(
assets: &MultiAssets,
_dest: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::transfer_reserve_asset())
}
fn transact(
_origin_type: &OriginKind,
_require_weight_at_most: &u64,
_origin_kind: &OriginKind,
_require_weight_at_most: &Weight,
_call: &DoubleEncoded<RuntimeCall>,
) -> XCMWeight {
XcmGeneric::<Runtime>::transact().ref_time()
) -> Weight {
XcmGeneric::<Runtime>::transact()
}
fn hrmp_new_channel_open_request(
_sender: &u32,
_max_message_size: &u32,
_max_capacity: &u32,
) -> XCMWeight {
) -> Weight {
// XCM Executor does not currently support HRMP channel operations
Weight::MAX.ref_time()
Weight::MAX
}
fn hrmp_channel_accepted(_recipient: &u32) -> XCMWeight {
fn hrmp_channel_accepted(_recipient: &u32) -> Weight {
// XCM Executor does not currently support HRMP channel operations
Weight::MAX.ref_time()
Weight::MAX
}
fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> XCMWeight {
fn hrmp_channel_closing(_initiator: &u32, _sender: &u32, _recipient: &u32) -> Weight {
// XCM Executor does not currently support HRMP channel operations
Weight::MAX.ref_time()
Weight::MAX
}
fn clear_origin() -> XCMWeight {
XcmGeneric::<Runtime>::clear_origin().ref_time()
fn clear_origin() -> Weight {
XcmGeneric::<Runtime>::clear_origin()
}
fn descend_origin(_who: &InteriorMultiLocation) -> XCMWeight {
XcmGeneric::<Runtime>::descend_origin().ref_time()
fn descend_origin(_who: &InteriorMultiLocation) -> Weight {
XcmGeneric::<Runtime>::descend_origin()
}
fn report_error(
_query_id: &QueryId,
_dest: &MultiLocation,
_max_response_weight: &u64,
) -> XCMWeight {
XcmGeneric::<Runtime>::report_error().ref_time()
fn report_error(_query_repsonse_info: &QueryResponseInfo) -> Weight {
XcmGeneric::<Runtime>::report_error()
}
fn deposit_asset(
assets: &MultiAssetFilter,
_max_assets: &u32, // TODO use max assets?
_dest: &MultiLocation,
) -> XCMWeight {
fn deposit_asset(assets: &MultiAssetFilter, _dest: &MultiLocation) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::deposit_asset())
}
fn deposit_reserve_asset(
assets: &MultiAssetFilter,
_max_assets: &u32, // TODO use max assets?
_dest: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::deposit_reserve_asset())
}
fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets) -> XCMWeight {
Weight::MAX.ref_time() // todo fix
fn exchange_asset(_give: &MultiAssetFilter, _receive: &MultiAssets, _maximal: &bool) -> Weight {
// Westend does not currently support exchange asset operations
Weight::MAX
}
fn initiate_reserve_withdraw(
assets: &MultiAssetFilter,
_reserve: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmGeneric::<Runtime>::initiate_reserve_withdraw())
}
fn initiate_teleport(
assets: &MultiAssetFilter,
_dest: &MultiLocation,
_xcm: &Xcm<()>,
) -> XCMWeight {
) -> Weight {
assets.weigh_multi_assets(XcmBalancesWeight::<Runtime>::initiate_teleport())
}
fn query_holding(
_query_id: &u64,
_dest: &MultiLocation,
_assets: &MultiAssetFilter,
_max_response_weight: &u64,
) -> XCMWeight {
XcmGeneric::<Runtime>::query_holding().ref_time()
fn report_holding(_response_info: &QueryResponseInfo, _assets: &MultiAssetFilter) -> Weight {
XcmGeneric::<Runtime>::report_holding()
}
fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> XCMWeight {
XcmGeneric::<Runtime>::buy_execution().ref_time()
fn buy_execution(_fees: &MultiAsset, _weight_limit: &WeightLimit) -> Weight {
XcmGeneric::<Runtime>::buy_execution()
}
fn refund_surplus() -> XCMWeight {
XcmGeneric::<Runtime>::refund_surplus().ref_time()
fn refund_surplus() -> Weight {
XcmGeneric::<Runtime>::refund_surplus()
}
fn set_error_handler(_xcm: &Xcm<RuntimeCall>) -> XCMWeight {
XcmGeneric::<Runtime>::set_error_handler().ref_time()
fn set_error_handler(_xcm: &Xcm<RuntimeCall>) -> Weight {
XcmGeneric::<Runtime>::set_error_handler()
}
fn set_appendix(_xcm: &Xcm<RuntimeCall>) -> XCMWeight {
XcmGeneric::<Runtime>::set_appendix().ref_time()
fn set_appendix(_xcm: &Xcm<RuntimeCall>) -> Weight {
XcmGeneric::<Runtime>::set_appendix()
}
fn clear_error() -> XCMWeight {
XcmGeneric::<Runtime>::clear_error().ref_time()
fn clear_error() -> Weight {
XcmGeneric::<Runtime>::clear_error()
}
fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> XCMWeight {
XcmGeneric::<Runtime>::claim_asset().ref_time()
fn claim_asset(_assets: &MultiAssets, _ticket: &MultiLocation) -> Weight {
XcmGeneric::<Runtime>::claim_asset()
}
fn trap(_code: &u64) -> XCMWeight {
XcmGeneric::<Runtime>::trap().ref_time()
fn trap(_code: &u64) -> Weight {
XcmGeneric::<Runtime>::trap()
}
fn subscribe_version(_query_id: &QueryId, _max_response_weight: &u64) -> XCMWeight {
XcmGeneric::<Runtime>::subscribe_version().ref_time()
fn subscribe_version(_query_id: &QueryId, _max_response_weight: &Weight) -> Weight {
XcmGeneric::<Runtime>::subscribe_version()
}
fn unsubscribe_version() -> XCMWeight {
XcmGeneric::<Runtime>::unsubscribe_version().ref_time()
fn unsubscribe_version() -> Weight {
XcmGeneric::<Runtime>::unsubscribe_version()
}
fn burn_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmGeneric::<Runtime>::burn_asset())
}
fn expect_asset(assets: &MultiAssets) -> Weight {
assets.weigh_multi_assets(XcmGeneric::<Runtime>::expect_asset())
}
fn expect_origin(_origin: &Option<MultiLocation>) -> Weight {
XcmGeneric::<Runtime>::expect_origin()
}
fn expect_error(_error: &Option<(u32, XcmError)>) -> Weight {
XcmGeneric::<Runtime>::expect_error()
}
fn query_pallet(_module_name: &Vec<u8>, _response_info: &QueryResponseInfo) -> Weight {
XcmGeneric::<Runtime>::query_pallet()
}
fn expect_pallet(
_index: &u32,
_name: &Vec<u8>,
_module_name: &Vec<u8>,
_crate_major: &u32,
_min_crate_minor: &u32,
) -> Weight {
XcmGeneric::<Runtime>::expect_pallet()
}
fn report_transact_status(_response_info: &QueryResponseInfo) -> Weight {
XcmGeneric::<Runtime>::report_transact_status()
}
fn clear_transact_status() -> Weight {
XcmGeneric::<Runtime>::clear_transact_status()
}
fn universal_origin(_: &Junction) -> Weight {
// Westend does not currently support universal origin operations
Weight::MAX
}
fn export_message(_: &NetworkId, _: &Junctions, _: &Xcm<()>) -> Weight {
// Westend does not currently support export message operations
Weight::MAX
}
fn lock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Westend does not currently support asset locking operations
Weight::MAX
}
fn unlock_asset(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Westend does not currently support asset locking operations
Weight::MAX
}
fn note_unlockable(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Westend does not currently support asset locking operations
Weight::MAX
}
fn request_unlock(_: &MultiAsset, _: &MultiLocation) -> Weight {
// Westend does not currently support asset locking operations
Weight::MAX
}
fn set_fees_mode(_: &bool) -> Weight {
XcmGeneric::<Runtime>::set_fees_mode()
}
fn set_topic(_topic: &[u8; 32]) -> Weight {
XcmGeneric::<Runtime>::set_topic()
}
fn clear_topic() -> Weight {
XcmGeneric::<Runtime>::clear_topic()
}
fn alias_origin(_: &MultiLocation) -> Weight {
// XCM Executor does not currently support alias origin operations
Weight::MAX
}
fn unpaid_execution(_: &WeightLimit, _: &Option<MultiLocation>) -> Weight {
XcmGeneric::<Runtime>::unpaid_execution()
}
}
@@ -1,4 +1,4 @@
// Copyright 2017-2021 Parity Technologies (UK) Ltd.
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
@@ -17,23 +17,25 @@
//! Autogenerated weights for `pallet_xcm_benchmarks::generic`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2021-12-01, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 128
//! DATE: 2022-12-12, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024
// Executed Command:
// target/release/polkadot
// /home/benchbot/cargo_target_dir/production/polkadot
// benchmark
// --chain=westend-dev
// pallet
// --steps=50
// --repeat=20
// --pallet=pallet_xcm_benchmarks::generic
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/polkadot/.git/.artifacts/bench.json
// --pallet=pallet_xcm_benchmarks::generic
// --chain=westend-dev
// --header=./file_header.txt
// --template=./xcm/pallet-xcm-benchmarks/template.hbs
// --output=./runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs
// --output=./runtime/westend/src/weights/xcm/
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
@@ -48,85 +50,135 @@ impl<T: frame_system::Config> WeightInfo<T> {
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
pub(crate) fn query_holding() -> Weight {
Weight::from_ref_time(39_278_000 as u64)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
pub(crate) fn report_holding() -> Weight {
Weight::from_ref_time(34_089_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
pub(crate) fn buy_execution() -> Weight {
Weight::from_ref_time(5_922_000 as u64)
Weight::from_ref_time(5_751_000 as u64)
}
// Storage: XcmPallet Queries (r:1 w:0)
pub(crate) fn query_response() -> Weight {
Weight::from_ref_time(20_625_000 as u64)
Weight::from_ref_time(17_938_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
}
pub(crate) fn transact() -> Weight {
Weight::from_ref_time(22_198_000 as u64)
Weight::from_ref_time(20_699_000 as u64)
}
pub(crate) fn refund_surplus() -> Weight {
Weight::from_ref_time(6_122_000 as u64)
Weight::from_ref_time(6_077_000 as u64)
}
pub(crate) fn set_error_handler() -> Weight {
Weight::from_ref_time(5_758_000 as u64)
Weight::from_ref_time(5_747_000 as u64)
}
pub(crate) fn set_appendix() -> Weight {
Weight::from_ref_time(5_764_000 as u64)
Weight::from_ref_time(5_837_000 as u64)
}
pub(crate) fn clear_error() -> Weight {
Weight::from_ref_time(5_679_000 as u64)
Weight::from_ref_time(5_712_000 as u64)
}
pub(crate) fn descend_origin() -> Weight {
Weight::from_ref_time(7_206_000 as u64)
Weight::from_ref_time(6_471_000 as u64)
}
pub(crate) fn clear_origin() -> Weight {
Weight::from_ref_time(5_738_000 as u64)
Weight::from_ref_time(5_725_000 as u64)
}
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
pub(crate) fn report_error() -> Weight {
Weight::from_ref_time(31_512_000 as u64)
Weight::from_ref_time(29_975_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
// Storage: XcmPallet AssetTraps (r:1 w:1)
pub(crate) fn claim_asset() -> Weight {
Weight::from_ref_time(13_594_000 as u64)
Weight::from_ref_time(21_598_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
pub(crate) fn trap() -> Weight {
Weight::from_ref_time(5_745_000 as u64)
Weight::from_ref_time(5_665_000 as u64)
}
// Storage: XcmPallet VersionNotifyTargets (r:1 w:1)
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
pub(crate) fn subscribe_version() -> Weight {
Weight::from_ref_time(38_138_000 as u64)
Weight::from_ref_time(38_343_000 as u64)
.saturating_add(T::DbWeight::get().reads(6 as u64))
.saturating_add(T::DbWeight::get().writes(4 as u64))
}
// Storage: XcmPallet VersionNotifyTargets (r:0 w:1)
pub(crate) fn unsubscribe_version() -> Weight {
Weight::from_ref_time(9_127_000 as u64)
Weight::from_ref_time(8_353_000 as u64)
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
pub(crate) fn initiate_reserve_withdraw() -> Weight {
Weight::from_ref_time(41_443_000 as u64)
Weight::from_ref_time(33_100_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
pub(crate) fn burn_asset() -> Weight {
Weight::from_ref_time(7_259_000 as u64)
}
pub(crate) fn expect_asset() -> Weight {
Weight::from_ref_time(5_848_000 as u64)
}
pub(crate) fn expect_origin() -> Weight {
Weight::from_ref_time(5_787_000 as u64)
}
pub(crate) fn expect_error() -> Weight {
Weight::from_ref_time(5_775_000 as u64)
}
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
pub(crate) fn query_pallet() -> Weight {
Weight::from_ref_time(34_846_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
pub(crate) fn expect_pallet() -> Weight {
Weight::from_ref_time(8_844_000 as u64)
}
// Storage: XcmPallet SupportedVersion (r:1 w:0)
// Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1)
// Storage: XcmPallet SafeXcmVersion (r:1 w:0)
// Storage: Dmp DownwardMessageQueues (r:1 w:1)
// Storage: Dmp DownwardMessageQueueHeads (r:1 w:1)
pub(crate) fn report_transact_status() -> Weight {
Weight::from_ref_time(50_256_000 as u64)
.saturating_add(T::DbWeight::get().reads(5 as u64))
.saturating_add(T::DbWeight::get().writes(3 as u64))
}
pub(crate) fn clear_transact_status() -> Weight {
Weight::from_ref_time(9_959_000 as u64)
}
pub(crate) fn set_topic() -> Weight {
Weight::from_ref_time(10_007_000 as u64)
}
pub(crate) fn clear_topic() -> Weight {
Weight::from_ref_time(8_289_000 as u64)
}
pub(crate) fn set_fees_mode() -> Weight {
Weight::from_ref_time(5_764_000 as u64)
}
pub(crate) fn unpaid_execution() -> Weight {
Weight::from_ref_time(5_924_000 as u64)
}
}
+162 -34
View File
@@ -17,52 +17,54 @@
//! XCM configurations for Westend.
use super::{
parachains_origin, weights, AccountId, Balances, ParaId, Runtime, RuntimeCall, RuntimeEvent,
RuntimeOrigin, WeightToFee, XcmPallet,
parachains_origin, weights, AccountId, AllPalletsWithSystem, Balances, ParaId, Runtime,
RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmPallet,
};
use frame_support::{
parameter_types,
traits::{Everything, Nothing},
traits::{Contains, Everything, Nothing},
};
use runtime_common::{xcm_sender, ToAuthor};
use sp_core::ConstU32;
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, ChildParachainAsNative,
AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses,
AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative,
ChildParachainConvertsVia, ChildSystemParachainAsSuperuser,
CurrencyAdapter as XcmCurrencyAdapter, IsChildSystemParachain, IsConcrete, LocationInverter,
CurrencyAdapter as XcmCurrencyAdapter, IsChildSystemParachain, IsConcrete, MintLocation,
SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
UsingComponents, WeightInfoBounds,
UsingComponents, WeightInfoBounds, WithComputedOrigin,
};
use xcm_executor::{traits::WithOriginFilter, XcmExecutor};
parameter_types! {
pub const WndLocation: MultiLocation = Here.into();
pub const Ancestry: MultiLocation = Here.into();
pub WestendNetwork: NetworkId =
NetworkId::Named(b"Westend".to_vec().try_into().expect("shorter than length limit; qed"));
pub const TokenLocation: MultiLocation = Here.into_location();
pub const ThisNetwork: NetworkId = Westend;
pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into();
pub CheckAccount: AccountId = XcmPallet::check_account();
pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local);
}
pub type LocationConverter =
(ChildParachainConvertsVia<ParaId, AccountId>, AccountId32Aliases<WestendNetwork, AccountId>);
(ChildParachainConvertsVia<ParaId, AccountId>, AccountId32Aliases<ThisNetwork, AccountId>);
pub type LocalAssetTransactor = XcmCurrencyAdapter<
// Use this currency:
Balances,
// Use this currency when it is a fungible asset matching the given location or name:
IsConcrete<WndLocation>,
IsConcrete<TokenLocation>,
// We can convert the MultiLocations with our converter above:
LocationConverter,
// Our chain's account ID type (we can't get away without mentioning it explicitly):
AccountId,
// It's a native asset so we keep track of the teleports to maintain total issuance.
CheckAccount,
LocalCheckAccount,
>;
type LocalOriginConverter = (
SovereignSignedViaLocation<LocationConverter, RuntimeOrigin>,
ChildParachainAsNative<parachains_origin::Origin, RuntimeOrigin>,
SignedAccountId32AsNative<WestendNetwork, RuntimeOrigin>,
SignedAccountId32AsNative<ThisNetwork, RuntimeOrigin>,
ChildSystemParachainAsSuperuser<ParaId, RuntimeOrigin>,
);
@@ -70,35 +72,143 @@ type LocalOriginConverter = (
/// individual routers.
pub type XcmRouter = (
// Only one router so far - use DMP to communicate with child parachains.
xcm_sender::ChildParachainRouter<Runtime, XcmPallet>,
xcm_sender::ChildParachainRouter<Runtime, XcmPallet, ()>,
);
parameter_types! {
pub const Westmint: MultiLocation = Parachain(1000).into();
pub const Collectives: MultiLocation = Parachain(1001).into();
pub const WestendForWestmint: (MultiAssetFilter, MultiLocation) =
(Wild(AllOf { fun: WildFungible, id: Concrete(WndLocation::get()) }), Westmint::get());
pub const WestendForCollectives: (MultiAssetFilter, MultiLocation) =
(Wild(AllOf { fun: WildFungible, id: Concrete(WndLocation::get()) }), Collectives::get());
pub const Westmint: MultiLocation = Parachain(1000).into_location();
pub const Collectives: MultiLocation = Parachain(1001).into_location();
pub const Wnd: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) });
pub const WndForWestmint: (MultiAssetFilter, MultiLocation) = (Wnd::get(), Westmint::get());
pub const WndForCollectives: (MultiAssetFilter, MultiLocation) = (Wnd::get(), Collectives::get());
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 64;
}
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parachain(1000).into());
}
pub type TrustedTeleporters =
(xcm_builder::Case<WestendForWestmint>, xcm_builder::Case<WestendForCollectives>);
(xcm_builder::Case<WndForWestmint>, xcm_builder::Case<WndForCollectives>);
/// The barriers one of which must be passed for an XCM message to be executed.
pub type Barrier = (
// Weight that is paid for may be consumed.
TakeWeightCredit,
// If the message is one that immediately attemps to pay for execution, then allow it.
AllowTopLevelPaidExecutionFrom<Everything>,
// Messages coming from system parachains need not pay for execution.
AllowUnpaidExecutionFrom<IsChildSystemParachain<ParaId>>,
// Expected responses are OK.
AllowKnownQueryResponses<XcmPallet>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom<Everything>,
WithComputedOrigin<
(
// If the message is one that immediately attemps to pay for execution, then allow it.
AllowTopLevelPaidExecutionFrom<Everything>,
// Messages coming from system parachains need not pay for execution.
AllowExplicitUnpaidExecutionFrom<IsChildSystemParachain<ParaId>>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom<Everything>,
),
UniversalLocation,
ConstU32<8>,
>,
);
/// A call filter for the XCM Transact instruction. This is a temporary measure until we
/// properly account for proof size weights.
///
/// Calls that are allowed through this filter must:
/// 1. Have a fixed weight;
/// 2. Cannot lead to another call being made;
/// 3. Have a defined proof size weight, e.g. no unbounded vecs in call parameters.
pub struct SafeCallFilter;
impl Contains<RuntimeCall> for SafeCallFilter {
fn contains(call: &RuntimeCall) -> bool {
#[cfg(feature = "runtime-benchmarks")]
{
if matches!(call, RuntimeCall::System(frame_system::Call::remark_with_event { .. })) {
return true
}
}
match call {
RuntimeCall::System(
frame_system::Call::kill_prefix { .. } | frame_system::Call::set_heap_pages { .. },
) |
RuntimeCall::Babe(..) |
RuntimeCall::Timestamp(..) |
RuntimeCall::Indices(..) |
RuntimeCall::Balances(..) |
RuntimeCall::Staking(
pallet_staking::Call::bond { .. } |
pallet_staking::Call::bond_extra { .. } |
pallet_staking::Call::unbond { .. } |
pallet_staking::Call::withdraw_unbonded { .. } |
pallet_staking::Call::validate { .. } |
pallet_staking::Call::nominate { .. } |
pallet_staking::Call::chill { .. } |
pallet_staking::Call::set_payee { .. } |
pallet_staking::Call::set_controller { .. } |
pallet_staking::Call::set_validator_count { .. } |
pallet_staking::Call::increase_validator_count { .. } |
pallet_staking::Call::scale_validator_count { .. } |
pallet_staking::Call::force_no_eras { .. } |
pallet_staking::Call::force_new_era { .. } |
pallet_staking::Call::set_invulnerables { .. } |
pallet_staking::Call::force_unstake { .. } |
pallet_staking::Call::force_new_era_always { .. } |
pallet_staking::Call::payout_stakers { .. } |
pallet_staking::Call::rebond { .. } |
pallet_staking::Call::reap_stash { .. } |
pallet_staking::Call::set_staking_configs { .. } |
pallet_staking::Call::chill_other { .. } |
pallet_staking::Call::force_apply_min_commission { .. },
) |
RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) |
RuntimeCall::Grandpa(..) |
RuntimeCall::ImOnline(..) |
RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) |
RuntimeCall::Identity(
pallet_identity::Call::add_registrar { .. } |
pallet_identity::Call::set_identity { .. } |
pallet_identity::Call::clear_identity { .. } |
pallet_identity::Call::request_judgement { .. } |
pallet_identity::Call::cancel_request { .. } |
pallet_identity::Call::set_fee { .. } |
pallet_identity::Call::set_account_id { .. } |
pallet_identity::Call::set_fields { .. } |
pallet_identity::Call::provide_judgement { .. } |
pallet_identity::Call::kill_identity { .. } |
pallet_identity::Call::add_sub { .. } |
pallet_identity::Call::rename_sub { .. } |
pallet_identity::Call::remove_sub { .. } |
pallet_identity::Call::quit_sub { .. },
) |
RuntimeCall::Recovery(..) |
RuntimeCall::Vesting(..) |
RuntimeCall::ElectionProviderMultiPhase(..) |
RuntimeCall::VoterList(..) |
RuntimeCall::NominationPools(
pallet_nomination_pools::Call::join { .. } |
pallet_nomination_pools::Call::bond_extra { .. } |
pallet_nomination_pools::Call::claim_payout { .. } |
pallet_nomination_pools::Call::unbond { .. } |
pallet_nomination_pools::Call::pool_withdraw_unbonded { .. } |
pallet_nomination_pools::Call::withdraw_unbonded { .. } |
pallet_nomination_pools::Call::create { .. } |
pallet_nomination_pools::Call::create_with_pool_id { .. } |
pallet_nomination_pools::Call::set_state { .. } |
pallet_nomination_pools::Call::set_configs { .. } |
pallet_nomination_pools::Call::update_roles { .. } |
pallet_nomination_pools::Call::chill { .. },
) |
RuntimeCall::XcmPallet(pallet_xcm::Call::limited_reserve_transfer_assets {
..
}) => true,
_ => false,
}
}
}
pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
@@ -107,22 +217,32 @@ impl xcm_executor::Config for XcmConfig {
type OriginConverter = LocalOriginConverter;
type IsReserve = ();
type IsTeleporter = TrustedTeleporters;
type LocationInverter = LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
type Weigher =
WeightInfoBounds<weights::xcm::WestendXcmWeight<RuntimeCall>, RuntimeCall, MaxInstructions>;
type Trader = UsingComponents<WeightToFee, WndLocation, AccountId, Balances, ToAuthor<Runtime>>;
type Trader =
UsingComponents<WeightToFee, TokenLocation, AccountId, Balances, ToAuthor<Runtime>>;
type ResponseHandler = XcmPallet;
type AssetTrap = XcmPallet;
type AssetLocker = ();
type AssetExchanger = ();
type AssetClaims = XcmPallet;
type SubscriptionService = XcmPallet;
type PalletInstancesInfo = AllPalletsWithSystem;
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = ();
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = WithOriginFilter<SafeCallFilter>;
type SafeCallFilter = SafeCallFilter;
}
/// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior location
/// of this chain.
pub type LocalOriginToLocation = (
// And a usual Signed origin to be used in XCM as a corresponding AccountId32
SignedToAccountId32<RuntimeOrigin, AccountId, WestendNetwork>,
SignedToAccountId32<RuntimeOrigin, AccountId, ThisNetwork>,
);
impl pallet_xcm::Config for Runtime {
@@ -133,14 +253,22 @@ impl pallet_xcm::Config for Runtime {
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
// ...but they must match our filter, which rejects everything.
type XcmExecuteFilter = Nothing;
type XcmExecutor = xcm_executor::XcmExecutor<XcmConfig>;
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = Everything;
type XcmReserveTransferFilter = Everything;
type Weigher =
WeightInfoBounds<weights::xcm::WestendXcmWeight<RuntimeCall>, RuntimeCall, MaxInstructions>;
type LocationInverter = LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
type Currency = Balances;
type CurrencyMatcher = IsConcrete<TokenLocation>;
type TrustedLockers = ();
type SovereignAccountOf = LocationConverter;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
}
+4
View File
@@ -163,6 +163,7 @@ MQC/SM
msg
multisig/S
multivalidator/SM
mutators
mutex
natively
NFA
@@ -220,6 +221,7 @@ proxy/G
proxying
PRs
PVF/S
querier
README/MS
redhat/M
register/CD
@@ -254,6 +256,7 @@ SS58
SSL
startup/MS
stateful
Statemine
str
struct/MS
subcommand/SM
@@ -291,6 +294,7 @@ UI
unapplied
unassign
unconcluded
unexpectable
unfinalize/B
unfinalized
union/MSG
+12 -8
View File
@@ -6,22 +6,26 @@ authors.workspace = true
edition.workspace = true
[dependencies]
derivative = { version = "2.2.0", default-features = false, features = [ "use_core" ] }
impl-trait-for-tuples = "0.2.2"
parity-scale-codec = { version = "3.1.5", default-features = false, features = ["derive", "max-encoded-len"] }
scale-info = { version = "2.1.2", default-features = false, features = ["derive"] }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
derivative = {version = "2.2.0", default-features = false, features = [ "use_core" ] }
log = { version = "0.4.17", default-features = false }
parity-scale-codec = { version = "3.1.5", default-features = false, features = [ "derive", "max-encoded-len" ] }
scale-info = { version = "2.1.2", default-features = false, features = ["derive"] }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-weights = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
serde = { version = "1.0.136", optional = true, features = ["derive"] }
xcm-procedural = { path = "procedural" }
[dev-dependencies]
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
[features]
default = ["std"]
wasm-api = []
runtime-benchmarks = [
"sp-runtime/runtime-benchmarks",
]
std = [
"parity-scale-codec/std",
"scale-info/std",
"sp-runtime/std",
"serde",
"sp-core/std",
"sp-weights/std",
]
@@ -14,18 +14,18 @@ frame-support = { default-features = false, branch = "master", git = "https://gi
frame-system = { default-features = false, branch = "master", git = "https://github.com/paritytech/substrate" }
sp-runtime = { default-features = false, branch = "master", git = "https://github.com/paritytech/substrate" }
sp-std = { default-features = false, branch = "master", git = "https://github.com/paritytech/substrate" }
sp-io = { default-features = false, branch = "master", git = "https://github.com/paritytech/substrate" }
xcm-executor = { path = "../xcm-executor", default-features = false }
frame-benchmarking = { default-features = false, branch = "master", git = "https://github.com/paritytech/substrate" }
xcm = { path = "..", default-features = false }
xcm-builder = { path = "../xcm-builder", default-features = false }
log = "0.4.17"
[dev-dependencies]
pallet-balances = { branch = "master", git = "https://github.com/paritytech/substrate" }
pallet-assets = { branch = "master", git = "https://github.com/paritytech/substrate" }
sp-core = { branch = "master", git = "https://github.com/paritytech/substrate" }
sp-io = { branch = "master", git = "https://github.com/paritytech/substrate" }
sp-tracing = { branch = "master", git = "https://github.com/paritytech/substrate" }
xcm-builder = { path = "../xcm-builder" }
xcm = { path = ".." }
# temp
pallet-xcm = { path = "../pallet-xcm" }
@@ -40,11 +40,14 @@ std = [
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std"
"sp-std/std",
"xcm-builder/std",
"xcm-executor/std"
]
runtime-benchmarks = [
"xcm/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
@@ -41,23 +41,31 @@ benchmarks_instance_pallet! {
withdraw_asset {
let (sender_account, sender_location) = account_and_location::<T>(1);
let worst_case_holding = T::worst_case_holding();
let worst_case_holding = T::worst_case_holding(0);
let asset = T::get_multi_asset();
<AssetTransactorOf<T>>::deposit_asset(&asset, &sender_location).unwrap();
<AssetTransactorOf<T>>::deposit_asset(
&asset,
&sender_location,
&XcmContext {
origin: Some(sender_location.clone()),
message_hash: [0; 32],
topic: None,
},
).unwrap();
// check the assets of origin.
assert!(!T::TransactAsset::balance(&sender_account).is_zero());
let mut executor = new_executor::<T>(sender_location);
executor.holding = worst_case_holding.into();
executor.set_holding(worst_case_holding.into());
let instruction = Instruction::<XcmCallOf<T>>::WithdrawAsset(vec![asset.clone()].into());
let xcm = Xcm(vec![instruction]);
}: {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
// check one of the assets of origin.
assert!(T::TransactAsset::balance(&sender_account).is_zero());
assert!(executor.holding.ensure_contains(&vec![asset].into()).is_ok());
assert!(executor.holding().ensure_contains(&vec![asset].into()).is_ok());
}
transfer_asset {
@@ -69,14 +77,22 @@ benchmarks_instance_pallet! {
let dest_location = T::valid_destination()?;
let dest_account = T::AccountIdConverter::convert(dest_location.clone()).unwrap();
<AssetTransactorOf<T>>::deposit_asset(&asset, &sender_location).unwrap();
<AssetTransactorOf<T>>::deposit_asset(
&asset,
&sender_location,
&XcmContext {
origin: Some(sender_location.clone()),
message_hash: [0; 32],
topic: None,
},
).unwrap();
assert!(T::TransactAsset::balance(&dest_account).is_zero());
let mut executor = new_executor::<T>(sender_location);
let instruction = Instruction::TransferAsset { assets, beneficiary: dest_location };
let xcm = Xcm(vec![instruction]);
}: {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
assert!(T::TransactAsset::balance(&sender_account).is_zero());
assert!(!T::TransactAsset::balance(&dest_account).is_zero());
@@ -88,7 +104,15 @@ benchmarks_instance_pallet! {
let dest_account = T::AccountIdConverter::convert(dest_location.clone()).unwrap();
let asset = T::get_multi_asset();
<AssetTransactorOf<T>>::deposit_asset(&asset, &sender_location).unwrap();
<AssetTransactorOf<T>>::deposit_asset(
&asset,
&sender_location,
&XcmContext {
origin: Some(sender_location.clone()),
message_hash: [0; 32],
topic: None,
},
).unwrap();
let assets: MultiAssets = vec![ asset ].into();
assert!(T::TransactAsset::balance(&dest_account).is_zero());
@@ -100,38 +124,19 @@ benchmarks_instance_pallet! {
};
let xcm = Xcm(vec![instruction]);
}: {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
assert!(T::TransactAsset::balance(&sender_account).is_zero());
assert!(!T::TransactAsset::balance(&dest_account).is_zero());
// TODO: Check sender queue is not empty. #4426
}
reserve_asset_deposited {
let (trusted_reserve, transferable_reserve_asset) = T::TrustedReserve::get()
.ok_or(BenchmarkError::Skip)?;
let assets: MultiAssets = vec![ transferable_reserve_asset ].into();
let mut executor = new_executor::<T>(trusted_reserve);
let instruction = Instruction::ReserveAssetDeposited(assets.clone());
let xcm = Xcm(vec![instruction]);
}: {
executor.execute(xcm).map_err(|_| {
BenchmarkError::Override(
BenchmarkResult::from_weight(T::BlockWeights::get().max_block)
)
})?;
} verify {
assert!(executor.holding.ensure_contains(&assets).is_ok());
}
receive_teleported_asset {
// If there is no trusted teleporter, then we skip this benchmark.
let (trusted_teleporter, teleportable_asset) = T::TrustedTeleporter::get()
.ok_or(BenchmarkError::Skip)?;
if let Some(checked_account) = T::CheckedAccount::get() {
if let Some((checked_account, _)) = T::CheckedAccount::get() {
T::TransactAsset::mint_into(
&checked_account,
<
@@ -148,18 +153,18 @@ benchmarks_instance_pallet! {
let instruction = Instruction::ReceiveTeleportedAsset(assets.clone());
let xcm = Xcm(vec![instruction]);
}: {
executor.execute(xcm).map_err(|_| {
executor.bench_process(xcm).map_err(|_| {
BenchmarkError::Override(
BenchmarkResult::from_weight(T::BlockWeights::get().max_block)
)
})?;
} verify {
assert!(executor.holding.ensure_contains(&assets).is_ok());
assert!(executor.holding().ensure_contains(&assets).is_ok());
}
deposit_asset {
let asset = T::get_multi_asset();
let mut holding = T::worst_case_holding();
let mut holding = T::worst_case_holding(1);
// Add our asset to the holding.
holding.push(asset.clone());
@@ -170,15 +175,14 @@ benchmarks_instance_pallet! {
assert!(T::TransactAsset::balance(&dest_account).is_zero());
let mut executor = new_executor::<T>(Default::default());
executor.holding = holding.into();
executor.set_holding(holding.into());
let instruction = Instruction::<XcmCallOf<T>>::DepositAsset {
assets: asset.into(),
max_assets: 1,
beneficiary: dest_location,
};
let xcm = Xcm(vec![instruction]);
}: {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
// dest should have received some asset.
assert!(!T::TransactAsset::balance(&dest_account).is_zero())
@@ -186,7 +190,7 @@ benchmarks_instance_pallet! {
deposit_reserve_asset {
let asset = T::get_multi_asset();
let mut holding = T::worst_case_holding();
let mut holding = T::worst_case_holding(1);
// Add our asset to the holding.
holding.push(asset.clone());
@@ -197,16 +201,15 @@ benchmarks_instance_pallet! {
assert!(T::TransactAsset::balance(&dest_account).is_zero());
let mut executor = new_executor::<T>(Default::default());
executor.holding = holding.into();
executor.set_holding(holding.into());
let instruction = Instruction::<XcmCallOf<T>>::DepositReserveAsset {
assets: asset.into(),
max_assets: 1,
dest: dest_location,
xcm: Xcm::new(),
};
let xcm = Xcm(vec![instruction]);
}: {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
// dest should have received some asset.
assert!(!T::TransactAsset::balance(&dest_account).is_zero())
@@ -214,16 +217,16 @@ benchmarks_instance_pallet! {
initiate_teleport {
let asset = T::get_multi_asset();
let mut holding = T::worst_case_holding();
let mut holding = T::worst_case_holding(0);
// Add our asset to the holding.
holding.push(asset.clone());
// Checked account starts at zero
assert!(T::CheckedAccount::get().map_or(true, |c| T::TransactAsset::balance(&c).is_zero()));
assert!(T::CheckedAccount::get().map_or(true, |(c, _)| T::TransactAsset::balance(&c).is_zero()));
let mut executor = new_executor::<T>(Default::default());
executor.holding = holding.into();
executor.set_holding(holding.into());
let instruction = Instruction::<XcmCallOf<T>>::InitiateTeleport {
assets: asset.into(),
dest: T::valid_destination()?,
@@ -231,9 +234,9 @@ benchmarks_instance_pallet! {
};
let xcm = Xcm(vec![instruction]);
}: {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
if let Some(checked_account) = T::CheckedAccount::get() {
if let Some((checked_account, _)) = T::CheckedAccount::get() {
// teleport checked account should have received some asset.
assert!(!T::TransactAsset::balance(&checked_account).is_zero());
}
@@ -18,7 +18,11 @@
use crate::{fungible as xcm_balances_benchmark, mock::*};
use frame_benchmarking::BenchmarkError;
use frame_support::{parameter_types, traits::Everything, weights::Weight};
use frame_support::{
parameter_types,
traits::{Everything, Nothing},
weights::Weight,
};
use sp_core::H256;
use sp_runtime::{
testing::Header,
@@ -26,7 +30,7 @@ use sp_runtime::{
BuildStorage,
};
use xcm::latest::prelude::*;
use xcm_builder::AllowUnpaidExecutionFrom;
use xcm_builder::{AllowUnpaidExecutionFrom, MintLocation};
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
@@ -47,9 +51,7 @@ frame_support::construct_runtime!(
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(
Weight::from_ref_time(1024).set_proof_size(u64::MAX),
);
frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, u64::MAX));
}
impl frame_system::Config for Test {
type BaseCallFilter = Everything;
@@ -119,13 +121,14 @@ pub type AssetTransactor = xcm_builder::CurrencyAdapter<
MatchAnyFungible,
AccountIdConverter,
u64,
CheckedAccount,
CheckingAccount,
>;
parameter_types! {
/// Maximum number of instructions in a single XCM fragment. A sanity check against weight
/// calculations getting too crazy.
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 64;
}
pub struct XcmConfig;
@@ -134,16 +137,25 @@ impl xcm_executor::Config for XcmConfig {
type XcmSender = DevNull;
type AssetTransactor = AssetTransactor;
type OriginConverter = ();
type IsReserve = TrustedReserves;
type IsReserve = ();
type IsTeleporter = TrustedTeleporters;
type LocationInverter = xcm_builder::LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type Barrier = AllowUnpaidExecutionFrom<Everything>;
type Weigher = xcm_builder::FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
type Trader = xcm_builder::FixedRateOfFungible<WeightPrice, ()>;
type ResponseHandler = DevNull;
type AssetTrap = ();
type AssetLocker = ();
type AssetExchanger = ();
type AssetClaims = ();
type SubscriptionService = ();
type PalletInstancesInfo = AllPalletsWithSystem;
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = ();
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = RuntimeCall;
type SafeCallFilter = Everything;
}
impl crate::Config for Test {
@@ -151,40 +163,37 @@ impl crate::Config for Test {
type AccountIdConverter = AccountIdConverter;
fn valid_destination() -> Result<MultiLocation, BenchmarkError> {
let valid_destination: MultiLocation =
X1(AccountId32 { network: NetworkId::Any, id: [0u8; 32] }).into();
X1(AccountId32 { network: None, id: [0u8; 32] }).into();
Ok(valid_destination)
}
fn worst_case_holding() -> MultiAssets {
crate::mock_worst_case_holding()
fn worst_case_holding(depositable_count: u32) -> MultiAssets {
crate::mock_worst_case_holding(
depositable_count,
<XcmConfig as xcm_executor::Config>::MaxAssetsIntoHolding::get(),
)
}
}
pub type TrustedTeleporters = (xcm_builder::Case<TeleConcreteFung>,);
pub type TrustedReserves = (xcm_builder::Case<RsrvConcreteFung>,);
pub type TrustedTeleporters = (xcm_builder::Case<TeleportConcreteFungible>,);
parameter_types! {
pub const CheckedAccount: Option<u64> = Some(100);
pub const ChildTeleporter: MultiLocation = Parachain(1000).into();
pub const CheckingAccount: Option<(u64, MintLocation)> = Some((100, MintLocation::Local));
pub const ChildTeleporter: MultiLocation = Parachain(1000).into_location();
pub const TrustedTeleporter: Option<(MultiLocation, MultiAsset)> = Some((
ChildTeleporter::get(),
MultiAsset { id: Concrete(Here.into()), fun: Fungible(100) },
MultiAsset { id: Concrete(Here.into_location()), fun: Fungible(100) },
));
pub const TrustedReserve: Option<(MultiLocation, MultiAsset)> = Some((
ChildTeleporter::get(),
MultiAsset { id: Concrete(Here.into()), fun: Fungible(100) },
));
pub const TeleConcreteFung: (MultiAssetFilter, MultiLocation) =
(Wild(AllOf { fun: WildFungible, id: Concrete(Here.into()) }), ChildTeleporter::get());
pub const RsrvConcreteFung: (MultiAssetFilter, MultiLocation) =
(Wild(AllOf { fun: WildFungible, id: Concrete(Here.into()) }), ChildTeleporter::get());
pub const TeleportConcreteFungible: (MultiAssetFilter, MultiLocation) =
(Wild(AllOf { fun: WildFungible, id: Concrete(Here.into_location()) }), ChildTeleporter::get());
pub const ReserveConcreteFungible: (MultiAssetFilter, MultiLocation) =
(Wild(AllOf { fun: WildFungible, id: Concrete(Here.into_location()) }), ChildTeleporter::get());
}
impl xcm_balances_benchmark::Config for Test {
type TransactAsset = Balances;
type CheckedAccount = CheckedAccount;
type CheckedAccount = CheckingAccount;
type TrustedTeleporter = TrustedTeleporter;
type TrustedReserve = TrustedReserve;
fn get_multi_asset() -> MultiAsset {
let amount =
@@ -34,15 +34,11 @@ pub mod pallet {
type TransactAsset: frame_support::traits::fungible::Mutate<Self::AccountId>;
/// The account used to check assets being teleported.
type CheckedAccount: Get<Option<Self::AccountId>>;
type CheckedAccount: Get<Option<(Self::AccountId, xcm_builder::MintLocation)>>;
/// A trusted location which we allow teleports from, and the asset we allow to teleport.
type TrustedTeleporter: Get<Option<(xcm::latest::MultiLocation, xcm::latest::MultiAsset)>>;
/// A trusted location where reserve assets are stored, and the asset we allow to be
/// reserves.
type TrustedReserve: Get<Option<(xcm::latest::MultiLocation, xcm::latest::MultiAsset)>>;
/// Give me a fungible asset that your asset transactor is going to accept.
fn get_multi_asset() -> xcm::latest::MultiAsset;
}
@@ -20,27 +20,33 @@ use codec::Encode;
use frame_benchmarking::{benchmarks, BenchmarkError};
use frame_support::dispatch::GetDispatchInfo;
use sp_std::vec;
use xcm::{latest::prelude::*, DoubleEncoded};
use xcm::{
latest::{prelude::*, MaybeErrorCode, Weight},
DoubleEncoded,
};
use xcm_executor::{ExecutorError, FeesMode};
benchmarks! {
query_holding {
let holding = T::worst_case_holding();
report_holding {
let holding = T::worst_case_holding(0);
let mut executor = new_executor::<T>(Default::default());
executor.holding = holding.clone().into();
executor.set_holding(holding.clone().into());
let instruction = Instruction::<XcmCallOf<T>>::QueryHolding {
query_id: Default::default(),
dest: T::valid_destination()?,
let instruction = Instruction::<XcmCallOf<T>>::ReportHolding {
response_info: QueryResponseInfo {
destination: T::valid_destination()?,
query_id: Default::default(),
max_weight: Weight::MAX,
},
// Worst case is looking through all holdings for every asset explicitly.
assets: Definite(holding),
max_response_weight: u64::MAX,
};
let xcm = Xcm(vec![instruction]);
} : {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
// The completion of execution above is enough to validate this is completed.
}
@@ -48,21 +54,21 @@ benchmarks! {
// This benchmark does not use any additional orders or instructions. This should be managed
// by the `deep` and `shallow` implementation.
buy_execution {
let holding = T::worst_case_holding().into();
let holding = T::worst_case_holding(0).into();
let mut executor = new_executor::<T>(Default::default());
executor.holding = holding;
executor.set_holding(holding);
let fee_asset = Concrete(Here.into());
let instruction = Instruction::<XcmCallOf<T>>::BuyExecution {
fees: (fee_asset, 100_000_000).into(), // should be something inside of holding
fees: (fee_asset, 100_000_000u128).into(), // should be something inside of holding
weight_limit: WeightLimit::Unlimited,
};
let xcm = Xcm(vec![instruction]);
} : {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
}
@@ -70,11 +76,12 @@ benchmarks! {
query_response {
let mut executor = new_executor::<T>(Default::default());
let (query_id, response) = T::worst_case_response();
let max_weight = u64::MAX;
let instruction = Instruction::QueryResponse { query_id, response, max_weight };
let max_weight = Weight::MAX;
let querier: Option<MultiLocation> = Some(Here.into());
let instruction = Instruction::QueryResponse { query_id, response, max_weight, querier };
let xcm = Xcm(vec![instruction]);
}: {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
// The assert above is enough to show this XCM succeeded
}
@@ -83,43 +90,38 @@ benchmarks! {
// and included in the final weight calculation. So this is just the overhead of submitting
// a noop call.
transact {
let origin = T::transact_origin()?;
let (origin, noop_call) = T::transact_origin_and_runtime_call()?;
let mut executor = new_executor::<T>(origin);
let noop_call: <T as Config>::RuntimeCall = frame_system::Call::remark_with_event {
remark: Default::default()
}.into();
let double_encoded_noop_call: DoubleEncoded<_> = noop_call.encode().into();
let instruction = Instruction::Transact {
origin_type: OriginKind::SovereignAccount,
require_weight_at_most: noop_call.get_dispatch_info().weight.ref_time(),
origin_kind: OriginKind::SovereignAccount,
require_weight_at_most: noop_call.get_dispatch_info().weight,
call: double_encoded_noop_call,
};
let xcm = Xcm(vec![instruction]);
let num_events = frame_system::Pallet::<T>::events().len();
}: {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
// TODO make better assertion? #4426
let num_events2 = frame_system::Pallet::<T>::events().len();
assert_eq!(num_events + 1, num_events2);
// TODO Make the assertion configurable?
}
refund_surplus {
let holding = T::worst_case_holding().into();
let holding = T::worst_case_holding(0).into();
let mut executor = new_executor::<T>(Default::default());
executor.holding = holding;
executor.total_surplus = 1337;
executor.total_refunded = 0;
executor.set_holding(holding);
executor.set_total_surplus(Weight::from_parts(1337, 1337));
executor.set_total_refunded(Weight::zero());
let instruction = Instruction::<XcmCallOf<T>>::RefundSurplus;
let xcm = Xcm(vec![instruction]);
} : {
let result = executor.execute(xcm)?;
let result = executor.bench_process(xcm)?;
} verify {
assert_eq!(executor.total_surplus, 1337);
assert_eq!(executor.total_refunded, 1337);
assert_eq!(executor.total_surplus(), &Weight::from_parts(1337, 1337));
assert_eq!(executor.total_refunded(), &Weight::from_parts(1337, 1337));
}
set_error_handler {
@@ -127,9 +129,9 @@ benchmarks! {
let instruction = Instruction::<XcmCallOf<T>>::SetErrorHandler(Xcm(vec![]));
let xcm = Xcm(vec![instruction]);
} : {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
assert_eq!(executor.error_handler, Xcm(vec![]));
assert_eq!(executor.error_handler(), &Xcm(vec![]));
}
set_appendix {
@@ -138,20 +140,20 @@ benchmarks! {
let instruction = Instruction::<XcmCallOf<T>>::SetAppendix(appendix);
let xcm = Xcm(vec![instruction]);
} : {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
assert_eq!(executor.appendix, Xcm(vec![]));
assert_eq!(executor.appendix(), &Xcm(vec![]));
}
clear_error {
let mut executor = new_executor::<T>(Default::default());
executor.error = Some((5u32, XcmError::Overflow));
executor.set_error(Some((5u32, XcmError::Overflow)));
let instruction = Instruction::<XcmCallOf<T>>::ClearError;
let xcm = Xcm(vec![instruction]);
} : {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
assert!(executor.error.is_none())
assert!(executor.error().is_none())
}
descend_origin {
@@ -160,11 +162,11 @@ benchmarks! {
let instruction = Instruction::DescendOrigin(who.clone());
let xcm = Xcm(vec![instruction]);
} : {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
assert_eq!(
executor.origin,
Some(MultiLocation {
executor.origin(),
&Some(MultiLocation {
parents: 0,
interior: who,
}),
@@ -176,22 +178,24 @@ benchmarks! {
let instruction = Instruction::ClearOrigin;
let xcm = Xcm(vec![instruction]);
} : {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
assert_eq!(executor.origin, None);
assert_eq!(executor.origin(), &None);
}
report_error {
let mut executor = new_executor::<T>(Default::default());
executor.error = Some((0u32, XcmError::Unimplemented));
executor.set_error(Some((0u32, XcmError::Unimplemented)));
let query_id = Default::default();
let dest = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
let max_response_weight = Default::default();
let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
let max_weight = Default::default();
let instruction = Instruction::ReportError { query_id, dest, max_response_weight };
let instruction = Instruction::ReportError(QueryResponseInfo {
query_id, destination, max_weight
});
let xcm = Xcm(vec![instruction]);
}: {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
// the execution succeeding is all we need to verify this xcm was successful
}
@@ -205,6 +209,11 @@ benchmarks! {
<T::XcmConfig as xcm_executor::Config>::AssetTrap::drop_assets(
&origin,
assets.clone().into(),
&XcmContext {
origin: Some(origin.clone()),
message_hash: [0; 32],
topic: None,
},
);
// Assets should be in the trap now.
@@ -213,9 +222,9 @@ benchmarks! {
let instruction = Instruction::ClaimAsset { assets: assets.clone(), ticket };
let xcm = Xcm(vec![instruction]);
} :{
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
assert!(executor.holding.ensure_contains(&assets).is_ok());
assert!(executor.holding().ensure_contains(&assets).is_ok());
}
trap {
@@ -225,14 +234,12 @@ benchmarks! {
// In order to access result in the verification below, it needs to be defined here.
let mut _result = Ok(());
} : {
_result = executor.execute(xcm);
_result = executor.bench_process(xcm);
} verify {
match _result {
Err(error) if error.xcm_error == XcmError::Trap(10) => {
// This is the success condition
},
_ => Err("xcm trap did not return the expected error")?
};
assert!(matches!(_result, Err(ExecutorError {
xcm_error: XcmError::Trap(10),
..
})));
}
subscribe_version {
@@ -244,7 +251,7 @@ benchmarks! {
let instruction = Instruction::SubscribeVersion { query_id, max_response_weight };
let xcm = Xcm(vec![instruction]);
} : {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
assert!(<T::XcmConfig as xcm_executor::Config>::SubscriptionService::is_subscribed(&origin));
}
@@ -252,13 +259,18 @@ benchmarks! {
unsubscribe_version {
use xcm_executor::traits::VersionChangeNotifier;
// First we need to subscribe to notifications.
let origin = T::transact_origin()?;
let (origin, _) = T::transact_origin_and_runtime_call()?;
let query_id = Default::default();
let max_response_weight = Default::default();
<T::XcmConfig as xcm_executor::Config>::SubscriptionService::start(
&origin,
query_id,
max_response_weight
max_response_weight,
&XcmContext {
origin: Some(origin.clone()),
message_hash: [0; 32],
topic: None,
},
).map_err(|_| "Could not start subscription")?;
assert!(<T::XcmConfig as xcm_executor::Config>::SubscriptionService::is_subscribed(&origin));
@@ -266,30 +278,330 @@ benchmarks! {
let instruction = Instruction::UnsubscribeVersion;
let xcm = Xcm(vec![instruction]);
} : {
executor.execute(xcm)?;
executor.bench_process(xcm)?;
} verify {
assert!(!<T::XcmConfig as xcm_executor::Config>::SubscriptionService::is_subscribed(&origin));
}
initiate_reserve_withdraw {
let holding = T::worst_case_holding();
let holding = T::worst_case_holding(1);
let assets_filter = MultiAssetFilter::Definite(holding.clone());
let reserve = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
let mut executor = new_executor::<T>(Default::default());
executor.holding = holding.into();
executor.set_holding(holding.into());
let instruction = Instruction::InitiateReserveWithdraw { assets: assets_filter, reserve, xcm: Xcm(vec![]) };
let xcm = Xcm(vec![instruction]);
}:{
executor.execute(xcm)?;
}: {
executor.bench_process(xcm)?;
} verify {
// The execute completing successfully is as good as we can check.
// TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426
}
burn_asset {
let holding = T::worst_case_holding(0);
let assets = holding.clone();
let mut executor = new_executor::<T>(Default::default());
executor.set_holding(holding.into());
let instruction = Instruction::BurnAsset(assets.into());
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
assert!(executor.holding().is_empty());
}
expect_asset {
let holding = T::worst_case_holding(0);
let assets = holding.clone();
let mut executor = new_executor::<T>(Default::default());
executor.set_holding(holding.into());
let instruction = Instruction::ExpectAsset(assets.into());
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
// `execute` completing successfully is as good as we can check.
}
expect_origin {
let expected_origin = Parent.into();
let mut executor = new_executor::<T>(Default::default());
let instruction = Instruction::ExpectOrigin(Some(expected_origin));
let xcm = Xcm(vec![instruction]);
let mut _result = Ok(());
}: {
_result = executor.bench_process(xcm);
} verify {
assert!(matches!(_result, Err(ExecutorError {
xcm_error: XcmError::ExpectationFalse,
..
})));
}
expect_error {
let mut executor = new_executor::<T>(Default::default());
executor.set_error(Some((3u32, XcmError::Overflow)));
let instruction = Instruction::ExpectError(None);
let xcm = Xcm(vec![instruction]);
let mut _result = Ok(());
}: {
_result = executor.bench_process(xcm);
} verify {
assert!(matches!(_result, Err(ExecutorError {
xcm_error: XcmError::ExpectationFalse,
..
})));
}
query_pallet {
let query_id = Default::default();
let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
let max_weight = Default::default();
let mut executor = new_executor::<T>(Default::default());
let instruction = Instruction::QueryPallet {
module_name: b"frame_system".to_vec(),
response_info: QueryResponseInfo { destination, query_id, max_weight },
};
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
// TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426
}
expect_pallet {
let mut executor = new_executor::<T>(Default::default());
let instruction = Instruction::ExpectPallet {
index: 0,
name: b"System".to_vec(),
module_name: b"frame_system".to_vec(),
crate_major: 4,
min_crate_minor: 0,
};
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
// the execution succeeding is all we need to verify this xcm was successful
}
report_transact_status {
let query_id = Default::default();
let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
let max_weight = Default::default();
let mut executor = new_executor::<T>(Default::default());
executor.set_transact_status(b"MyError".to_vec().into());
let instruction = Instruction::ReportTransactStatus(QueryResponseInfo {
query_id,
destination,
max_weight,
});
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
// TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426
}
clear_transact_status {
let mut executor = new_executor::<T>(Default::default());
executor.set_transact_status(MaybeErrorCode::Error(b"MyError".to_vec()));
let instruction = Instruction::ClearTransactStatus;
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
assert_eq!(executor.transact_status(), &MaybeErrorCode::Success);
}
set_topic {
let mut executor = new_executor::<T>(Default::default());
let instruction = Instruction::SetTopic([1; 32]);
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
assert_eq!(executor.topic(), &Some([1; 32]));
}
clear_topic {
let mut executor = new_executor::<T>(Default::default());
executor.set_topic(Some([2; 32]));
let instruction = Instruction::ClearTopic;
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
assert_eq!(executor.topic(), &None);
}
exchange_asset {
let (give, want) = T::worst_case_asset_exchange().map_err(|_| BenchmarkError::Skip)?;
let assets = give.clone();
let mut executor = new_executor::<T>(Default::default());
executor.set_holding(give.into());
let instruction = Instruction::ExchangeAsset {
give: assets.into(),
want: want.clone(),
maximal: true,
};
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
assert_eq!(executor.holding(), &want.into());
}
universal_origin {
let alias = T::universal_alias().map_err(|_| BenchmarkError::Skip)?;
let mut executor = new_executor::<T>(Here.into_location());
let instruction = Instruction::UniversalOrigin(alias.clone());
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
use frame_support::traits::Get;
let universal_location = <T::XcmConfig as xcm_executor::Config>::UniversalLocation::get();
assert_eq!(executor.origin(), &Some(X1(alias).relative_to(&universal_location)));
}
set_fees_mode {
let mut executor = new_executor::<T>(Default::default());
executor.set_fees_mode(FeesMode { jit_withdraw: false });
let instruction = Instruction::SetFeesMode { jit_withdraw: true };
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
assert_eq!(executor.fees_mode(), &FeesMode { jit_withdraw: true });
}
lock_asset {
let (unlocker, owner, asset) = T::unlockable_asset()?;
let mut executor = new_executor::<T>(owner);
executor.set_holding(asset.clone().into());
let instruction = Instruction::LockAsset { asset, unlocker };
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
// TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426
}
unlock_asset {
use xcm_executor::traits::{AssetLock, Enact};
let (unlocker, owner, asset) = T::unlockable_asset()?;
let mut executor = new_executor::<T>(unlocker.clone());
// We first place the asset in lock first...
<T::XcmConfig as xcm_executor::Config>::AssetLocker::prepare_lock(
unlocker,
asset.clone(),
owner.clone(),
)
.map_err(|_| BenchmarkError::Skip)?
.enact()
.map_err(|_| BenchmarkError::Skip)?;
// ... then unlock them with the UnlockAsset instruction.
let instruction = Instruction::UnlockAsset { asset, target: owner };
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
}
note_unlockable {
use xcm_executor::traits::{AssetLock, Enact};
let (unlocker, owner, asset) = T::unlockable_asset()?;
let mut executor = new_executor::<T>(unlocker.clone());
// We first place the asset in lock first...
<T::XcmConfig as xcm_executor::Config>::AssetLocker::prepare_lock(
unlocker,
asset.clone(),
owner.clone(),
)
.map_err(|_| BenchmarkError::Skip)?
.enact()
.map_err(|_| BenchmarkError::Skip)?;
// ... then note them as unlockable with the NoteUnlockable instruction.
let instruction = Instruction::NoteUnlockable { asset, owner };
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
}
request_unlock {
use xcm_executor::traits::{AssetLock, Enact};
let (locker, owner, asset) = T::unlockable_asset()?;
// We first place the asset in lock first...
<T::XcmConfig as xcm_executor::Config>::AssetLocker::prepare_lock(
locker.clone(),
asset.clone(),
owner.clone(),
)
.map_err(|_| BenchmarkError::Skip)?
.enact()
.map_err(|_| BenchmarkError::Skip)?;
// ... then request for an unlock with the RequestUnlock instruction.
let mut executor = new_executor::<T>(owner);
let instruction = Instruction::RequestUnlock { asset, locker };
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
} verify {
// TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426
}
unpaid_execution {
let mut executor = new_executor::<T>(Default::default());
executor.set_origin(Some(Here.into()));
let instruction = Instruction::<XcmCallOf<T>>::UnpaidExecution {
weight_limit: WeightLimit::Unlimited,
check_origin: Some(Here.into()),
};
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
}
impl_benchmark_test_suite!(
Pallet,
crate::generic::mock::new_test_ext(),
crate::generic::mock::Test
);
}
@@ -30,7 +30,10 @@ use sp_runtime::{
BuildStorage,
};
use xcm_builder::{
test_utils::{Assets, TestAssetTrap, TestSubscriptionService},
test_utils::{
Assets, TestAssetExchanger, TestAssetLocker, TestAssetTrap, TestSubscriptionService,
TestUniversalAliases,
},
AllowUnpaidExecutionFrom,
};
use xcm_executor::traits::ConvertOrigin;
@@ -52,9 +55,7 @@ frame_support::construct_runtime!(
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(
Weight::from_ref_time(1024).set_proof_size(u64::MAX),
);
frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, u64::MAX));
}
impl frame_system::Config for Test {
@@ -87,17 +88,22 @@ impl frame_system::Config for Test {
/// The benchmarks in this pallet should never need an asset transactor to begin with.
pub struct NoAssetTransactor;
impl xcm_executor::traits::TransactAsset for NoAssetTransactor {
fn deposit_asset(_: &MultiAsset, _: &MultiLocation) -> Result<(), XcmError> {
fn deposit_asset(_: &MultiAsset, _: &MultiLocation, _: &XcmContext) -> Result<(), XcmError> {
unreachable!();
}
fn withdraw_asset(_: &MultiAsset, _: &MultiLocation) -> Result<Assets, XcmError> {
fn withdraw_asset(
_: &MultiAsset,
_: &MultiLocation,
_: Option<&XcmContext>,
) -> Result<Assets, XcmError> {
unreachable!();
}
}
parameter_types! {
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 64;
}
pub struct XcmConfig;
@@ -108,14 +114,24 @@ impl xcm_executor::Config for XcmConfig {
type OriginConverter = AlwaysSignedByDefault<RuntimeOrigin>;
type IsReserve = AllAssetLocationsPass;
type IsTeleporter = ();
type LocationInverter = xcm_builder::LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type Barrier = AllowUnpaidExecutionFrom<Everything>;
type Weigher = xcm_builder::FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
type Trader = xcm_builder::FixedRateOfFungible<WeightPrice, ()>;
type ResponseHandler = DevNull;
type AssetTrap = TestAssetTrap;
type AssetLocker = TestAssetLocker;
type AssetExchanger = TestAssetExchanger;
type AssetClaims = TestAssetTrap;
type SubscriptionService = TestSubscriptionService;
type PalletInstancesInfo = AllPalletsWithSystem;
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = ();
// No bridges yet...
type MessageExporter = ();
type UniversalAliases = TestUniversalAliases;
type CallDispatcher = RuntimeCall;
type SafeCallFilter = Everything;
}
impl crate::Config for Test {
@@ -123,12 +139,15 @@ impl crate::Config for Test {
type AccountIdConverter = AccountIdConverter;
fn valid_destination() -> Result<MultiLocation, BenchmarkError> {
let valid_destination: MultiLocation =
Junction::AccountId32 { network: NetworkId::Any, id: [0u8; 32] }.into();
Junction::AccountId32 { network: None, id: [0u8; 32] }.into();
Ok(valid_destination)
}
fn worst_case_holding() -> MultiAssets {
crate::mock_worst_case_holding()
fn worst_case_holding(depositable_count: u32) -> MultiAssets {
crate::mock_worst_case_holding(
depositable_count,
<XcmConfig as xcm_executor::Config>::MaxAssetsIntoHolding::get(),
)
}
}
@@ -140,10 +159,19 @@ impl generic::Config for Test {
(0, Response::Assets(assets))
}
fn transact_origin() -> Result<MultiLocation, BenchmarkError> {
fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError> {
Ok(Default::default())
}
fn universal_alias() -> Result<Junction, BenchmarkError> {
Ok(GlobalConsensus(ByGenesis([0; 32])))
}
fn transact_origin_and_runtime_call(
) -> Result<(MultiLocation, <Self as generic::Config>::RuntimeCall), BenchmarkError> {
Ok((Default::default(), frame_system::Call::remark_with_event { remark: vec![] }.into()))
}
fn subscribe_origin() -> Result<MultiLocation, BenchmarkError> {
Ok(Default::default())
}
@@ -153,6 +181,11 @@ impl generic::Config for Test {
let ticket = MultiLocation { parents: 0, interior: X1(GeneralIndex(0)) };
Ok((Default::default(), ticket, assets))
}
fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> {
let assets: MultiAsset = (Concrete(Here.into()), 100).into();
Ok((Default::default(), Default::default(), assets))
}
}
pub fn new_test_ext() -> sp_io::TestExternalities {
@@ -12,7 +12,7 @@ pub mod pallet {
dispatch::{Dispatchable, GetDispatchInfo},
pallet_prelude::Encode,
};
use xcm::latest::{MultiAssets, MultiLocation, Response};
use xcm::latest::{Junction, MultiAsset, MultiAssets, MultiLocation, Response};
#[pallet::config]
pub trait Config<I: 'static = ()>: frame_system::Config + crate::Config {
@@ -24,18 +24,37 @@ pub mod pallet {
/// The response which causes the most runtime weight.
fn worst_case_response() -> (u64, Response);
/// The `MultiLocation` used for successful transaction XCMs.
/// The pair of asset collections which causes the most runtime weight if demanded to be
/// exchanged.
///
/// If set to `None`, benchmarks which rely on a `transact_origin` will be skipped.
fn transact_origin() -> Result<MultiLocation, BenchmarkError>;
/// The first element in the returned tuple represents the assets that are being exchanged
/// from, whereas the second element represents the assets that are being exchanged to.
///
/// If set to `Err`, benchmarks which rely on an `exchange_asset` will be skipped.
fn worst_case_asset_exchange() -> Result<(MultiAssets, MultiAssets), BenchmarkError>;
/// A `Junction` that is one of the `UniversalAliases` configured by the XCM executor.
///
/// If set to `Err`, benchmarks which rely on a universal alias will be skipped.
fn universal_alias() -> Result<Junction, BenchmarkError>;
/// The `MultiLocation` and `RuntimeCall` used for successful transaction XCMs.
///
/// If set to `Err`, benchmarks which rely on a `transact_origin_and_runtime_call` will be
/// skipped.
fn transact_origin_and_runtime_call(
) -> Result<(MultiLocation, <Self as crate::generic::Config<I>>::RuntimeCall), BenchmarkError>;
/// A valid `MultiLocation` we can successfully subscribe to.
///
/// If set to `None`, benchmarks which rely on a `subscribe_origin` will be skipped.
/// If set to `Err`, benchmarks which rely on a `subscribe_origin` will be skipped.
fn subscribe_origin() -> Result<MultiLocation, BenchmarkError>;
/// Return an origin, ticket, and assets that can be trapped and claimed.
fn claimable_asset() -> Result<(MultiLocation, MultiLocation, MultiAssets), BenchmarkError>;
/// Return an unlocker, owner and assets that can be locked and unlocked.
fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError>;
}
#[pallet::pallet]
+12 -12
View File
@@ -22,7 +22,7 @@ use codec::Encode;
use frame_benchmarking::{account, BenchmarkError};
use sp_std::prelude::*;
use xcm::latest::prelude::*;
use xcm_executor::traits::Convert;
use xcm_executor::{traits::Convert, Config as XcmConfig};
pub mod fungible;
pub mod generic;
@@ -36,7 +36,7 @@ pub trait Config: frame_system::Config {
///
/// These might affect the execution of XCM messages, such as defining how the
/// `TransactAsset` is implemented.
type XcmConfig: xcm_executor::Config;
type XcmConfig: XcmConfig;
/// A converter between a multi-location to a sovereign account.
type AccountIdConverter: Convert<MultiLocation, Self::AccountId>;
@@ -46,7 +46,7 @@ pub trait Config: frame_system::Config {
fn valid_destination() -> Result<MultiLocation, BenchmarkError>;
/// Worst case scenario for a holding account in this runtime.
fn worst_case_holding() -> MultiAssets;
fn worst_case_holding(depositable_count: u32) -> MultiAssets;
}
const SEED: u32 = 0;
@@ -56,15 +56,15 @@ pub type ExecutorOf<T> = xcm_executor::XcmExecutor<<T as Config>::XcmConfig>;
/// The overarching call type.
pub type OverArchingCallOf<T> = <T as frame_system::Config>::RuntimeCall;
/// The asset transactor of our executor
pub type AssetTransactorOf<T> = <<T as Config>::XcmConfig as xcm_executor::Config>::AssetTransactor;
pub type AssetTransactorOf<T> = <<T as Config>::XcmConfig as XcmConfig>::AssetTransactor;
/// The call type of executor's config. Should eventually resolve to the same overarching call type.
pub type XcmCallOf<T> = <<T as Config>::XcmConfig as xcm_executor::Config>::RuntimeCall;
pub type XcmCallOf<T> = <<T as Config>::XcmConfig as XcmConfig>::RuntimeCall;
pub fn mock_worst_case_holding() -> MultiAssets {
const HOLDING_FUNGIBLES: u32 = 99;
const HOLDING_NON_FUNGIBLES: u32 = 99;
pub fn mock_worst_case_holding(depositable_count: u32, max_assets: u32) -> MultiAssets {
let fungibles_amount: u128 = 100;
(0..HOLDING_FUNGIBLES)
let holding_fungibles = max_assets / 2 - depositable_count;
let holding_non_fungibles = holding_fungibles;
(0..holding_fungibles)
.map(|i| {
MultiAsset {
id: Concrete(GeneralIndex(i as u128).into()),
@@ -73,7 +73,7 @@ pub fn mock_worst_case_holding() -> MultiAssets {
.into()
})
.chain(core::iter::once(MultiAsset { id: Concrete(Here.into()), fun: Fungible(u128::MAX) }))
.chain((0..HOLDING_NON_FUNGIBLES).map(|i| MultiAsset {
.chain((0..holding_non_fungibles).map(|i| MultiAsset {
id: Concrete(GeneralIndex(i as u128).into()),
fun: NonFungible(asset_instance_from(i)),
}))
@@ -89,7 +89,7 @@ pub fn asset_instance_from(x: u32) -> AssetInstance {
}
pub fn new_executor<T: Config>(origin: MultiLocation) -> ExecutorOf<T> {
ExecutorOf::<T>::new(origin)
ExecutorOf::<T>::new(origin, [0; 32])
}
/// Build a multi-location from an account id.
@@ -99,7 +99,7 @@ fn account_id_junction<T: frame_system::Config>(index: u32) -> Junction {
encoded.resize(32, 0u8);
let mut id = [0u8; 32];
id.copy_from_slice(&encoded);
Junction::AccountId32 { network: NetworkId::Any, id }
Junction::AccountId32 { network: None, id }
}
pub fn account_and_location<T: Config>(index: u32) -> (T::AccountId, MultiLocation) {
+23 -13
View File
@@ -15,24 +15,34 @@
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use crate::*;
use frame_support::parameter_types;
use xcm::latest::Weight as XCMWeight;
use xcm_executor::traits::FilterAssetLocation;
use frame_support::{parameter_types, traits::ContainsPair};
use xcm::latest::Weight;
// An xcm sender/receiver akin to > /dev/null
pub struct DevNull;
impl xcm::opaque::latest::SendXcm for DevNull {
fn send_xcm(_: impl Into<MultiLocation>, _: Xcm<()>) -> SendResult {
Ok(())
type Ticket = ();
fn validate(_: &mut Option<MultiLocation>, _: &mut Option<Xcm<()>>) -> SendResult<()> {
Ok(((), MultiAssets::new()))
}
fn deliver(_: ()) -> Result<XcmHash, SendError> {
Ok([0; 32])
}
}
impl xcm_executor::traits::OnResponse for DevNull {
fn expecting_response(_: &MultiLocation, _: u64) -> bool {
fn expecting_response(_: &MultiLocation, _: u64, _: Option<&MultiLocation>) -> bool {
false
}
fn on_response(_: &MultiLocation, _: u64, _: Response, _: XCMWeight) -> XCMWeight {
0
fn on_response(
_: &MultiLocation,
_: u64,
_: Option<&MultiLocation>,
_: Response,
_: Weight,
_: &XcmContext,
) -> Weight {
Weight::zero()
}
}
@@ -52,14 +62,14 @@ impl xcm_executor::traits::Convert<MultiLocation, u64> for AccountIdConverter {
}
parameter_types! {
pub Ancestry: MultiLocation = Junction::Parachain(101).into();
pub UnitWeightCost: u64 = 10;
pub WeightPrice: (AssetId, u128) = (Concrete(Here.into()), 1_000_000);
pub UniversalLocation: InteriorMultiLocation = Junction::Parachain(101).into();
pub UnitWeightCost: Weight = Weight::from_parts(10, 10);
pub WeightPrice: (AssetId, u128, u128) = (Concrete(Here.into()), 1_000_000, 1024);
}
pub struct AllAssetLocationsPass;
impl FilterAssetLocation for AllAssetLocationsPass {
fn filter_asset_location(_: &MultiAsset, _: &MultiLocation) -> bool {
impl ContainsPair<MultiAsset, MultiLocation> for AllAssetLocationsPass {
fn contains(_: &MultiAsset, _: &MultiLocation) -> bool {
true
}
}
+10 -6
View File
@@ -11,11 +11,13 @@ scale-info = { version = "2.1.2", default-features = false, features = ["derive"
serde = { version = "1.0.137", optional = true, features = ["derive"] }
log = { version = "0.4.17", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
xcm = { path = "..", default-features = false }
xcm-executor = { path = "../xcm-executor", default-features = false }
@@ -23,9 +25,8 @@ xcm-executor = { path = "../xcm-executor", default-features = false }
[dev-dependencies]
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
polkadot-runtime-parachains = { path = "../../runtime/parachains" }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
xcm-builder = { path = "../xcm-builder" }
polkadot-parachain = { path = "../../parachain" }
xcm-builder = { path = "../xcm-builder" }
[features]
default = ["std"]
@@ -35,13 +36,16 @@ std = [
"serde",
"sp-std/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"xcm/std",
"xcm-executor/std",
]
runtime-benchmarks = [
"frame-system/runtime-benchmarks"
"frame-benchmarking/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
try-runtime = ["frame-support/try-runtime"]
+192
View File
@@ -0,0 +1,192 @@
// Copyright 2022 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/>.
use super::*;
use frame_benchmarking::{benchmarks, BenchmarkError, BenchmarkResult};
use frame_support::weights::Weight;
use frame_system::RawOrigin;
use sp_core::{bounded::WeakBoundedVec, ConstU32};
use sp_std::prelude::*;
use xcm::{latest::prelude::*, v2};
type RuntimeOrigin<T> = <T as frame_system::Config>::RuntimeOrigin;
benchmarks! {
send {
let send_origin = T::SendXcmOrigin::successful_origin();
if T::SendXcmOrigin::try_origin(send_origin.clone()).is_err() {
return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))
}
let msg = Xcm(vec![ClearOrigin]);
let versioned_dest: VersionedMultiLocation = T::ReachableDest::get().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?
.into();
let versioned_msg = VersionedXcm::from(msg);
}: _<RuntimeOrigin<T>>(send_origin, Box::new(versioned_dest), Box::new(versioned_msg))
teleport_assets {
let asset: MultiAsset = (Here, 10).into();
let send_origin = T::ExecuteXcmOrigin::successful_origin();
let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone())
.map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
if !T::XcmTeleportFilter::contains(&(origin_location, vec![asset.clone()])) {
return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))
}
let recipient = [0u8; 32];
let versioned_dest: VersionedMultiLocation = T::ReachableDest::get().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?
.into();
let versioned_beneficiary: VersionedMultiLocation =
AccountId32 { network: None, id: recipient.into() }.into();
let versioned_assets: VersionedMultiAssets = asset.into();
}: _<RuntimeOrigin<T>>(send_origin, Box::new(versioned_dest), Box::new(versioned_beneficiary), Box::new(versioned_assets), 0)
reserve_transfer_assets {
let asset: MultiAsset = (Here, 10).into();
let send_origin = T::ExecuteXcmOrigin::successful_origin();
let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone())
.map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
if !T::XcmReserveTransferFilter::contains(&(origin_location, vec![asset.clone()])) {
return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))
}
let recipient = [0u8; 32];
let versioned_dest: VersionedMultiLocation = T::ReachableDest::get().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?
.into();
let versioned_beneficiary: VersionedMultiLocation =
AccountId32 { network: None, id: recipient.into() }.into();
let versioned_assets: VersionedMultiAssets = asset.into();
}: _<RuntimeOrigin<T>>(send_origin, Box::new(versioned_dest), Box::new(versioned_beneficiary), Box::new(versioned_assets), 0)
execute {
let execute_origin = T::ExecuteXcmOrigin::successful_origin();
let origin_location = T::ExecuteXcmOrigin::try_origin(execute_origin.clone())
.map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
let msg = Xcm(vec![ClearOrigin]);
if !T::XcmExecuteFilter::contains(&(origin_location, msg.clone())) {
return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))
}
let versioned_msg = VersionedXcm::from(msg);
}: _<RuntimeOrigin<T>>(execute_origin, Box::new(versioned_msg), Weight::zero())
force_xcm_version {
let loc = T::ReachableDest::get().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?;
let xcm_version = 2;
}: _(RawOrigin::Root, Box::new(loc), xcm_version)
force_default_xcm_version {}: _(RawOrigin::Root, Some(2))
force_subscribe_version_notify {
let versioned_loc: VersionedMultiLocation = T::ReachableDest::get().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?
.into();
}: _(RawOrigin::Root, Box::new(versioned_loc))
force_unsubscribe_version_notify {
let loc = T::ReachableDest::get().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?;
let versioned_loc: VersionedMultiLocation = loc.into();
let _ = Pallet::<T>::request_version_notify(loc);
}: _(RawOrigin::Root, Box::new(versioned_loc))
migrate_supported_version {
let old_version = XCM_VERSION - 1;
let loc = VersionedMultiLocation::from(MultiLocation::from(Parent));
SupportedVersion::<T>::insert(old_version, loc, old_version);
}: {
Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateSupportedVersion, Weight::zero());
}
migrate_version_notifiers {
let old_version = XCM_VERSION - 1;
let loc = VersionedMultiLocation::from(MultiLocation::from(Parent));
VersionNotifiers::<T>::insert(old_version, loc, 0);
}: {
Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateVersionNotifiers, Weight::zero());
}
already_notified_target {
let loc = T::ReachableDest::get().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(T::DbWeight::get().reads(1))),
)?;
let loc = VersionedMultiLocation::from(loc);
let current_version = T::AdvertisedXcmVersion::get();
VersionNotifyTargets::<T>::insert(current_version, loc, (0, Weight::zero(), current_version));
}: {
Pallet::<T>::check_xcm_version_change(VersionMigrationStage::NotifyCurrentTargets(None), Weight::zero());
}
notify_current_targets {
let loc = T::ReachableDest::get().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(T::DbWeight::get().reads_writes(1, 3))),
)?;
let loc = VersionedMultiLocation::from(loc);
let current_version = T::AdvertisedXcmVersion::get();
let old_version = current_version - 1;
VersionNotifyTargets::<T>::insert(current_version, loc, (0, Weight::zero(), old_version));
}: {
Pallet::<T>::check_xcm_version_change(VersionMigrationStage::NotifyCurrentTargets(None), Weight::zero());
}
notify_target_migration_fail {
let bad_loc: v2::MultiLocation = v2::Junction::Plurality {
id: v2::BodyId::Named(WeakBoundedVec::<u8, ConstU32<32>>::try_from(vec![0; 32])
.expect("vec has a length of 32 bits; qed")),
part: v2::BodyPart::Voice,
}
.into();
let bad_loc = VersionedMultiLocation::from(bad_loc);
let current_version = T::AdvertisedXcmVersion::get();
VersionNotifyTargets::<T>::insert(current_version, bad_loc, (0, Weight::zero(), current_version));
}: {
Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateAndNotifyOldTargets, Weight::zero());
}
migrate_version_notify_targets {
let current_version = T::AdvertisedXcmVersion::get();
let old_version = current_version - 1;
let loc = VersionedMultiLocation::from(MultiLocation::from(Parent));
VersionNotifyTargets::<T>::insert(old_version, loc, (0, Weight::zero(), current_version));
}: {
Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateAndNotifyOldTargets, Weight::zero());
}
migrate_and_notify_old_targets {
let loc = T::ReachableDest::get().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(T::DbWeight::get().reads_writes(1, 3))),
)?;
let loc = VersionedMultiLocation::from(loc);
let old_version = T::AdvertisedXcmVersion::get() - 1;
VersionNotifyTargets::<T>::insert(old_version, loc, (0, Weight::zero(), old_version));
}: {
Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateAndNotifyOldTargets, Weight::zero());
}
impl_benchmark_test_suite!(
Pallet,
crate::mock::new_test_ext_with_balances(Vec::new()),
crate::mock::Test
);
}
File diff suppressed because it is too large Load Diff
+63
View File
@@ -0,0 +1,63 @@
// Copyright 2022 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/>.
use crate::{Config, Pallet, Store};
use frame_support::{
pallet_prelude::*,
traits::{OnRuntimeUpgrade, StorageVersion},
weights::Weight,
};
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
const DEFAULT_PROOF_SIZE: u64 = 64 * 1024;
pub mod v1 {
use super::*;
pub struct MigrateToV1<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<sp_std::vec::Vec<u8>, &'static str> {
ensure!(StorageVersion::get::<Pallet<T>>() == 0, "must upgrade linearly");
Ok(sp_std::vec::Vec::new())
}
fn on_runtime_upgrade() -> Weight {
if StorageVersion::get::<Pallet<T>>() == 0 {
let mut weight = T::DbWeight::get().reads(1);
let translate = |pre: (u64, u64, u32)| -> Option<(u64, Weight, u32)> {
weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
let translated = (pre.0, Weight::from_parts(pre.1, DEFAULT_PROOF_SIZE), pre.2);
log::info!("Migrated VersionNotifyTarget {:?} to {:?}", pre, translated);
Some(translated)
};
<Pallet<T> as Store>::VersionNotifyTargets::translate_values(translate);
log::info!("v1 applied successfully");
STORAGE_VERSION.put::<Pallet<T>>();
weight.saturating_add(T::DbWeight::get().writes(1))
} else {
log::warn!("skipping v1, should be removed");
T::DbWeight::get().reads(1)
}
}
}
}
+86 -29
View File
@@ -14,23 +14,28 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use frame_support::{construct_runtime, parameter_types, traits::Everything};
use codec::Encode;
use frame_support::{
construct_runtime, parameter_types,
traits::{Everything, Nothing},
weights::Weight,
};
use polkadot_parachain::primitives::Id as ParaId;
use polkadot_runtime_parachains::origin;
use sp_core::H256;
use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32};
pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData};
use xcm::latest::prelude::*;
use xcm::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia,
ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible,
FixedWeightBounds, IsConcrete, LocationInverter, SignedAccountId32AsNative,
SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
FixedWeightBounds, IsConcrete, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit,
};
use xcm_executor::XcmExecutor;
use crate as pallet_xcm;
use crate::{self as pallet_xcm, TestWeightInfo};
pub type AccountId = AccountId32;
pub type Balance = u128;
@@ -74,23 +79,27 @@ pub mod pallet_test_notifier {
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(1_000_000)]
pub fn prepare_new_query(origin: OriginFor<T>) -> DispatchResult {
#[pallet::weight(Weight::from_parts(1_000_000, 1_000_000))]
pub fn prepare_new_query(origin: OriginFor<T>, querier: MultiLocation) -> DispatchResult {
let who = ensure_signed(origin)?;
let id = who
.using_encoded(|mut d| <[u8; 32]>::decode(&mut d))
.map_err(|_| Error::<T>::BadAccountFormat)?;
let qid = crate::Pallet::<T>::new_query(
Junction::AccountId32 { network: Any, id }.into(),
Junction::AccountId32 { network: None, id },
100u32.into(),
querier,
);
Self::deposit_event(Event::<T>::QueryPrepared(qid));
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight(1_000_000)]
pub fn prepare_new_notify_query(origin: OriginFor<T>) -> DispatchResult {
#[pallet::weight(Weight::from_parts(1_000_000, 1_000_000))]
pub fn prepare_new_notify_query(
origin: OriginFor<T>,
querier: MultiLocation,
) -> DispatchResult {
let who = ensure_signed(origin)?;
let id = who
.using_encoded(|mut d| <[u8; 32]>::decode(&mut d))
@@ -98,16 +107,17 @@ pub mod pallet_test_notifier {
let call =
Call::<T>::notification_received { query_id: 0, response: Default::default() };
let qid = crate::Pallet::<T>::new_notify_query(
Junction::AccountId32 { network: Any, id }.into(),
Junction::AccountId32 { network: None, id },
<T as Config>::RuntimeCall::from(call),
100u32.into(),
querier,
);
Self::deposit_event(Event::<T>::NotifyQueryPrepared(qid));
Ok(())
}
#[pallet::call_index(2)]
#[pallet::weight(1_000_000)]
#[pallet::weight(Weight::from_parts(1_000_000, 1_000_000))]
pub fn notification_received(
origin: OriginFor<T>,
query_id: QueryId,
@@ -150,23 +160,40 @@ pub(crate) fn take_sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> {
/// Sender that never returns error, always sends
pub struct TestSendXcm;
impl SendXcm for TestSendXcm {
fn send_xcm(dest: impl Into<MultiLocation>, msg: Xcm<()>) -> SendResult {
SENT_XCM.with(|q| q.borrow_mut().push((dest.into(), msg)));
Ok(())
type Ticket = (MultiLocation, Xcm<()>);
fn validate(
dest: &mut Option<MultiLocation>,
msg: &mut Option<Xcm<()>>,
) -> SendResult<(MultiLocation, Xcm<()>)> {
let pair = (dest.take().unwrap(), msg.take().unwrap());
Ok((pair, MultiAssets::new()))
}
fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<XcmHash, SendError> {
let hash = fake_message_hash(&pair.1);
SENT_XCM.with(|q| q.borrow_mut().push(pair));
Ok(hash)
}
}
/// Sender that returns error if `X8` junction and stops routing
pub struct TestSendXcmErrX8;
impl SendXcm for TestSendXcmErrX8 {
fn send_xcm(dest: impl Into<MultiLocation>, msg: Xcm<()>) -> SendResult {
let dest = dest.into();
type Ticket = (MultiLocation, Xcm<()>);
fn validate(
dest: &mut Option<MultiLocation>,
msg: &mut Option<Xcm<()>>,
) -> SendResult<(MultiLocation, Xcm<()>)> {
let (dest, msg) = (dest.take().unwrap(), msg.take().unwrap());
if dest.len() == 8 {
Err(SendError::Transport("Destination location full"))
} else {
SENT_XCM.with(|q| q.borrow_mut().push((dest, msg)));
Ok(())
Ok(((dest, msg), MultiAssets::new()))
}
}
fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<XcmHash, SendError> {
let hash = fake_message_hash(&pair.1);
SENT_XCM.with(|q| q.borrow_mut().push(pair));
Ok(hash)
}
}
parameter_types! {
@@ -219,9 +246,9 @@ impl pallet_balances::Config for Test {
}
parameter_types! {
pub const RelayLocation: MultiLocation = Here.into();
pub const AnyNetwork: NetworkId = NetworkId::Any;
pub Ancestry: MultiLocation = Here.into();
pub const RelayLocation: MultiLocation = Here.into_location();
pub const AnyNetwork: Option<NetworkId> = None;
pub UniversalLocation: InteriorMultiLocation = Here;
pub UnitWeightCost: u64 = 1_000;
}
@@ -239,10 +266,11 @@ type LocalOriginConverter = (
);
parameter_types! {
pub const BaseXcmWeight: u64 = 1_000;
pub CurrencyPerSecond: (AssetId, u128) = (Concrete(RelayLocation::get()), 1);
pub const BaseXcmWeight: Weight = Weight::from_parts(1_000, 1_000);
pub CurrencyPerSecondPerByte: (AssetId, u128, u128) = (Concrete(RelayLocation::get()), 1, 1);
pub TrustedAssets: (MultiAssetFilter, MultiLocation) = (All.into(), Here.into());
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 64;
}
pub type Barrier = (
@@ -260,20 +288,34 @@ impl xcm_executor::Config for XcmConfig {
type OriginConverter = LocalOriginConverter;
type IsReserve = ();
type IsTeleporter = Case<TrustedAssets>;
type LocationInverter = LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
type Trader = FixedRateOfFungible<CurrencyPerSecond, ()>;
type Trader = FixedRateOfFungible<CurrencyPerSecondPerByte, ()>;
type ResponseHandler = XcmPallet;
type AssetTrap = XcmPallet;
type AssetLocker = ();
type AssetExchanger = ();
type AssetClaims = XcmPallet;
type SubscriptionService = XcmPallet;
type PalletInstancesInfo = AllPalletsWithSystem;
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = ();
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = RuntimeCall;
type SafeCallFilter = Everything;
}
pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, AnyNetwork>;
parameter_types! {
pub static AdvertisedXcmVersion: pallet_xcm::XcmVersion = 2;
pub static AdvertisedXcmVersion: pallet_xcm::XcmVersion = 3;
}
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parachain(1000).into());
}
impl pallet_xcm::Config for Test {
@@ -286,11 +328,19 @@ impl pallet_xcm::Config for Test {
type XcmTeleportFilter = Everything;
type XcmReserveTransferFilter = Everything;
type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
type LocationInverter = LocationInverter<Ancestry>;
type UniversalLocation = UniversalLocation;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = AdvertisedXcmVersion;
type TrustedLockers = ();
type SovereignAccountOf = AccountId32Aliases<(), AccountId32>;
type Currency = Balances;
type CurrencyMatcher = IsConcrete<RelayLocation>;
type MaxLockers = frame_support::traits::ConstU32<8>;
type WeightInfo = TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
}
impl origin::Config for Test {}
@@ -314,7 +364,10 @@ pub(crate) fn buy_execution<C>(fees: impl Into<MultiAsset>) -> Instruction<C> {
BuyExecution { fees: fees.into(), weight_limit: Unlimited }
}
pub(crate) fn buy_limited_execution<C>(fees: impl Into<MultiAsset>, weight: u64) -> Instruction<C> {
pub(crate) fn buy_limited_execution<C>(
fees: impl Into<MultiAsset>,
weight: Weight,
) -> Instruction<C> {
use xcm::latest::prelude::*;
BuyExecution { fees: fees.into(), weight_limit: Limited(weight) }
}
@@ -338,3 +391,7 @@ pub(crate) fn new_test_ext_with_balances(
ext.execute_with(|| System::set_block_number(1));
ext
}
pub(crate) fn fake_message_hash<T>(message: &Xcm<T>) -> XcmHash {
message.using_encoded(sp_io::hashing::blake2_256)
}
File diff suppressed because it is too large Load Diff
+18 -11
View File
@@ -18,20 +18,13 @@
use proc_macro::TokenStream;
mod v0;
mod v1;
mod v2;
mod v3;
mod weight_info;
#[proc_macro]
pub fn impl_conversion_functions_for_multilocation_v0(input: TokenStream) -> TokenStream {
v0::multilocation::generate_conversion_functions(input)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
#[proc_macro]
pub fn impl_conversion_functions_for_multilocation_v1(input: TokenStream) -> TokenStream {
v1::multilocation::generate_conversion_functions(input)
pub fn impl_conversion_functions_for_multilocation_v2(input: TokenStream) -> TokenStream {
v2::multilocation::generate_conversion_functions(input)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
@@ -40,3 +33,17 @@ pub fn impl_conversion_functions_for_multilocation_v1(input: TokenStream) -> Tok
pub fn derive_xcm_weight_info(item: TokenStream) -> TokenStream {
weight_info::derive(item)
}
#[proc_macro]
pub fn impl_conversion_functions_for_multilocation_v3(input: TokenStream) -> TokenStream {
v3::multilocation::generate_conversion_functions(input)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
#[proc_macro]
pub fn impl_conversion_functions_for_junctions_v3(input: TokenStream) -> TokenStream {
v3::junctions::generate_conversion_functions(input)
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
@@ -1,115 +0,0 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> syn::Result<TokenStream> {
if !input.is_empty() {
return Err(syn::Error::new(Span::call_site(), "No arguments expected"))
}
let from_tuples = generate_conversion_from_tuples();
let from_v1 = generate_conversion_from_v1();
Ok(quote! {
#from_tuples
#from_v1
})
}
fn generate_conversion_from_tuples() -> TokenStream {
let from_tuples = (0..8usize)
.map(|num_junctions| {
let junctions =
(0..=num_junctions).map(|_| format_ident!("Junction")).collect::<Vec<_>>();
let idents = (0..=num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
let variant = &format_ident!("X{}", num_junctions + 1);
let array_size = num_junctions + 1;
quote! {
impl From<( #(#junctions,)* )> for MultiLocation {
fn from( ( #(#idents,)* ): ( #(#junctions,)* ) ) -> Self {
MultiLocation::#variant( #(#idents),* )
}
}
impl From<[Junction; #array_size]> for MultiLocation {
fn from(j: [Junction; #array_size]) -> Self {
let [#(#idents),*] = j;
MultiLocation::#variant( #(#idents),* )
}
}
}
})
.collect::<TokenStream>();
quote! {
impl From<()> for MultiLocation {
fn from(_: ()) -> Self {
MultiLocation::Null
}
}
impl From<Junction> for MultiLocation {
fn from(x: Junction) -> Self {
MultiLocation::X1(x)
}
}
impl From<[Junction; 0]> for MultiLocation {
fn from(_: [Junction; 0]) -> Self {
MultiLocation::Null
}
}
#from_tuples
}
}
fn generate_conversion_from_v1() -> TokenStream {
let match_variants = (0..8u8)
.map(|cur_num| {
let variant = format_ident!("X{}", cur_num + 1);
let idents = (1..=cur_num).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
quote! {
crate::v1::Junctions::#variant( j0 #(, #idents)* ) => res
.pushed_with(Junction::from(j0))
#( .and_then(|res| res.pushed_with(Junction::from(#idents))) )*
.map_err(|_| ()),
}
})
.collect::<TokenStream>();
quote! {
impl TryFrom<crate::v1::MultiLocation> for MultiLocation {
type Error = ();
fn try_from(v1: crate::v1::MultiLocation) -> core::result::Result<Self, ()> {
let mut res = MultiLocation::Null;
for _ in 0..v1.parents {
res.push(Junction::Parent)?;
}
match v1.interior {
crate::v1::Junctions::Here => Ok(res),
#match_variants
}
}
}
}
}
-17
View File
@@ -1,17 +0,0 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
pub mod multilocation;
@@ -1,206 +0,0 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{Result, Token};
pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result<TokenStream> {
if !input.is_empty() {
return Err(syn::Error::new(Span::call_site(), "No arguments expected"))
}
// Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents.
let from_tuples = generate_conversion_from_tuples(8);
let from_v0 = generate_conversion_from_v0();
Ok(quote! {
#from_tuples
#from_v0
})
}
fn generate_conversion_from_tuples(max_parents: u8) -> TokenStream {
let mut from_tuples = (0..8usize)
.map(|num_junctions| {
let junctions =
(0..=num_junctions).map(|_| format_ident!("Junction")).collect::<Vec<_>>();
let idents = (0..=num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
let variant = &format_ident!("X{}", num_junctions + 1);
let array_size = num_junctions + 1;
let mut from_tuple = quote! {
impl From<( #(#junctions,)* )> for MultiLocation {
fn from( ( #(#idents,)* ): ( #(#junctions,)* ) ) -> Self {
MultiLocation { parents: 0, interior: Junctions::#variant( #(#idents),* ) }
}
}
impl From<(u8, #(#junctions),*)> for MultiLocation {
fn from( ( parents, #(#idents),* ): (u8, #(#junctions),* ) ) -> Self {
MultiLocation { parents, interior: Junctions::#variant( #(#idents),* ) }
}
}
impl From<(Ancestor, #(#junctions),*)> for MultiLocation {
fn from( ( Ancestor(parents), #(#idents),* ): (Ancestor, #(#junctions),* ) ) -> Self {
MultiLocation { parents, interior: Junctions::#variant( #(#idents),* ) }
}
}
impl From<[Junction; #array_size]> for MultiLocation {
fn from(j: [Junction; #array_size]) -> Self {
let [#(#idents),*] = j;
MultiLocation { parents: 0, interior: Junctions::#variant( #(#idents),* ) }
}
}
};
let from_parent_tuples = (1..=max_parents).map(|cur_parents| {
let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::<Vec<_>>();
let underscores =
(0..cur_parents).map(|_| Token![_](Span::call_site())).collect::<Vec<_>>();
quote! {
impl From<( #(#parents,)* #(#junctions),* )> for MultiLocation {
fn from( (#(#underscores,)* #(#idents),*): ( #(#parents,)* #(#junctions),* ) ) -> Self {
MultiLocation { parents: #cur_parents, interior: Junctions::#variant( #(#idents),* ) }
}
}
}
});
from_tuple.extend(from_parent_tuples);
from_tuple
})
.collect::<TokenStream>();
let from_parent_junctions_tuples = (1..=max_parents).map(|cur_parents| {
let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::<Vec<_>>();
let underscores =
(0..cur_parents).map(|_| Token![_](Span::call_site())).collect::<Vec<_>>();
quote! {
impl From<( #(#parents,)* Junctions )> for MultiLocation {
fn from( (#(#underscores,)* junctions): ( #(#parents,)* Junctions ) ) -> Self {
MultiLocation { parents: #cur_parents, interior: junctions }
}
}
}
});
from_tuples.extend(from_parent_junctions_tuples);
quote! {
impl From<Junctions> for MultiLocation {
fn from(junctions: Junctions) -> Self {
MultiLocation { parents: 0, interior: junctions }
}
}
impl From<(u8, Junctions)> for MultiLocation {
fn from((parents, interior): (u8, Junctions)) -> Self {
MultiLocation { parents, interior }
}
}
impl From<(Ancestor, Junctions)> for MultiLocation {
fn from((Ancestor(parents), interior): (Ancestor, Junctions)) -> Self {
MultiLocation { parents, interior }
}
}
impl From<()> for MultiLocation {
fn from(_: ()) -> Self {
MultiLocation { parents: 0, interior: Junctions::Here }
}
}
impl From<(u8,)> for MultiLocation {
fn from((parents,): (u8,)) -> Self {
MultiLocation { parents, interior: Junctions::Here }
}
}
impl From<Junction> for MultiLocation {
fn from(x: Junction) -> Self {
MultiLocation { parents: 0, interior: Junctions::X1(x) }
}
}
impl From<[Junction; 0]> for MultiLocation {
fn from(_: [Junction; 0]) -> Self {
MultiLocation { parents: 0, interior: Junctions::Here }
}
}
#from_tuples
}
}
fn generate_conversion_from_v0() -> TokenStream {
let match_variants = (0..8u8)
.map(|cur_num| {
let num_ancestors = cur_num + 1;
let variant = format_ident!("X{}", num_ancestors);
let idents = (0..=cur_num).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
let intermediate_match_arms = (1..num_ancestors)
.rev()
.map(|parent_count| {
let parent_idents =
(0..parent_count).map(|j| format_ident!("j{}", j)).collect::<Vec<_>>();
let junction_idents = (parent_count..num_ancestors)
.map(|j| format_ident!("j{}", j))
.collect::<Vec<_>>();
let junction_variant = format_ident!("X{}", num_ancestors - parent_count);
quote! {
crate::v0::MultiLocation::#variant( #(#idents),* )
if #( #parent_idents.is_parent() )&&* =>
Ok(MultiLocation {
parents: #parent_count,
interior: #junction_variant( #( TryInto::try_into(#junction_idents)? ),* ),
}),
}
})
.collect::<TokenStream>();
quote! {
crate::v0::MultiLocation::#variant( #(#idents),* )
if #( #idents.is_parent() )&&* =>
Ok(MultiLocation::ancestor(#num_ancestors)),
#intermediate_match_arms
crate::v0::MultiLocation::#variant( #(#idents),* ) =>
Ok( #variant( #( TryInto::try_into(#idents)? ),* ).into() ),
}
})
.collect::<TokenStream>();
quote! {
impl core::convert::TryFrom<crate::v0::MultiLocation> for MultiLocation {
type Error = ();
fn try_from(mut v0: crate::v0::MultiLocation) -> core::result::Result<Self, ()> {
use Junctions::*;
v0.canonicalize();
match v0 {
crate::v0::MultiLocation::Null => Ok(Here.into()),
#match_variants
}
}
}
}
}
+183
View File
@@ -0,0 +1,183 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
pub mod multilocation {
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{Result, Token};
pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result<TokenStream> {
if !input.is_empty() {
return Err(syn::Error::new(Span::call_site(), "No arguments expected"))
}
// Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents.
let from_tuples = generate_conversion_from_tuples(8);
let from_v3 = generate_conversion_from_v3();
Ok(quote! {
#from_tuples
#from_v3
})
}
fn generate_conversion_from_tuples(max_parents: u8) -> TokenStream {
let mut from_tuples = (0..8usize)
.map(|num_junctions| {
let junctions =
(0..=num_junctions).map(|_| format_ident!("Junction")).collect::<Vec<_>>();
let idents =
(0..=num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
let variant = &format_ident!("X{}", num_junctions + 1);
let array_size = num_junctions + 1;
let mut from_tuple = quote! {
impl From<( #(#junctions,)* )> for MultiLocation {
fn from( ( #(#idents,)* ): ( #(#junctions,)* ) ) -> Self {
MultiLocation { parents: 0, interior: Junctions::#variant( #(#idents),* ) }
}
}
impl From<(u8, #(#junctions),*)> for MultiLocation {
fn from( ( parents, #(#idents),* ): (u8, #(#junctions),* ) ) -> Self {
MultiLocation { parents, interior: Junctions::#variant( #(#idents),* ) }
}
}
impl From<(Ancestor, #(#junctions),*)> for MultiLocation {
fn from( ( Ancestor(parents), #(#idents),* ): (Ancestor, #(#junctions),* ) ) -> Self {
MultiLocation { parents, interior: Junctions::#variant( #(#idents),* ) }
}
}
impl From<[Junction; #array_size]> for MultiLocation {
fn from(j: [Junction; #array_size]) -> Self {
let [#(#idents),*] = j;
MultiLocation { parents: 0, interior: Junctions::#variant( #(#idents),* ) }
}
}
};
let from_parent_tuples = (1..=max_parents).map(|cur_parents| {
let parents =
(0..cur_parents).map(|_| format_ident!("Parent")).collect::<Vec<_>>();
let underscores =
(0..cur_parents).map(|_| Token![_](Span::call_site())).collect::<Vec<_>>();
quote! {
impl From<( #(#parents,)* #(#junctions),* )> for MultiLocation {
fn from( (#(#underscores,)* #(#idents),*): ( #(#parents,)* #(#junctions),* ) ) -> Self {
MultiLocation { parents: #cur_parents, interior: Junctions::#variant( #(#idents),* ) }
}
}
}
});
from_tuple.extend(from_parent_tuples);
from_tuple
})
.collect::<TokenStream>();
let from_parent_junctions_tuples = (1..=max_parents).map(|cur_parents| {
let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::<Vec<_>>();
let underscores =
(0..cur_parents).map(|_| Token![_](Span::call_site())).collect::<Vec<_>>();
quote! {
impl From<( #(#parents,)* Junctions )> for MultiLocation {
fn from( (#(#underscores,)* junctions): ( #(#parents,)* Junctions ) ) -> Self {
MultiLocation { parents: #cur_parents, interior: junctions }
}
}
}
});
from_tuples.extend(from_parent_junctions_tuples);
quote! {
impl From<Junctions> for MultiLocation {
fn from(junctions: Junctions) -> Self {
MultiLocation { parents: 0, interior: junctions }
}
}
impl From<(u8, Junctions)> for MultiLocation {
fn from((parents, interior): (u8, Junctions)) -> Self {
MultiLocation { parents, interior }
}
}
impl From<(Ancestor, Junctions)> for MultiLocation {
fn from((Ancestor(parents), interior): (Ancestor, Junctions)) -> Self {
MultiLocation { parents, interior }
}
}
impl From<()> for MultiLocation {
fn from(_: ()) -> Self {
MultiLocation { parents: 0, interior: Junctions::Here }
}
}
impl From<(u8,)> for MultiLocation {
fn from((parents,): (u8,)) -> Self {
MultiLocation { parents, interior: Junctions::Here }
}
}
impl From<Junction> for MultiLocation {
fn from(x: Junction) -> Self {
MultiLocation { parents: 0, interior: Junctions::X1(x) }
}
}
impl From<[Junction; 0]> for MultiLocation {
fn from(_: [Junction; 0]) -> Self {
MultiLocation { parents: 0, interior: Junctions::Here }
}
}
#from_tuples
}
}
fn generate_conversion_from_v3() -> TokenStream {
let match_variants = (0..8u8)
.map(|cur_num| {
let num_ancestors = cur_num + 1;
let variant = format_ident!("X{}", num_ancestors);
let idents = (0..=cur_num).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
quote! {
crate::v3::Junctions::#variant( #(#idents),* ) =>
#variant( #( core::convert::TryInto::try_into(#idents)? ),* ),
}
})
.collect::<TokenStream>();
quote! {
impl core::convert::TryFrom<crate::v3::Junctions> for Junctions {
type Error = ();
fn try_from(mut new: crate::v3::Junctions) -> core::result::Result<Self, ()> {
use Junctions::*;
Ok(match new {
crate::v3::Junctions::Here => Here,
#match_variants
})
}
}
}
}
}
+186
View File
@@ -0,0 +1,186 @@
// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{Result, Token};
const MAX_JUNCTIONS: usize = 8;
pub mod multilocation {
use super::*;
pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result<TokenStream> {
if !input.is_empty() {
return Err(syn::Error::new(Span::call_site(), "No arguments expected"))
}
let from_tuples = generate_conversion_from_tuples(8, 8);
Ok(quote! {
#from_tuples
})
}
fn generate_conversion_from_tuples(max_junctions: usize, max_parents: usize) -> TokenStream {
let mut from_tuples = (0..=max_junctions)
.map(|num_junctions| {
let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::<Vec<_>>();
let idents =
(0..num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
let array_size = num_junctions;
let interior = if num_junctions == 0 {
quote!(Junctions::Here)
} else {
let variant = format_ident!("X{}", num_junctions);
quote! {
Junctions::#variant( #(#idents .into()),* )
}
};
let mut from_tuple = quote! {
impl< #(#types : Into<Junction>,)* > From<( Ancestor, #( #types ),* )> for MultiLocation {
fn from( ( Ancestor(parents), #(#idents),* ): ( Ancestor, #( #types ),* ) ) -> Self {
MultiLocation { parents, interior: #interior }
}
}
impl From<[Junction; #array_size]> for MultiLocation {
fn from(j: [Junction; #array_size]) -> Self {
let [#(#idents),*] = j;
MultiLocation { parents: 0, interior: #interior }
}
}
};
let from_parent_tuples = (0..=max_parents).map(|cur_parents| {
let parents =
(0..cur_parents).map(|_| format_ident!("Parent")).collect::<Vec<_>>();
let underscores =
(0..cur_parents).map(|_| Token![_](Span::call_site())).collect::<Vec<_>>();
quote! {
impl< #(#types : Into<Junction>,)* > From<( #( #parents , )* #( #types , )* )> for MultiLocation {
fn from( ( #(#underscores,)* #(#idents,)* ): ( #(#parents,)* #(#types,)* ) ) -> Self {
Self { parents: #cur_parents as u8, interior: #interior }
}
}
}
});
from_tuple.extend(from_parent_tuples);
from_tuple
})
.collect::<TokenStream>();
let from_parent_junctions_tuples = (0..=max_parents).map(|cur_parents| {
let parents = (0..cur_parents).map(|_| format_ident!("Parent")).collect::<Vec<_>>();
let underscores =
(0..cur_parents).map(|_| Token![_](Span::call_site())).collect::<Vec<_>>();
quote! {
impl From<( #(#parents,)* Junctions )> for MultiLocation {
fn from( (#(#underscores,)* junctions): ( #(#parents,)* Junctions ) ) -> Self {
MultiLocation { parents: #cur_parents as u8, interior: junctions }
}
}
}
});
from_tuples.extend(from_parent_junctions_tuples);
quote! {
impl From<(Ancestor, Junctions)> for MultiLocation {
fn from((Ancestor(parents), interior): (Ancestor, Junctions)) -> Self {
MultiLocation { parents, interior }
}
}
impl From<Junction> for MultiLocation {
fn from(x: Junction) -> Self {
MultiLocation { parents: 0, interior: Junctions::X1(x) }
}
}
#from_tuples
}
}
}
pub mod junctions {
use super::*;
pub fn generate_conversion_functions(input: proc_macro::TokenStream) -> Result<TokenStream> {
if !input.is_empty() {
return Err(syn::Error::new(Span::call_site(), "No arguments expected"))
}
// Support up to 8 Parents in a tuple, assuming that most use cases don't go past 8 parents.
let from_v2 = generate_conversion_from_v2(MAX_JUNCTIONS);
let from_tuples = generate_conversion_from_tuples(MAX_JUNCTIONS);
Ok(quote! {
#from_v2
#from_tuples
})
}
fn generate_conversion_from_tuples(max_junctions: usize) -> TokenStream {
(1..=max_junctions)
.map(|num_junctions| {
let idents =
(0..num_junctions).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
let types = (0..num_junctions).map(|i| format_ident!("J{}", i)).collect::<Vec<_>>();
let variant = &format_ident!("X{}", num_junctions);
quote! {
impl<#(#types : Into<Junction>,)*> From<( #(#types,)* )> for Junctions {
fn from( ( #(#idents,)* ): ( #(#types,)* ) ) -> Self {
Self::#variant( #(#idents .into()),* )
}
}
}
})
.collect()
}
fn generate_conversion_from_v2(max_junctions: usize) -> TokenStream {
let match_variants = (0..max_junctions)
.map(|cur_num| {
let num_ancestors = cur_num + 1;
let variant = format_ident!("X{}", num_ancestors);
let idents = (0..=cur_num).map(|i| format_ident!("j{}", i)).collect::<Vec<_>>();
quote! {
crate::v2::Junctions::#variant( #(#idents),* ) =>
#variant( #( core::convert::TryInto::try_into(#idents)? ),* ),
}
})
.collect::<TokenStream>();
quote! {
impl core::convert::TryFrom<crate::v2::Junctions> for Junctions {
type Error = ();
fn try_from(mut old: crate::v2::Junctions) -> core::result::Result<Self, ()> {
use Junctions::*;
Ok(match old {
crate::v2::Junctions::Here => Here,
#match_variants
})
}
}
}
}
}
+320 -299
View File
@@ -23,17 +23,19 @@
#![no_std]
extern crate alloc;
use alloc::vec::Vec;
use derivative::Derivative;
use parity_scale_codec::{Decode, Encode, Error as CodecError, Input};
use parity_scale_codec::{Decode, Encode, Error as CodecError, Input, MaxEncodedLen};
use scale_info::TypeInfo;
pub mod v0;
pub mod v1;
pub mod v2;
pub mod v3;
pub mod lts {
pub use super::v3::*;
}
pub mod latest {
pub use super::v2::*;
pub use super::v3::*;
}
mod double_encoded;
@@ -65,241 +67,284 @@ pub trait IntoVersion: Sized {
}
}
/// A single `MultiLocation` value, together with its version code.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
pub enum VersionedMultiLocation {
V0(v0::MultiLocation),
V1(v1::MultiLocation),
pub trait TryAs<T> {
fn try_as(&self) -> Result<&T, ()>;
}
impl IntoVersion for VersionedMultiLocation {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
0 => Self::V0(self.try_into()?),
1 | 2 => Self::V1(self.try_into()?),
_ => return Err(()),
})
}
}
macro_rules! versioned_type {
($(#[$attr:meta])* pub enum $n:ident {
V3($v3:ty),
}) => {
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(
Clone(bound = ""),
Eq(bound = ""),
PartialEq(bound = ""),
Debug(bound = "")
)]
#[codec(encode_bound())]
#[codec(decode_bound())]
$(#[$attr])*
pub enum $n {
#[codec(index = 0)]
V3($v3),
}
impl $n {
pub fn try_as<T>(&self) -> Result<&T, ()> where Self: TryAs<T> {
<Self as TryAs<T>>::try_as(&self)
}
}
impl TryAs<$v3> for $n {
fn try_as(&self) -> Result<&$v3, ()> {
match &self {
Self::V3(ref x) => Ok(x),
}
}
}
impl IntoVersion for $n {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
3 => Self::V3(self.try_into()?),
_ => return Err(()),
})
}
}
impl<T: Into<$v3>> From<T> for $n {
fn from(x: T) -> Self {
$n::V3(x.into())
}
}
impl TryFrom<$n> for $v3 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V3(x) => Ok(x),
}
}
}
impl MaxEncodedLen for $n {
fn max_encoded_len() -> usize {
<$v3>::max_encoded_len()
}
}
};
impl From<v0::MultiLocation> for VersionedMultiLocation {
fn from(x: v0::MultiLocation) -> Self {
VersionedMultiLocation::V0(x)
}
}
($(#[$attr:meta])* pub enum $n:ident {
V2($v2:ty),
V3($v3:ty),
}) => {
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(
Clone(bound = ""),
Eq(bound = ""),
PartialEq(bound = ""),
Debug(bound = "")
)]
#[codec(encode_bound())]
#[codec(decode_bound())]
$(#[$attr])*
pub enum $n {
#[codec(index = 0)]
V2($v2),
#[codec(index = 1)]
V3($v3),
}
impl $n {
pub fn try_as<T>(&self) -> Result<&T, ()> where Self: TryAs<T> {
<Self as TryAs<T>>::try_as(&self)
}
}
impl TryAs<$v2> for $n {
fn try_as(&self) -> Result<&$v2, ()> {
match &self {
Self::V2(ref x) => Ok(x),
_ => Err(()),
}
}
}
impl TryAs<$v3> for $n {
fn try_as(&self) -> Result<&$v3, ()> {
match &self {
Self::V3(ref x) => Ok(x),
_ => Err(()),
}
}
}
impl IntoVersion for $n {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
1 | 2 => Self::V2(self.try_into()?),
3 => Self::V3(self.try_into()?),
_ => return Err(()),
})
}
}
impl From<$v2> for $n {
fn from(x: $v2) -> Self {
$n::V2(x)
}
}
impl<T: Into<$v3>> From<T> for $n {
fn from(x: T) -> Self {
$n::V3(x.into())
}
}
impl TryFrom<$n> for $v2 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V2(x) => Ok(x),
V3(x) => x.try_into(),
}
}
}
impl TryFrom<$n> for $v3 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V2(x) => x.try_into(),
V3(x) => Ok(x),
}
}
}
impl MaxEncodedLen for $n {
fn max_encoded_len() -> usize {
<$v3>::max_encoded_len()
}
}
};
impl<T: Into<v1::MultiLocation>> From<T> for VersionedMultiLocation {
fn from(x: T) -> Self {
VersionedMultiLocation::V1(x.into())
}
}
impl TryFrom<VersionedMultiLocation> for v0::MultiLocation {
type Error = ();
fn try_from(x: VersionedMultiLocation) -> Result<Self, ()> {
use VersionedMultiLocation::*;
match x {
V0(x) => Ok(x),
V1(x) => x.try_into(),
($(#[$attr:meta])* pub enum $n:ident {
V2($v2:ty),
V3($v3:ty),
}) => {
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
$(#[$attr])*
pub enum $n {
#[codec(index = 1)]
V2($v2),
#[codec(index = 2)]
V3($v3),
}
impl $n {
pub fn try_as<T>(&self) -> Result<&T, ()> where Self: TryAs<T> {
<Self as TryAs<T>>::try_as(&self)
}
}
impl TryAs<$v2> for $n {
fn try_as(&self) -> Result<&$v2, ()> {
match &self {
Self::V2(ref x) => Ok(x),
_ => Err(()),
}
}
}
impl TryAs<$v3> for $n {
fn try_as(&self) -> Result<&$v3, ()> {
match &self {
Self::V3(ref x) => Ok(x),
_ => Err(()),
}
}
}
impl IntoVersion for $n {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
2 => Self::V2(self.try_into()?),
3 => Self::V3(self.try_into()?),
_ => return Err(()),
})
}
}
impl From<$v2> for $n {
fn from(x: $v2) -> Self {
$n::V2(x)
}
}
impl<T: Into<$v3>> From<T> for $n {
fn from(x: T) -> Self {
$n::V3(x.into())
}
}
impl TryFrom<$n> for $v2 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V2(x) => Ok(x),
V3(x) => x.try_into(),
}
}
}
impl TryFrom<$n> for $v3 {
type Error = ();
fn try_from(x: $n) -> Result<Self, ()> {
use $n::*;
match x {
V2(x) => x.try_into(),
V3(x) => Ok(x),
}
}
}
impl MaxEncodedLen for $n {
fn max_encoded_len() -> usize {
<$v3>::max_encoded_len()
}
}
}
}
impl TryFrom<VersionedMultiLocation> for v1::MultiLocation {
type Error = ();
fn try_from(x: VersionedMultiLocation) -> Result<Self, ()> {
use VersionedMultiLocation::*;
match x {
V0(x) => x.try_into(),
V1(x) => Ok(x),
}
versioned_type! {
/// A single version's `Response` value, together with its version code.
pub enum VersionedAssetId {
V3(v3::AssetId),
}
}
/// A single `Response` value, together with its version code.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
pub enum VersionedResponse {
V0(v0::Response),
V1(v1::Response),
V2(v2::Response),
}
impl IntoVersion for VersionedResponse {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
0 => Self::V0(self.try_into()?),
1 => Self::V1(self.try_into()?),
2 => Self::V2(self.try_into()?),
_ => return Err(()),
})
versioned_type! {
/// A single version's `Response` value, together with its version code.
pub enum VersionedResponse {
V2(v2::Response),
V3(v3::Response),
}
}
impl From<v0::Response> for VersionedResponse {
fn from(x: v0::Response) -> Self {
VersionedResponse::V0(x)
versioned_type! {
/// A single `MultiLocation` value, together with its version code.
#[derive(Ord, PartialOrd)]
pub enum VersionedMultiLocation {
V2(v2::MultiLocation),
V3(v3::MultiLocation),
}
}
impl From<v1::Response> for VersionedResponse {
fn from(x: v1::Response) -> Self {
VersionedResponse::V1(x)
versioned_type! {
/// A single `InteriorMultiLocation` value, together with its version code.
pub enum VersionedInteriorMultiLocation {
V2(v2::InteriorMultiLocation),
V3(v3::InteriorMultiLocation),
}
}
impl<T: Into<v2::Response>> From<T> for VersionedResponse {
fn from(x: T) -> Self {
VersionedResponse::V2(x.into())
versioned_type! {
/// A single `MultiAsset` value, together with its version code.
pub enum VersionedMultiAsset {
V2(v2::MultiAsset),
V3(v3::MultiAsset),
}
}
impl TryFrom<VersionedResponse> for v0::Response {
type Error = ();
fn try_from(x: VersionedResponse) -> Result<Self, ()> {
use VersionedResponse::*;
match x {
V0(x) => Ok(x),
V1(x) => x.try_into(),
V2(x) => VersionedResponse::V1(x.try_into()?).try_into(),
}
}
}
impl TryFrom<VersionedResponse> for v1::Response {
type Error = ();
fn try_from(x: VersionedResponse) -> Result<Self, ()> {
use VersionedResponse::*;
match x {
V0(x) => x.try_into(),
V1(x) => Ok(x),
V2(x) => x.try_into(),
}
}
}
impl TryFrom<VersionedResponse> for v2::Response {
type Error = ();
fn try_from(x: VersionedResponse) -> Result<Self, ()> {
use VersionedResponse::*;
match x {
V0(x) => VersionedResponse::V1(x.try_into()?).try_into(),
V1(x) => x.try_into(),
V2(x) => Ok(x),
}
}
}
/// A single `MultiAsset` value, together with its version code.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
pub enum VersionedMultiAsset {
V0(v0::MultiAsset),
V1(v1::MultiAsset),
}
impl IntoVersion for VersionedMultiAsset {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
0 => Self::V0(self.try_into()?),
1 | 2 => Self::V1(self.try_into()?),
_ => return Err(()),
})
}
}
impl From<v0::MultiAsset> for VersionedMultiAsset {
fn from(x: v0::MultiAsset) -> Self {
VersionedMultiAsset::V0(x)
}
}
impl<T: Into<v1::MultiAsset>> From<T> for VersionedMultiAsset {
fn from(x: T) -> Self {
VersionedMultiAsset::V1(x.into())
}
}
impl TryFrom<VersionedMultiAsset> for v0::MultiAsset {
type Error = ();
fn try_from(x: VersionedMultiAsset) -> Result<Self, ()> {
use VersionedMultiAsset::*;
match x {
V0(x) => Ok(x),
V1(x) => x.try_into(),
}
}
}
impl TryFrom<VersionedMultiAsset> for v1::MultiAsset {
type Error = ();
fn try_from(x: VersionedMultiAsset) -> Result<Self, ()> {
use VersionedMultiAsset::*;
match x {
V0(x) => x.try_into(),
V1(x) => Ok(x),
}
}
}
/// A single `MultiAssets` value, together with its version code.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
pub enum VersionedMultiAssets {
V0(Vec<v0::MultiAsset>),
V1(v1::MultiAssets),
}
impl IntoVersion for VersionedMultiAssets {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
0 => Self::V0(self.try_into()?),
1 | 2 => Self::V1(self.try_into()?),
_ => return Err(()),
})
}
}
impl From<Vec<v0::MultiAsset>> for VersionedMultiAssets {
fn from(x: Vec<v0::MultiAsset>) -> Self {
VersionedMultiAssets::V0(x)
}
}
impl<T: Into<v1::MultiAssets>> From<T> for VersionedMultiAssets {
fn from(x: T) -> Self {
VersionedMultiAssets::V1(x.into())
}
}
impl TryFrom<VersionedMultiAssets> for Vec<v0::MultiAsset> {
type Error = ();
fn try_from(x: VersionedMultiAssets) -> Result<Self, ()> {
use VersionedMultiAssets::*;
match x {
V0(x) => Ok(x),
V1(x) => x.try_into(),
}
}
}
impl TryFrom<VersionedMultiAssets> for v1::MultiAssets {
type Error = ();
fn try_from(x: VersionedMultiAssets) -> Result<Self, ()> {
use VersionedMultiAssets::*;
match x {
V0(x) => x.try_into(),
V1(x) => Ok(x),
}
versioned_type! {
/// A single `MultiAssets` value, together with its version code.
pub enum VersionedMultiAssets {
V2(v2::MultiAssets),
V3(v3::MultiAssets),
}
}
@@ -310,61 +355,31 @@ impl TryFrom<VersionedMultiAssets> for v1::MultiAssets {
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
pub enum VersionedXcm<RuntimeCall> {
V0(v0::Xcm<RuntimeCall>),
V1(v1::Xcm<RuntimeCall>),
#[codec(index = 2)]
V2(v2::Xcm<RuntimeCall>),
#[codec(index = 3)]
V3(v3::Xcm<RuntimeCall>),
}
impl<C> IntoVersion for VersionedXcm<C> {
fn into_version(self, n: Version) -> Result<Self, ()> {
Ok(match n {
0 => Self::V0(self.try_into()?),
1 => Self::V1(self.try_into()?),
2 => Self::V2(self.try_into()?),
3 => Self::V3(self.try_into()?),
_ => return Err(()),
})
}
}
impl<RuntimeCall> From<v0::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v0::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V0(x)
}
}
impl<RuntimeCall> From<v1::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v1::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V1(x)
}
}
impl<RuntimeCall> From<v2::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v2::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V2(x)
}
}
impl<RuntimeCall> TryFrom<VersionedXcm<RuntimeCall>> for v0::Xcm<RuntimeCall> {
type Error = ();
fn try_from(x: VersionedXcm<RuntimeCall>) -> Result<Self, ()> {
use VersionedXcm::*;
match x {
V0(x) => Ok(x),
V1(x) => x.try_into(),
V2(x) => V1(x.try_into()?).try_into(),
}
}
}
impl<RuntimeCall> TryFrom<VersionedXcm<RuntimeCall>> for v1::Xcm<RuntimeCall> {
type Error = ();
fn try_from(x: VersionedXcm<RuntimeCall>) -> Result<Self, ()> {
use VersionedXcm::*;
match x {
V0(x) => x.try_into(),
V1(x) => Ok(x),
V2(x) => x.try_into(),
}
impl<RuntimeCall> From<v3::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v3::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V3(x)
}
}
@@ -373,9 +388,19 @@ impl<RuntimeCall> TryFrom<VersionedXcm<RuntimeCall>> for v2::Xcm<RuntimeCall> {
fn try_from(x: VersionedXcm<RuntimeCall>) -> Result<Self, ()> {
use VersionedXcm::*;
match x {
V0(x) => V1(x.try_into()?).try_into(),
V1(x) => x.try_into(),
V2(x) => Ok(x),
V3(x) => x.try_into(),
}
}
}
impl<Call> TryFrom<VersionedXcm<Call>> for v3::Xcm<Call> {
type Error = ();
fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
use VersionedXcm::*;
match x {
V2(x) => x.try_into(),
V3(x) => Ok(x),
}
}
}
@@ -398,28 +423,6 @@ impl WrapVersion for () {
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to version 0 before wrapping it.
pub struct AlwaysV0;
impl WrapVersion for AlwaysV0 {
fn wrap_version<RuntimeCall>(
_: &latest::MultiLocation,
xcm: impl Into<VersionedXcm<RuntimeCall>>,
) -> Result<VersionedXcm<RuntimeCall>, ()> {
Ok(VersionedXcm::<RuntimeCall>::V0(xcm.into().try_into()?))
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to version 1 before wrapping it.
pub struct AlwaysV1;
impl WrapVersion for AlwaysV1 {
fn wrap_version<RuntimeCall>(
_: &latest::MultiLocation,
xcm: impl Into<VersionedXcm<RuntimeCall>>,
) -> Result<VersionedXcm<RuntimeCall>, ()> {
Ok(VersionedXcm::<RuntimeCall>::V1(xcm.into().try_into()?))
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to version 2 before wrapping it.
pub struct AlwaysV2;
impl WrapVersion for AlwaysV2 {
@@ -431,42 +434,54 @@ impl WrapVersion for AlwaysV2 {
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to the latest version before wrapping it.
pub type AlwaysLatest = AlwaysV1;
/// `WrapVersion` implementation which attempts to always convert the XCM to version 2 before wrapping it.
pub struct AlwaysV3;
impl WrapVersion for AlwaysV3 {
fn wrap_version<Call>(
_: &latest::MultiLocation,
xcm: impl Into<VersionedXcm<Call>>,
) -> Result<VersionedXcm<Call>, ()> {
Ok(VersionedXcm::<Call>::V3(xcm.into().try_into()?))
}
}
/// `WrapVersion` implementation which attempts to always convert the XCM to the release version before wrapping it.
pub type AlwaysRelease = AlwaysV0;
/// `WrapVersion` implementation which attempts to always convert the XCM to the latest version
/// before wrapping it.
pub type AlwaysLatest = AlwaysV3;
/// `WrapVersion` implementation which attempts to always convert the XCM to the most recent Long-
/// Term-Support version before wrapping it.
pub type AlwaysLts = AlwaysV3;
pub mod prelude {
pub use super::{
latest::prelude::*, AlwaysLatest, AlwaysRelease, AlwaysV0, AlwaysV1, AlwaysV2, IntoVersion,
Unsupported, Version as XcmVersion, VersionedMultiAsset, VersionedMultiAssets,
VersionedMultiLocation, VersionedResponse, VersionedXcm, WrapVersion,
latest::prelude::*, AlwaysLatest, AlwaysLts, AlwaysV2, AlwaysV3, IntoVersion, Unsupported,
Version as XcmVersion, VersionedAssetId, VersionedInteriorMultiLocation,
VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation, VersionedResponse,
VersionedXcm, WrapVersion,
};
}
pub mod opaque {
pub mod v0 {
// Everything from v0
pub use crate::v0::*;
// Then override with the opaque types in v0
pub use crate::v0::opaque::{Order, Xcm};
}
pub mod v1 {
// Everything from v1
pub use crate::v1::*;
// Then override with the opaque types in v1
pub use crate::v1::opaque::{Order, Xcm};
}
pub mod v2 {
// Everything from v1
// Everything from v2
pub use crate::v2::*;
// Then override with the opaque types in v2
pub use crate::v2::opaque::{Instruction, Xcm};
}
pub mod v3 {
// Everything from v3
pub use crate::v3::*;
// Then override with the opaque types in v3
pub use crate::v3::opaque::{Instruction, Xcm};
}
pub mod latest {
pub use super::v2::*;
pub use super::v3::*;
}
pub mod lts {
pub use super::v3::*;
}
/// The basic `VersionedXcm` type which just uses the `Vec<u8>` as an encoded call.
@@ -477,3 +492,9 @@ pub mod opaque {
pub trait GetWeight<W> {
fn weight(&self) -> latest::Weight;
}
#[test]
fn conversion_works() {
use latest::prelude::*;
let _: VersionedMultiAssets = (Here, 1u128).into();
}
-214
View File
@@ -1,214 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Support data structures for `MultiLocation`, primarily the `Junction` datatype.
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::{traits::ConstU32, WeakBoundedVec};
/// A global identifier of an account-bearing consensus system.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
pub enum NetworkId {
/// Unidentified/any.
Any,
/// Some named network.
Named(WeakBoundedVec<u8, ConstU32<32>>),
/// The Polkadot Relay chain
Polkadot,
/// Kusama.
Kusama,
}
/// An identifier of a pluralistic body.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
pub enum BodyId {
/// The only body in its context.
Unit,
/// A named body.
Named(WeakBoundedVec<u8, ConstU32<32>>),
/// An indexed body.
Index(#[codec(compact)] u32),
/// The unambiguous executive body (for Polkadot, this would be the Polkadot council).
Executive,
/// The unambiguous technical body (for Polkadot, this would be the Technical Committee).
Technical,
/// The unambiguous legislative body (for Polkadot, this could be considered the opinion of a majority of
/// lock-voters).
Legislative,
/// The unambiguous judicial body (this doesn't exist on Polkadot, but if it were to get a "grand oracle", it
/// may be considered as that).
Judicial,
/// The unambiguous defense body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `staking_admin` track).
Defense,
/// The unambiguous administration body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `general_admin` track).
Administration,
/// The unambiguous treasury body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `treasurer` track).
Treasury,
}
/// A part of a pluralistic body.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
pub enum BodyPart {
/// The body's declaration, under whatever means it decides.
Voice,
/// A given number of members of the body.
Members {
#[codec(compact)]
count: u32,
},
/// A given number of members of the body, out of some larger caucus.
Fraction {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
/// No less than the given proportion of members of the body.
AtLeastProportion {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
/// More than than the given proportion of members of the body.
MoreThanProportion {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
}
impl BodyPart {
/// Returns `true` if the part represents a strict majority (> 50%) of the body in question.
pub fn is_majority(&self) -> bool {
match self {
BodyPart::Fraction { nom, denom } if *nom * 2 > *denom => true,
BodyPart::AtLeastProportion { nom, denom } if *nom * 2 > *denom => true,
BodyPart::MoreThanProportion { nom, denom } if *nom * 2 >= *denom => true,
_ => false,
}
}
}
/// A single item in a path to describe the relative location of a consensus system.
///
/// Each item assumes a pre-existing location as its context and is defined in terms of it.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
pub enum Junction {
/// The consensus system of which the context is a member and state-wise super-set.
///
/// NOTE: This item is *not* a sub-consensus item: a consensus system may not identify itself trustlessly as
/// a location that includes this junction.
Parent,
/// An indexed parachain belonging to and operated by the context.
///
/// Generally used when the context is a Polkadot Relay-chain.
Parachain(#[codec(compact)] u32),
/// A 32-byte identifier for an account of a specific network that is respected as a sovereign endpoint within
/// the context.
///
/// Generally used when the context is a Substrate-based chain.
AccountId32 { network: NetworkId, id: [u8; 32] },
/// An 8-byte index for an account of a specific network that is respected as a sovereign endpoint within
/// the context.
///
/// May be used when the context is a Frame-based chain and includes e.g. an indices pallet.
AccountIndex64 {
network: NetworkId,
#[codec(compact)]
index: u64,
},
/// A 20-byte identifier for an account of a specific network that is respected as a sovereign endpoint within
/// the context.
///
/// May be used when the context is an Ethereum or Bitcoin chain or smart-contract.
AccountKey20 { network: NetworkId, key: [u8; 20] },
/// An instanced, indexed pallet that forms a constituent part of the context.
///
/// Generally used when the context is a Frame-based chain.
PalletInstance(u8),
/// A non-descript index within the context location.
///
/// Usage will vary widely owing to its generality.
///
/// NOTE: Try to avoid using this and instead use a more specific item.
GeneralIndex(#[codec(compact)] u128),
/// A nondescript datum acting as a key within the context location.
///
/// Usage will vary widely owing to its generality.
///
/// NOTE: Try to avoid using this and instead use a more specific item.
GeneralKey(WeakBoundedVec<u8, ConstU32<32>>),
/// The unambiguous child.
///
/// Not currently used except as a fallback when deriving ancestry.
OnlyChild,
/// A pluralistic body existing within consensus.
///
/// Typical to be used to represent a governance origin of a chain, but could in principle be used to represent
/// things such as multisigs also.
Plurality { id: BodyId, part: BodyPart },
}
impl From<crate::v1::Junction> for Junction {
fn from(v1: crate::v1::Junction) -> Junction {
use crate::v1::Junction::*;
match v1 {
Parachain(id) => Self::Parachain(id),
AccountId32 { network, id } => Self::AccountId32 { network, id },
AccountIndex64 { network, index } => Self::AccountIndex64 { network, index },
AccountKey20 { network, key } => Self::AccountKey20 { network, key },
PalletInstance(index) => Self::PalletInstance(index),
GeneralIndex(index) => Self::GeneralIndex(index),
GeneralKey(key) => Self::GeneralKey(key),
OnlyChild => Self::OnlyChild,
Plurality { id, part } => Self::Plurality { id, part },
}
}
}
impl Junction {
/// Returns true if this junction is a `Parent` item.
pub fn is_parent(&self) -> bool {
match self {
Junction::Parent => true,
_ => false,
}
}
/// Returns true if this junction can be considered an interior part of its context. This is generally `true`,
/// except for the `Parent` item.
pub fn is_interior(&self) -> bool {
match self {
Junction::Parent => false,
Junction::Parachain(..) |
Junction::AccountId32 { .. } |
Junction::AccountIndex64 { .. } |
Junction::AccountKey20 { .. } |
Junction::PalletInstance { .. } |
Junction::GeneralIndex { .. } |
Junction::GeneralKey(..) |
Junction::OnlyChild |
Junction::Plurality { .. } => true,
}
}
}
-389
View File
@@ -1,389 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Version 0 of the Cross-Consensus Message format data structures.
use crate::DoubleEncoded;
use alloc::vec::Vec;
use core::result;
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
use scale_info::TypeInfo;
mod junction;
mod multi_asset;
mod multi_location;
mod order;
mod traits;
use super::v1::{MultiLocation as MultiLocation1, Response as Response1, Xcm as Xcm1};
pub use junction::{BodyId, BodyPart, Junction, NetworkId};
pub use multi_asset::{AssetInstance, MultiAsset};
pub use multi_location::MultiLocation::{self, *};
pub use order::Order;
pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm};
/// A prelude for importing all types typically used when interacting with XCM messages.
pub mod prelude {
pub use super::{
junction::{BodyId, Junction::*},
multi_asset::{
AssetInstance::{self, *},
MultiAsset::{self, *},
},
multi_location::MultiLocation::{self, *},
order::Order::{self, *},
traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm},
Junction::*,
OriginKind,
Xcm::{self, *},
};
}
// TODO: #2841 #XCMENCODE Efficient encodings for MultiAssets, Vec<Order>, using initial byte values 128+ to encode
// the number of items in the vector.
/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`.
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
pub enum OriginKind {
/// Origin should just be the native dispatch origin representation for the sender in the
/// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin
/// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a
/// primary/native dispatch origin form.
Native,
/// Origin should just be the standard account-based origin with the sovereign account of
/// the sender. For Cumulus/Frame chains, this is the `Signed` origin.
SovereignAccount,
/// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin.
/// This will not usually be an available option.
Superuser,
/// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be
/// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be
/// the `pallet_xcm::Origin::Xcm` type.
Xcm,
}
/// Response data to a query.
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
pub enum Response {
/// Some assets.
Assets(Vec<MultiAsset>),
}
/// Cross-Consensus Message: A message from one consensus system to another.
///
/// Consensus systems that may send and receive messages include blockchains and smart contracts.
///
/// All messages are delivered from a known *origin*, expressed as a `MultiLocation`.
///
/// This is the inner XCM format and is version-sensitive. Messages are typically passed using the outer
/// XCM format, known as `VersionedXcm`.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
pub enum Xcm<RuntimeCall> {
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into `holding`. Execute the
/// orders (`effects`).
///
/// - `assets`: The asset(s) to be withdrawn into holding.
/// - `effects`: The order(s) to execute on the holding account.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 0)]
WithdrawAsset { assets: Vec<MultiAsset>, effects: Vec<Order<RuntimeCall>> },
/// Asset(s) (`assets`) have been received into the ownership of this system on the `origin` system.
///
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
/// been placed into `holding`.
///
/// - `assets`: The asset(s) that are minted into holding.
/// - `effects`: The order(s) to execute on the holding account.
///
/// Safety: `origin` must be trusted to have received and be storing `assets` such that they may later be
/// withdrawn should this system send a corresponding message.
///
/// Kind: *Trusted Indication*.
///
/// Errors:
#[codec(index = 1)]
ReserveAssetDeposit { assets: Vec<MultiAsset>, effects: Vec<Order<RuntimeCall>> },
/// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should be
/// created on this system.
///
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
/// been placed into `holding`.
///
/// - `assets`: The asset(s) that are minted into holding.
/// - `effects`: The order(s) to execute on the holding account.
///
/// Safety: `origin` must be trusted to have irrevocably destroyed the `assets` prior as a consequence of
/// sending this message.
///
/// Kind: *Trusted Indication*.
///
/// Errors:
#[codec(index = 2)]
TeleportAsset { assets: Vec<MultiAsset>, effects: Vec<Order<RuntimeCall>> },
/// Indication of the contents of the holding account corresponding to the `QueryHolding` order of `query_id`.
///
/// - `query_id`: The identifier of the query that resulted in this message being sent.
/// - `assets`: The message content.
///
/// Safety: No concerns.
///
/// Kind: *Information*.
///
/// Errors:
#[codec(index = 3)]
QueryResponse {
#[codec(compact)]
query_id: u64,
response: Response,
},
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
/// ownership of `dest` within this consensus system.
///
/// - `assets`: The asset(s) to be withdrawn.
/// - `dest`: The new owner for the assets.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 4)]
TransferAsset { assets: Vec<MultiAsset>, dest: MultiLocation },
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
/// ownership of `dest` within this consensus system.
///
/// Send an onward XCM message to `dest` of `ReserveAssetDeposit` with the given `effects`.
///
/// - `assets`: The asset(s) to be withdrawn.
/// - `dest`: The new owner for the assets.
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposit` which is sent onwards to
/// `dest`.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 5)]
TransferReserveAsset { assets: Vec<MultiAsset>, dest: MultiLocation, effects: Vec<Order<()>> },
/// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed by the kind
/// of origin `origin_type`.
///
/// - `origin_type`: The means of expressing the message origin as a dispatch origin.
/// - `max_weight`: The weight of `call`; this should be at least the chain's calculated weight and will
/// be used in the weight determination arithmetic.
/// - `call`: The encoded transaction to be applied.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 6)]
Transact {
origin_type: OriginKind,
require_weight_at_most: u64,
call: DoubleEncoded<RuntimeCall>,
},
/// A message to notify about a new incoming HRMP channel. This message is meant to be sent by the
/// relay-chain to a para.
///
/// - `sender`: The sender in the to-be opened channel. Also, the initiator of the channel opening.
/// - `max_message_size`: The maximum size of a message proposed by the sender.
/// - `max_capacity`: The maximum number of messages that can be queued in the channel.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
#[codec(index = 7)]
HrmpNewChannelOpenRequest {
#[codec(compact)]
sender: u32,
#[codec(compact)]
max_message_size: u32,
#[codec(compact)]
max_capacity: u32,
},
/// A message to notify about that a previously sent open channel request has been accepted by
/// the recipient. That means that the channel will be opened during the next relay-chain session
/// change. This message is meant to be sent by the relay-chain to a para.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
///
/// Errors:
#[codec(index = 8)]
HrmpChannelAccepted {
#[codec(compact)]
recipient: u32,
},
/// A message to notify that the other party in an open channel decided to close it. In particular,
/// `initiator` is going to close the channel opened from `sender` to the `recipient`. The close
/// will be enacted at the next relay-chain session change. This message is meant to be sent by
/// the relay-chain to a para.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
///
/// Errors:
#[codec(index = 9)]
HrmpChannelClosing {
#[codec(compact)]
initiator: u32,
#[codec(compact)]
sender: u32,
#[codec(compact)]
recipient: u32,
},
/// A message to indicate that the embedded XCM is actually arriving on behalf of some consensus
/// location within the origin.
///
/// Safety: `who` must be an interior location of the context. This basically means that no `Parent`
/// junctions are allowed in it. This should be verified at the time of XCM execution.
///
/// Kind: *Instruction*
///
/// Errors:
#[codec(index = 10)]
RelayedFrom { who: MultiLocation, message: alloc::boxed::Box<Xcm<RuntimeCall>> },
}
impl<RuntimeCall> Xcm<RuntimeCall> {
pub fn into<C>(self) -> Xcm<C> {
Xcm::from(self)
}
pub fn from<C>(xcm: Xcm<C>) -> Self {
use Xcm::*;
match xcm {
WithdrawAsset { assets, effects } =>
WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() },
ReserveAssetDeposit { assets, effects } => ReserveAssetDeposit {
assets,
effects: effects.into_iter().map(Order::into).collect(),
},
TeleportAsset { assets, effects } =>
TeleportAsset { assets, effects: effects.into_iter().map(Order::into).collect() },
QueryResponse { query_id, response } => QueryResponse { query_id, response },
TransferAsset { assets, dest } => TransferAsset { assets, dest },
TransferReserveAsset { assets, dest, effects } =>
TransferReserveAsset { assets, dest, effects },
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call: call.into() },
RelayedFrom { who, message } =>
RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) },
}
}
}
pub mod opaque {
/// The basic concrete type of `generic::Xcm`, which doesn't make any assumptions about the format of a
/// call other than it is pre-encoded.
pub type Xcm = super::Xcm<()>;
pub use super::order::opaque::*;
}
// Convert from a v1 response to a v0 response
impl TryFrom<Response1> for Response {
type Error = ();
fn try_from(new_response: Response1) -> result::Result<Self, ()> {
Ok(match new_response {
Response1::Assets(assets) => Self::Assets(assets.try_into()?),
Response1::Version(..) => return Err(()),
})
}
}
impl<RuntimeCall> TryFrom<Xcm1<RuntimeCall>> for Xcm<RuntimeCall> {
type Error = ();
fn try_from(x: Xcm1<RuntimeCall>) -> result::Result<Xcm<RuntimeCall>, ()> {
use Xcm::*;
Ok(match x {
Xcm1::WithdrawAsset { assets, effects } => WithdrawAsset {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::ReserveAssetDeposited { assets, effects } => ReserveAssetDeposit {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::ReceiveTeleportedAsset { assets, effects } => TeleportAsset {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::QueryResponse { query_id, response } =>
QueryResponse { query_id, response: response.try_into()? },
Xcm1::TransferAsset { assets, beneficiary } =>
TransferAsset { assets: assets.try_into()?, dest: beneficiary.try_into()? },
Xcm1::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset {
assets: assets.try_into()?,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Xcm1::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
Xcm1::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
Xcm1::HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
Xcm1::Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call: call.into() },
Xcm1::RelayedFrom { who, message } => RelayedFrom {
who: MultiLocation1 { interior: who, parents: 0 }.try_into()?,
message: alloc::boxed::Box::new((*message).try_into()?),
},
Xcm1::SubscribeVersion { .. } | Xcm1::UnsubscribeVersion => return Err(()),
})
}
}
-407
View File
@@ -1,407 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use super::MultiLocation;
use crate::v1::{MultiAssetFilter, MultiAssets, WildMultiAsset};
use alloc::{vec, vec::Vec};
use core::result;
use parity_scale_codec::{self, Decode, Encode};
use scale_info::TypeInfo;
pub use crate::v1::AssetInstance;
/// A single general identifier for an asset.
///
/// Represents both fungible and non-fungible assets. May only be used to represent a single asset class.
///
/// Wildcards may or may not be allowed by the interpreting context.
///
/// Assets classes may be identified in one of two ways: either an abstract identifier or a concrete identifier.
/// Implementations may support only one of these. A single asset may be referenced from multiple asset identifiers,
/// though will tend to have only a single *preferred* identifier.
///
/// ### Abstract identifiers
///
/// Abstract identifiers are absolute identifiers that represent a notional asset which can exist within multiple
/// consensus systems. These tend to be simpler to deal with since their broad meaning is unchanged regardless stay of
/// the consensus system in which it is interpreted.
///
/// However, in the attempt to provide uniformity across consensus systems, they may conflate different instantiations
/// of some notional asset (e.g. the reserve asset and a local reserve-backed derivative of it) under the same name,
/// leading to confusion. It also implies that one notional asset is accounted for locally in only one way. This may not
/// be the case, e.g. where there are multiple bridge instances each providing a bridged "BTC" token yet none being
/// fungible between the others.
///
/// Since they are meant to be absolute and universal, a global registry is needed to ensure that name collisions do not
/// occur.
///
/// An abstract identifier is represented as a simple variable-size byte string. As of writing, no global registry
/// exists and no proposals have been put forth for asset labeling.
///
/// ### Concrete identifiers
///
/// Concrete identifiers are *relative identifiers* that specifically identify a single asset through its location in a
/// consensus system relative to the context interpreting. Use of a `MultiLocation` ensures that similar but non
/// fungible variants of the same underlying asset can be properly distinguished, and obviates the need for any kind of
/// central registry.
///
/// The limitation is that the asset identifier cannot be trivially copied between consensus systems and must instead be
/// "re-anchored" whenever being moved to a new consensus system, using the two systems' relative paths.
///
/// Throughout XCM, messages are authored such that *when interpreted from the receiver's point of view* they will have
/// the desired meaning/effect. This means that relative paths should always by constructed to be read from the point of
/// view of the receiving system, *which may be have a completely different meaning in the authoring system*.
///
/// Concrete identifiers are the preferred way of identifying an asset since they are entirely unambiguous.
///
/// A concrete identifier is represented by a `MultiLocation`. If a system has an unambiguous primary asset (such as
/// Bitcoin with BTC or Ethereum with ETH), then it will conventionally be identified as the chain itself. Alternative
/// and more specific ways of referring to an asset within a system include:
///
/// - `<chain>/PalletInstance(<id>)` for a Frame chain with a single-asset pallet instance (such as an instance of the
/// Balances pallet).
/// - `<chain>/PalletInstance(<id>)/GeneralIndex(<index>)` for a Frame chain with an indexed multi-asset pallet instance
/// (such as an instance of the Assets pallet).
/// - `<chain>/AccountId32` for an ERC-20-style single-asset smart-contract on a Frame-based contracts chain.
/// - `<chain>/AccountKey20` for an ERC-20-style single-asset smart-contract on an Ethereum-like chain.
///
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)]
pub enum MultiAsset {
/// No assets. Rarely used.
None,
/// All assets. Typically used for the subset of assets to be used for an `Order`, and in that context means
/// "all assets currently in holding".
All,
/// All fungible assets. Typically used for the subset of assets to be used for an `Order`, and in that context
/// means "all fungible assets currently in holding".
AllFungible,
/// All non-fungible assets. Typically used for the subset of assets to be used for an `Order`, and in that
/// context means "all non-fungible assets currently in holding".
AllNonFungible,
/// All fungible assets of a given abstract asset `id`entifier.
AllAbstractFungible { id: Vec<u8> },
/// All non-fungible assets of a given abstract asset `class`.
AllAbstractNonFungible { class: Vec<u8> },
/// All fungible assets of a given concrete asset `id`entifier.
AllConcreteFungible { id: MultiLocation },
/// All non-fungible assets of a given concrete asset `class`.
AllConcreteNonFungible { class: MultiLocation },
/// Some specific `amount` of the fungible asset identified by an abstract `id`.
AbstractFungible {
id: Vec<u8>,
#[codec(compact)]
amount: u128,
},
/// Some specific `instance` of the non-fungible asset whose `class` is identified abstractly.
AbstractNonFungible { class: Vec<u8>, instance: AssetInstance },
/// Some specific `amount` of the fungible asset identified by an concrete `id`.
ConcreteFungible {
id: MultiLocation,
#[codec(compact)]
amount: u128,
},
/// Some specific `instance` of the non-fungible asset whose `class` is identified concretely.
ConcreteNonFungible { class: MultiLocation, instance: AssetInstance },
}
impl MultiAsset {
/// Returns `true` if the `MultiAsset` is a wildcard and can refer to classes of assets, instead of just one.
///
/// Typically can also be inferred by the name starting with `All`.
pub fn is_wildcard(&self) -> bool {
match self {
MultiAsset::None |
MultiAsset::AbstractFungible { .. } |
MultiAsset::AbstractNonFungible { .. } |
MultiAsset::ConcreteFungible { .. } |
MultiAsset::ConcreteNonFungible { .. } => false,
MultiAsset::All |
MultiAsset::AllFungible |
MultiAsset::AllNonFungible |
MultiAsset::AllAbstractFungible { .. } |
MultiAsset::AllConcreteFungible { .. } |
MultiAsset::AllAbstractNonFungible { .. } |
MultiAsset::AllConcreteNonFungible { .. } => true,
}
}
fn is_none(&self) -> bool {
match self {
MultiAsset::None |
MultiAsset::AbstractFungible { amount: 0, .. } |
MultiAsset::ConcreteFungible { amount: 0, .. } => true,
_ => false,
}
}
fn is_fungible(&self) -> bool {
match self {
MultiAsset::All |
MultiAsset::AllFungible |
MultiAsset::AllAbstractFungible { .. } |
MultiAsset::AllConcreteFungible { .. } |
MultiAsset::AbstractFungible { .. } |
MultiAsset::ConcreteFungible { .. } => true,
_ => false,
}
}
fn is_non_fungible(&self) -> bool {
match self {
MultiAsset::All |
MultiAsset::AllNonFungible |
MultiAsset::AllAbstractNonFungible { .. } |
MultiAsset::AllConcreteNonFungible { .. } |
MultiAsset::AbstractNonFungible { .. } |
MultiAsset::ConcreteNonFungible { .. } => true,
_ => false,
}
}
fn is_concrete_fungible(&self, id: &MultiLocation) -> bool {
match self {
MultiAsset::AllFungible => true,
MultiAsset::AllConcreteFungible { id: i } |
MultiAsset::ConcreteFungible { id: i, .. } => i == id,
_ => false,
}
}
fn is_abstract_fungible(&self, id: &[u8]) -> bool {
match self {
MultiAsset::AllFungible => true,
MultiAsset::AllAbstractFungible { id: i } |
MultiAsset::AbstractFungible { id: i, .. } => i == id,
_ => false,
}
}
fn is_concrete_non_fungible(&self, class: &MultiLocation) -> bool {
match self {
MultiAsset::AllNonFungible => true,
MultiAsset::AllConcreteNonFungible { class: i } |
MultiAsset::ConcreteNonFungible { class: i, .. } => i == class,
_ => false,
}
}
fn is_abstract_non_fungible(&self, class: &[u8]) -> bool {
match self {
MultiAsset::AllNonFungible => true,
MultiAsset::AllAbstractNonFungible { class: i } |
MultiAsset::AbstractNonFungible { class: i, .. } => i == class,
_ => false,
}
}
fn is_all(&self) -> bool {
matches!(self, MultiAsset::All)
}
/// Returns true if `self` is a super-set of the given `inner`.
///
/// Typically, any wildcard is never contained in anything else, and a wildcard can contain any other non-wildcard.
/// For more details, see the implementation and tests.
pub fn contains(&self, inner: &MultiAsset) -> bool {
use MultiAsset::*;
// Inner cannot be wild
if inner.is_wildcard() {
return false
}
// Everything contains nothing.
if inner.is_none() {
return true
}
// Everything contains anything.
if self.is_all() {
return true
}
// Nothing contains nothing.
if self.is_none() {
return false
}
match self {
// Anything fungible contains "all fungibles"
AllFungible => inner.is_fungible(),
// Anything non-fungible contains "all non-fungibles"
AllNonFungible => inner.is_non_fungible(),
AllConcreteFungible { id } => inner.is_concrete_fungible(id),
AllAbstractFungible { id } => inner.is_abstract_fungible(id),
AllConcreteNonFungible { class } => inner.is_concrete_non_fungible(class),
AllAbstractNonFungible { class } => inner.is_abstract_non_fungible(class),
ConcreteFungible { id, amount } => matches!(
inner,
ConcreteFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount
),
AbstractFungible { id, amount } => matches!(
inner,
AbstractFungible { id: inner_id , amount: inner_amount } if inner_id == id && amount >= inner_amount
),
ConcreteNonFungible { .. } => self == inner,
AbstractNonFungible { .. } => self == inner,
_ => false,
}
}
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
use MultiAsset::*;
match self {
AllConcreteFungible { ref mut id } |
AllConcreteNonFungible { class: ref mut id } |
ConcreteFungible { ref mut id, .. } |
ConcreteNonFungible { class: ref mut id, .. } =>
id.prepend_with(prepend.clone()).map_err(|_| ()),
_ => Ok(()),
}
}
}
impl TryFrom<crate::v1::MultiAsset> for MultiAsset {
type Error = ();
fn try_from(m: crate::v1::MultiAsset) -> result::Result<MultiAsset, ()> {
use crate::v1::{AssetId::*, Fungibility::*};
use MultiAsset::*;
Ok(match (m.id, m.fun) {
(Concrete(id), Fungible(amount)) => ConcreteFungible { id: id.try_into()?, amount },
(Concrete(class), NonFungible(instance)) =>
ConcreteNonFungible { class: class.try_into()?, instance },
(Abstract(id), Fungible(amount)) => AbstractFungible { id, amount },
(Abstract(class), NonFungible(instance)) => AbstractNonFungible { class, instance },
})
}
}
impl TryFrom<MultiAssets> for Vec<MultiAsset> {
type Error = ();
fn try_from(m: MultiAssets) -> result::Result<Vec<MultiAsset>, ()> {
m.drain().into_iter().map(MultiAsset::try_from).collect()
}
}
impl TryFrom<WildMultiAsset> for MultiAsset {
type Error = ();
fn try_from(m: WildMultiAsset) -> result::Result<MultiAsset, ()> {
use crate::v1::{AssetId::*, WildFungibility::*};
use MultiAsset::*;
Ok(match m {
WildMultiAsset::All => All,
WildMultiAsset::AllOf { id, fun } => match (id, fun) {
(Concrete(id), Fungible) => AllConcreteFungible { id: id.try_into()? },
(Concrete(class), NonFungible) =>
AllConcreteNonFungible { class: class.try_into()? },
(Abstract(id), Fungible) => AllAbstractFungible { id },
(Abstract(class), NonFungible) => AllAbstractNonFungible { class },
},
})
}
}
impl TryFrom<WildMultiAsset> for Vec<MultiAsset> {
type Error = ();
fn try_from(m: WildMultiAsset) -> result::Result<Vec<MultiAsset>, ()> {
Ok(vec![m.try_into()?])
}
}
impl TryFrom<MultiAssetFilter> for Vec<MultiAsset> {
type Error = ();
fn try_from(m: MultiAssetFilter) -> result::Result<Vec<MultiAsset>, ()> {
match m {
MultiAssetFilter::Definite(assets) => assets.try_into(),
MultiAssetFilter::Wild(wildcard) => wildcard.try_into(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn contains_works() {
use alloc::vec;
use MultiAsset::*;
// trivial case: all contains any non-wildcard.
assert!(All.contains(&None));
assert!(All.contains(&AbstractFungible { id: alloc::vec![99u8], amount: 1 }));
// trivial case: none contains nothing, except itself.
assert!(None.contains(&None));
assert!(!None.contains(&AllFungible));
assert!(!None.contains(&All));
// A bit more sneaky: Nothing can contain wildcard, even All ir the thing itself.
assert!(!All.contains(&All));
assert!(!All.contains(&AllFungible));
assert!(!AllFungible.contains(&AllFungible));
assert!(!AllNonFungible.contains(&AllNonFungible));
// For fungibles, containing is basically equality, or equal id with higher amount.
assert!(!AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![1u8], amount: 99 }));
assert!(AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![99u8], amount: 99 }));
assert!(AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![99u8], amount: 9 }));
assert!(!AbstractFungible { id: vec![99u8], amount: 99 }
.contains(&AbstractFungible { id: vec![99u8], amount: 100 }));
// For non-fungibles, containing is equality.
assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) }
.contains(&AbstractNonFungible {
class: vec![98u8],
instance: AssetInstance::Index(9)
}));
assert!(!AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(8) }
.contains(&AbstractNonFungible {
class: vec![99u8],
instance: AssetInstance::Index(9)
}));
assert!(AbstractNonFungible { class: vec![99u8], instance: AssetInstance::Index(9) }
.contains(&AbstractNonFungible {
class: vec![99u8],
instance: AssetInstance::Index(9)
}));
}
}
-745
View File
@@ -1,745 +0,0 @@
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use super::Junction;
use core::{mem, result};
use parity_scale_codec::{self, Decode, Encode};
/// A relative path between state-bearing consensus systems.
///
/// A location in a consensus system is defined as an *isolatable state machine* held within global consensus. The
/// location in question need not have a sophisticated consensus algorithm of its own; a single account within
/// Ethereum, for example, could be considered a location.
///
/// A very-much non-exhaustive list of types of location include:
/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a parachain.
/// - A layer-0 super-chain, e.g. the Polkadot Relay chain.
/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum.
/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based Substrate chain.
/// - An account.
///
/// A `MultiLocation` is a *relative identifier*, meaning that it can only be used to define the relative path
/// between two locations, and cannot generally be used to refer to a location universally. It is comprised of a
/// number of *junctions*, each morphing the previous location, either diving down into one of its internal locations,
/// called a *sub-consensus*, or going up into its parent location. Correct `MultiLocation` values must have all
/// `Parent` junctions as a prefix to all *sub-consensus* junctions.
///
/// This specific `MultiLocation` implementation uses a Rust `enum` in order to make pattern matching easier.
///
/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, scale_info::TypeInfo)]
pub enum MultiLocation {
/// The interpreting consensus system.
Null,
/// A relative path comprising 1 junction.
X1(Junction),
/// A relative path comprising 2 junctions.
X2(Junction, Junction),
/// A relative path comprising 3 junctions.
X3(Junction, Junction, Junction),
/// A relative path comprising 4 junctions.
X4(Junction, Junction, Junction, Junction),
/// A relative path comprising 5 junctions.
X5(Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 6 junctions.
X6(Junction, Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 7 junctions.
X7(Junction, Junction, Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 8 junctions.
X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction),
}
/// Maximum number of junctions a `MultiLocation` can contain.
pub const MAX_MULTILOCATION_LENGTH: usize = 8;
xcm_procedural::impl_conversion_functions_for_multilocation_v0!();
pub struct MultiLocationIterator(MultiLocation);
impl Iterator for MultiLocationIterator {
type Item = Junction;
fn next(&mut self) -> Option<Junction> {
self.0.take_first()
}
}
pub struct MultiLocationReverseIterator(MultiLocation);
impl Iterator for MultiLocationReverseIterator {
type Item = Junction;
fn next(&mut self) -> Option<Junction> {
self.0.take_last()
}
}
pub struct MultiLocationRefIterator<'a>(&'a MultiLocation, usize);
impl<'a> Iterator for MultiLocationRefIterator<'a> {
type Item = &'a Junction;
fn next(&mut self) -> Option<&'a Junction> {
let result = self.0.at(self.1);
self.1 += 1;
result
}
}
pub struct MultiLocationReverseRefIterator<'a>(&'a MultiLocation, usize);
impl<'a> Iterator for MultiLocationReverseRefIterator<'a> {
type Item = &'a Junction;
fn next(&mut self) -> Option<&'a Junction> {
self.1 += 1;
self.0.at(self.0.len().checked_sub(self.1)?)
}
}
impl MultiLocation {
/// Returns first junction, or `None` if the location is empty.
pub fn first(&self) -> Option<&Junction> {
match &self {
MultiLocation::Null => None,
MultiLocation::X1(ref a) => Some(a),
MultiLocation::X2(ref a, ..) => Some(a),
MultiLocation::X3(ref a, ..) => Some(a),
MultiLocation::X4(ref a, ..) => Some(a),
MultiLocation::X5(ref a, ..) => Some(a),
MultiLocation::X6(ref a, ..) => Some(a),
MultiLocation::X7(ref a, ..) => Some(a),
MultiLocation::X8(ref a, ..) => Some(a),
}
}
/// Returns last junction, or `None` if the location is empty.
pub fn last(&self) -> Option<&Junction> {
match &self {
MultiLocation::Null => None,
MultiLocation::X1(ref a) => Some(a),
MultiLocation::X2(.., ref a) => Some(a),
MultiLocation::X3(.., ref a) => Some(a),
MultiLocation::X4(.., ref a) => Some(a),
MultiLocation::X5(.., ref a) => Some(a),
MultiLocation::X6(.., ref a) => Some(a),
MultiLocation::X7(.., ref a) => Some(a),
MultiLocation::X8(.., ref a) => Some(a),
}
}
/// Splits off the first junction, returning the remaining suffix (first item in tuple) and the first element
/// (second item in tuple) or `None` if it was empty.
pub fn split_first(self) -> (MultiLocation, Option<Junction>) {
match self {
MultiLocation::Null => (MultiLocation::Null, None),
MultiLocation::X1(a) => (MultiLocation::Null, Some(a)),
MultiLocation::X2(a, b) => (MultiLocation::X1(b), Some(a)),
MultiLocation::X3(a, b, c) => (MultiLocation::X2(b, c), Some(a)),
MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(b, c, d), Some(a)),
MultiLocation::X5(a, b, c, d, e) => (MultiLocation::X4(b, c, d, e), Some(a)),
MultiLocation::X6(a, b, c, d, e, f) => (MultiLocation::X5(b, c, d, e, f), Some(a)),
MultiLocation::X7(a, b, c, d, e, f, g) =>
(MultiLocation::X6(b, c, d, e, f, g), Some(a)),
MultiLocation::X8(a, b, c, d, e, f, g, h) =>
(MultiLocation::X7(b, c, d, e, f, g, h), Some(a)),
}
}
/// Splits off the last junction, returning the remaining prefix (first item in tuple) and the last element
/// (second item in tuple) or `None` if it was empty.
pub fn split_last(self) -> (MultiLocation, Option<Junction>) {
match self {
MultiLocation::Null => (MultiLocation::Null, None),
MultiLocation::X1(a) => (MultiLocation::Null, Some(a)),
MultiLocation::X2(a, b) => (MultiLocation::X1(a), Some(b)),
MultiLocation::X3(a, b, c) => (MultiLocation::X2(a, b), Some(c)),
MultiLocation::X4(a, b, c, d) => (MultiLocation::X3(a, b, c), Some(d)),
MultiLocation::X5(a, b, c, d, e) => (MultiLocation::X4(a, b, c, d), Some(e)),
MultiLocation::X6(a, b, c, d, e, f) => (MultiLocation::X5(a, b, c, d, e), Some(f)),
MultiLocation::X7(a, b, c, d, e, f, g) =>
(MultiLocation::X6(a, b, c, d, e, f), Some(g)),
MultiLocation::X8(a, b, c, d, e, f, g, h) =>
(MultiLocation::X7(a, b, c, d, e, f, g), Some(h)),
}
}
/// Removes the first element from `self`, returning it (or `None` if it was empty).
pub fn take_first(&mut self) -> Option<Junction> {
let mut d = MultiLocation::Null;
mem::swap(&mut *self, &mut d);
let (tail, head) = d.split_first();
*self = tail;
head
}
/// Removes the last element from `self`, returning it (or `None` if it was empty).
pub fn take_last(&mut self) -> Option<Junction> {
let mut d = MultiLocation::Null;
mem::swap(&mut *self, &mut d);
let (head, tail) = d.split_last();
*self = head;
tail
}
/// Consumes `self` and returns a `MultiLocation` suffixed with `new`, or an `Err` with the original value of
/// `self` in case of overflow.
pub fn pushed_with(self, new: Junction) -> result::Result<Self, Self> {
Ok(match self {
MultiLocation::Null => MultiLocation::X1(new),
MultiLocation::X1(a) => MultiLocation::X2(a, new),
MultiLocation::X2(a, b) => MultiLocation::X3(a, b, new),
MultiLocation::X3(a, b, c) => MultiLocation::X4(a, b, c, new),
MultiLocation::X4(a, b, c, d) => MultiLocation::X5(a, b, c, d, new),
MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(a, b, c, d, e, new),
MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(a, b, c, d, e, f, new),
MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(a, b, c, d, e, f, g, new),
s => Err(s)?,
})
}
/// Consumes `self` and returns a `MultiLocation` prefixed with `new`, or an `Err` with the original value of
/// `self` in case of overflow.
pub fn pushed_front_with(self, new: Junction) -> result::Result<Self, Self> {
Ok(match self {
MultiLocation::Null => MultiLocation::X1(new),
MultiLocation::X1(a) => MultiLocation::X2(new, a),
MultiLocation::X2(a, b) => MultiLocation::X3(new, a, b),
MultiLocation::X3(a, b, c) => MultiLocation::X4(new, a, b, c),
MultiLocation::X4(a, b, c, d) => MultiLocation::X5(new, a, b, c, d),
MultiLocation::X5(a, b, c, d, e) => MultiLocation::X6(new, a, b, c, d, e),
MultiLocation::X6(a, b, c, d, e, f) => MultiLocation::X7(new, a, b, c, d, e, f),
MultiLocation::X7(a, b, c, d, e, f, g) => MultiLocation::X8(new, a, b, c, d, e, f, g),
s => Err(s)?,
})
}
/// Returns the number of junctions in `self`.
pub fn len(&self) -> usize {
match &self {
MultiLocation::Null => 0,
MultiLocation::X1(..) => 1,
MultiLocation::X2(..) => 2,
MultiLocation::X3(..) => 3,
MultiLocation::X4(..) => 4,
MultiLocation::X5(..) => 5,
MultiLocation::X6(..) => 6,
MultiLocation::X7(..) => 7,
MultiLocation::X8(..) => 8,
}
}
/// Returns the junction at index `i`, or `None` if the location doesn't contain that many elements.
pub fn at(&self, i: usize) -> Option<&Junction> {
Some(match (i, &self) {
(0, MultiLocation::X1(ref a)) => a,
(0, MultiLocation::X2(ref a, ..)) => a,
(0, MultiLocation::X3(ref a, ..)) => a,
(0, MultiLocation::X4(ref a, ..)) => a,
(0, MultiLocation::X5(ref a, ..)) => a,
(0, MultiLocation::X6(ref a, ..)) => a,
(0, MultiLocation::X7(ref a, ..)) => a,
(0, MultiLocation::X8(ref a, ..)) => a,
(1, MultiLocation::X2(_, ref a)) => a,
(1, MultiLocation::X3(_, ref a, ..)) => a,
(1, MultiLocation::X4(_, ref a, ..)) => a,
(1, MultiLocation::X5(_, ref a, ..)) => a,
(1, MultiLocation::X6(_, ref a, ..)) => a,
(1, MultiLocation::X7(_, ref a, ..)) => a,
(1, MultiLocation::X8(_, ref a, ..)) => a,
(2, MultiLocation::X3(_, _, ref a)) => a,
(2, MultiLocation::X4(_, _, ref a, ..)) => a,
(2, MultiLocation::X5(_, _, ref a, ..)) => a,
(2, MultiLocation::X6(_, _, ref a, ..)) => a,
(2, MultiLocation::X7(_, _, ref a, ..)) => a,
(2, MultiLocation::X8(_, _, ref a, ..)) => a,
(3, MultiLocation::X4(_, _, _, ref a)) => a,
(3, MultiLocation::X5(_, _, _, ref a, ..)) => a,
(3, MultiLocation::X6(_, _, _, ref a, ..)) => a,
(3, MultiLocation::X7(_, _, _, ref a, ..)) => a,
(3, MultiLocation::X8(_, _, _, ref a, ..)) => a,
(4, MultiLocation::X5(_, _, _, _, ref a)) => a,
(4, MultiLocation::X6(_, _, _, _, ref a, ..)) => a,
(4, MultiLocation::X7(_, _, _, _, ref a, ..)) => a,
(4, MultiLocation::X8(_, _, _, _, ref a, ..)) => a,
(5, MultiLocation::X6(_, _, _, _, _, ref a)) => a,
(5, MultiLocation::X7(_, _, _, _, _, ref a, ..)) => a,
(5, MultiLocation::X8(_, _, _, _, _, ref a, ..)) => a,
(6, MultiLocation::X7(_, _, _, _, _, _, ref a)) => a,
(6, MultiLocation::X8(_, _, _, _, _, _, ref a, ..)) => a,
(7, MultiLocation::X8(_, _, _, _, _, _, _, ref a)) => a,
_ => return None,
})
}
/// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't contain that many
/// elements.
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
Some(match (i, self) {
(0, MultiLocation::X1(ref mut a)) => a,
(0, MultiLocation::X2(ref mut a, ..)) => a,
(0, MultiLocation::X3(ref mut a, ..)) => a,
(0, MultiLocation::X4(ref mut a, ..)) => a,
(0, MultiLocation::X5(ref mut a, ..)) => a,
(0, MultiLocation::X6(ref mut a, ..)) => a,
(0, MultiLocation::X7(ref mut a, ..)) => a,
(0, MultiLocation::X8(ref mut a, ..)) => a,
(1, MultiLocation::X2(_, ref mut a)) => a,
(1, MultiLocation::X3(_, ref mut a, ..)) => a,
(1, MultiLocation::X4(_, ref mut a, ..)) => a,
(1, MultiLocation::X5(_, ref mut a, ..)) => a,
(1, MultiLocation::X6(_, ref mut a, ..)) => a,
(1, MultiLocation::X7(_, ref mut a, ..)) => a,
(1, MultiLocation::X8(_, ref mut a, ..)) => a,
(2, MultiLocation::X3(_, _, ref mut a)) => a,
(2, MultiLocation::X4(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X5(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X6(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X7(_, _, ref mut a, ..)) => a,
(2, MultiLocation::X8(_, _, ref mut a, ..)) => a,
(3, MultiLocation::X4(_, _, _, ref mut a)) => a,
(3, MultiLocation::X5(_, _, _, ref mut a, ..)) => a,
(3, MultiLocation::X6(_, _, _, ref mut a, ..)) => a,
(3, MultiLocation::X7(_, _, _, ref mut a, ..)) => a,
(3, MultiLocation::X8(_, _, _, ref mut a, ..)) => a,
(4, MultiLocation::X5(_, _, _, _, ref mut a)) => a,
(4, MultiLocation::X6(_, _, _, _, ref mut a, ..)) => a,
(4, MultiLocation::X7(_, _, _, _, ref mut a, ..)) => a,
(4, MultiLocation::X8(_, _, _, _, ref mut a, ..)) => a,
(5, MultiLocation::X6(_, _, _, _, _, ref mut a)) => a,
(5, MultiLocation::X7(_, _, _, _, _, ref mut a, ..)) => a,
(5, MultiLocation::X8(_, _, _, _, _, ref mut a, ..)) => a,
(6, MultiLocation::X7(_, _, _, _, _, _, ref mut a)) => a,
(6, MultiLocation::X8(_, _, _, _, _, _, ref mut a, ..)) => a,
(7, MultiLocation::X8(_, _, _, _, _, _, _, ref mut a)) => a,
_ => return None,
})
}
/// Returns a reference iterator over the junctions.
pub fn iter(&self) -> MultiLocationRefIterator {
MultiLocationRefIterator(&self, 0)
}
/// Returns a reference iterator over the junctions in reverse.
pub fn iter_rev(&self) -> MultiLocationReverseRefIterator {
MultiLocationReverseRefIterator(&self, 0)
}
/// Consumes `self` and returns an iterator over the junctions.
pub fn into_iter(self) -> MultiLocationIterator {
MultiLocationIterator(self)
}
/// Consumes `self` and returns an iterator over the junctions in reverse.
pub fn into_iter_rev(self) -> MultiLocationReverseIterator {
MultiLocationReverseIterator(self)
}
/// Ensures that self begins with `prefix` and that it has a single `Junction` item following.
/// If so, returns a reference to this `Junction` item.
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parent, PalletInstance(3), OnlyChild);
/// assert_eq!(m.match_and_split(&X2(Parent, PalletInstance(3))), Some(&OnlyChild));
/// assert_eq!(m.match_and_split(&X1(Parent)), None);
/// # }
/// ```
pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> {
if prefix.len() + 1 != self.len() || !self.starts_with(prefix) {
return None
}
return self.at(prefix.len())
}
/// Returns whether `self` begins with or is equal to `prefix`.
///
/// # Example
/// ```rust
/// # use xcm::v0::{Junction::*, MultiLocation::*};
/// let m = X4(Parent, PalletInstance(3), OnlyChild, OnlyChild);
/// assert!(m.starts_with(&X2(Parent, PalletInstance(3))));
/// assert!(m.starts_with(&m));
/// assert!(!m.starts_with(&X2(Parent, GeneralIndex(99))));
/// assert!(!m.starts_with(&X1(PalletInstance(3))));
/// ```
pub fn starts_with(&self, prefix: &MultiLocation) -> bool {
if self.len() < prefix.len() {
return false
}
prefix.iter().zip(self.iter()).all(|(l, r)| l == r)
}
/// Mutates `self`, suffixing it with `new`. Returns `Err` in case of overflow.
pub fn push(&mut self, new: Junction) -> result::Result<(), ()> {
let mut n = MultiLocation::Null;
mem::swap(&mut *self, &mut n);
match n.pushed_with(new) {
Ok(result) => {
*self = result;
Ok(())
},
Err(old) => {
*self = old;
Err(())
},
}
}
/// Mutates `self`, prefixing it with `new`. Returns `Err` in case of overflow.
pub fn push_front(&mut self, new: Junction) -> result::Result<(), ()> {
let mut n = MultiLocation::Null;
mem::swap(&mut *self, &mut n);
match n.pushed_front_with(new) {
Ok(result) => {
*self = result;
Ok(())
},
Err(old) => {
*self = old;
Err(())
},
}
}
/// Returns the number of `Parent` junctions at the beginning of `self`.
pub fn leading_parent_count(&self) -> usize {
use Junction::Parent;
match self {
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 8,
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, ..) => 7,
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent) => 7,
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6,
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, Parent, ..) => 6,
MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, Parent) => 6,
MultiLocation::X8(Parent, Parent, Parent, Parent, Parent, ..) => 5,
MultiLocation::X7(Parent, Parent, Parent, Parent, Parent, ..) => 5,
MultiLocation::X6(Parent, Parent, Parent, Parent, Parent, ..) => 5,
MultiLocation::X5(Parent, Parent, Parent, Parent, Parent) => 5,
MultiLocation::X8(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X7(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X6(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X5(Parent, Parent, Parent, Parent, ..) => 4,
MultiLocation::X4(Parent, Parent, Parent, Parent) => 4,
MultiLocation::X8(Parent, Parent, Parent, ..) => 3,
MultiLocation::X7(Parent, Parent, Parent, ..) => 3,
MultiLocation::X6(Parent, Parent, Parent, ..) => 3,
MultiLocation::X5(Parent, Parent, Parent, ..) => 3,
MultiLocation::X4(Parent, Parent, Parent, ..) => 3,
MultiLocation::X3(Parent, Parent, Parent) => 3,
MultiLocation::X8(Parent, Parent, ..) => 2,
MultiLocation::X7(Parent, Parent, ..) => 2,
MultiLocation::X6(Parent, Parent, ..) => 2,
MultiLocation::X5(Parent, Parent, ..) => 2,
MultiLocation::X4(Parent, Parent, ..) => 2,
MultiLocation::X3(Parent, Parent, ..) => 2,
MultiLocation::X2(Parent, Parent) => 2,
MultiLocation::X8(Parent, ..) => 1,
MultiLocation::X7(Parent, ..) => 1,
MultiLocation::X6(Parent, ..) => 1,
MultiLocation::X5(Parent, ..) => 1,
MultiLocation::X4(Parent, ..) => 1,
MultiLocation::X3(Parent, ..) => 1,
MultiLocation::X2(Parent, ..) => 1,
MultiLocation::X1(Parent) => 1,
_ => 0,
}
}
/// This function ensures a multi-junction is in its canonicalized/normalized form, removing
/// any internal `[Non-Parent, Parent]` combinations.
pub fn canonicalize(&mut self) {
let mut normalized = MultiLocation::Null;
let mut iter = self.iter();
// We build up the the new normalized path by taking items from the original multi-location.
// When the next item we would add is `Parent`, we instead remove the last item assuming
// it is non-parent.
const EXPECT_MESSAGE: &'static str =
"`self` is a well formed multi-location with N junctions; \
this loop iterates over the junctions of `self`; \
the loop can push to the new multi-location at most one time; \
thus the size of the new multi-location is at most N junctions; \
qed";
while let Some(j) = iter.next() {
if j == &Junction::Parent {
match normalized.last() {
None | Some(Junction::Parent) => {},
Some(_) => {
normalized.take_last();
continue
},
}
}
normalized.push(j.clone()).expect(EXPECT_MESSAGE);
}
core::mem::swap(self, &mut normalized);
}
/// Mutate `self` so that it is suffixed with `suffix`. The correct normalized form is returned,
/// removing any internal `[Non-Parent, Parent]` combinations.
///
/// In the case of overflow, `self` is unmodified and we return `Err` with `suffix`.
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parent, Parachain(21), OnlyChild);
/// assert_eq!(m.append_with(X2(Parent, PalletInstance(3))), Ok(()));
/// assert_eq!(m, X3(Parent, Parachain(21), PalletInstance(3)));
/// # }
/// ```
pub fn append_with(&mut self, suffix: MultiLocation) -> Result<(), MultiLocation> {
let mut prefix = suffix;
core::mem::swap(self, &mut prefix);
match self.prepend_with(prefix) {
Ok(()) => Ok(()),
Err(prefix) => {
let mut suffix = prefix;
core::mem::swap(self, &mut suffix);
Err(suffix)
},
}
}
/// Mutate `self` so that it is prefixed with `prefix`. The correct normalized form is returned,
/// removing any internal [Non-Parent, `Parent`] combinations.
///
/// In the case of overflow, `self` is unmodified and we return `Err` with `prefix`.
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation::*, Junction::*, NetworkId::Any};
/// # fn main() {
/// let mut m = X3(Parent, Parent, PalletInstance(3));
/// assert_eq!(m.prepend_with(X3(Parent, Parachain(21), OnlyChild)), Ok(()));
/// assert_eq!(m, X2(Parent, PalletInstance(3)));
/// # }
/// ```
pub fn prepend_with(&mut self, prefix: MultiLocation) -> Result<(), MultiLocation> {
let mut prefix = prefix;
// This will guarantee that all `Parent` junctions in the prefix are leading, which is
// important for calculating the `skipped` items below.
prefix.canonicalize();
let self_leading_parents = self.leading_parent_count();
// These are the number of `non-parent` items in the prefix that we can
// potentially remove if the original location leads with parents.
let prefix_rest = prefix.len() - prefix.leading_parent_count();
// 2 * skipped items will be removed when performing the normalization below.
let skipped = self_leading_parents.min(prefix_rest);
// Pre-pending this prefix would create a multi-location with too many junctions.
if self.len() + prefix.len() - 2 * skipped > MAX_MULTILOCATION_LENGTH {
return Err(prefix)
}
// Here we cancel out `[Non-Parent, Parent]` items (normalization), where
// the non-parent item comes from the end of the prefix, and the parent item
// comes from the front of the original location.
//
// We calculated already how many of these there should be above.
for _ in 0..skipped {
let _non_parent = prefix.take_last();
let _parent = self.take_first();
debug_assert!(
_non_parent.is_some() && _non_parent != Some(Junction::Parent),
"prepend_with should always remove a non-parent from the end of the prefix",
);
debug_assert!(
_parent == Some(Junction::Parent),
"prepend_with should always remove a parent from the front of the location",
);
}
for j in prefix.into_iter_rev() {
self.push_front(j)
.expect("len + prefix minus 2*skipped is less than max length; qed");
}
Ok(())
}
/// Returns true iff `self` is an interior location. For this it may not contain any `Junction`s
/// for which `Junction::is_interior` returns `false`. This is generally true, except for the
/// `Parent` item.
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation::*, Junction::*, NetworkId::Any};
/// # fn main() {
/// let parent = X1(Parent);
/// assert_eq!(parent.is_interior(), false);
/// let m = X2(PalletInstance(12), AccountIndex64 { network: Any, index: 23 });
/// assert_eq!(m.is_interior(), true);
/// # }
/// ```
pub fn is_interior(&self) -> bool {
self.iter().all(Junction::is_interior)
}
}
#[cfg(test)]
mod tests {
use super::MultiLocation::{self, *};
use crate::opaque::v0::{Junction::*, NetworkId::Any};
#[test]
fn match_and_split_works() {
let m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 });
assert_eq!(m.match_and_split(&X1(Parent)), None);
assert_eq!(
m.match_and_split(&X2(Parent, Parachain(42))),
Some(&AccountIndex64 { network: Any, index: 23 })
);
assert_eq!(m.match_and_split(&m), None);
}
#[test]
fn starts_with_works() {
let full = X3(Parent, Parachain(1000), AccountIndex64 { network: Any, index: 23 });
let identity = full.clone();
let prefix = X2(Parent, Parachain(1000));
let wrong_parachain = X2(Parent, Parachain(1001));
let wrong_account = X3(Parent, Parachain(1000), AccountIndex64 { network: Any, index: 24 });
let no_parents = X1(Parachain(1000));
let too_many_parents = X3(Parent, Parent, Parachain(1000));
assert!(full.starts_with(&identity));
assert!(full.starts_with(&prefix));
assert!(!full.starts_with(&wrong_parachain));
assert!(!full.starts_with(&wrong_account));
assert!(!full.starts_with(&no_parents));
assert!(!full.starts_with(&too_many_parents));
}
#[test]
fn append_with_works() {
let acc = AccountIndex64 { network: Any, index: 23 };
let mut m = X2(Parent, Parachain(42));
assert_eq!(m.append_with(X2(PalletInstance(3), acc.clone())), Ok(()));
assert_eq!(m, X4(Parent, Parachain(42), PalletInstance(3), acc.clone()));
// cannot append to create overly long multilocation
let acc = AccountIndex64 { network: Any, index: 23 };
let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42));
let suffix = X2(PalletInstance(3), acc.clone());
assert_eq!(m.append_with(suffix.clone()), Err(suffix));
}
#[test]
fn prepend_with_works() {
let mut m = X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 });
assert_eq!(m.prepend_with(X2(Parent, OnlyChild)), Ok(()));
assert_eq!(m, X3(Parent, Parachain(42), AccountIndex64 { network: Any, index: 23 }));
// cannot prepend to create overly long multilocation
let mut m = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parachain(42));
let prefix = X2(Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
// Can handle shared prefix and resizing correctly.
let mut m = X1(Parent);
let prefix = X8(
Parachain(100),
OnlyChild,
OnlyChild,
OnlyChild,
OnlyChild,
OnlyChild,
OnlyChild,
Parent,
);
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, X5(Parachain(100), OnlyChild, OnlyChild, OnlyChild, OnlyChild));
let mut m = X1(Parent);
let prefix = X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
let mut m = X1(Parent);
let prefix = X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, X8(Parent, Parent, Parent, Parent, Parent, Parent, Parent, Parent));
let mut m = X1(Parent);
let prefix = X8(Parent, Parent, Parent, Parent, OnlyChild, Parent, Parent, Parent);
assert_eq!(m.prepend_with(prefix.clone()), Ok(()));
assert_eq!(m, X7(Parent, Parent, Parent, Parent, Parent, Parent, Parent));
}
#[test]
fn canonicalize_works() {
let mut m = X1(Parent);
m.canonicalize();
assert_eq!(m, X1(Parent));
let mut m = X1(Parachain(1));
m.canonicalize();
assert_eq!(m, X1(Parachain(1)));
let mut m = X6(Parent, Parachain(1), Parent, Parachain(2), Parent, Parachain(3));
m.canonicalize();
assert_eq!(m, X2(Parent, Parachain(3)));
let mut m = X5(Parachain(1), Parent, Parachain(2), Parent, Parachain(3));
m.canonicalize();
assert_eq!(m, X1(Parachain(3)));
let mut m = X6(Parachain(1), Parent, Parachain(2), Parent, Parachain(3), Parent);
m.canonicalize();
assert_eq!(m, Null);
let mut m = X5(Parachain(1), Parent, Parent, Parent, Parachain(3));
m.canonicalize();
assert_eq!(m, X3(Parent, Parent, Parachain(3)));
let mut m = X4(Parachain(1), Parachain(2), Parent, Parent);
m.canonicalize();
assert_eq!(m, Null);
let mut m = X4(Parent, Parent, Parachain(1), Parachain(2));
m.canonicalize();
assert_eq!(m, X4(Parent, Parent, Parachain(1), Parachain(2)));
}
#[test]
fn conversion_from_other_types_works() {
use crate::v1::{self, Junction, Junctions};
fn takes_multilocation<Arg: Into<MultiLocation>>(_arg: Arg) {}
takes_multilocation(Null);
takes_multilocation(Parent);
takes_multilocation([Parent, Parachain(4)]);
assert_eq!(v1::MultiLocation::here().try_into(), Ok(MultiLocation::Null));
assert_eq!(
v1::MultiLocation::new(1, Junctions::X1(Junction::Parachain(8))).try_into(),
Ok(X2(Parent, Parachain(8))),
);
assert_eq!(
v1::MultiLocation::new(24, Junctions::Here).try_into(),
Err::<MultiLocation, ()>(()),
);
}
}
-204
View File
@@ -1,204 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Version 0 of the Cross-Consensus Message format data structures.
use super::{super::v1::Order as Order1, MultiAsset, MultiLocation, Xcm};
use alloc::vec::Vec;
use core::result;
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
/// An instruction to be executed on some or all of the assets in holding, used by asset-related XCM messages.
#[derive(Derivative, Encode, Decode, scale_info::TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
pub enum Order<RuntimeCall> {
/// Do nothing. Not generally used.
#[codec(index = 0)]
Null,
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within
/// this consensus system.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `dest`: The new owner for the assets.
///
/// Errors:
#[codec(index = 1)]
DepositAsset { assets: Vec<MultiAsset>, dest: MultiLocation },
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within
/// this consensus system.
///
/// Send an onward XCM message to `dest` of `ReserveAssetDeposit` with the given `effects`.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `dest`: The new owner for the assets.
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposit` which is sent onwards to
/// `dest`.
///
/// Errors:
#[codec(index = 2)]
DepositReserveAsset { assets: Vec<MultiAsset>, dest: MultiLocation, effects: Vec<Order<()>> },
/// Remove the asset(s) (`give`) from holding and replace them with alternative assets.
///
/// The minimum amount of assets to be received into holding for the order not to fail may be stated.
///
/// - `give`: The asset(s) to remove from holding.
/// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for. The meaning of wildcards
/// is undefined and they should be not be used.
///
/// Errors:
#[codec(index = 3)]
ExchangeAsset { give: Vec<MultiAsset>, receive: Vec<MultiAsset> },
/// Remove the asset(s) (`assets`) from holding and send a `WithdrawAsset` XCM message to a reserve location.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `reserve`: A valid location that acts as a reserve for all asset(s) in `assets`. The sovereign account
/// of this consensus system *on the reserve location* will have appropriate assets withdrawn and `effects` will
/// be executed on them. There will typically be only one valid location on any given asset/chain combination.
/// - `effects`: The orders to execute on the assets once withdrawn *on the reserve location*.
///
/// Errors:
#[codec(index = 4)]
InitiateReserveWithdraw {
assets: Vec<MultiAsset>,
reserve: MultiLocation,
effects: Vec<Order<()>>,
},
/// Remove the asset(s) (`assets`) from holding and send a `TeleportAsset` XCM message to a destination location.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `destination`: A valid location that has a bi-lateral teleportation arrangement.
/// - `effects`: The orders to execute on the assets once arrived *on the destination location*.
///
/// Errors:
#[codec(index = 5)]
InitiateTeleport { assets: Vec<MultiAsset>, dest: MultiLocation, effects: Vec<Order<()>> },
/// Send a `Balances` XCM message with the `assets` value equal to the holding contents, or a portion thereof.
///
/// - `query_id`: An identifier that will be replicated into the returned XCM message.
/// - `dest`: A valid destination for the returned XCM message. This may be limited to the current origin.
/// - `assets`: A filter for the assets that should be reported back. The assets reported back will be, asset-
/// wise, *the lesser of this value and the holding account*. No wildcards will be used when reporting assets
/// back.
///
/// Errors:
#[codec(index = 6)]
QueryHolding {
#[codec(compact)]
query_id: u64,
dest: MultiLocation,
assets: Vec<MultiAsset>,
},
/// Pay for the execution of some XCM with up to `weight` picoseconds of execution time, paying for this with
/// up to `fees` from the holding account.
///
/// Errors:
#[codec(index = 7)]
BuyExecution {
fees: MultiAsset,
weight: u64,
debt: u64,
halt_on_error: bool,
xcm: Vec<Xcm<RuntimeCall>>,
},
}
pub mod opaque {
pub type Order = super::Order<()>;
}
impl<RuntimeCall> Order<RuntimeCall> {
pub fn into<C>(self) -> Order<C> {
Order::from(self)
}
pub fn from<C>(order: Order<C>) -> Self {
use Order::*;
match order {
Null => Null,
DepositAsset { assets, dest } => DepositAsset { assets, dest },
DepositReserveAsset { assets, dest, effects } =>
DepositReserveAsset { assets, dest, effects },
ExchangeAsset { give, receive } => ExchangeAsset { give, receive },
InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw { assets, reserve, effects },
InitiateTeleport { assets, dest, effects } =>
InitiateTeleport { assets, dest, effects },
QueryHolding { query_id, dest, assets } => QueryHolding { query_id, dest, assets },
BuyExecution { fees, weight, debt, halt_on_error, xcm } => {
let xcm = xcm.into_iter().map(Xcm::from).collect();
BuyExecution { fees, weight, debt, halt_on_error, xcm }
},
}
}
}
impl<RuntimeCall> TryFrom<Order1<RuntimeCall>> for Order<RuntimeCall> {
type Error = ();
fn try_from(old: Order1<RuntimeCall>) -> result::Result<Order<RuntimeCall>, ()> {
use Order::*;
Ok(match old {
Order1::Noop => Null,
Order1::DepositAsset { assets, beneficiary, .. } =>
DepositAsset { assets: assets.try_into()?, dest: beneficiary.try_into()? },
Order1::DepositReserveAsset { assets, dest, effects, .. } => DepositReserveAsset {
assets: assets.try_into()?,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order1::ExchangeAsset { give, receive } =>
ExchangeAsset { give: give.try_into()?, receive: receive.try_into()? },
Order1::InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw {
assets: assets.try_into()?,
reserve: reserve.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order1::InitiateTeleport { assets, dest, effects } => InitiateTeleport {
assets: assets.try_into()?,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Order1::QueryHolding { query_id, dest, assets } =>
QueryHolding { query_id, dest: dest.try_into()?, assets: assets.try_into()? },
Order1::BuyExecution { fees, weight, debt, halt_on_error, instructions } => {
let xcm = instructions
.into_iter()
.map(Xcm::<RuntimeCall>::try_from)
.collect::<result::Result<_, _>>()?;
BuyExecution { fees: fees.try_into()?, weight, debt, halt_on_error, xcm }
},
})
}
}
-267
View File
@@ -1,267 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use core::result;
use parity_scale_codec::{Decode, Encode};
use super::{MultiLocation, Xcm};
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, scale_info::TypeInfo)]
pub enum Error {
Undefined,
/// An arithmetic overflow happened.
Overflow,
/// The operation is intentionally unsupported.
Unimplemented,
UnhandledXcmVersion,
/// The implementation does not handle a given XCM.
UnhandledXcmMessage,
/// The implementation does not handle an effect present in an XCM.
UnhandledEffect,
EscalationOfPrivilege,
UntrustedReserveLocation,
UntrustedTeleportLocation,
DestinationBufferOverflow,
/// The message and destination was recognized as being reachable but the operation could not be completed.
/// A human-readable explanation of the specific issue is provided.
SendFailed(#[codec(skip)] &'static str),
/// The message and destination combination was not recognized as being reachable.
CannotReachDestination(MultiLocation, Xcm<()>),
MultiLocationFull,
FailedToDecode,
BadOrigin,
ExceedsMaxMessageSize,
/// An asset transaction (like withdraw or deposit) failed.
/// See implementers of the `TransactAsset` trait for sources.
/// Causes can include type conversion failures between id or balance types.
FailedToTransactAsset(#[codec(skip)] &'static str),
/// Execution of the XCM would potentially result in a greater weight used than the pre-specified
/// weight limit. The amount that is potentially required is the parameter.
WeightLimitReached(Weight),
/// An asset wildcard was passed where it was not expected (e.g. as the asset to withdraw in a
/// `WithdrawAsset` XCM).
Wildcard,
/// The case where an XCM message has specified a weight limit on an interior call and this
/// limit is too low.
///
/// Used by:
/// - `Transact`
MaxWeightInvalid,
/// The fees specified by the XCM message were not found in the holding account.
///
/// Used by:
/// - `BuyExecution`
NotHoldingFees,
/// The weight of an XCM message is not computable ahead of execution. This generally means at least part
/// of the message is invalid, which could be due to it containing overly nested structures or an invalid
/// nested data segment (e.g. for the call in `Transact`).
WeightNotComputable,
/// The XCM did not pass the barrier condition for execution. The barrier condition differs on different
/// chains and in different circumstances, but generally it means that the conditions surrounding the message
/// were not such that the chain considers the message worth spending time executing. Since most chains
/// lift the barrier to execution on appropriate payment, presentation of an NFT voucher, or based on the
/// message origin, it means that none of those were the case.
Barrier,
/// Indicates that it is not possible for a location to have an asset be withdrawn or transferred from its
/// ownership. This probably means it doesn't own (enough of) it, but may also indicate that it is under a
/// lock, hold, freeze or is otherwise unavailable.
NotWithdrawable,
/// Indicates that the consensus system cannot deposit an asset under the ownership of a particular location.
LocationCannotHold,
/// The assets given to purchase weight is are insufficient for the weight desired.
TooExpensive,
/// The given asset is not handled.
AssetNotFound,
/// `execute_xcm` has been called too many times recursively.
RecursionLimitReached,
}
impl From<()> for Error {
fn from(_: ()) -> Self {
Self::Undefined
}
}
pub type Result = result::Result<(), Error>;
/// Local weight type; execution time in picoseconds.
pub type Weight = u64;
/// Outcome of an XCM execution.
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, scale_info::TypeInfo)]
pub enum Outcome {
/// Execution completed successfully; given weight was used.
Complete(Weight),
/// Execution started, but did not complete successfully due to the given error; given weight was used.
Incomplete(Weight, Error),
/// Execution did not start due to the given error.
Error(Error),
}
impl Outcome {
pub fn ensure_complete(self) -> Result {
match self {
Outcome::Complete(_) => Ok(()),
Outcome::Incomplete(_, e) => Err(e),
Outcome::Error(e) => Err(e),
}
}
pub fn ensure_execution(self) -> result::Result<Weight, Error> {
match self {
Outcome::Complete(w) => Ok(w),
Outcome::Incomplete(w, _) => Ok(w),
Outcome::Error(e) => Err(e),
}
}
/// How much weight was used by the XCM execution attempt.
pub fn weight_used(&self) -> Weight {
match self {
Outcome::Complete(w) => *w,
Outcome::Incomplete(w, _) => *w,
Outcome::Error(_) => 0,
}
}
}
/// Type of XCM message executor.
pub trait ExecuteXcm<RuntimeCall> {
/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The weight limit is
/// a basic hard-limit and the implementation may place further restrictions or requirements on weight and
/// other aspects.
fn execute_xcm(
origin: MultiLocation,
message: Xcm<RuntimeCall>,
weight_limit: Weight,
) -> Outcome {
log::debug!(
target: "xcm::execute_xcm",
"origin: {:?}, message: {:?}, weight_limit: {:?}",
origin,
message,
weight_limit,
);
Self::execute_xcm_in_credit(origin, message, weight_limit, 0)
}
/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight.
///
/// Some amount of `weight_credit` may be provided which, depending on the implementation, may allow
/// execution without associated payment.
fn execute_xcm_in_credit(
origin: MultiLocation,
message: Xcm<RuntimeCall>,
weight_limit: Weight,
weight_credit: Weight,
) -> Outcome;
}
impl<C> ExecuteXcm<C> for () {
fn execute_xcm_in_credit(
_origin: MultiLocation,
_message: Xcm<C>,
_weight_limit: Weight,
_weight_credit: Weight,
) -> Outcome {
Outcome::Error(Error::Unimplemented)
}
}
/// Utility for sending an XCM message.
///
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return
/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination`
/// might alter the destination and the XCM message for to the next router.
///
///
/// # Example
/// ```rust
/// # use xcm::v0::{MultiLocation, Xcm, Junction, Error, OriginKind, SendXcm, Result};
/// # use parity_scale_codec::Encode;
///
/// /// A sender that only passes the message through and does nothing.
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// return Err(Error::CannotReachDestination(destination, message))
/// }
/// }
///
/// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing.
/// struct Sender2;
/// impl SendXcm for Sender2 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// if let MultiLocation::X2(j1, j2) = destination {
/// Ok(())
/// } else {
/// Err(Error::Undefined)
/// }
/// }
/// }
///
/// /// A sender that accepts a message from an X1 parent junction, passing through otherwise.
/// struct Sender3;
/// impl SendXcm for Sender3 {
/// fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
/// match destination {
/// MultiLocation::X1(j) if j == Junction::Parent => Ok(()),
/// _ => Err(Error::CannotReachDestination(destination, message)),
/// }
/// }
/// }
///
/// // A call to send via XCM. We don't really care about this.
/// # fn main() {
/// let call: Vec<u8> = ().encode();
/// let message = Xcm::Transact { origin_type: OriginKind::Superuser, require_weight_at_most: 0, call: call.into() };
/// let destination = MultiLocation::X1(Junction::Parent);
///
/// assert!(
/// // Sender2 will block this.
/// <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone())
/// .is_err()
/// );
///
/// assert!(
/// // Sender3 will catch this.
/// <(Sender1, Sender3) as SendXcm>::send_xcm(destination.clone(), message.clone())
/// .is_ok()
/// );
/// # }
/// ```
pub trait SendXcm {
/// Send an XCM `message` to a given `destination`.
///
/// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST*
/// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without
/// trying other type fields.
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl SendXcm for Tuple {
fn send_xcm(destination: MultiLocation, message: Xcm<()>) -> Result {
for_tuples!( #(
// we shadow `destination` and `message` in each expansion for the next one.
let (destination, message) = match Tuple::send_xcm(destination, message) {
Err(Error::CannotReachDestination(d, m)) => (d, m),
o @ _ => return o,
};
)* );
Err(Error::CannotReachDestination(destination, message))
}
}
-518
View File
@@ -1,518 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! # XCM Version 1
//! Version 1 of the Cross-Consensus Message format data structures. The comprehensive list of
//! changes can be found in
//! [this PR description](https://github.com/paritytech/polkadot/pull/2815#issue-608567900).
//!
//! ## Changes to be aware of
//! Most changes should automatically be resolved via the conversion traits (i.e. `TryFrom` and
//! `From`). The list here is mostly for incompatible changes that result in an `Err(())` when
//! attempting to convert XCM objects from v0.
//!
//! ### Junction
//! - `v0::Junction::Parent` cannot be converted to v1, because the way we represent parents in v1
//! has changed - instead of being a property of the junction, v1 `MultiLocation`s now have an
//! extra field representing the number of parents that the `MultiLocation` contains.
//!
//! ### `MultiLocation`
//! - The `try_from` conversion method will always canonicalize the v0 `MultiLocation` before
//! attempting to do the proper conversion. Since canonicalization is not a fallible operation,
//! we do not expect v0 `MultiLocation` to ever fail to be upgraded to v1.
//!
//! ### `MultiAsset`
//! - Stronger typing to differentiate between a single class of `MultiAsset` and several classes
//! of `MultiAssets` is introduced. As the name suggests, a `Vec<MultiAsset>` that is used on all
//! APIs will instead be using a new type called `MultiAssets` (note the `s`).
//! - All `MultiAsset` variants whose name contains "All" in it, namely `v0::MultiAsset::All`,
//! `v0::MultiAsset::AllFungible`, `v0::MultiAsset::AllNonFungible`,
//! `v0::MultiAsset::AllAbstractFungible`, `v0::MultiAsset::AllAbstractNonFungible`,
//! `v0::MultiAsset::AllConcreteFungible` and `v0::MultiAsset::AllConcreteNonFungible`, will fail
//! to convert to v1 `MultiAsset`, since v1 does not contain these variants.
//! - Similarly, all `MultiAsset` variants whose name contains "All" in it can be converted into a
//! `WildMultiAsset`.
//! - `v0::MultiAsset::None` is not represented at all in v1.
//!
//! ### XCM
//! - No special attention necessary
//!
//! ### Order
//! - `v1::Order::DepositAsset` and `v1::Order::DepositReserveAsset` both introduced a new
//! `max_asset` field that limits the maximum classes of assets that can be deposited. During
//! conversion from v0, the `max_asset` field defaults to 1.
//! - v1 Orders that contain `MultiAsset` as argument(s) will need to explicitly specify the amount
//! and details of assets. This is to prevent accidental misuse of `All` to possibly transfer,
//! spend or otherwise perform unintended operations on `All` assets.
//! - v1 Orders that do allow the notion of `All` to be used as wildcards, will instead use a new
//! type called `MultiAssetFilter`.
use super::{
v0::{Response as OldResponse, Xcm as OldXcm},
v2::{Instruction, Response as NewResponse, Xcm as NewXcm},
};
use crate::DoubleEncoded;
use alloc::vec::Vec;
use core::{fmt::Debug, result};
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
use scale_info::TypeInfo;
mod junction;
mod multiasset;
mod multilocation;
mod order;
mod traits; // the new multiasset.
pub use junction::Junction;
pub use multiasset::{
AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets,
WildFungibility, WildMultiAsset,
};
pub use multilocation::{
Ancestor, AncestorThen, InteriorMultiLocation, Junctions, MultiLocation, Parent, ParentThen,
};
pub use order::Order;
pub use traits::{Error, ExecuteXcm, Outcome, Result, SendXcm};
// These parts of XCM v0 have been unchanged in XCM v1, and are re-imported here.
pub use super::v0::{BodyId, BodyPart, NetworkId, OriginKind};
/// A prelude for importing all types typically used when interacting with XCM messages.
pub mod prelude {
pub use super::{
junction::Junction::{self, *},
opaque,
order::Order::{self, *},
Ancestor, AncestorThen,
AssetId::{self, *},
AssetInstance::{self, *},
BodyId, BodyPart, Error as XcmError, ExecuteXcm,
Fungibility::{self, *},
InteriorMultiLocation,
Junctions::{self, *},
MultiAsset,
MultiAssetFilter::{self, *},
MultiAssets, MultiLocation,
NetworkId::{self, *},
OriginKind, Outcome, Parent, ParentThen, Response, Result as XcmResult, SendXcm,
WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
WildMultiAsset::{self, *},
Xcm::{self, *},
};
}
/// Response data to a query.
#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
pub enum Response {
/// Some assets.
Assets(MultiAssets),
/// An XCM version.
Version(super::Version),
}
/// Cross-Consensus Message: A message from one consensus system to another.
///
/// Consensus systems that may send and receive messages include blockchains and smart contracts.
///
/// All messages are delivered from a known *origin*, expressed as a `MultiLocation`.
///
/// This is the inner XCM format and is version-sensitive. Messages are typically passed using the outer
/// XCM format, known as `VersionedXcm`.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
pub enum Xcm<RuntimeCall> {
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place them into `holding`. Execute the
/// orders (`effects`).
///
/// - `assets`: The asset(s) to be withdrawn into holding.
/// - `effects`: The order(s) to execute on the holding register.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 0)]
WithdrawAsset { assets: MultiAssets, effects: Vec<Order<RuntimeCall>> },
/// Asset(s) (`assets`) have been received into the ownership of this system on the `origin` system.
///
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
/// been placed into `holding`.
///
/// - `assets`: The asset(s) that are minted into holding.
/// - `effects`: The order(s) to execute on the holding register.
///
/// Safety: `origin` must be trusted to have received and be storing `assets` such that they may later be
/// withdrawn should this system send a corresponding message.
///
/// Kind: *Trusted Indication*.
///
/// Errors:
#[codec(index = 1)]
ReserveAssetDeposited { assets: MultiAssets, effects: Vec<Order<RuntimeCall>> },
/// Asset(s) (`assets`) have been destroyed on the `origin` system and equivalent assets should be
/// created on this system.
///
/// Some orders are given (`effects`) which should be executed once the corresponding derivative assets have
/// been placed into the Holding Register.
///
/// - `assets`: The asset(s) that are minted into the Holding Register.
/// - `effects`: The order(s) to execute on the Holding Register.
///
/// Safety: `origin` must be trusted to have irrevocably destroyed the corresponding `assets` prior as a consequence
/// of sending this message.
///
/// Kind: *Trusted Indication*.
///
/// Errors:
#[codec(index = 2)]
ReceiveTeleportedAsset { assets: MultiAssets, effects: Vec<Order<RuntimeCall>> },
/// Indication of the contents of the holding register corresponding to the `QueryHolding` order of `query_id`.
///
/// - `query_id`: The identifier of the query that resulted in this message being sent.
/// - `assets`: The message content.
///
/// Safety: No concerns.
///
/// Kind: *Information*.
///
/// Errors:
#[codec(index = 3)]
QueryResponse {
#[codec(compact)]
query_id: u64,
response: Response,
},
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
/// ownership of `beneficiary`.
///
/// - `assets`: The asset(s) to be withdrawn.
/// - `beneficiary`: The new owner for the assets.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 4)]
TransferAsset { assets: MultiAssets, beneficiary: MultiLocation },
/// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the
/// ownership of `dest` within this consensus system (i.e. its sovereign account).
///
/// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`.
///
/// - `assets`: The asset(s) to be withdrawn.
/// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the
/// assets and the notification target for the reserve asset deposit message.
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to
/// `dest`.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 5)]
TransferReserveAsset { assets: MultiAssets, dest: MultiLocation, effects: Vec<Order<()>> },
/// Apply the encoded transaction `call`, whose dispatch-origin should be `origin` as expressed by the kind
/// of origin `origin_type`.
///
/// - `origin_type`: The means of expressing the message origin as a dispatch origin.
/// - `max_weight`: The weight of `call`; this should be at least the chain's calculated weight and will
/// be used in the weight determination arithmetic.
/// - `call`: The encoded transaction to be applied.
///
/// Safety: No concerns.
///
/// Kind: *Instruction*.
///
/// Errors:
#[codec(index = 6)]
Transact {
origin_type: OriginKind,
require_weight_at_most: u64,
call: DoubleEncoded<RuntimeCall>,
},
/// A message to notify about a new incoming HRMP channel. This message is meant to be sent by the
/// relay-chain to a para.
///
/// - `sender`: The sender in the to-be opened channel. Also, the initiator of the channel opening.
/// - `max_message_size`: The maximum size of a message proposed by the sender.
/// - `max_capacity`: The maximum number of messages that can be queued in the channel.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
#[codec(index = 7)]
HrmpNewChannelOpenRequest {
#[codec(compact)]
sender: u32,
#[codec(compact)]
max_message_size: u32,
#[codec(compact)]
max_capacity: u32,
},
/// A message to notify about that a previously sent open channel request has been accepted by
/// the recipient. That means that the channel will be opened during the next relay-chain session
/// change. This message is meant to be sent by the relay-chain to a para.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
///
/// Errors:
#[codec(index = 8)]
HrmpChannelAccepted {
#[codec(compact)]
recipient: u32,
},
/// A message to notify that the other party in an open channel decided to close it. In particular,
/// `initiator` is going to close the channel opened from `sender` to the `recipient`. The close
/// will be enacted at the next relay-chain session change. This message is meant to be sent by
/// the relay-chain to a para.
///
/// Safety: The message should originate directly from the relay-chain.
///
/// Kind: *System Notification*
///
/// Errors:
#[codec(index = 9)]
HrmpChannelClosing {
#[codec(compact)]
initiator: u32,
#[codec(compact)]
sender: u32,
#[codec(compact)]
recipient: u32,
},
/// A message to indicate that the embedded XCM is actually arriving on behalf of some consensus
/// location within the origin.
///
/// Kind: *Instruction*
///
/// Errors:
#[codec(index = 10)]
RelayedFrom { who: InteriorMultiLocation, message: alloc::boxed::Box<Xcm<RuntimeCall>> },
/// Ask the destination system to respond with the most recent version of XCM that they
/// support in a `QueryResponse` instruction. Any changes to this should also elicit similar
/// responses when they happen.
///
/// Kind: *Instruction*
#[codec(index = 11)]
SubscribeVersion {
#[codec(compact)]
query_id: u64,
#[codec(compact)]
max_response_weight: u64,
},
/// Cancel the effect of a previous `SubscribeVersion` instruction.
///
/// Kind: *Instruction*
#[codec(index = 12)]
UnsubscribeVersion,
}
impl<RuntimeCall> Xcm<RuntimeCall> {
pub fn into<C>(self) -> Xcm<C> {
Xcm::from(self)
}
pub fn from<C>(xcm: Xcm<C>) -> Self {
use Xcm::*;
match xcm {
WithdrawAsset { assets, effects } =>
WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() },
ReserveAssetDeposited { assets, effects } => ReserveAssetDeposited {
assets,
effects: effects.into_iter().map(Order::into).collect(),
},
ReceiveTeleportedAsset { assets, effects } => ReceiveTeleportedAsset {
assets,
effects: effects.into_iter().map(Order::into).collect(),
},
QueryResponse { query_id, response } => QueryResponse { query_id, response },
TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary },
TransferReserveAsset { assets, dest, effects } =>
TransferReserveAsset { assets, dest, effects },
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call: call.into() },
RelayedFrom { who, message } =>
RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) },
SubscribeVersion { query_id, max_response_weight } =>
SubscribeVersion { query_id, max_response_weight },
UnsubscribeVersion => UnsubscribeVersion,
}
}
}
pub mod opaque {
/// The basic concrete type of `generic::Xcm`, which doesn't make any assumptions about the format of a
/// call other than it is pre-encoded.
pub type Xcm = super::Xcm<()>;
pub use super::order::opaque::*;
}
// Convert from a v0 response to a v1 response
impl TryFrom<OldResponse> for Response {
type Error = ();
fn try_from(old_response: OldResponse) -> result::Result<Self, ()> {
match old_response {
OldResponse::Assets(assets) => Ok(Self::Assets(assets.try_into()?)),
}
}
}
impl<RuntimeCall> TryFrom<OldXcm<RuntimeCall>> for Xcm<RuntimeCall> {
type Error = ();
fn try_from(old: OldXcm<RuntimeCall>) -> result::Result<Xcm<RuntimeCall>, ()> {
use Xcm::*;
Ok(match old {
OldXcm::WithdrawAsset { assets, effects } => WithdrawAsset {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
OldXcm::ReserveAssetDeposit { assets, effects } => ReserveAssetDeposited {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
OldXcm::TeleportAsset { assets, effects } => ReceiveTeleportedAsset {
assets: assets.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
OldXcm::QueryResponse { query_id, response } =>
QueryResponse { query_id, response: response.try_into()? },
OldXcm::TransferAsset { assets, dest } =>
TransferAsset { assets: assets.try_into()?, beneficiary: dest.try_into()? },
OldXcm::TransferReserveAsset { assets, dest, effects } => TransferReserveAsset {
assets: assets.try_into()?,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
OldXcm::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
OldXcm::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
OldXcm::HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
OldXcm::Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call: call.into() },
OldXcm::RelayedFrom { who, message } => RelayedFrom {
who: MultiLocation::try_from(who)?.try_into()?,
message: alloc::boxed::Box::new((*message).try_into()?),
},
})
}
}
impl<RuntimeCall> TryFrom<NewXcm<RuntimeCall>> for Xcm<RuntimeCall> {
type Error = ();
fn try_from(old: NewXcm<RuntimeCall>) -> result::Result<Xcm<RuntimeCall>, ()> {
use Xcm::*;
let mut iter = old.0.into_iter();
let instruction = iter.next().ok_or(())?;
Ok(match instruction {
Instruction::WithdrawAsset(assets) => {
let effects = iter.map(Order::try_from).collect::<result::Result<_, _>>()?;
WithdrawAsset { assets, effects }
},
Instruction::ReserveAssetDeposited(assets) => {
if !matches!(iter.next(), Some(Instruction::ClearOrigin)) {
return Err(())
}
let effects = iter.map(Order::try_from).collect::<result::Result<_, _>>()?;
ReserveAssetDeposited { assets, effects }
},
Instruction::ReceiveTeleportedAsset(assets) => {
if !matches!(iter.next(), Some(Instruction::ClearOrigin)) {
return Err(())
}
let effects = iter.map(Order::try_from).collect::<result::Result<_, _>>()?;
ReceiveTeleportedAsset { assets, effects }
},
Instruction::QueryResponse { query_id, response, max_weight } => {
// Cannot handle special response weights.
if max_weight > 0 {
return Err(())
}
QueryResponse { query_id, response: response.try_into()? }
},
Instruction::TransferAsset { assets, beneficiary } =>
TransferAsset { assets, beneficiary },
Instruction::TransferReserveAsset { assets, dest, xcm } => TransferReserveAsset {
assets,
dest,
effects: xcm
.0
.into_iter()
.map(Order::try_from)
.collect::<result::Result<_, _>>()?,
},
Instruction::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
Instruction::HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
Instruction::HrmpChannelClosing { initiator, sender, recipient } =>
HrmpChannelClosing { initiator, sender, recipient },
Instruction::Transact { origin_type, require_weight_at_most, call } =>
Transact { origin_type, require_weight_at_most, call },
Instruction::SubscribeVersion { query_id, max_response_weight } =>
SubscribeVersion { query_id, max_response_weight },
Instruction::UnsubscribeVersion => UnsubscribeVersion,
_ => return Err(()),
})
}
}
// Convert from a v1 response to a v2 response
impl TryFrom<NewResponse> for Response {
type Error = ();
fn try_from(response: NewResponse) -> result::Result<Self, ()> {
match response {
NewResponse::Assets(assets) => Ok(Self::Assets(assets)),
NewResponse::Version(version) => Ok(Self::Version(version)),
_ => Err(()),
}
}
}
-292
View File
@@ -1,292 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Version 1 of the Cross-Consensus Message format data structures.
use super::{MultiAsset, MultiAssetFilter, MultiAssets, MultiLocation, Xcm};
use crate::{v0::Order as OldOrder, v2::Instruction};
use alloc::{vec, vec::Vec};
use core::result;
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
use scale_info::TypeInfo;
/// An instruction to be executed on some or all of the assets in holding, used by asset-related XCM messages.
#[derive(Derivative, Encode, Decode, TypeInfo)]
#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(RuntimeCall))]
pub enum Order<RuntimeCall> {
/// Do nothing. Not generally used.
#[codec(index = 0)]
Noop,
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `beneficiary`
/// within this consensus system.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first
/// `max_assets` assets/instances of those matched by `assets` will be removed, prioritized under standard asset
/// ordering. Any others will remain in holding.
/// - `beneficiary`: The new owner for the assets.
///
/// Errors:
#[codec(index = 1)]
DepositAsset { assets: MultiAssetFilter, max_assets: u32, beneficiary: MultiLocation },
/// Remove the asset(s) (`assets`) from holding and place equivalent assets under the ownership of `dest` within
/// this consensus system (i.e. its sovereign account).
///
/// Send an onward XCM message to `dest` of `ReserveAssetDeposited` with the given `effects`.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `max_assets`: The maximum number of unique assets/asset instances to remove from holding. Only the first
/// `max_assets` assets/instances of those matched by `assets` will be removed, prioritized under standard asset
/// ordering. Any others will remain in holding.
/// - `dest`: The location whose sovereign account will own the assets and thus the effective beneficiary for the
/// assets and the notification target for the reserve asset deposit message.
/// - `effects`: The orders that should be contained in the `ReserveAssetDeposited` which is sent onwards to
/// `dest`.
///
/// Errors:
#[codec(index = 2)]
DepositReserveAsset {
assets: MultiAssetFilter,
max_assets: u32,
dest: MultiLocation,
effects: Vec<Order<()>>,
},
/// Remove the asset(s) (`give`) from holding and replace them with alternative assets.
///
/// The minimum amount of assets to be received into holding for the order not to fail may be stated.
///
/// - `give`: The asset(s) to remove from holding.
/// - `receive`: The minimum amount of assets(s) which `give` should be exchanged for.
///
/// Errors:
#[codec(index = 3)]
ExchangeAsset { give: MultiAssetFilter, receive: MultiAssets },
/// Remove the asset(s) (`assets`) from holding and send a `WithdrawAsset` XCM message to a reserve location.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `reserve`: A valid location that acts as a reserve for all asset(s) in `assets`. The sovereign account
/// of this consensus system *on the reserve location* will have appropriate assets withdrawn and `effects` will
/// be executed on them. There will typically be only one valid location on any given asset/chain combination.
/// - `effects`: The orders to execute on the assets once withdrawn *on the reserve location*.
///
/// Errors:
#[codec(index = 4)]
InitiateReserveWithdraw {
assets: MultiAssetFilter,
reserve: MultiLocation,
effects: Vec<Order<()>>,
},
/// Remove the asset(s) (`assets`) from holding and send a `ReceiveTeleportedAsset` XCM message to a `destination`
/// location.
///
/// - `assets`: The asset(s) to remove from holding.
/// - `destination`: A valid location that has a bi-lateral teleportation arrangement.
/// - `effects`: The orders to execute on the assets once arrived *on the destination location*.
///
/// NOTE: The `destination` location *MUST* respect this origin as a valid teleportation origin for all `assets`.
/// If it does not, then the assets may be lost.
///
/// Errors:
#[codec(index = 5)]
InitiateTeleport { assets: MultiAssetFilter, dest: MultiLocation, effects: Vec<Order<()>> },
/// Send a `Balances` XCM message with the `assets` value equal to the holding contents, or a portion thereof.
///
/// - `query_id`: An identifier that will be replicated into the returned XCM message.
/// - `dest`: A valid destination for the returned XCM message. This may be limited to the current origin.
/// - `assets`: A filter for the assets that should be reported back. The assets reported back will be, asset-
/// wise, *the lesser of this value and the holding register*. No wildcards will be used when reporting assets
/// back.
///
/// Errors:
#[codec(index = 6)]
QueryHolding {
#[codec(compact)]
query_id: u64,
dest: MultiLocation,
assets: MultiAssetFilter,
},
/// Pay for the execution of some XCM `instructions` and `orders` with up to `weight` picoseconds of execution time,
/// paying for this with up to `fees` from the Holding Register.
///
/// - `fees`: The asset(s) to remove from holding to pay for fees.
/// - `weight`: The amount of weight to purchase; this should be at least the shallow weight of `effects` and `xcm`.
/// - `debt`: The amount of weight-debt already incurred to be paid off; this should be equal to the unpaid weight of
/// any surrounding operations/orders.
/// - `halt_on_error`: If `true`, the execution of the `orders` and `operations` will halt on the first failure. If
/// `false`, then execution will continue regardless.
/// - `instructions`: XCM instructions to be executed outside of the context of the current Holding Register;
/// execution of these instructions happens AFTER the execution of the `orders`. The (shallow) weight for these
/// must be paid for with the `weight` purchased.
/// Errors:
#[codec(index = 7)]
BuyExecution {
fees: MultiAsset,
weight: u64,
debt: u64,
halt_on_error: bool,
instructions: Vec<Xcm<RuntimeCall>>,
},
}
pub mod opaque {
pub type Order = super::Order<()>;
}
impl<RuntimeCall> Order<RuntimeCall> {
pub fn into<C>(self) -> Order<C> {
Order::from(self)
}
pub fn from<C>(order: Order<C>) -> Self {
use Order::*;
match order {
Noop => Noop,
DepositAsset { assets, max_assets, beneficiary } =>
DepositAsset { assets, max_assets, beneficiary },
DepositReserveAsset { assets, max_assets, dest, effects } =>
DepositReserveAsset { assets, max_assets, dest, effects },
ExchangeAsset { give, receive } => ExchangeAsset { give, receive },
InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw { assets, reserve, effects },
InitiateTeleport { assets, dest, effects } =>
InitiateTeleport { assets, dest, effects },
QueryHolding { query_id, dest, assets } => QueryHolding { query_id, dest, assets },
BuyExecution { fees, weight, debt, halt_on_error, instructions } => {
let instructions = instructions.into_iter().map(Xcm::from).collect();
BuyExecution { fees, weight, debt, halt_on_error, instructions }
},
}
}
}
impl<RuntimeCall> TryFrom<OldOrder<RuntimeCall>> for Order<RuntimeCall> {
type Error = ();
fn try_from(old: OldOrder<RuntimeCall>) -> result::Result<Order<RuntimeCall>, ()> {
use Order::*;
Ok(match old {
OldOrder::Null => Noop,
OldOrder::DepositAsset { assets, dest } => DepositAsset {
assets: assets.try_into()?,
max_assets: 1,
beneficiary: dest.try_into()?,
},
OldOrder::DepositReserveAsset { assets, dest, effects } => DepositReserveAsset {
assets: assets.try_into()?,
max_assets: 1,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
OldOrder::ExchangeAsset { give, receive } =>
ExchangeAsset { give: give.try_into()?, receive: receive.try_into()? },
OldOrder::InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw {
assets: assets.try_into()?,
reserve: reserve.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
OldOrder::InitiateTeleport { assets, dest, effects } => InitiateTeleport {
assets: assets.try_into()?,
dest: dest.try_into()?,
effects: effects
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
OldOrder::QueryHolding { query_id, dest, assets } =>
QueryHolding { query_id, dest: dest.try_into()?, assets: assets.try_into()? },
OldOrder::BuyExecution { fees, weight, debt, halt_on_error, xcm } => {
let instructions = xcm
.into_iter()
.map(Xcm::<RuntimeCall>::try_from)
.collect::<result::Result<_, _>>()?;
BuyExecution { fees: fees.try_into()?, weight, debt, halt_on_error, instructions }
},
})
}
}
impl<RuntimeCall> TryFrom<Instruction<RuntimeCall>> for Order<RuntimeCall> {
type Error = ();
fn try_from(old: Instruction<RuntimeCall>) -> result::Result<Order<RuntimeCall>, ()> {
use Order::*;
Ok(match old {
Instruction::DepositAsset { assets, max_assets, beneficiary } =>
DepositAsset { assets, max_assets, beneficiary },
Instruction::DepositReserveAsset { assets, max_assets, dest, xcm } =>
DepositReserveAsset {
assets,
max_assets,
dest,
effects: xcm
.0
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Instruction::ExchangeAsset { give, receive } => ExchangeAsset { give, receive },
Instruction::InitiateReserveWithdraw { assets, reserve, xcm } =>
InitiateReserveWithdraw {
assets,
reserve,
effects: xcm
.0
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Instruction::InitiateTeleport { assets, dest, xcm } => InitiateTeleport {
assets,
dest,
effects: xcm
.0
.into_iter()
.map(Order::<()>::try_from)
.collect::<result::Result<_, _>>()?,
},
Instruction::QueryHolding { query_id, dest, assets, max_response_weight } => {
// Cannot handle special response weights.
if max_response_weight > 0 {
return Err(())
}
QueryHolding { query_id, dest, assets }
},
Instruction::BuyExecution { fees, weight_limit } => {
let instructions = vec![];
let halt_on_error = true;
let weight = 0;
let debt = Option::<u64>::from(weight_limit).ok_or(())?;
BuyExecution { fees, weight, debt, halt_on_error, instructions }
},
_ => return Err(()),
})
}
}
-277
View File
@@ -1,277 +0,0 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use core::result;
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
use super::{MultiLocation, Xcm};
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
pub enum Error {
Undefined,
/// An arithmetic overflow happened.
Overflow,
/// The operation is intentionally unsupported.
Unimplemented,
UnhandledXcmVersion,
/// The implementation does not handle a given XCM.
UnhandledXcmMessage,
/// The implementation does not handle an effect present in an XCM.
UnhandledEffect,
EscalationOfPrivilege,
UntrustedReserveLocation,
UntrustedTeleportLocation,
DestinationBufferOverflow,
/// The message and destination was recognized as being reachable but the operation could not be completed.
/// A human-readable explanation of the specific issue is provided.
SendFailed(#[codec(skip)] &'static str),
/// The message and destination combination was not recognized as being reachable.
CannotReachDestination(MultiLocation, Xcm<()>),
MultiLocationFull,
FailedToDecode,
BadOrigin,
ExceedsMaxMessageSize,
/// An asset transaction (like withdraw or deposit) failed.
/// See implementers of the `TransactAsset` trait for sources.
/// Causes can include type conversion failures between id or balance types.
FailedToTransactAsset(#[codec(skip)] &'static str),
/// Execution of the XCM would potentially result in a greater weight used than the pre-specified
/// weight limit. The amount that is potentially required is the parameter.
WeightLimitReached(Weight),
/// An asset wildcard was passed where it was not expected (e.g. as the asset to withdraw in a
/// `WithdrawAsset` XCM).
Wildcard,
/// The case where an XCM message has specified a weight limit on an interior call and this
/// limit is too low.
///
/// Used by:
/// - `Transact`
MaxWeightInvalid,
/// The fees specified by the XCM message were not found in the holding register.
///
/// Used by:
/// - `BuyExecution`
NotHoldingFees,
/// The weight of an XCM message is not computable ahead of execution. This generally means at least part
/// of the message is invalid, which could be due to it containing overly nested structures or an invalid
/// nested data segment (e.g. for the call in `Transact`).
WeightNotComputable,
/// The XCM did not pass the barrier condition for execution. The barrier condition differs on different
/// chains and in different circumstances, but generally it means that the conditions surrounding the message
/// were not such that the chain considers the message worth spending time executing. Since most chains
/// lift the barrier to execution on appropriate payment, presentation of an NFT voucher, or based on the
/// message origin, it means that none of those were the case.
Barrier,
/// Indicates that it is not possible for a location to have an asset be withdrawn or transferred from its
/// ownership. This probably means it doesn't own (enough of) it, but may also indicate that it is under a
/// lock, hold, freeze or is otherwise unavailable.
NotWithdrawable,
/// Indicates that the consensus system cannot deposit an asset under the ownership of a particular location.
LocationCannotHold,
/// The assets given to purchase weight is are insufficient for the weight desired.
TooExpensive,
/// The given asset is not handled.
AssetNotFound,
/// The given message cannot be translated into a format that the destination can be expected to interpret.
DestinationUnsupported,
/// `execute_xcm` has been called too many times recursively.
RecursionLimitReached,
}
impl From<()> for Error {
fn from(_: ()) -> Self {
Self::Undefined
}
}
pub type Result = result::Result<(), Error>;
/// Local weight type; execution time in picoseconds.
pub type Weight = u64;
/// Outcome of an XCM execution.
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
pub enum Outcome {
/// Execution completed successfully; given weight was used.
Complete(Weight),
/// Execution started, but did not complete successfully due to the given error; given weight was used.
Incomplete(Weight, Error),
/// Execution did not start due to the given error.
Error(Error),
}
impl Outcome {
pub fn ensure_complete(self) -> Result {
match self {
Outcome::Complete(_) => Ok(()),
Outcome::Incomplete(_, e) => Err(e),
Outcome::Error(e) => Err(e),
}
}
pub fn ensure_execution(self) -> result::Result<Weight, Error> {
match self {
Outcome::Complete(w) => Ok(w),
Outcome::Incomplete(w, _) => Ok(w),
Outcome::Error(e) => Err(e),
}
}
/// How much weight was used by the XCM execution attempt.
pub fn weight_used(&self) -> Weight {
match self {
Outcome::Complete(w) => *w,
Outcome::Incomplete(w, _) => *w,
Outcome::Error(_) => 0,
}
}
}
/// Type of XCM message executor.
pub trait ExecuteXcm<RuntimeCall> {
/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The weight limit is
/// a basic hard-limit and the implementation may place further restrictions or requirements on weight and
/// other aspects.
fn execute_xcm(
origin: impl Into<MultiLocation>,
message: Xcm<RuntimeCall>,
weight_limit: Weight,
) -> Outcome {
let origin = origin.into();
log::debug!(
target: "xcm::execute_xcm",
"origin: {:?}, message: {:?}, weight_limit: {:?}",
origin,
message,
weight_limit,
);
Self::execute_xcm_in_credit(origin, message, weight_limit, 0)
}
/// Execute some XCM `message` from `origin` using no more than `weight_limit` weight.
///
/// Some amount of `weight_credit` may be provided which, depending on the implementation, may allow
/// execution without associated payment.
fn execute_xcm_in_credit(
origin: impl Into<MultiLocation>,
message: Xcm<RuntimeCall>,
weight_limit: Weight,
weight_credit: Weight,
) -> Outcome;
}
impl<C> ExecuteXcm<C> for () {
fn execute_xcm_in_credit(
_origin: impl Into<MultiLocation>,
_message: Xcm<C>,
_weight_limit: Weight,
_weight_credit: Weight,
) -> Outcome {
Outcome::Error(Error::Unimplemented)
}
}
/// Utility for sending an XCM message.
///
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return
/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination`
/// might alter the destination and the XCM message for to the next router.
///
///
/// # Example
/// ```rust
/// # use xcm::v1::{MultiLocation, Xcm, Junction, Junctions, Error, OriginKind, SendXcm, Result, Parent};
/// # use parity_scale_codec::Encode;
///
/// /// A sender that only passes the message through and does nothing.
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> Result {
/// return Err(Error::CannotReachDestination(destination.into(), message))
/// }
/// }
///
/// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing.
/// struct Sender2;
/// impl SendXcm for Sender2 {
/// fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> Result {
/// let destination = destination.into();
/// if matches!(destination.interior(), Junctions::X2(j1, j2))
/// && destination.parent_count() == 0
/// {
/// Ok(())
/// } else {
/// Err(Error::Undefined)
/// }
/// }
/// }
///
/// /// A sender that accepts a message from an X1 parent junction, passing through otherwise.
/// struct Sender3;
/// impl SendXcm for Sender3 {
/// fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> Result {
/// let destination = destination.into();
/// if matches!(destination.interior(), Junctions::Here)
/// && destination.parent_count() == 1
/// {
/// Ok(())
/// } else {
/// Err(Error::CannotReachDestination(destination, message))
/// }
/// }
/// }
///
/// // A call to send via XCM. We don't really care about this.
/// # fn main() {
/// let call: Vec<u8> = ().encode();
/// let message = Xcm::Transact { origin_type: OriginKind::Superuser, require_weight_at_most: 0, call: call.into() };
///
/// assert!(
/// // Sender2 will block this.
/// <(Sender1, Sender2, Sender3) as SendXcm>::send_xcm(Parent, message.clone())
/// .is_err()
/// );
///
/// assert!(
/// // Sender3 will catch this.
/// <(Sender1, Sender3) as SendXcm>::send_xcm(Parent, message.clone())
/// .is_ok()
/// );
/// # }
/// ```
pub trait SendXcm {
/// Send an XCM `message` to a given `destination`.
///
/// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST*
/// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without
/// trying other type fields.
fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> Result;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl SendXcm for Tuple {
fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> Result {
for_tuples!( #(
// we shadow `destination` and `message` in each expansion for the next one.
let (destination, message) = match Tuple::send_xcm(destination, message) {
Err(Error::CannotReachDestination(d, m)) => (d, m),
o @ _ => return o,
};
)* );
Err(Error::CannotReachDestination(destination.into(), message))
}
}
@@ -17,15 +17,16 @@
//! Support data structures for `MultiLocation`, primarily the `Junction` datatype.
use super::{BodyId, BodyPart, Junctions, MultiLocation, NetworkId};
use crate::v0::Junction as Junction0;
use crate::v3::Junction as NewJunction;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::{traits::ConstU32, WeakBoundedVec};
use sp_core::{bounded::WeakBoundedVec, ConstU32};
/// A single item in a path to describe the relative location of a consensus system.
///
/// Each item assumes a pre-existing location as its context and is defined in terms of it.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum Junction {
/// An indexed parachain belonging to and operated by the context.
///
@@ -77,23 +78,30 @@ pub enum Junction {
Plurality { id: BodyId, part: BodyPart },
}
impl TryFrom<Junction0> for Junction {
impl TryFrom<NewJunction> for Junction {
type Error = ();
fn try_from(value: Junction0) -> Result<Self, Self::Error> {
match value {
Junction0::Parent => Err(()),
Junction0::Parachain(id) => Ok(Self::Parachain(id)),
Junction0::AccountId32 { network, id } => Ok(Self::AccountId32 { network, id }),
Junction0::AccountIndex64 { network, index } =>
Ok(Self::AccountIndex64 { network, index }),
Junction0::AccountKey20 { network, key } => Ok(Self::AccountKey20 { network, key }),
Junction0::PalletInstance(index) => Ok(Self::PalletInstance(index)),
Junction0::GeneralIndex(id) => Ok(Self::GeneralIndex(id)),
Junction0::GeneralKey(key) => Ok(Self::GeneralKey(key)),
Junction0::OnlyChild => Ok(Self::OnlyChild),
Junction0::Plurality { id, part } => Ok(Self::Plurality { id: id.into(), part }),
}
fn try_from(value: NewJunction) -> Result<Self, Self::Error> {
use NewJunction::*;
Ok(match value {
Parachain(id) => Self::Parachain(id),
AccountId32 { network, id } => Self::AccountId32 { network: network.try_into()?, id },
AccountIndex64 { network, index } =>
Self::AccountIndex64 { network: network.try_into()?, index },
AccountKey20 { network, key } =>
Self::AccountKey20 { network: network.try_into()?, key },
PalletInstance(index) => Self::PalletInstance(index),
GeneralIndex(id) => Self::GeneralIndex(id),
GeneralKey(key) => Self::GeneralKey(
key[..]
.to_vec()
.try_into()
.expect("array is of size 32 and so will never be out of bounds; qed"),
),
OnlyChild => Self::OnlyChild,
Plurality { id, part } => Self::Plurality { id: id.into(), part: part.into() },
_ => return Err(()),
})
}
}
+392 -208
View File
@@ -50,23 +50,198 @@
//! `DepositAsset` instructions. Failing that, dispatch calls to `teleport_assets` and
//! `reserve_transfer_assets` will fail with `UnweighableMessage`.
use super::v1::{Order as OldOrder, Response as OldResponse, Xcm as OldXcm};
use crate::{DoubleEncoded, GetWeight};
use super::{
v3::{
BodyId as NewBodyId, BodyPart as NewBodyPart, Instruction as NewInstruction,
NetworkId as NewNetworkId, Response as NewResponse, WeightLimit as NewWeightLimit,
Xcm as NewXcm,
},
DoubleEncoded, GetWeight,
};
use alloc::{vec, vec::Vec};
use core::{fmt::Debug, result};
use derivative::Derivative;
use parity_scale_codec::{self, Decode, Encode};
use parity_scale_codec::{self, Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_core::{bounded::WeakBoundedVec, ConstU32};
mod junction;
mod multiasset;
mod multilocation;
mod traits;
pub use traits::{Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm};
// These parts of XCM v1 have been unchanged in XCM v2, and are re-imported here.
pub use super::v1::{
Ancestor, AncestorThen, AssetId, AssetInstance, BodyId, BodyPart, Fungibility,
InteriorMultiLocation, Junction, Junctions, MultiAsset, MultiAssetFilter, MultiAssets,
MultiLocation, NetworkId, OriginKind, Parent, ParentThen, WildFungibility, WildMultiAsset,
pub use junction::Junction;
pub use multiasset::{
AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets,
WildFungibility, WildMultiAsset,
};
pub use multilocation::{
Ancestor, AncestorThen, InteriorMultiLocation, Junctions, MultiLocation, Parent, ParentThen,
};
pub use traits::{Error, ExecuteXcm, Outcome, Result, SendError, SendResult, SendXcm};
/// Basically just the XCM (more general) version of `ParachainDispatchOrigin`.
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
pub enum OriginKind {
/// Origin should just be the native dispatch origin representation for the sender in the
/// local runtime framework. For Cumulus/Frame chains this is the `Parachain` or `Relay` origin
/// if coming from a chain, though there may be others if the `MultiLocation` XCM origin has a
/// primary/native dispatch origin form.
Native,
/// Origin should just be the standard account-based origin with the sovereign account of
/// the sender. For Cumulus/Frame chains, this is the `Signed` origin.
SovereignAccount,
/// Origin should be the super-user. For Cumulus/Frame chains, this is the `Root` origin.
/// This will not usually be an available option.
Superuser,
/// Origin should be interpreted as an XCM native origin and the `MultiLocation` should be
/// encoded directly in the dispatch origin unchanged. For Cumulus/Frame chains, this will be
/// the `pallet_xcm::Origin::Xcm` type.
Xcm,
}
/// A global identifier of an account-bearing consensus system.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum NetworkId {
/// Unidentified/any.
Any,
/// Some named network.
Named(WeakBoundedVec<u8, ConstU32<32>>),
/// The Polkadot Relay chain
Polkadot,
/// Kusama.
Kusama,
}
impl TryInto<NetworkId> for Option<NewNetworkId> {
type Error = ();
fn try_into(self) -> result::Result<NetworkId, ()> {
use NewNetworkId::*;
Ok(match self {
None => NetworkId::Any,
Some(Polkadot) => NetworkId::Polkadot,
Some(Kusama) => NetworkId::Kusama,
_ => return Err(()),
})
}
}
/// An identifier of a pluralistic body.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum BodyId {
/// The only body in its context.
Unit,
/// A named body.
Named(WeakBoundedVec<u8, ConstU32<32>>),
/// An indexed body.
Index(#[codec(compact)] u32),
/// The unambiguous executive body (for Polkadot, this would be the Polkadot council).
Executive,
/// The unambiguous technical body (for Polkadot, this would be the Technical Committee).
Technical,
/// The unambiguous legislative body (for Polkadot, this could be considered the opinion of a majority of
/// lock-voters).
Legislative,
/// The unambiguous judicial body (this doesn't exist on Polkadot, but if it were to get a "grand oracle", it
/// may be considered as that).
Judicial,
/// The unambiguous defense body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `staking_admin` track).
Defense,
/// The unambiguous administration body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `general_admin` track).
Administration,
/// The unambiguous treasury body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `treasurer` track).
Treasury,
}
impl From<NewBodyId> for BodyId {
fn from(n: NewBodyId) -> Self {
use NewBodyId::*;
match n {
Unit => Self::Unit,
Moniker(n) => Self::Named(
n[..]
.to_vec()
.try_into()
.expect("array size is 4 and so will never be out of bounds; qed"),
),
Index(n) => Self::Index(n),
Executive => Self::Executive,
Technical => Self::Technical,
Legislative => Self::Legislative,
Judicial => Self::Judicial,
Defense => Self::Defense,
Administration => Self::Administration,
Treasury => Self::Treasury,
}
}
}
/// A part of a pluralistic body.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum BodyPart {
/// The body's declaration, under whatever means it decides.
Voice,
/// A given number of members of the body.
Members {
#[codec(compact)]
count: u32,
},
/// A given number of members of the body, out of some larger caucus.
Fraction {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
/// No less than the given proportion of members of the body.
AtLeastProportion {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
/// More than than the given proportion of members of the body.
MoreThanProportion {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
}
impl BodyPart {
/// Returns `true` if the part represents a strict majority (> 50%) of the body in question.
pub fn is_majority(&self) -> bool {
match self {
BodyPart::Fraction { nom, denom } if *nom * 2 > *denom => true,
BodyPart::AtLeastProportion { nom, denom } if *nom * 2 > *denom => true,
BodyPart::MoreThanProportion { nom, denom } if *nom * 2 >= *denom => true,
_ => false,
}
}
}
impl From<NewBodyPart> for BodyPart {
fn from(n: NewBodyPart) -> Self {
use NewBodyPart::*;
match n {
Voice => Self::Voice,
Members { count } => Self::Members { count },
Fraction { nom, denom } => Self::Fraction { nom, denom },
AtLeastProportion { nom, denom } => Self::AtLeastProportion { nom, denom },
MoreThanProportion { nom, denom } => Self::MoreThanProportion { nom, denom },
}
}
}
/// This module's XCM version.
pub const VERSION: super::Version = 2;
@@ -218,6 +393,17 @@ impl From<WeightLimit> for Option<u64> {
}
}
impl TryFrom<NewWeightLimit> for WeightLimit {
type Error = ();
fn try_from(x: NewWeightLimit) -> result::Result<Self, Self::Error> {
use NewWeightLimit::*;
match x {
Limited(w) => Ok(Self::Limited(w.ref_time())),
Unlimited => Ok(Self::Unlimited),
}
}
}
/// Local weight type; execution time in picoseconds.
pub type Weight = u64;
@@ -422,6 +608,12 @@ pub enum Instruction<RuntimeCall> {
/// A `QueryResponse` message of type `ExecutionOutcome` is sent to `dest` with the given
/// `query_id` and the outcome of the XCM.
///
/// - `query_id`: An identifier that will be replicated into the returned XCM message.
/// - `dest`: A valid destination for the returned XCM message.
/// - `max_response_weight`: The maximum amount of weight that the `QueryResponse` item which
/// is sent as a reply may take to execute. NOTE: If this is unexpectedly large then the
/// response may not execute at all.
///
/// Kind: *Instruction*
///
/// Errors:
@@ -633,7 +825,14 @@ pub enum Instruction<RuntimeCall> {
/// support in a `QueryResponse` instruction. Any changes to this should also elicit similar
/// responses when they happen.
///
/// - `query_id`: An identifier that will be replicated into the returned XCM message.
/// - `max_response_weight`: The maximum amount of weight that the `QueryResponse` item which
/// is sent as a reply may take to execute. NOTE: If this is unexpectedly large then the
/// response may not execute at all.
///
/// Kind: *Instruction*
///
/// Errors: *Fallible*
SubscribeVersion {
#[codec(compact)]
query_id: QueryId,
@@ -644,6 +843,8 @@ pub enum Instruction<RuntimeCall> {
/// Cancel the effect of a previous `SubscribeVersion` instruction.
///
/// Kind: *Instruction*
///
/// Errors: *Fallible*
UnsubscribeVersion,
}
@@ -708,48 +909,81 @@ impl<RuntimeCall> Instruction<RuntimeCall> {
// TODO: Automate Generation
impl<RuntimeCall, W: XcmWeightInfo<RuntimeCall>> GetWeight<W> for Instruction<RuntimeCall> {
fn weight(&self) -> Weight {
fn weight(&self) -> sp_weights::Weight {
use Instruction::*;
match self {
WithdrawAsset(assets) => W::withdraw_asset(assets),
ReserveAssetDeposited(assets) => W::reserve_asset_deposited(assets),
ReceiveTeleportedAsset(assets) => W::receive_teleported_asset(assets),
WithdrawAsset(assets) => sp_weights::Weight::from_ref_time(W::withdraw_asset(assets)),
ReserveAssetDeposited(assets) =>
sp_weights::Weight::from_ref_time(W::reserve_asset_deposited(assets)),
ReceiveTeleportedAsset(assets) =>
sp_weights::Weight::from_ref_time(W::receive_teleported_asset(assets)),
QueryResponse { query_id, response, max_weight } =>
W::query_response(query_id, response, max_weight),
TransferAsset { assets, beneficiary } => W::transfer_asset(assets, beneficiary),
sp_weights::Weight::from_ref_time(W::query_response(query_id, response, max_weight)),
TransferAsset { assets, beneficiary } =>
sp_weights::Weight::from_ref_time(W::transfer_asset(assets, beneficiary)),
TransferReserveAsset { assets, dest, xcm } =>
W::transfer_reserve_asset(&assets, dest, xcm),
sp_weights::Weight::from_ref_time(W::transfer_reserve_asset(&assets, dest, xcm)),
Transact { origin_type, require_weight_at_most, call } =>
W::transact(origin_type, require_weight_at_most, call),
sp_weights::Weight::from_ref_time(W::transact(
origin_type,
require_weight_at_most,
call,
)),
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
W::hrmp_new_channel_open_request(sender, max_message_size, max_capacity),
HrmpChannelAccepted { recipient } => W::hrmp_channel_accepted(recipient),
sp_weights::Weight::from_ref_time(W::hrmp_new_channel_open_request(
sender,
max_message_size,
max_capacity,
)),
HrmpChannelAccepted { recipient } =>
sp_weights::Weight::from_ref_time(W::hrmp_channel_accepted(recipient)),
HrmpChannelClosing { initiator, sender, recipient } =>
W::hrmp_channel_closing(initiator, sender, recipient),
ClearOrigin => W::clear_origin(),
DescendOrigin(who) => W::descend_origin(who),
sp_weights::Weight::from_ref_time(W::hrmp_channel_closing(
initiator, sender, recipient,
)),
ClearOrigin => sp_weights::Weight::from_ref_time(W::clear_origin()),
DescendOrigin(who) => sp_weights::Weight::from_ref_time(W::descend_origin(who)),
ReportError { query_id, dest, max_response_weight } =>
W::report_error(query_id, dest, max_response_weight),
sp_weights::Weight::from_ref_time(W::report_error(
query_id,
dest,
max_response_weight,
)),
DepositAsset { assets, max_assets, beneficiary } =>
W::deposit_asset(assets, max_assets, beneficiary),
sp_weights::Weight::from_ref_time(W::deposit_asset(assets, max_assets, beneficiary)),
DepositReserveAsset { assets, max_assets, dest, xcm } =>
W::deposit_reserve_asset(assets, max_assets, dest, xcm),
ExchangeAsset { give, receive } => W::exchange_asset(give, receive),
InitiateReserveWithdraw { assets, reserve, xcm } =>
sp_weights::Weight::from_ref_time(W::deposit_reserve_asset(
assets, max_assets, dest, xcm,
)),
ExchangeAsset { give, receive } =>
sp_weights::Weight::from_ref_time(W::exchange_asset(give, receive)),
InitiateReserveWithdraw { assets, reserve, xcm } => sp_weights::Weight::from_ref_time(
W::initiate_reserve_withdraw(assets, reserve, xcm),
InitiateTeleport { assets, dest, xcm } => W::initiate_teleport(assets, dest, xcm),
),
InitiateTeleport { assets, dest, xcm } =>
sp_weights::Weight::from_ref_time(W::initiate_teleport(assets, dest, xcm)),
QueryHolding { query_id, dest, assets, max_response_weight } =>
W::query_holding(query_id, dest, assets, max_response_weight),
BuyExecution { fees, weight_limit } => W::buy_execution(fees, weight_limit),
RefundSurplus => W::refund_surplus(),
SetErrorHandler(xcm) => W::set_error_handler(xcm),
SetAppendix(xcm) => W::set_appendix(xcm),
ClearError => W::clear_error(),
ClaimAsset { assets, ticket } => W::claim_asset(assets, ticket),
Trap(code) => W::trap(code),
sp_weights::Weight::from_ref_time(W::query_holding(
query_id,
dest,
assets,
max_response_weight,
)),
BuyExecution { fees, weight_limit } =>
sp_weights::Weight::from_ref_time(W::buy_execution(fees, weight_limit)),
RefundSurplus => sp_weights::Weight::from_ref_time(W::refund_surplus()),
SetErrorHandler(xcm) => sp_weights::Weight::from_ref_time(W::set_error_handler(xcm)),
SetAppendix(xcm) => sp_weights::Weight::from_ref_time(W::set_appendix(xcm)),
ClearError => sp_weights::Weight::from_ref_time(W::clear_error()),
ClaimAsset { assets, ticket } =>
sp_weights::Weight::from_ref_time(W::claim_asset(assets, ticket)),
Trap(code) => sp_weights::Weight::from_ref_time(W::trap(code)),
SubscribeVersion { query_id, max_response_weight } =>
W::subscribe_version(query_id, max_response_weight),
UnsubscribeVersion => W::unsubscribe_version(),
sp_weights::Weight::from_ref_time(W::subscribe_version(
query_id,
max_response_weight,
)),
UnsubscribeVersion => sp_weights::Weight::from_ref_time(W::unsubscribe_version()),
}
}
}
@@ -764,180 +998,130 @@ pub mod opaque {
pub type Instruction = super::Instruction<()>;
}
// Convert from a v1 response to a v2 response
impl TryFrom<OldResponse> for Response {
// Convert from a v3 response to a v2 response
impl TryFrom<NewResponse> for Response {
type Error = ();
fn try_from(old_response: OldResponse) -> result::Result<Self, ()> {
match old_response {
OldResponse::Assets(assets) => Ok(Self::Assets(assets)),
OldResponse::Version(version) => Ok(Self::Version(version)),
}
}
}
impl<RuntimeCall> TryFrom<OldXcm<RuntimeCall>> for Xcm<RuntimeCall> {
type Error = ();
fn try_from(old: OldXcm<RuntimeCall>) -> result::Result<Xcm<RuntimeCall>, ()> {
use Instruction::*;
Ok(Xcm(match old {
OldXcm::WithdrawAsset { assets, effects } => Some(Ok(WithdrawAsset(assets)))
.into_iter()
.chain(effects.into_iter().map(Instruction::try_from))
.collect::<result::Result<Vec<_>, _>>()?,
OldXcm::ReserveAssetDeposited { assets, effects } =>
Some(Ok(ReserveAssetDeposited(assets)))
.into_iter()
.chain(Some(Ok(ClearOrigin)).into_iter())
.chain(effects.into_iter().map(Instruction::try_from))
.collect::<result::Result<Vec<_>, _>>()?,
OldXcm::ReceiveTeleportedAsset { assets, effects } =>
Some(Ok(ReceiveTeleportedAsset(assets)))
.into_iter()
.chain(Some(Ok(ClearOrigin)).into_iter())
.chain(effects.into_iter().map(Instruction::try_from))
.collect::<result::Result<Vec<_>, _>>()?,
OldXcm::QueryResponse { query_id, response } => vec![QueryResponse {
query_id,
response: response.try_into()?,
max_weight: 50_000_000,
}],
OldXcm::TransferAsset { assets, beneficiary } =>
vec![TransferAsset { assets, beneficiary }],
OldXcm::TransferReserveAsset { assets, dest, effects } => vec![TransferReserveAsset {
assets,
dest,
xcm: Xcm(effects
.into_iter()
.map(Instruction::<()>::try_from)
.collect::<result::Result<_, _>>()?),
}],
OldXcm::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
vec![HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }],
OldXcm::HrmpChannelAccepted { recipient } => vec![HrmpChannelAccepted { recipient }],
OldXcm::HrmpChannelClosing { initiator, sender, recipient } =>
vec![HrmpChannelClosing { initiator, sender, recipient }],
OldXcm::Transact { origin_type, require_weight_at_most, call } =>
vec![Transact { origin_type, require_weight_at_most, call }],
// We don't handle this one at all due to nested XCM.
OldXcm::RelayedFrom { .. } => return Err(()),
OldXcm::SubscribeVersion { query_id, max_response_weight } =>
vec![SubscribeVersion { query_id, max_response_weight }],
OldXcm::UnsubscribeVersion => vec![UnsubscribeVersion],
}))
}
}
impl<RuntimeCall> TryFrom<OldOrder<RuntimeCall>> for Instruction<RuntimeCall> {
type Error = ();
fn try_from(old: OldOrder<RuntimeCall>) -> result::Result<Instruction<RuntimeCall>, ()> {
use Instruction::*;
Ok(match old {
OldOrder::Noop => return Err(()),
OldOrder::DepositAsset { assets, max_assets, beneficiary } =>
DepositAsset { assets, max_assets, beneficiary },
OldOrder::DepositReserveAsset { assets, max_assets, dest, effects } =>
DepositReserveAsset {
assets,
max_assets,
dest,
xcm: Xcm(effects
.into_iter()
.map(Instruction::<()>::try_from)
.collect::<result::Result<_, _>>()?),
},
OldOrder::ExchangeAsset { give, receive } => ExchangeAsset { give, receive },
OldOrder::InitiateReserveWithdraw { assets, reserve, effects } =>
InitiateReserveWithdraw {
assets,
reserve,
xcm: Xcm(effects
.into_iter()
.map(Instruction::<()>::try_from)
.collect::<result::Result<_, _>>()?),
},
OldOrder::InitiateTeleport { assets, dest, effects } => InitiateTeleport {
assets,
dest,
xcm: Xcm(effects
.into_iter()
.map(Instruction::<()>::try_from)
.collect::<result::Result<_, _>>()?),
},
OldOrder::QueryHolding { query_id, dest, assets } =>
QueryHolding { query_id, dest, assets, max_response_weight: 0 },
OldOrder::BuyExecution { fees, debt, instructions, .. } => {
// We don't handle nested XCM.
if !instructions.is_empty() {
return Err(())
}
BuyExecution { fees, weight_limit: WeightLimit::Limited(debt) }
},
fn try_from(response: NewResponse) -> result::Result<Self, ()> {
Ok(match response {
NewResponse::Assets(assets) => Self::Assets(assets.try_into()?),
NewResponse::Version(version) => Self::Version(version),
NewResponse::ExecutionResult(error) => Self::ExecutionResult(match error {
Some((i, e)) => Some((i, e.try_into()?)),
None => None,
}),
NewResponse::Null => Self::Null,
_ => return Err(()),
})
}
}
#[cfg(test)]
mod tests {
use super::{prelude::*, *};
#[test]
fn basic_roundtrip_works() {
let xcm =
Xcm::<()>(vec![TransferAsset { assets: (Here, 1).into(), beneficiary: Here.into() }]);
let old_xcm =
OldXcm::<()>::TransferAsset { assets: (Here, 1).into(), beneficiary: Here.into() };
assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
assert_eq!(new_xcm, xcm);
}
#[test]
fn teleport_roundtrip_works() {
let xcm = Xcm::<()>(vec![
ReceiveTeleportedAsset((Here, 1).into()),
ClearOrigin,
DepositAsset { assets: Wild(All), max_assets: 1, beneficiary: Here.into() },
]);
let old_xcm: OldXcm<()> = OldXcm::<()>::ReceiveTeleportedAsset {
assets: (Here, 1).into(),
effects: vec![OldOrder::DepositAsset {
assets: Wild(All),
max_assets: 1,
beneficiary: Here.into(),
}],
};
assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
assert_eq!(new_xcm, xcm);
}
#[test]
fn reserve_deposit_roundtrip_works() {
let xcm = Xcm::<()>(vec![
ReserveAssetDeposited((Here, 1).into()),
ClearOrigin,
BuyExecution { fees: (Here, 1).into(), weight_limit: Some(1).into() },
DepositAsset { assets: Wild(All), max_assets: 1, beneficiary: Here.into() },
]);
let old_xcm: OldXcm<()> = OldXcm::<()>::ReserveAssetDeposited {
assets: (Here, 1).into(),
effects: vec![
OldOrder::BuyExecution {
fees: (Here, 1).into(),
debt: 1,
weight: 0,
instructions: vec![],
halt_on_error: true,
},
OldOrder::DepositAsset {
assets: Wild(All),
max_assets: 1,
beneficiary: Here.into(),
},
],
};
assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
assert_eq!(new_xcm, xcm);
// Convert from a v3 XCM to a v2 XCM.
impl<RuntimeCall> TryFrom<NewXcm<RuntimeCall>> for Xcm<RuntimeCall> {
type Error = ();
fn try_from(new_xcm: NewXcm<RuntimeCall>) -> result::Result<Self, ()> {
Ok(Xcm(new_xcm.0.into_iter().map(TryInto::try_into).collect::<result::Result<_, _>>()?))
}
}
// Convert from a v3 instruction to a v2 instruction
impl<RuntimeCall> TryFrom<NewInstruction<RuntimeCall>> for Instruction<RuntimeCall> {
type Error = ();
fn try_from(instruction: NewInstruction<RuntimeCall>) -> result::Result<Self, ()> {
use NewInstruction::*;
Ok(match instruction {
WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?),
ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?),
ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?),
QueryResponse { query_id, response, max_weight, .. } => Self::QueryResponse {
query_id,
response: response.try_into()?,
max_weight: max_weight.ref_time(),
},
TransferAsset { assets, beneficiary } => Self::TransferAsset {
assets: assets.try_into()?,
beneficiary: beneficiary.try_into()?,
},
TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset {
assets: assets.try_into()?,
dest: dest.try_into()?,
xcm: xcm.try_into()?,
},
HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient },
HrmpChannelClosing { initiator, sender, recipient } =>
Self::HrmpChannelClosing { initiator, sender, recipient },
Transact { origin_kind, require_weight_at_most, call } => Self::Transact {
origin_type: origin_kind,
require_weight_at_most: require_weight_at_most.ref_time(),
call: call.into(),
},
ReportError(response_info) => Self::ReportError {
query_id: response_info.query_id,
dest: response_info.destination.try_into()?,
max_response_weight: response_info.max_weight.ref_time(),
},
DepositAsset { assets, beneficiary } => {
let max_assets = assets.count().ok_or(())?;
let beneficiary = beneficiary.try_into()?;
let assets = assets.try_into()?;
Self::DepositAsset { assets, max_assets, beneficiary }
},
DepositReserveAsset { assets, dest, xcm } => {
let max_assets = assets.count().ok_or(())?;
let dest = dest.try_into()?;
let xcm = xcm.try_into()?;
let assets = assets.try_into()?;
Self::DepositReserveAsset { assets, max_assets, dest, xcm }
},
ExchangeAsset { give, want, .. } => {
let give = give.try_into()?;
let receive = want.try_into()?;
Self::ExchangeAsset { give, receive }
},
InitiateReserveWithdraw { assets, reserve, xcm } => {
// No `max_assets` here, so if there's a connt, then we cannot translate.
let assets = assets.try_into()?;
let reserve = reserve.try_into()?;
let xcm = xcm.try_into()?;
Self::InitiateReserveWithdraw { assets, reserve, xcm }
},
InitiateTeleport { assets, dest, xcm } => {
// No `max_assets` here, so if there's a connt, then we cannot translate.
let assets = assets.try_into()?;
let dest = dest.try_into()?;
let xcm = xcm.try_into()?;
Self::InitiateTeleport { assets, dest, xcm }
},
ReportHolding { response_info, assets } => Self::QueryHolding {
query_id: response_info.query_id,
dest: response_info.destination.try_into()?,
assets: assets.try_into()?,
max_response_weight: response_info.max_weight.ref_time(),
},
BuyExecution { fees, weight_limit } => {
let fees = fees.try_into()?;
let weight_limit = weight_limit.try_into()?;
Self::BuyExecution { fees, weight_limit }
},
ClearOrigin => Self::ClearOrigin,
DescendOrigin(who) => Self::DescendOrigin(who.try_into()?),
RefundSurplus => Self::RefundSurplus,
SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?),
SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?),
ClearError => Self::ClearError,
ClaimAsset { assets, ticket } => {
let assets = assets.try_into()?;
let ticket = ticket.try_into()?;
Self::ClaimAsset { assets, ticket }
},
Trap(code) => Self::Trap(code),
SubscribeVersion { query_id, max_response_weight } => Self::SubscribeVersion {
query_id,
max_response_weight: max_response_weight.ref_time(),
},
UnsubscribeVersion => Self::UnsubscribeVersion,
_ => return Err(()),
})
}
}
@@ -24,13 +24,20 @@
//! account.
use super::MultiLocation;
use crate::v3::{
AssetId as NewAssetId, AssetInstance as NewAssetInstance, Fungibility as NewFungibility,
MultiAsset as NewMultiAsset, MultiAssetFilter as NewMultiAssetFilter,
MultiAssets as NewMultiAssets, WildFungibility as NewWildFungibility,
WildMultiAsset as NewWildMultiAsset,
};
use alloc::{vec, vec::Vec};
use core::{cmp::Ordering, result};
use core::cmp::Ordering;
use parity_scale_codec::{self as codec, Decode, Encode};
use scale_info::TypeInfo;
/// A general identifier for an instance of a non-fungible asset class.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum AssetInstance {
/// Undefined - used if the non-fungible asset class has only one instance.
Undefined,
@@ -91,8 +98,24 @@ impl From<Vec<u8>> for AssetInstance {
}
}
impl TryFrom<NewAssetInstance> for AssetInstance {
type Error = ();
fn try_from(value: NewAssetInstance) -> Result<Self, Self::Error> {
use NewAssetInstance::*;
Ok(match value {
Undefined => Self::Undefined,
Index(n) => Self::Index(n),
Array4(n) => Self::Array4(n),
Array8(n) => Self::Array8(n),
Array16(n) => Self::Array16(n),
Array32(n) => Self::Array32(n),
})
}
}
/// Classification of an asset being concrete or abstract.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum AssetId {
Concrete(MultiLocation),
Abstract(Vec<u8>),
@@ -110,6 +133,20 @@ impl From<Vec<u8>> for AssetId {
}
}
impl TryFrom<NewAssetId> for AssetId {
type Error = ();
fn try_from(old: NewAssetId) -> Result<Self, ()> {
use NewAssetId::*;
Ok(match old {
Concrete(l) => Self::Concrete(l.try_into()?),
Abstract(v) => {
let zeroes = v.iter().rev().position(|n| *n != 0).unwrap_or(v.len());
Self::Abstract(v[0..(32 - zeroes)].to_vec())
},
})
}
}
impl AssetId {
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
pub fn prepend_with(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
@@ -142,6 +179,7 @@ impl AssetId {
/// Classification of whether an asset is fungible or not, along with a mandatory amount or instance.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum Fungibility {
Fungible(#[codec(compact)] u128),
NonFungible(AssetInstance),
@@ -168,7 +206,19 @@ impl<T: Into<AssetInstance>> From<T> for Fungibility {
}
}
impl TryFrom<NewFungibility> for Fungibility {
type Error = ();
fn try_from(value: NewFungibility) -> Result<Self, Self::Error> {
use NewFungibility::*;
Ok(match value {
Fungible(n) => Self::Fungible(n),
NonFungible(i) => Self::NonFungible(i.try_into()?),
})
}
}
#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiAsset {
pub id: AssetId,
pub fun: Fungibility,
@@ -243,47 +293,16 @@ impl MultiAsset {
}
}
impl TryFrom<super::super::v0::MultiAsset> for MultiAsset {
impl TryFrom<NewMultiAsset> for MultiAsset {
type Error = ();
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<MultiAsset, ()> {
use super::super::v0::MultiAsset as V0;
use AssetId::*;
use Fungibility::*;
let (id, fun) = match old {
V0::ConcreteFungible { id, amount } => (Concrete(id.try_into()?), Fungible(amount)),
V0::ConcreteNonFungible { class, instance } =>
(Concrete(class.try_into()?), NonFungible(instance)),
V0::AbstractFungible { id, amount } => (Abstract(id), Fungible(amount)),
V0::AbstractNonFungible { class, instance } => (Abstract(class), NonFungible(instance)),
_ => return Err(()),
};
Ok(MultiAsset { id, fun })
}
}
impl TryFrom<super::super::v0::MultiAsset> for Option<MultiAsset> {
type Error = ();
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<Option<MultiAsset>, ()> {
match old {
super::super::v0::MultiAsset::None => return Ok(None),
x => return Ok(Some(x.try_into()?)),
}
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAsset {
type Error = ();
fn try_from(mut old: Vec<super::super::v0::MultiAsset>) -> result::Result<MultiAsset, ()> {
if old.len() == 1 {
old.remove(0).try_into()
} else {
Err(())
}
fn try_from(new: NewMultiAsset) -> Result<Self, ()> {
Ok(Self { id: new.id.try_into()?, fun: new.fun.try_into()? })
}
}
/// A `Vec` of `MultiAsset`s. There may be no duplicate fungible items in here and when decoding, they must be sorted.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiAssets(Vec<MultiAsset>);
impl Decode for MultiAssets {
@@ -293,15 +312,15 @@ impl Decode for MultiAssets {
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAssets {
impl TryFrom<NewMultiAssets> for MultiAssets {
type Error = ();
fn try_from(old: Vec<super::super::v0::MultiAsset>) -> result::Result<MultiAssets, ()> {
let v = old
fn try_from(new: NewMultiAssets) -> Result<Self, ()> {
let v = new
.into_inner()
.into_iter()
.map(Option::<MultiAsset>::try_from)
.filter_map(|x| x.transpose())
.collect::<result::Result<Vec<MultiAsset>, ()>>()?;
Ok(v.into())
.map(MultiAsset::try_from)
.collect::<Result<Vec<_>, ()>>()?;
Ok(MultiAssets(v))
}
}
@@ -435,7 +454,8 @@ impl MultiAssets {
self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix))
}
/// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location.
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `ancestry`.
pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> {
self.0.iter_mut().try_for_each(|i| i.reanchor(target, ancestry))
}
@@ -445,15 +465,29 @@ impl MultiAssets {
self.0.get(index)
}
}
/// Classification of whether an asset is fungible or not.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum WildFungibility {
Fungible,
NonFungible,
}
impl TryFrom<NewWildFungibility> for WildFungibility {
type Error = ();
fn try_from(value: NewWildFungibility) -> Result<Self, Self::Error> {
use NewWildFungibility::*;
Ok(match value {
Fungible => Self::Fungible,
NonFungible => Self::NonFungible,
})
}
}
/// A wildcard representing a set of assets.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum WildMultiAsset {
/// All assets in the holding register, up to `usize` individual assets (different instances of non-fungibles could
/// be separate assets).
@@ -463,35 +497,6 @@ pub enum WildMultiAsset {
AllOf { id: AssetId, fun: WildFungibility },
}
impl TryFrom<super::super::v0::MultiAsset> for WildMultiAsset {
type Error = ();
fn try_from(old: super::super::v0::MultiAsset) -> result::Result<WildMultiAsset, ()> {
use super::super::v0::MultiAsset as V0;
use AssetId::*;
use WildFungibility::*;
let (id, fun) = match old {
V0::All => return Ok(WildMultiAsset::All),
V0::AllConcreteFungible { id } => (Concrete(id.try_into()?), Fungible),
V0::AllConcreteNonFungible { class } => (Concrete(class.try_into()?), NonFungible),
V0::AllAbstractFungible { id } => (Abstract(id), Fungible),
V0::AllAbstractNonFungible { class } => (Abstract(class), NonFungible),
_ => return Err(()),
};
Ok(WildMultiAsset::AllOf { id, fun })
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for WildMultiAsset {
type Error = ();
fn try_from(mut old: Vec<super::super::v0::MultiAsset>) -> result::Result<WildMultiAsset, ()> {
if old.len() == 1 {
old.remove(0).try_into()
} else {
Err(())
}
}
}
impl WildMultiAsset {
/// Returns true if `self` is a super-set of the given `inner`.
///
@@ -505,7 +510,8 @@ impl WildMultiAsset {
}
}
/// Prepend a `MultiLocation` to any concrete asset components, giving it a new root location.
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `ancestry`.
pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> {
use WildMultiAsset::*;
match self {
@@ -526,6 +532,7 @@ impl<A: Into<AssetId>, B: Into<WildFungibility>> From<(A, B)> for WildMultiAsset
/// Note: Vectors of wildcards whose encoding is supported in XCM v0 are unsupported
/// in this implementation and will result in a decode error.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum MultiAssetFilter {
Definite(MultiAssets),
Wild(WildMultiAsset),
@@ -567,7 +574,8 @@ impl MultiAssetFilter {
}
}
/// Prepend a `MultiLocation` to any concrete asset components, giving it a new root location.
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `ancestry`.
pub fn reanchor(&mut self, target: &MultiLocation, ancestry: &MultiLocation) -> Result<(), ()> {
match self {
MultiAssetFilter::Definite(ref mut assets) => assets.reanchor(target, ancestry),
@@ -576,15 +584,25 @@ impl MultiAssetFilter {
}
}
impl TryFrom<Vec<super::super::v0::MultiAsset>> for MultiAssetFilter {
impl TryFrom<NewWildMultiAsset> for WildMultiAsset {
type Error = ();
fn try_from(
mut old: Vec<super::super::v0::MultiAsset>,
) -> result::Result<MultiAssetFilter, ()> {
if old.len() == 1 && old[0].is_wildcard() {
Ok(MultiAssetFilter::Wild(old.remove(0).try_into()?))
} else {
Ok(MultiAssetFilter::Definite(old.try_into()?))
}
fn try_from(new: NewWildMultiAsset) -> Result<Self, ()> {
use NewWildMultiAsset::*;
Ok(match new {
AllOf { id, fun } | AllOfCounted { id, fun, .. } =>
Self::AllOf { id: id.try_into()?, fun: fun.try_into()? },
All | AllCounted(_) => Self::All,
})
}
}
impl TryFrom<NewMultiAssetFilter> for MultiAssetFilter {
type Error = ();
fn try_from(old: NewMultiAssetFilter) -> Result<Self, ()> {
use NewMultiAssetFilter::*;
Ok(match old {
Definite(x) => Self::Definite(x.try_into()?),
Wild(x) => Self::Wild(x.try_into()?),
})
}
}
@@ -17,6 +17,7 @@
//! Cross-Consensus Message format data structures.
use super::Junction;
use crate::v3::MultiLocation as NewMultiLocation;
use core::{mem, result};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
@@ -48,6 +49,7 @@ use scale_info::TypeInfo;
///
/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system.
#[derive(Clone, Decode, Encode, Eq, PartialEq, Ord, PartialOrd, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiLocation {
/// The number of parent junctions at the beginning of this `MultiLocation`.
pub parents: u8,
@@ -236,7 +238,7 @@ impl MultiLocation {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation};
/// # use xcm::v2::{Junctions::*, Junction::*, MultiLocation};
/// # fn main() {
/// let mut m = MultiLocation::new(1, X2(PalletInstance(3), OnlyChild));
/// assert_eq!(
@@ -258,7 +260,7 @@ impl MultiLocation {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation};
/// # use xcm::v2::{Junctions::*, Junction::*, MultiLocation};
/// let m = MultiLocation::new(1, X3(PalletInstance(3), OnlyChild, OnlyChild));
/// assert!(m.starts_with(&MultiLocation::new(1, X1(PalletInstance(3)))));
/// assert!(!m.starts_with(&MultiLocation::new(1, X1(GeneralIndex(99)))));
@@ -277,7 +279,7 @@ impl MultiLocation {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation};
/// # use xcm::v2::{Junctions::*, Junction::*, MultiLocation};
/// # fn main() {
/// let mut m = MultiLocation::new(1, X1(Parachain(21)));
/// assert_eq!(m.append_with(X1(PalletInstance(3))), Ok(()));
@@ -300,7 +302,7 @@ impl MultiLocation {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*, MultiLocation};
/// # use xcm::v2::{Junctions::*, Junction::*, MultiLocation};
/// # fn main() {
/// let mut m = MultiLocation::new(2, X1(PalletInstance(3)));
/// assert_eq!(m.prepend_with(MultiLocation::new(1, X2(Parachain(21), OnlyChild))), Ok(()));
@@ -342,6 +344,21 @@ impl MultiLocation {
Ok(())
}
/// Consume `self` and return the value representing the same location from the point of view
/// of `target`. The context of `self` is provided as `ancestry`.
///
/// Returns an `Err` with the unmodified `self` in the case of error.
pub fn reanchored(
mut self,
target: &MultiLocation,
ancestry: &MultiLocation,
) -> Result<Self, Self> {
match self.reanchor(target, ancestry) {
Ok(()) => Ok(self),
Err(()) => Err(self),
}
}
/// Mutate `self` so that it represents the same location from the point of view of `target`.
/// The context of `self` is provided as `ancestry`.
///
@@ -397,6 +414,13 @@ impl MultiLocation {
}
}
impl TryFrom<NewMultiLocation> for MultiLocation {
type Error = ();
fn try_from(x: NewMultiLocation) -> result::Result<Self, ()> {
Ok(MultiLocation { parents: x.parents, interior: x.interior.try_into()? })
}
}
/// A unit struct which can be converted into a `MultiLocation` of `parents` value 1.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Parent;
@@ -408,7 +432,7 @@ impl From<Parent> for MultiLocation {
/// A tuple struct which can be converted into a `MultiLocation` of `parents` value 1 with the inner interior.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct ParentThen(Junctions);
pub struct ParentThen(pub Junctions);
impl From<ParentThen> for MultiLocation {
fn from(ParentThen(interior): ParentThen) -> Self {
MultiLocation { parents: 1, interior }
@@ -417,7 +441,7 @@ impl From<ParentThen> for MultiLocation {
/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Ancestor(u8);
pub struct Ancestor(pub u8);
impl From<Ancestor> for MultiLocation {
fn from(Ancestor(parents): Ancestor) -> Self {
MultiLocation { parents, interior: Junctions::Here }
@@ -426,14 +450,14 @@ impl From<Ancestor> for MultiLocation {
/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value and the inner interior.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct AncestorThen(u8, Junctions);
impl From<AncestorThen> for MultiLocation {
fn from(AncestorThen(parents, interior): AncestorThen) -> Self {
MultiLocation { parents, interior }
pub struct AncestorThen<Interior>(pub u8, pub Interior);
impl<Interior: Into<Junctions>> From<AncestorThen<Interior>> for MultiLocation {
fn from(AncestorThen(parents, interior): AncestorThen<Interior>) -> Self {
MultiLocation { parents, interior: interior.into() }
}
}
xcm_procedural::impl_conversion_functions_for_multilocation_v1!();
xcm_procedural::impl_conversion_functions_for_multilocation_v2!();
/// Maximum number of `Junction`s that a `Junctions` can contain.
const MAX_JUNCTIONS: usize = 8;
@@ -444,6 +468,7 @@ const MAX_JUNCTIONS: usize = 8;
/// Parent junctions cannot be constructed with this type. Refer to `MultiLocation` for
/// instructions on constructing parent junctions.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum Junctions {
/// The interpreting consensus system.
Here,
@@ -811,7 +836,7 @@ impl Junctions {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*};
/// # use xcm::v2::{Junctions::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parachain(2), PalletInstance(3), OnlyChild);
/// assert_eq!(m.match_and_split(&X2(Parachain(2), PalletInstance(3))), Some(&OnlyChild));
@@ -829,7 +854,7 @@ impl Junctions {
///
/// # Example
/// ```rust
/// # use xcm::v1::{Junctions::*, Junction::*};
/// # use xcm::v2::{Junctions::*, Junction::*};
/// let mut j = X3(Parachain(2), PalletInstance(3), OnlyChild);
/// assert!(j.starts_with(&X2(Parachain(2), PalletInstance(3))));
/// assert!(j.starts_with(&j));
@@ -859,7 +884,7 @@ impl TryFrom<MultiLocation> for Junctions {
#[cfg(test)]
mod tests {
use super::{Ancestor, AncestorThen, Junctions::*, MultiLocation, Parent, ParentThen};
use crate::opaque::v1::{Junction::*, NetworkId::*};
use crate::opaque::v2::{Junction::*, NetworkId::*};
use parity_scale_codec::{Decode, Encode};
#[test]
@@ -1062,7 +1087,6 @@ mod tests {
#[test]
fn conversion_from_other_types_works() {
use crate::v0;
fn takes_multilocation<Arg: Into<MultiLocation>>(_arg: Arg) {}
takes_multilocation(Parent);
@@ -1079,27 +1103,5 @@ mod tests {
takes_multilocation((Parent, Here));
takes_multilocation(ParentThen(X1(Parachain(75))));
takes_multilocation([Parachain(100), PalletInstance(3)]);
assert_eq!(v0::MultiLocation::Null.try_into(), Ok(MultiLocation::here()));
assert_eq!(
v0::MultiLocation::X1(v0::Junction::Parent).try_into(),
Ok(MultiLocation::parent())
);
assert_eq!(
v0::MultiLocation::X2(v0::Junction::Parachain(88), v0::Junction::Parent).try_into(),
Ok(MultiLocation::here()),
);
assert_eq!(
v0::MultiLocation::X3(
v0::Junction::Parent,
v0::Junction::Parent,
v0::Junction::GeneralKey(b"foo".to_vec().try_into().unwrap()),
)
.try_into(),
Ok(MultiLocation {
parents: 2,
interior: X1(GeneralKey(b"foo".to_vec().try_into().unwrap()))
}),
);
}
}
+41 -8
View File
@@ -16,6 +16,7 @@
//! Cross-Consensus Message format data structures.
use crate::v3::Error as NewError;
use core::result;
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
@@ -110,10 +111,42 @@ pub enum Error {
WeightNotComputable,
}
impl TryFrom<NewError> for Error {
type Error = ();
fn try_from(new_error: NewError) -> result::Result<Error, ()> {
use NewError::*;
Ok(match new_error {
Overflow => Self::Overflow,
Unimplemented => Self::Unimplemented,
UntrustedReserveLocation => Self::UntrustedReserveLocation,
UntrustedTeleportLocation => Self::UntrustedTeleportLocation,
LocationFull => Self::MultiLocationFull,
LocationNotInvertible => Self::MultiLocationNotInvertible,
BadOrigin => Self::BadOrigin,
InvalidLocation => Self::InvalidLocation,
AssetNotFound => Self::AssetNotFound,
FailedToTransactAsset(s) => Self::FailedToTransactAsset(s),
NotWithdrawable => Self::NotWithdrawable,
LocationCannotHold => Self::LocationCannotHold,
ExceedsMaxMessageSize => Self::ExceedsMaxMessageSize,
DestinationUnsupported => Self::DestinationUnsupported,
Transport(s) => Self::Transport(s),
Unroutable => Self::Unroutable,
UnknownClaim => Self::UnknownClaim,
FailedToDecode => Self::FailedToDecode,
MaxWeightInvalid => Self::MaxWeightInvalid,
NotHoldingFees => Self::NotHoldingFees,
TooExpensive => Self::TooExpensive,
Trap(i) => Self::Trap(i),
_ => return Err(()),
})
}
}
impl From<SendError> for Error {
fn from(e: SendError) -> Self {
match e {
SendError::CannotReachDestination(..) | SendError::Unroutable => Error::Unroutable,
SendError::NotApplicable(..) | SendError::Unroutable => Error::Unroutable,
SendError::Transport(s) => Error::Transport(s),
SendError::DestinationUnsupported => Error::DestinationUnsupported,
SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize,
@@ -210,7 +243,7 @@ pub enum SendError {
///
/// This is not considered fatal: if there are alternative transport routes available, then
/// they may be attempted. For this reason, the destination and message are contained.
CannotReachDestination(MultiLocation, Xcm<()>),
NotApplicable(MultiLocation, Xcm<()>),
/// Destination is routable, but there is some issue with the transport mechanism. This is
/// considered fatal.
/// A human-readable explanation of the specific issue is provided.
@@ -231,7 +264,7 @@ pub type SendResult = result::Result<(), SendError>;
/// Utility for sending an XCM message.
///
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each router might return
/// `CannotReachDestination` to pass the execution to the next sender item. Note that each `CannotReachDestination`
/// `NotApplicable` to pass the execution to the next sender item. Note that each `NotApplicable`
/// might alter the destination and the XCM message for to the next router.
///
///
@@ -244,7 +277,7 @@ pub type SendResult = result::Result<(), SendError>;
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> SendResult {
/// return Err(SendError::CannotReachDestination(destination.into(), message))
/// return Err(SendError::NotApplicable(destination.into(), message))
/// }
/// }
///
@@ -267,7 +300,7 @@ pub type SendResult = result::Result<(), SendError>;
/// let destination = destination.into();
/// match destination {
/// MultiLocation { parents: 1, interior: Here } => Ok(()),
/// _ => Err(SendError::CannotReachDestination(destination, message)),
/// _ => Err(SendError::NotApplicable(destination, message)),
/// }
/// }
/// }
@@ -298,7 +331,7 @@ pub trait SendXcm {
/// Send an XCM `message` to a given `destination`.
///
/// If it is not a destination which can be reached with this type but possibly could by others, then it *MUST*
/// return `CannotReachDestination`. Any other error will cause the tuple implementation to exit early without
/// return `NotApplicable`. Any other error will cause the tuple implementation to exit early without
/// trying other type fields.
fn send_xcm(destination: impl Into<MultiLocation>, message: Xcm<()>) -> SendResult;
}
@@ -309,10 +342,10 @@ impl SendXcm for Tuple {
for_tuples!( #(
// we shadow `destination` and `message` in each expansion for the next one.
let (destination, message) = match Tuple::send_xcm(destination, message) {
Err(SendError::CannotReachDestination(d, m)) => (d, m),
Err(SendError::NotApplicable(d, m)) => (d, m),
o @ _ => return o,
};
)* );
Err(SendError::CannotReachDestination(destination.into(), message))
Err(SendError::NotApplicable(destination.into(), message))
}
}
+342
View File
@@ -0,0 +1,342 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Support data structures for `MultiLocation`, primarily the `Junction` datatype.
use super::{Junctions, MultiLocation};
use crate::{
v2::{
BodyId as OldBodyId, BodyPart as OldBodyPart, Junction as OldJunction,
NetworkId as OldNetworkId,
},
VersionedMultiLocation,
};
use core::convert::{TryFrom, TryInto};
use parity_scale_codec::{self, Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
/// A global identifier of a data structure existing within consensus.
///
/// Maintenance note: Networks with global consensus and which are practically bridgeable within the
/// Polkadot ecosystem are given preference over explicit naming in this enumeration.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum NetworkId {
/// Network specified by the first 32 bytes of its genesis block.
ByGenesis([u8; 32]),
/// Network defined by the first 32-bytes of the hash and number of some block it contains.
ByFork { block_number: u64, block_hash: [u8; 32] },
/// The Polkadot mainnet Relay-chain.
Polkadot,
/// The Kusama canary-net Relay-chain.
Kusama,
/// The Westend testnet Relay-chain.
Westend,
/// The Rococo testnet Relay-chain.
Rococo,
/// The Wococo testnet Relay-chain.
Wococo,
/// An Ethereum network specified by its chain ID.
Ethereum {
/// The EIP-155 chain ID.
#[codec(compact)]
chain_id: u64,
},
/// The Bitcoin network, including hard-forks supported by Bitcoin Core development team.
BitcoinCore,
/// The Bitcoin network, including hard-forks supported by Bitcoin Cash developers.
BitcoinCash,
}
impl From<OldNetworkId> for Option<NetworkId> {
fn from(old: OldNetworkId) -> Option<NetworkId> {
use OldNetworkId::*;
match old {
Any => None,
Named(_) => None,
Polkadot => Some(NetworkId::Polkadot),
Kusama => Some(NetworkId::Kusama),
}
}
}
/// An identifier of a pluralistic body.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum BodyId {
/// The only body in its context.
Unit,
/// A named body.
Moniker([u8; 4]),
/// An indexed body.
Index(#[codec(compact)] u32),
/// The unambiguous executive body (for Polkadot, this would be the Polkadot council).
Executive,
/// The unambiguous technical body (for Polkadot, this would be the Technical Committee).
Technical,
/// The unambiguous legislative body (for Polkadot, this could be considered the opinion of a majority of
/// lock-voters).
Legislative,
/// The unambiguous judicial body (this doesn't exist on Polkadot, but if it were to get a "grand oracle", it
/// may be considered as that).
Judicial,
/// The unambiguous defense body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `staking_admin` track).
Defense,
/// The unambiguous administration body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `general_admin` track).
Administration,
/// The unambiguous treasury body (for Polkadot, an opinion on the topic given via a public referendum
/// on the `treasurer` track).
Treasury,
}
impl TryFrom<OldBodyId> for BodyId {
type Error = ();
fn try_from(value: OldBodyId) -> Result<Self, ()> {
use OldBodyId::*;
Ok(match value {
Unit => Self::Unit,
Named(n) =>
if n.len() == 4 {
let mut r = [0u8; 4];
r.copy_from_slice(&n[..]);
Self::Moniker(r)
} else {
return Err(())
},
Index(n) => Self::Index(n),
Executive => Self::Executive,
Technical => Self::Technical,
Legislative => Self::Legislative,
Judicial => Self::Judicial,
Defense => Self::Defense,
Administration => Self::Administration,
Treasury => Self::Treasury,
})
}
}
/// A part of a pluralistic body.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum BodyPart {
/// The body's declaration, under whatever means it decides.
Voice,
/// A given number of members of the body.
Members {
#[codec(compact)]
count: u32,
},
/// A given number of members of the body, out of some larger caucus.
Fraction {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
/// No less than the given proportion of members of the body.
AtLeastProportion {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
/// More than than the given proportion of members of the body.
MoreThanProportion {
#[codec(compact)]
nom: u32,
#[codec(compact)]
denom: u32,
},
}
impl BodyPart {
/// Returns `true` if the part represents a strict majority (> 50%) of the body in question.
pub fn is_majority(&self) -> bool {
match self {
BodyPart::Fraction { nom, denom } if *nom * 2 > *denom => true,
BodyPart::AtLeastProportion { nom, denom } if *nom * 2 > *denom => true,
BodyPart::MoreThanProportion { nom, denom } if *nom * 2 >= *denom => true,
_ => false,
}
}
}
impl TryFrom<OldBodyPart> for BodyPart {
type Error = ();
fn try_from(value: OldBodyPart) -> Result<Self, ()> {
use OldBodyPart::*;
Ok(match value {
Voice => Self::Voice,
Members { count } => Self::Members { count },
Fraction { nom, denom } => Self::Fraction { nom, denom },
AtLeastProportion { nom, denom } => Self::AtLeastProportion { nom, denom },
MoreThanProportion { nom, denom } => Self::MoreThanProportion { nom, denom },
})
}
}
/// A single item in a path to describe the relative location of a consensus system.
///
/// Each item assumes a pre-existing location as its context and is defined in terms of it.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum Junction {
/// An indexed parachain belonging to and operated by the context.
///
/// Generally used when the context is a Polkadot Relay-chain.
Parachain(#[codec(compact)] u32),
/// A 32-byte identifier for an account of a specific network that is respected as a sovereign endpoint within
/// the context.
///
/// Generally used when the context is a Substrate-based chain.
AccountId32 { network: Option<NetworkId>, id: [u8; 32] },
/// An 8-byte index for an account of a specific network that is respected as a sovereign endpoint within
/// the context.
///
/// May be used when the context is a Frame-based chain and includes e.g. an indices pallet.
AccountIndex64 {
network: Option<NetworkId>,
#[codec(compact)]
index: u64,
},
/// A 20-byte identifier for an account of a specific network that is respected as a sovereign endpoint within
/// the context.
///
/// May be used when the context is an Ethereum or Bitcoin chain or smart-contract.
AccountKey20 { network: Option<NetworkId>, key: [u8; 20] },
/// An instanced, indexed pallet that forms a constituent part of the context.
///
/// Generally used when the context is a Frame-based chain.
PalletInstance(u8),
/// A non-descript index within the context location.
///
/// Usage will vary widely owing to its generality.
///
/// NOTE: Try to avoid using this and instead use a more specific item.
GeneralIndex(#[codec(compact)] u128),
/// A nondescript 128-byte datum acting as a key within the context location.
///
/// Usage will vary widely owing to its generality.
///
/// NOTE: Try to avoid using this and instead use a more specific item.
GeneralKey([u8; 32]),
/// The unambiguous child.
///
/// Not currently used except as a fallback when deriving context.
OnlyChild,
/// A pluralistic body existing within consensus.
///
/// Typical to be used to represent a governance origin of a chain, but could in principle be used to represent
/// things such as multisigs also.
Plurality { id: BodyId, part: BodyPart },
/// A global network capable of externalizing its own consensus. This is not generally
/// meaningful outside of the universal level.
GlobalConsensus(NetworkId),
}
impl From<NetworkId> for Junction {
fn from(n: NetworkId) -> Self {
Self::GlobalConsensus(n)
}
}
impl From<[u8; 32]> for Junction {
fn from(id: [u8; 32]) -> Self {
Self::AccountId32 { network: None, id }
}
}
impl From<[u8; 20]> for Junction {
fn from(key: [u8; 20]) -> Self {
Self::AccountKey20 { network: None, key }
}
}
impl From<u64> for Junction {
fn from(index: u64) -> Self {
Self::AccountIndex64 { network: None, index }
}
}
impl From<u128> for Junction {
fn from(id: u128) -> Self {
Self::GeneralIndex(id)
}
}
impl TryFrom<OldJunction> for Junction {
type Error = ();
fn try_from(value: OldJunction) -> Result<Self, ()> {
use OldJunction::*;
Ok(match value {
Parachain(id) => Self::Parachain(id),
AccountId32 { network, id } => Self::AccountId32 { network: network.into(), id },
AccountIndex64 { network, index } =>
Self::AccountIndex64 { network: network.into(), index },
AccountKey20 { network, key } => Self::AccountKey20 { network: network.into(), key },
PalletInstance(index) => Self::PalletInstance(index),
GeneralIndex(id) => Self::GeneralIndex(id),
GeneralKey(_key) => return Err(()),
OnlyChild => Self::OnlyChild,
Plurality { id, part } =>
Self::Plurality { id: id.try_into()?, part: part.try_into()? },
})
}
}
impl Junction {
/// Convert `self` into a `MultiLocation` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub const fn into_location(self) -> MultiLocation {
MultiLocation { parents: 0, interior: Junctions::X1(self) }
}
/// Convert `self` into a `MultiLocation` containing `n` parents.
///
/// Similar to `Self::into_location`, with the added ability to specify the number of parent junctions.
pub const fn into_exterior(self, n: u8) -> MultiLocation {
MultiLocation { parents: n, interior: Junctions::X1(self) }
}
/// Convert `self` into a `VersionedMultiLocation` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub const fn into_versioned(self) -> VersionedMultiLocation {
self.into_location().into_versioned()
}
/// Remove the `NetworkId` value.
pub fn remove_network_id(&mut self) {
use Junction::*;
match self {
AccountId32 { ref mut network, .. } |
AccountIndex64 { ref mut network, .. } |
AccountKey20 { ref mut network, .. } => *network = None,
_ => {},
}
}
}
+708
View File
@@ -0,0 +1,708 @@
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! XCM `Junctions`/`InteriorMultiLocation` datatype.
use super::{Junction, MultiLocation, NetworkId};
use core::{convert::TryFrom, mem, result};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
/// Maximum number of `Junction`s that a `Junctions` can contain.
pub(crate) const MAX_JUNCTIONS: usize = 8;
/// Non-parent junctions that can be constructed, up to the length of 8. This specific `Junctions`
/// implementation uses a Rust `enum` in order to make pattern matching easier.
///
/// Parent junctions cannot be constructed with this type. Refer to `MultiLocation` for
/// instructions on constructing parent junctions.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum Junctions {
/// The interpreting consensus system.
Here,
/// A relative path comprising 1 junction.
X1(Junction),
/// A relative path comprising 2 junctions.
X2(Junction, Junction),
/// A relative path comprising 3 junctions.
X3(Junction, Junction, Junction),
/// A relative path comprising 4 junctions.
X4(Junction, Junction, Junction, Junction),
/// A relative path comprising 5 junctions.
X5(Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 6 junctions.
X6(Junction, Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 7 junctions.
X7(Junction, Junction, Junction, Junction, Junction, Junction, Junction),
/// A relative path comprising 8 junctions.
X8(Junction, Junction, Junction, Junction, Junction, Junction, Junction, Junction),
}
pub struct JunctionsIterator(Junctions);
impl Iterator for JunctionsIterator {
type Item = Junction;
fn next(&mut self) -> Option<Junction> {
self.0.take_first()
}
}
impl DoubleEndedIterator for JunctionsIterator {
fn next_back(&mut self) -> Option<Junction> {
self.0.take_last()
}
}
pub struct JunctionsRefIterator<'a> {
junctions: &'a Junctions,
next: usize,
back: usize,
}
impl<'a> Iterator for JunctionsRefIterator<'a> {
type Item = &'a Junction;
fn next(&mut self) -> Option<&'a Junction> {
if self.next.saturating_add(self.back) >= self.junctions.len() {
return None
}
let result = self.junctions.at(self.next);
self.next += 1;
result
}
}
impl<'a> DoubleEndedIterator for JunctionsRefIterator<'a> {
fn next_back(&mut self) -> Option<&'a Junction> {
let next_back = self.back.saturating_add(1);
// checked_sub here, because if the result is less than 0, we end iteration
let index = self.junctions.len().checked_sub(next_back)?;
if self.next > index {
return None
}
self.back = next_back;
self.junctions.at(index)
}
}
impl<'a> IntoIterator for &'a Junctions {
type Item = &'a Junction;
type IntoIter = JunctionsRefIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
JunctionsRefIterator { junctions: self, next: 0, back: 0 }
}
}
impl IntoIterator for Junctions {
type Item = Junction;
type IntoIter = JunctionsIterator;
fn into_iter(self) -> Self::IntoIter {
JunctionsIterator(self)
}
}
impl Junctions {
/// Convert `self` into a `MultiLocation` containing 0 parents.
///
/// Similar to `Into::into`, except that this method can be used in a const evaluation context.
pub const fn into_location(self) -> MultiLocation {
MultiLocation { parents: 0, interior: self }
}
/// Convert `self` into a `MultiLocation` containing `n` parents.
///
/// Similar to `Self::into_location`, with the added ability to specify the number of parent junctions.
pub const fn into_exterior(self, n: u8) -> MultiLocation {
MultiLocation { parents: n, interior: self }
}
/// Remove the `NetworkId` value in any `Junction`s.
pub fn remove_network_id(&mut self) {
self.for_each_mut(Junction::remove_network_id);
}
/// Treating `self` as the universal context, return the location of the local consensus system
/// from the point of view of the given `target`.
pub fn invert_target(mut self, target: &MultiLocation) -> Result<MultiLocation, ()> {
let mut junctions = Self::Here;
for _ in 0..target.parent_count() {
junctions = junctions
.pushed_front_with(self.take_last().unwrap_or(Junction::OnlyChild))
.map_err(|_| ())?;
}
let parents = target.interior().len() as u8;
Ok(MultiLocation::new(parents, junctions))
}
/// Execute a function `f` on every junction. We use this since we cannot implement a mutable
/// `Iterator` without unsafe code.
pub fn for_each_mut(&mut self, mut x: impl FnMut(&mut Junction)) {
match self {
Junctions::Here => {},
Junctions::X1(a) => {
x(a);
},
Junctions::X2(a, b) => {
x(a);
x(b);
},
Junctions::X3(a, b, c) => {
x(a);
x(b);
x(c);
},
Junctions::X4(a, b, c, d) => {
x(a);
x(b);
x(c);
x(d);
},
Junctions::X5(a, b, c, d, e) => {
x(a);
x(b);
x(c);
x(d);
x(e);
},
Junctions::X6(a, b, c, d, e, f) => {
x(a);
x(b);
x(c);
x(d);
x(e);
x(f);
},
Junctions::X7(a, b, c, d, e, f, g) => {
x(a);
x(b);
x(c);
x(d);
x(e);
x(f);
x(g);
},
Junctions::X8(a, b, c, d, e, f, g, h) => {
x(a);
x(b);
x(c);
x(d);
x(e);
x(f);
x(g);
x(h);
},
}
}
/// Extract the network ID treating this value as a universal location.
///
/// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate
/// that this value is not a universal location.
pub fn global_consensus(&self) -> Result<NetworkId, ()> {
if let Some(Junction::GlobalConsensus(network)) = self.first() {
Ok(*network)
} else {
Err(())
}
}
/// Extract the network ID and the interior consensus location, treating this value as a
/// universal location.
///
/// This will return an `Err` if the first item is not a `GlobalConsensus`, which would indicate
/// that this value is not a universal location.
pub fn split_global(self) -> Result<(NetworkId, Junctions), ()> {
match self.split_first() {
(location, Some(Junction::GlobalConsensus(network))) => Ok((network, location)),
_ => return Err(()),
}
}
/// Treat `self` as a universal location and the context of `relative`, returning the universal
/// location of relative.
///
/// This will return an error if `relative` has as many (or more) parents than there are
/// junctions in `self`, implying that relative refers into a different global consensus.
pub fn within_global(mut self, relative: MultiLocation) -> Result<Self, ()> {
if self.len() <= relative.parents as usize {
return Err(())
}
for _ in 0..relative.parents {
self.take_last();
}
for j in relative.interior {
self.push(j).map_err(|_| ())?;
}
Ok(self)
}
/// Consumes `self` and returns how `viewer` would address it locally.
pub fn relative_to(mut self, viewer: &Junctions) -> MultiLocation {
let mut i = 0;
while match (self.first(), viewer.at(i)) {
(Some(x), Some(y)) => x == y,
_ => false,
} {
self = self.split_first().0;
// NOTE: Cannot overflow as loop can only iterate at most `MAX_JUNCTIONS` times.
i += 1;
}
// AUDIT NOTES:
// - above loop ensures that `i <= viewer.len()`.
// - `viewer.len()` is at most `MAX_JUNCTIONS`, so won't overflow a `u8`.
MultiLocation { parents: (viewer.len() - i) as u8, interior: self }
}
/// Returns first junction, or `None` if the location is empty.
pub fn first(&self) -> Option<&Junction> {
match &self {
Junctions::Here => None,
Junctions::X1(ref a) => Some(a),
Junctions::X2(ref a, ..) => Some(a),
Junctions::X3(ref a, ..) => Some(a),
Junctions::X4(ref a, ..) => Some(a),
Junctions::X5(ref a, ..) => Some(a),
Junctions::X6(ref a, ..) => Some(a),
Junctions::X7(ref a, ..) => Some(a),
Junctions::X8(ref a, ..) => Some(a),
}
}
/// Returns last junction, or `None` if the location is empty.
pub fn last(&self) -> Option<&Junction> {
match &self {
Junctions::Here => None,
Junctions::X1(ref a) => Some(a),
Junctions::X2(.., ref a) => Some(a),
Junctions::X3(.., ref a) => Some(a),
Junctions::X4(.., ref a) => Some(a),
Junctions::X5(.., ref a) => Some(a),
Junctions::X6(.., ref a) => Some(a),
Junctions::X7(.., ref a) => Some(a),
Junctions::X8(.., ref a) => Some(a),
}
}
/// Splits off the first junction, returning the remaining suffix (first item in tuple) and the first element
/// (second item in tuple) or `None` if it was empty.
pub fn split_first(self) -> (Junctions, Option<Junction>) {
match self {
Junctions::Here => (Junctions::Here, None),
Junctions::X1(a) => (Junctions::Here, Some(a)),
Junctions::X2(a, b) => (Junctions::X1(b), Some(a)),
Junctions::X3(a, b, c) => (Junctions::X2(b, c), Some(a)),
Junctions::X4(a, b, c, d) => (Junctions::X3(b, c, d), Some(a)),
Junctions::X5(a, b, c, d, e) => (Junctions::X4(b, c, d, e), Some(a)),
Junctions::X6(a, b, c, d, e, f) => (Junctions::X5(b, c, d, e, f), Some(a)),
Junctions::X7(a, b, c, d, e, f, g) => (Junctions::X6(b, c, d, e, f, g), Some(a)),
Junctions::X8(a, b, c, d, e, f, g, h) => (Junctions::X7(b, c, d, e, f, g, h), Some(a)),
}
}
/// Splits off the last junction, returning the remaining prefix (first item in tuple) and the last element
/// (second item in tuple) or `None` if it was empty.
pub fn split_last(self) -> (Junctions, Option<Junction>) {
match self {
Junctions::Here => (Junctions::Here, None),
Junctions::X1(a) => (Junctions::Here, Some(a)),
Junctions::X2(a, b) => (Junctions::X1(a), Some(b)),
Junctions::X3(a, b, c) => (Junctions::X2(a, b), Some(c)),
Junctions::X4(a, b, c, d) => (Junctions::X3(a, b, c), Some(d)),
Junctions::X5(a, b, c, d, e) => (Junctions::X4(a, b, c, d), Some(e)),
Junctions::X6(a, b, c, d, e, f) => (Junctions::X5(a, b, c, d, e), Some(f)),
Junctions::X7(a, b, c, d, e, f, g) => (Junctions::X6(a, b, c, d, e, f), Some(g)),
Junctions::X8(a, b, c, d, e, f, g, h) => (Junctions::X7(a, b, c, d, e, f, g), Some(h)),
}
}
/// Removes the first element from `self`, returning it (or `None` if it was empty).
pub fn take_first(&mut self) -> Option<Junction> {
let mut d = Junctions::Here;
mem::swap(&mut *self, &mut d);
let (tail, head) = d.split_first();
*self = tail;
head
}
/// Removes the last element from `self`, returning it (or `None` if it was empty).
pub fn take_last(&mut self) -> Option<Junction> {
let mut d = Junctions::Here;
mem::swap(&mut *self, &mut d);
let (head, tail) = d.split_last();
*self = head;
tail
}
/// Mutates `self` to be appended with `new` or returns an `Err` with `new` if would overflow.
pub fn push(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
let new = new.into();
let mut dummy = Junctions::Here;
mem::swap(self, &mut dummy);
match dummy.pushed_with(new) {
Ok(s) => {
*self = s;
Ok(())
},
Err((s, j)) => {
*self = s;
Err(j)
},
}
}
/// Mutates `self` to be prepended with `new` or returns an `Err` with `new` if would overflow.
pub fn push_front(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
let new = new.into();
let mut dummy = Junctions::Here;
mem::swap(self, &mut dummy);
match dummy.pushed_front_with(new) {
Ok(s) => {
*self = s;
Ok(())
},
Err((s, j)) => {
*self = s;
Err(j)
},
}
}
/// Consumes `self` and returns a `Junctions` suffixed with `new`, or an `Err` with the
/// original value of `self` and `new` in case of overflow.
pub fn pushed_with(self, new: impl Into<Junction>) -> result::Result<Self, (Self, Junction)> {
let new = new.into();
Ok(match self {
Junctions::Here => Junctions::X1(new),
Junctions::X1(a) => Junctions::X2(a, new),
Junctions::X2(a, b) => Junctions::X3(a, b, new),
Junctions::X3(a, b, c) => Junctions::X4(a, b, c, new),
Junctions::X4(a, b, c, d) => Junctions::X5(a, b, c, d, new),
Junctions::X5(a, b, c, d, e) => Junctions::X6(a, b, c, d, e, new),
Junctions::X6(a, b, c, d, e, f) => Junctions::X7(a, b, c, d, e, f, new),
Junctions::X7(a, b, c, d, e, f, g) => Junctions::X8(a, b, c, d, e, f, g, new),
s => Err((s, new))?,
})
}
/// Consumes `self` and returns a `Junctions` prefixed with `new`, or an `Err` with the
/// original value of `self` and `new` in case of overflow.
pub fn pushed_front_with(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
let new = new.into();
Ok(match self {
Junctions::Here => Junctions::X1(new),
Junctions::X1(a) => Junctions::X2(new, a),
Junctions::X2(a, b) => Junctions::X3(new, a, b),
Junctions::X3(a, b, c) => Junctions::X4(new, a, b, c),
Junctions::X4(a, b, c, d) => Junctions::X5(new, a, b, c, d),
Junctions::X5(a, b, c, d, e) => Junctions::X6(new, a, b, c, d, e),
Junctions::X6(a, b, c, d, e, f) => Junctions::X7(new, a, b, c, d, e, f),
Junctions::X7(a, b, c, d, e, f, g) => Junctions::X8(new, a, b, c, d, e, f, g),
s => Err((s, new))?,
})
}
/// Mutate `self` so that it is suffixed with `suffix`.
///
/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation};
/// # fn main() {
/// let mut m = X1(Parachain(21));
/// assert_eq!(m.append_with(X1(PalletInstance(3))), Ok(()));
/// assert_eq!(m, X2(Parachain(21), PalletInstance(3)));
/// # }
/// ```
pub fn append_with(&mut self, suffix: impl Into<Junctions>) -> Result<(), Junctions> {
let suffix = suffix.into();
if self.len().saturating_add(suffix.len()) > MAX_JUNCTIONS {
return Err(suffix)
}
for j in suffix.into_iter() {
self.push(j).expect("Already checked the sum of the len()s; qed")
}
Ok(())
}
/// Returns the number of junctions in `self`.
pub const fn len(&self) -> usize {
match &self {
Junctions::Here => 0,
Junctions::X1(..) => 1,
Junctions::X2(..) => 2,
Junctions::X3(..) => 3,
Junctions::X4(..) => 4,
Junctions::X5(..) => 5,
Junctions::X6(..) => 6,
Junctions::X7(..) => 7,
Junctions::X8(..) => 8,
}
}
/// Returns the junction at index `i`, or `None` if the location doesn't contain that many elements.
pub fn at(&self, i: usize) -> Option<&Junction> {
Some(match (i, self) {
(0, Junctions::X1(ref a)) => a,
(0, Junctions::X2(ref a, ..)) => a,
(0, Junctions::X3(ref a, ..)) => a,
(0, Junctions::X4(ref a, ..)) => a,
(0, Junctions::X5(ref a, ..)) => a,
(0, Junctions::X6(ref a, ..)) => a,
(0, Junctions::X7(ref a, ..)) => a,
(0, Junctions::X8(ref a, ..)) => a,
(1, Junctions::X2(_, ref a)) => a,
(1, Junctions::X3(_, ref a, ..)) => a,
(1, Junctions::X4(_, ref a, ..)) => a,
(1, Junctions::X5(_, ref a, ..)) => a,
(1, Junctions::X6(_, ref a, ..)) => a,
(1, Junctions::X7(_, ref a, ..)) => a,
(1, Junctions::X8(_, ref a, ..)) => a,
(2, Junctions::X3(_, _, ref a)) => a,
(2, Junctions::X4(_, _, ref a, ..)) => a,
(2, Junctions::X5(_, _, ref a, ..)) => a,
(2, Junctions::X6(_, _, ref a, ..)) => a,
(2, Junctions::X7(_, _, ref a, ..)) => a,
(2, Junctions::X8(_, _, ref a, ..)) => a,
(3, Junctions::X4(_, _, _, ref a)) => a,
(3, Junctions::X5(_, _, _, ref a, ..)) => a,
(3, Junctions::X6(_, _, _, ref a, ..)) => a,
(3, Junctions::X7(_, _, _, ref a, ..)) => a,
(3, Junctions::X8(_, _, _, ref a, ..)) => a,
(4, Junctions::X5(_, _, _, _, ref a)) => a,
(4, Junctions::X6(_, _, _, _, ref a, ..)) => a,
(4, Junctions::X7(_, _, _, _, ref a, ..)) => a,
(4, Junctions::X8(_, _, _, _, ref a, ..)) => a,
(5, Junctions::X6(_, _, _, _, _, ref a)) => a,
(5, Junctions::X7(_, _, _, _, _, ref a, ..)) => a,
(5, Junctions::X8(_, _, _, _, _, ref a, ..)) => a,
(6, Junctions::X7(_, _, _, _, _, _, ref a)) => a,
(6, Junctions::X8(_, _, _, _, _, _, ref a, ..)) => a,
(7, Junctions::X8(_, _, _, _, _, _, _, ref a)) => a,
_ => return None,
})
}
/// Returns a mutable reference to the junction at index `i`, or `None` if the location doesn't contain that many
/// elements.
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
Some(match (i, self) {
(0, Junctions::X1(ref mut a)) => a,
(0, Junctions::X2(ref mut a, ..)) => a,
(0, Junctions::X3(ref mut a, ..)) => a,
(0, Junctions::X4(ref mut a, ..)) => a,
(0, Junctions::X5(ref mut a, ..)) => a,
(0, Junctions::X6(ref mut a, ..)) => a,
(0, Junctions::X7(ref mut a, ..)) => a,
(0, Junctions::X8(ref mut a, ..)) => a,
(1, Junctions::X2(_, ref mut a)) => a,
(1, Junctions::X3(_, ref mut a, ..)) => a,
(1, Junctions::X4(_, ref mut a, ..)) => a,
(1, Junctions::X5(_, ref mut a, ..)) => a,
(1, Junctions::X6(_, ref mut a, ..)) => a,
(1, Junctions::X7(_, ref mut a, ..)) => a,
(1, Junctions::X8(_, ref mut a, ..)) => a,
(2, Junctions::X3(_, _, ref mut a)) => a,
(2, Junctions::X4(_, _, ref mut a, ..)) => a,
(2, Junctions::X5(_, _, ref mut a, ..)) => a,
(2, Junctions::X6(_, _, ref mut a, ..)) => a,
(2, Junctions::X7(_, _, ref mut a, ..)) => a,
(2, Junctions::X8(_, _, ref mut a, ..)) => a,
(3, Junctions::X4(_, _, _, ref mut a)) => a,
(3, Junctions::X5(_, _, _, ref mut a, ..)) => a,
(3, Junctions::X6(_, _, _, ref mut a, ..)) => a,
(3, Junctions::X7(_, _, _, ref mut a, ..)) => a,
(3, Junctions::X8(_, _, _, ref mut a, ..)) => a,
(4, Junctions::X5(_, _, _, _, ref mut a)) => a,
(4, Junctions::X6(_, _, _, _, ref mut a, ..)) => a,
(4, Junctions::X7(_, _, _, _, ref mut a, ..)) => a,
(4, Junctions::X8(_, _, _, _, ref mut a, ..)) => a,
(5, Junctions::X6(_, _, _, _, _, ref mut a)) => a,
(5, Junctions::X7(_, _, _, _, _, ref mut a, ..)) => a,
(5, Junctions::X8(_, _, _, _, _, ref mut a, ..)) => a,
(6, Junctions::X7(_, _, _, _, _, _, ref mut a)) => a,
(6, Junctions::X8(_, _, _, _, _, _, ref mut a, ..)) => a,
(7, Junctions::X8(_, _, _, _, _, _, _, ref mut a)) => a,
_ => return None,
})
}
/// Returns a reference iterator over the junctions.
pub fn iter(&self) -> JunctionsRefIterator {
JunctionsRefIterator { junctions: self, next: 0, back: 0 }
}
/// Ensures that self begins with `prefix` and that it has a single `Junction` item following.
/// If so, returns a reference to this `Junction` item.
///
/// # Example
/// ```rust
/// # use xcm::v3::{Junctions::*, Junction::*};
/// # fn main() {
/// let mut m = X3(Parachain(2), PalletInstance(3), OnlyChild);
/// assert_eq!(m.match_and_split(&X2(Parachain(2), PalletInstance(3))), Some(&OnlyChild));
/// assert_eq!(m.match_and_split(&X1(Parachain(2))), None);
/// # }
/// ```
pub fn match_and_split(&self, prefix: &Junctions) -> Option<&Junction> {
if prefix.len() + 1 != self.len() {
return None
}
for i in 0..prefix.len() {
if prefix.at(i) != self.at(i) {
return None
}
}
return self.at(prefix.len())
}
pub fn starts_with(&self, prefix: &Junctions) -> bool {
prefix.len() <= self.len() && prefix.iter().zip(self.iter()).all(|(x, y)| x == y)
}
}
impl TryFrom<MultiLocation> for Junctions {
type Error = MultiLocation;
fn try_from(x: MultiLocation) -> result::Result<Self, MultiLocation> {
if x.parents > 0 {
Err(x)
} else {
Ok(x.interior)
}
}
}
impl<T: Into<Junction>> From<T> for Junctions {
fn from(x: T) -> Self {
Self::X1(x.into())
}
}
impl From<[Junction; 0]> for Junctions {
fn from(_: [Junction; 0]) -> Self {
Self::Here
}
}
impl From<()> for Junctions {
fn from(_: ()) -> Self {
Self::Here
}
}
xcm_procedural::impl_conversion_functions_for_junctions_v3!();
#[cfg(test)]
mod tests {
use super::{super::prelude::*, *};
#[test]
fn inverting_works() {
let context: InteriorMultiLocation = (Parachain(1000), PalletInstance(42)).into();
let target = (Parent, PalletInstance(69)).into();
let expected = (Parent, PalletInstance(42)).into();
let inverted = context.invert_target(&target).unwrap();
assert_eq!(inverted, expected);
let context: InteriorMultiLocation =
(Parachain(1000), PalletInstance(42), GeneralIndex(1)).into();
let target = (Parent, Parent, PalletInstance(69), GeneralIndex(2)).into();
let expected = (Parent, Parent, PalletInstance(42), GeneralIndex(1)).into();
let inverted = context.invert_target(&target).unwrap();
assert_eq!(inverted, expected);
}
#[test]
fn relative_to_works() {
use Junctions::*;
use NetworkId::*;
assert_eq!(X1(Polkadot.into()).relative_to(&X1(Kusama.into())), (Parent, Polkadot).into());
let base = X3(Kusama.into(), Parachain(1), PalletInstance(1));
// Ancestors.
assert_eq!(Here.relative_to(&base), (Parent, Parent, Parent).into());
assert_eq!(X1(Kusama.into()).relative_to(&base), (Parent, Parent).into());
assert_eq!(X2(Kusama.into(), Parachain(1)).relative_to(&base), (Parent,).into());
assert_eq!(
X3(Kusama.into(), Parachain(1), PalletInstance(1)).relative_to(&base),
Here.into()
);
// Ancestors with one child.
assert_eq!(
X1(Polkadot.into()).relative_to(&base),
(Parent, Parent, Parent, Polkadot).into()
);
assert_eq!(
X2(Kusama.into(), Parachain(2)).relative_to(&base),
(Parent, Parent, Parachain(2)).into()
);
assert_eq!(
X3(Kusama.into(), Parachain(1), PalletInstance(2)).relative_to(&base),
(Parent, PalletInstance(2)).into()
);
assert_eq!(
X4(Kusama.into(), Parachain(1), PalletInstance(1), [1u8; 32].into()).relative_to(&base),
([1u8; 32],).into()
);
// Ancestors with grandchildren.
assert_eq!(
X2(Polkadot.into(), Parachain(1)).relative_to(&base),
(Parent, Parent, Parent, Polkadot, Parachain(1)).into()
);
assert_eq!(
X3(Kusama.into(), Parachain(2), PalletInstance(1)).relative_to(&base),
(Parent, Parent, Parachain(2), PalletInstance(1)).into()
);
assert_eq!(
X4(Kusama.into(), Parachain(1), PalletInstance(2), [1u8; 32].into()).relative_to(&base),
(Parent, PalletInstance(2), [1u8; 32]).into()
);
assert_eq!(
X5(Kusama.into(), Parachain(1), PalletInstance(1), [1u8; 32].into(), 1u128.into())
.relative_to(&base),
([1u8; 32], 1u128).into()
);
}
#[test]
fn global_consensus_works() {
use Junctions::*;
use NetworkId::*;
assert_eq!(X1(Polkadot.into()).global_consensus(), Ok(Polkadot));
assert_eq!(X2(Kusama.into(), 1u64.into()).global_consensus(), Ok(Kusama));
assert_eq!(Here.global_consensus(), Err(()));
assert_eq!(X1(1u64.into()).global_consensus(), Err(()));
assert_eq!(X2(1u64.into(), Kusama.into()).global_consensus(), Err(()));
}
#[test]
fn test_conversion() {
use super::{Junction::*, Junctions::*, NetworkId::*};
let x: Junctions = GlobalConsensus(Polkadot).into();
assert_eq!(x, X1(GlobalConsensus(Polkadot)));
let x: Junctions = Polkadot.into();
assert_eq!(x, X1(GlobalConsensus(Polkadot)));
let x: Junctions = (Polkadot, Kusama).into();
assert_eq!(x, X2(GlobalConsensus(Polkadot), GlobalConsensus(Kusama)));
}
}
File diff suppressed because it is too large Load Diff
+951
View File
@@ -0,0 +1,951 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format asset data structures.
//!
//! This encompasses four types for representing assets:
//! - `MultiAsset`: A description of a single asset, either an instance of a non-fungible or some amount of a fungible.
//! - `MultiAssets`: A collection of `MultiAsset`s. These are stored in a `Vec` and sorted with fungibles first.
//! - `Wild`: A single asset wildcard, this can either be "all" assets, or all assets of a specific kind.
//! - `MultiAssetFilter`: A combination of `Wild` and `MultiAssets` designed for efficiently filtering an XCM holding
//! account.
use super::{InteriorMultiLocation, MultiLocation};
use crate::v2::{
AssetId as OldAssetId, AssetInstance as OldAssetInstance, Fungibility as OldFungibility,
MultiAsset as OldMultiAsset, MultiAssetFilter as OldMultiAssetFilter,
MultiAssets as OldMultiAssets, WildFungibility as OldWildFungibility,
WildMultiAsset as OldWildMultiAsset,
};
use alloc::{vec, vec::Vec};
use core::{
cmp::Ordering,
convert::{TryFrom, TryInto},
};
use parity_scale_codec::{self as codec, Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
/// A general identifier for an instance of a non-fungible asset class.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum AssetInstance {
/// Undefined - used if the non-fungible asset class has only one instance.
Undefined,
/// A compact index. Technically this could be greater than `u128`, but this implementation supports only
/// values up to `2**128 - 1`.
Index(#[codec(compact)] u128),
/// A 4-byte fixed-length datum.
Array4([u8; 4]),
/// An 8-byte fixed-length datum.
Array8([u8; 8]),
/// A 16-byte fixed-length datum.
Array16([u8; 16]),
/// A 32-byte fixed-length datum.
Array32([u8; 32]),
}
impl TryFrom<OldAssetInstance> for AssetInstance {
type Error = ();
fn try_from(value: OldAssetInstance) -> Result<Self, Self::Error> {
use OldAssetInstance::*;
Ok(match value {
Undefined => Self::Undefined,
Index(n) => Self::Index(n),
Array4(n) => Self::Array4(n),
Array8(n) => Self::Array8(n),
Array16(n) => Self::Array16(n),
Array32(n) => Self::Array32(n),
Blob(_) => return Err(()),
})
}
}
impl From<()> for AssetInstance {
fn from(_: ()) -> Self {
Self::Undefined
}
}
impl From<[u8; 4]> for AssetInstance {
fn from(x: [u8; 4]) -> Self {
Self::Array4(x)
}
}
impl From<[u8; 8]> for AssetInstance {
fn from(x: [u8; 8]) -> Self {
Self::Array8(x)
}
}
impl From<[u8; 16]> for AssetInstance {
fn from(x: [u8; 16]) -> Self {
Self::Array16(x)
}
}
impl From<[u8; 32]> for AssetInstance {
fn from(x: [u8; 32]) -> Self {
Self::Array32(x)
}
}
impl From<u8> for AssetInstance {
fn from(x: u8) -> Self {
Self::Index(x as u128)
}
}
impl From<u16> for AssetInstance {
fn from(x: u16) -> Self {
Self::Index(x as u128)
}
}
impl From<u32> for AssetInstance {
fn from(x: u32) -> Self {
Self::Index(x as u128)
}
}
impl From<u64> for AssetInstance {
fn from(x: u64) -> Self {
Self::Index(x as u128)
}
}
impl TryFrom<AssetInstance> for () {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Undefined => Ok(()),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for [u8; 4] {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Array4(x) => Ok(x),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for [u8; 8] {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Array8(x) => Ok(x),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for [u8; 16] {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Array16(x) => Ok(x),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for [u8; 32] {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Array32(x) => Ok(x),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for u8 {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for u16 {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for u32 {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for u64 {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<AssetInstance> for u128 {
type Error = ();
fn try_from(x: AssetInstance) -> Result<Self, ()> {
match x {
AssetInstance::Index(x) => Ok(x),
_ => Err(()),
}
}
}
/// Classification of whether an asset is fungible or not, along with a mandatory amount or instance.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum Fungibility {
/// A fungible asset; we record a number of units, as a `u128` in the inner item.
Fungible(#[codec(compact)] u128),
/// A non-fungible asset. We record the instance identifier in the inner item. Only one asset
/// of each instance identifier may ever be in existence at once.
NonFungible(AssetInstance),
}
impl Fungibility {
pub fn is_kind(&self, w: WildFungibility) -> bool {
use Fungibility::*;
use WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible};
matches!((self, w), (Fungible(_), WildFungible) | (NonFungible(_), WildNonFungible))
}
}
impl From<i32> for Fungibility {
fn from(amount: i32) -> Fungibility {
debug_assert_ne!(amount, 0);
Fungibility::Fungible(amount as u128)
}
}
impl From<u128> for Fungibility {
fn from(amount: u128) -> Fungibility {
debug_assert_ne!(amount, 0);
Fungibility::Fungible(amount)
}
}
impl<T: Into<AssetInstance>> From<T> for Fungibility {
fn from(instance: T) -> Fungibility {
Fungibility::NonFungible(instance.into())
}
}
impl TryFrom<OldFungibility> for Fungibility {
type Error = ();
fn try_from(value: OldFungibility) -> Result<Self, Self::Error> {
use OldFungibility::*;
Ok(match value {
Fungible(n) => Self::Fungible(n),
NonFungible(i) => Self::NonFungible(i.try_into()?),
})
}
}
/// Classification of whether an asset is fungible or not.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum WildFungibility {
/// The asset is fungible.
Fungible,
/// The asset is not fungible.
NonFungible,
}
impl TryFrom<OldWildFungibility> for WildFungibility {
type Error = ();
fn try_from(value: OldWildFungibility) -> Result<Self, Self::Error> {
use OldWildFungibility::*;
Ok(match value {
Fungible => Self::Fungible,
NonFungible => Self::NonFungible,
})
}
}
/// Classification of an asset being concrete or abstract.
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum AssetId {
/// A specific location identifying an asset.
Concrete(MultiLocation),
/// An abstract location; this is a name which may mean different specific locations on
/// different chains at different times.
Abstract([u8; 32]),
}
impl<T: Into<MultiLocation>> From<T> for AssetId {
fn from(x: T) -> Self {
Self::Concrete(x.into())
}
}
impl From<[u8; 32]> for AssetId {
fn from(x: [u8; 32]) -> Self {
Self::Abstract(x)
}
}
impl TryFrom<OldAssetId> for AssetId {
type Error = ();
fn try_from(old: OldAssetId) -> Result<Self, ()> {
use OldAssetId::*;
Ok(match old {
Concrete(l) => Self::Concrete(l.try_into()?),
Abstract(v) if v.len() <= 32 => {
let mut r = [0u8; 32];
r[..v.len()].copy_from_slice(&v[..]);
Self::Abstract(r)
},
_ => return Err(()),
})
}
}
impl AssetId {
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
pub fn prepend_with(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
if let AssetId::Concrete(ref mut l) = self {
l.prepend_with(*prepend).map_err(|_| ())?;
}
Ok(())
}
/// Mutate the asset to represent the same value from the perspective of a new `target`
/// location. The local chain's location is provided in `context`.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
if let AssetId::Concrete(ref mut l) = self {
l.reanchor(target, context)?;
}
Ok(())
}
/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `MultiAsset` value.
pub fn into_multiasset(self, fun: Fungibility) -> MultiAsset {
MultiAsset { fun, id: self }
}
/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding `WildMultiAsset`
/// wildcard (`AllOf`) value.
pub fn into_wild(self, fun: WildFungibility) -> WildMultiAsset {
WildMultiAsset::AllOf { fun, id: self }
}
}
/// Either an amount of a single fungible asset, or a single well-identified non-fungible asset.
#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiAsset {
/// The overall asset identity (aka *class*, in the case of a non-fungible).
pub id: AssetId,
/// The fungibility of the asset, which contains either the amount (in the case of a fungible
/// asset) or the *insance ID`, the secondary asset identifier.
pub fun: Fungibility,
}
impl PartialOrd for MultiAsset {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for MultiAsset {
fn cmp(&self, other: &Self) -> Ordering {
match (&self.fun, &other.fun) {
(Fungibility::Fungible(..), Fungibility::NonFungible(..)) => Ordering::Less,
(Fungibility::NonFungible(..), Fungibility::Fungible(..)) => Ordering::Greater,
_ => (&self.id, &self.fun).cmp(&(&other.id, &other.fun)),
}
}
}
impl<A: Into<AssetId>, B: Into<Fungibility>> From<(A, B)> for MultiAsset {
fn from((id, fun): (A, B)) -> MultiAsset {
MultiAsset { fun: fun.into(), id: id.into() }
}
}
impl MultiAsset {
pub fn is_fungible(&self, maybe_id: Option<AssetId>) -> bool {
use Fungibility::*;
matches!(self.fun, Fungible(..)) && maybe_id.map_or(true, |i| i == self.id)
}
pub fn is_non_fungible(&self, maybe_id: Option<AssetId>) -> bool {
use Fungibility::*;
matches!(self.fun, NonFungible(..)) && maybe_id.map_or(true, |i| i == self.id)
}
/// Prepend a `MultiLocation` to a concrete asset, giving it a new root location.
pub fn prepend_with(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
self.id.prepend_with(prepend)
}
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `context`.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
self.id.reanchor(target, context)
}
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `context`.
pub fn reanchored(
mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<Self, ()> {
self.id.reanchor(target, context)?;
Ok(self)
}
/// Returns true if `self` is a super-set of the given `inner` asset.
pub fn contains(&self, inner: &MultiAsset) -> bool {
use Fungibility::*;
if self.id == inner.id {
match (&self.fun, &inner.fun) {
(Fungible(a), Fungible(i)) if a >= i => return true,
(NonFungible(a), NonFungible(i)) if a == i => return true,
_ => (),
}
}
false
}
}
impl TryFrom<OldMultiAsset> for MultiAsset {
type Error = ();
fn try_from(old: OldMultiAsset) -> Result<Self, ()> {
Ok(Self { id: old.id.try_into()?, fun: old.fun.try_into()? })
}
}
/// A `Vec` of `MultiAsset`s.
///
/// There are a number of invariants which the construction and mutation functions must ensure are
/// maintained:
/// - It may contain no items of duplicate asset class;
/// - All items must be ordered;
/// - The number of items should grow no larger than `MAX_ITEMS_IN_MULTIASSETS`.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, TypeInfo, Default)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiAssets(Vec<MultiAsset>);
/// Maximum number of items we expect in a single `MultiAssets` value. Note this is not (yet)
/// enforced, and just serves to provide a sensible `max_encoded_len` for `MultiAssets`.
const MAX_ITEMS_IN_MULTIASSETS: usize = 20;
impl MaxEncodedLen for MultiAssets {
fn max_encoded_len() -> usize {
MultiAsset::max_encoded_len() * MAX_ITEMS_IN_MULTIASSETS
}
}
impl Decode for MultiAssets {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, parity_scale_codec::Error> {
Self::from_sorted_and_deduplicated(Vec::<MultiAsset>::decode(input)?)
.map_err(|()| "Out of order".into())
}
}
impl TryFrom<OldMultiAssets> for MultiAssets {
type Error = ();
fn try_from(old: OldMultiAssets) -> Result<Self, ()> {
let v = old
.drain()
.into_iter()
.map(MultiAsset::try_from)
.collect::<Result<Vec<_>, ()>>()?;
Ok(MultiAssets(v))
}
}
impl From<Vec<MultiAsset>> for MultiAssets {
fn from(mut assets: Vec<MultiAsset>) -> Self {
let mut res = Vec::with_capacity(assets.len());
if !assets.is_empty() {
assets.sort();
let mut iter = assets.into_iter();
if let Some(first) = iter.next() {
let last = iter.fold(first, |a, b| -> MultiAsset {
match (a, b) {
(
MultiAsset { fun: Fungibility::Fungible(a_amount), id: a_id },
MultiAsset { fun: Fungibility::Fungible(b_amount), id: b_id },
) if a_id == b_id => MultiAsset {
id: a_id,
fun: Fungibility::Fungible(a_amount.saturating_add(b_amount)),
},
(
MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id },
MultiAsset { fun: Fungibility::NonFungible(b_instance), id: b_id },
) if a_id == b_id && a_instance == b_instance =>
MultiAsset { fun: Fungibility::NonFungible(a_instance), id: a_id },
(to_push, to_remember) => {
res.push(to_push);
to_remember
},
}
});
res.push(last);
}
}
Self(res)
}
}
impl<T: Into<MultiAsset>> From<T> for MultiAssets {
fn from(x: T) -> Self {
Self(vec![x.into()])
}
}
impl MultiAssets {
/// A new (empty) value.
pub fn new() -> Self {
Self(Vec::new())
}
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
/// which contain no duplicates.
///
/// Returns `Ok` if the operation succeeds and `Err` if `r` is out of order or had duplicates. If you can't
/// guarantee that `r` is sorted and deduplicated, then use `From::<Vec<MultiAsset>>::from` which is infallible.
pub fn from_sorted_and_deduplicated(r: Vec<MultiAsset>) -> Result<Self, ()> {
if r.is_empty() {
return Ok(Self(Vec::new()))
}
r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&MultiAsset, ()> {
if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) {
Ok(b)
} else {
Err(())
}
})?;
Ok(Self(r))
}
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
/// which contain no duplicates.
///
/// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation.
/// Generally though you should avoid using it unless you have a strict proof that `r` is valid.
#[cfg(test)]
pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<MultiAsset>) -> Self {
Self::from_sorted_and_deduplicated(r).expect("Invalid input r is not sorted/deduped")
}
/// Create a new instance of `MultiAssets` from a `Vec<MultiAsset>` whose contents are sorted and
/// which contain no duplicates.
///
/// In release mode, this skips any checks to ensure that `r` is correct, making it a negligible-cost operation.
/// Generally though you should avoid using it unless you have a strict proof that `r` is valid.
///
/// In test mode, this checks anyway and panics on fail.
#[cfg(not(test))]
pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<MultiAsset>) -> Self {
Self(r)
}
/// Add some asset onto the list, saturating. This is quite a laborious operation since it maintains the ordering.
pub fn push(&mut self, a: MultiAsset) {
for asset in self.0.iter_mut().filter(|x| x.id == a.id) {
match (&a.fun, &mut asset.fun) {
(Fungibility::Fungible(amount), Fungibility::Fungible(balance)) => {
*balance = balance.saturating_add(*amount);
return
},
(Fungibility::NonFungible(inst1), Fungibility::NonFungible(inst2))
if inst1 == inst2 =>
return,
_ => (),
}
}
self.0.push(a);
self.0.sort();
}
/// Returns `true` if this definitely represents no asset.
pub fn is_none(&self) -> bool {
self.0.is_empty()
}
/// Returns true if `self` is a super-set of the given `inner` asset.
pub fn contains(&self, inner: &MultiAsset) -> bool {
self.0.iter().any(|i| i.contains(inner))
}
/// Consume `self` and return the inner vec.
#[deprecated = "Use `into_inner()` instead"]
pub fn drain(self) -> Vec<MultiAsset> {
self.0
}
/// Consume `self` and return the inner vec.
pub fn into_inner(self) -> Vec<MultiAsset> {
self.0
}
/// Return a reference to the inner vec.
pub fn inner(&self) -> &Vec<MultiAsset> {
&self.0
}
/// Return the number of distinct asset instances contained.
pub fn len(&self) -> usize {
self.0.len()
}
/// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location.
pub fn prepend_with(&mut self, prefix: &MultiLocation) -> Result<(), ()> {
self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix))
}
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `context`.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
self.0.iter_mut().try_for_each(|i| i.reanchor(target, context))
}
/// Return a reference to an item at a specific index or `None` if it doesn't exist.
pub fn get(&self, index: usize) -> Option<&MultiAsset> {
self.0.get(index)
}
}
/// A wildcard representing a set of assets.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum WildMultiAsset {
/// All assets in Holding.
All,
/// All assets in Holding of a given fungibility and ID.
AllOf { id: AssetId, fun: WildFungibility },
/// All assets in Holding, up to `u32` individual assets (different instances of non-fungibles
/// are separate assets).
AllCounted(#[codec(compact)] u32),
/// All assets in Holding of a given fungibility and ID up to `count` individual assets
/// (different instances of non-fungibles are separate assets).
AllOfCounted {
id: AssetId,
fun: WildFungibility,
#[codec(compact)]
count: u32,
},
}
impl TryFrom<OldWildMultiAsset> for WildMultiAsset {
type Error = ();
fn try_from(old: OldWildMultiAsset) -> Result<WildMultiAsset, ()> {
use OldWildMultiAsset::*;
Ok(match old {
AllOf { id, fun } => Self::AllOf { id: id.try_into()?, fun: fun.try_into()? },
All => Self::All,
})
}
}
impl TryFrom<(OldWildMultiAsset, u32)> for WildMultiAsset {
type Error = ();
fn try_from(old: (OldWildMultiAsset, u32)) -> Result<WildMultiAsset, ()> {
use OldWildMultiAsset::*;
let count = old.1;
Ok(match old.0 {
AllOf { id, fun } =>
Self::AllOfCounted { id: id.try_into()?, fun: fun.try_into()?, count },
All => Self::AllCounted(count),
})
}
}
impl WildMultiAsset {
/// Returns true if `self` is a super-set of the given `inner` asset.
pub fn contains(&self, inner: &MultiAsset) -> bool {
use WildMultiAsset::*;
match self {
AllOfCounted { count: 0, .. } | AllCounted(0) => false,
AllOf { fun, id } | AllOfCounted { id, fun, .. } =>
inner.fun.is_kind(*fun) && &inner.id == id,
All | AllCounted(_) => true,
}
}
/// Returns true if the wild element of `self` matches `inner`.
///
/// Note that for `Counted` variants of wildcards, then it will disregard the count except for
/// always returning `false` when equal to 0.
#[deprecated = "Use `contains` instead"]
pub fn matches(&self, inner: &MultiAsset) -> bool {
self.contains(inner)
}
/// Mutate the asset to represent the same value from the perspective of a new `target`
/// location. The local chain's location is provided in `context`.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
use WildMultiAsset::*;
match self {
AllOf { ref mut id, .. } | AllOfCounted { ref mut id, .. } =>
id.reanchor(target, context),
All | AllCounted(_) => Ok(()),
}
}
/// Maximum count of assets allowed to match, if any.
pub fn count(&self) -> Option<u32> {
use WildMultiAsset::*;
match self {
AllOfCounted { count, .. } | AllCounted(count) => Some(*count),
All | AllOf { .. } => None,
}
}
/// Explicit limit on number of assets allowed to match, if any.
pub fn limit(&self) -> Option<u32> {
self.count()
}
/// Consume self and return the equivalent version but counted and with the `count` set to the
/// given parameter.
pub fn counted(self, count: u32) -> Self {
use WildMultiAsset::*;
match self {
AllOfCounted { fun, id, .. } | AllOf { fun, id } => AllOfCounted { fun, id, count },
All | AllCounted(_) => AllCounted(count),
}
}
}
impl<A: Into<AssetId>, B: Into<WildFungibility>> From<(A, B)> for WildMultiAsset {
fn from((id, fun): (A, B)) -> WildMultiAsset {
WildMultiAsset::AllOf { fun: fun.into(), id: id.into() }
}
}
/// `MultiAsset` collection, defined either by a number of `MultiAssets` or a single wildcard.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub enum MultiAssetFilter {
/// Specify the filter as being everything contained by the given `MultiAssets` inner.
Definite(MultiAssets),
/// Specify the filter as the given `WildMultiAsset` wildcard.
Wild(WildMultiAsset),
}
impl<T: Into<WildMultiAsset>> From<T> for MultiAssetFilter {
fn from(x: T) -> Self {
Self::Wild(x.into())
}
}
impl From<MultiAsset> for MultiAssetFilter {
fn from(x: MultiAsset) -> Self {
Self::Definite(vec![x].into())
}
}
impl From<Vec<MultiAsset>> for MultiAssetFilter {
fn from(x: Vec<MultiAsset>) -> Self {
Self::Definite(x.into())
}
}
impl From<MultiAssets> for MultiAssetFilter {
fn from(x: MultiAssets) -> Self {
Self::Definite(x)
}
}
impl MultiAssetFilter {
/// Returns true if `inner` would be matched by `self`.
///
/// Note that for `Counted` variants of wildcards, then it will disregard the count except for
/// always returning `false` when equal to 0.
pub fn matches(&self, inner: &MultiAsset) -> bool {
match self {
MultiAssetFilter::Definite(ref assets) => assets.contains(inner),
MultiAssetFilter::Wild(ref wild) => wild.contains(inner),
}
}
/// Mutate the location of the asset identifier if concrete, giving it the same location
/// relative to a `target` context. The local context is provided as `context`.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
match self {
MultiAssetFilter::Definite(ref mut assets) => assets.reanchor(target, context),
MultiAssetFilter::Wild(ref mut wild) => wild.reanchor(target, context),
}
}
/// Maximum count of assets it is possible to match, if known.
pub fn count(&self) -> Option<u32> {
use MultiAssetFilter::*;
match self {
Definite(x) => Some(x.len() as u32),
Wild(x) => x.count(),
}
}
/// Explicit limit placed on the number of items, if any.
pub fn limit(&self) -> Option<u32> {
use MultiAssetFilter::*;
match self {
Definite(_) => None,
Wild(x) => x.limit(),
}
}
}
impl TryFrom<OldMultiAssetFilter> for MultiAssetFilter {
type Error = ();
fn try_from(old: OldMultiAssetFilter) -> Result<MultiAssetFilter, ()> {
Ok(match old {
OldMultiAssetFilter::Definite(x) => Self::Definite(x.try_into()?),
OldMultiAssetFilter::Wild(x) => Self::Wild(x.try_into()?),
})
}
}
impl TryFrom<(OldMultiAssetFilter, u32)> for MultiAssetFilter {
type Error = ();
fn try_from(old: (OldMultiAssetFilter, u32)) -> Result<MultiAssetFilter, ()> {
let count = old.1;
Ok(match old.0 {
OldMultiAssetFilter::Definite(x) if count >= x.len() as u32 =>
Self::Definite(x.try_into()?),
OldMultiAssetFilter::Wild(x) => Self::Wild((x, count).try_into()?),
_ => return Err(()),
})
}
}
#[cfg(test)]
mod tests {
use super::super::prelude::*;
#[test]
fn conversion_works() {
let _: MultiAssets = (Here, 1u128).into();
}
#[test]
fn from_sorted_and_deduplicated_works() {
use super::*;
use alloc::vec;
let empty = vec![];
let r = MultiAssets::from_sorted_and_deduplicated(empty);
assert_eq!(r, Ok(MultiAssets(vec![])));
let dup_fun = vec![(Here, 100).into(), (Here, 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(dup_fun);
assert!(r.is_err());
let dup_nft = vec![(Here, *b"notgood!").into(), (Here, *b"notgood!").into()];
let r = MultiAssets::from_sorted_and_deduplicated(dup_nft);
assert!(r.is_err());
let good_fun = vec![(Here, 10).into(), (Parent, 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(good_fun.clone());
assert_eq!(r, Ok(MultiAssets(good_fun)));
let bad_fun = vec![(Parent, 10).into(), (Here, 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(bad_fun);
assert!(r.is_err());
let good_abstract_fun = vec![(Here, 100).into(), ([0u8; 32], 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(good_abstract_fun.clone());
assert_eq!(r, Ok(MultiAssets(good_abstract_fun)));
let bad_abstract_fun = vec![([0u8; 32], 10).into(), (Here, 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(bad_abstract_fun);
assert!(r.is_err());
let good_nft = vec![(Here, ()).into(), (Here, *b"good").into()];
let r = MultiAssets::from_sorted_and_deduplicated(good_nft.clone());
assert_eq!(r, Ok(MultiAssets(good_nft)));
let bad_nft = vec![(Here, *b"bad!").into(), (Here, ()).into()];
let r = MultiAssets::from_sorted_and_deduplicated(bad_nft);
assert!(r.is_err());
let good_abstract_nft = vec![(Here, ()).into(), ([0u8; 32], ()).into()];
let r = MultiAssets::from_sorted_and_deduplicated(good_abstract_nft.clone());
assert_eq!(r, Ok(MultiAssets(good_abstract_nft)));
let bad_abstract_nft = vec![([0u8; 32], ()).into(), (Here, ()).into()];
let r = MultiAssets::from_sorted_and_deduplicated(bad_abstract_nft);
assert!(r.is_err());
let mixed_good = vec![(Here, 10).into(), (Here, *b"good").into()];
let r = MultiAssets::from_sorted_and_deduplicated(mixed_good.clone());
assert_eq!(r, Ok(MultiAssets(mixed_good)));
let mixed_bad = vec![(Here, *b"bad!").into(), (Here, 10).into()];
let r = MultiAssets::from_sorted_and_deduplicated(mixed_bad);
assert!(r.is_err());
}
}
+710
View File
@@ -0,0 +1,710 @@
// Copyright 2020-2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! XCM `MultiLocation` datatype.
use super::{Junction, Junctions};
use crate::{v2::MultiLocation as OldMultiLocation, VersionedMultiLocation};
use core::{
convert::{TryFrom, TryInto},
result,
};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
/// A relative path between state-bearing consensus systems.
///
/// A location in a consensus system is defined as an *isolatable state machine* held within global
/// consensus. The location in question need not have a sophisticated consensus algorithm of its
/// own; a single account within Ethereum, for example, could be considered a location.
///
/// A very-much non-exhaustive list of types of location include:
/// - A (normal, layer-1) block chain, e.g. the Bitcoin mainnet or a parachain.
/// - A layer-0 super-chain, e.g. the Polkadot Relay chain.
/// - A layer-2 smart contract, e.g. an ERC-20 on Ethereum.
/// - A logical functional component of a chain, e.g. a single instance of a pallet on a Frame-based
/// Substrate chain.
/// - An account.
///
/// A `MultiLocation` is a *relative identifier*, meaning that it can only be used to define the
/// relative path between two locations, and cannot generally be used to refer to a location
/// universally. It is comprised of an integer number of parents specifying the number of times to
/// "escape" upwards into the containing consensus system and then a number of *junctions*, each
/// diving down and specifying some interior portion of state (which may be considered a
/// "sub-consensus" system).
///
/// This specific `MultiLocation` implementation uses a `Junctions` datatype which is a Rust `enum`
/// in order to make pattern matching easier. There are occasions where it is important to ensure
/// that a value is strictly an interior location, in those cases, `Junctions` may be used.
///
/// The `MultiLocation` value of `Null` simply refers to the interpreting consensus system.
#[derive(
Copy, Clone, Decode, Encode, Eq, PartialEq, Ord, PartialOrd, Debug, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiLocation {
/// The number of parent junctions at the beginning of this `MultiLocation`.
pub parents: u8,
/// The interior (i.e. non-parent) junctions that this `MultiLocation` contains.
pub interior: Junctions,
}
impl Default for MultiLocation {
fn default() -> Self {
Self { parents: 0, interior: Junctions::Here }
}
}
/// A relative location which is constrained to be an interior location of the context.
///
/// See also `MultiLocation`.
pub type InteriorMultiLocation = Junctions;
impl MultiLocation {
/// Creates a new `MultiLocation` with the given number of parents and interior junctions.
pub fn new(parents: u8, interior: impl Into<Junctions>) -> MultiLocation {
MultiLocation { parents, interior: interior.into() }
}
/// Consume `self` and return the equivalent `VersionedMultiLocation` value.
pub const fn into_versioned(self) -> VersionedMultiLocation {
VersionedMultiLocation::V3(self)
}
/// Creates a new `MultiLocation` with 0 parents and a `Here` interior.
///
/// The resulting `MultiLocation` can be interpreted as the "current consensus system".
pub const fn here() -> MultiLocation {
MultiLocation { parents: 0, interior: Junctions::Here }
}
/// Creates a new `MultiLocation` which evaluates to the parent context.
pub const fn parent() -> MultiLocation {
MultiLocation { parents: 1, interior: Junctions::Here }
}
/// Creates a new `MultiLocation` which evaluates to the grand parent context.
pub const fn grandparent() -> MultiLocation {
MultiLocation { parents: 2, interior: Junctions::Here }
}
/// Creates a new `MultiLocation` with `parents` and an empty (`Here`) interior.
pub const fn ancestor(parents: u8) -> MultiLocation {
MultiLocation { parents, interior: Junctions::Here }
}
/// Whether the `MultiLocation` has no parents and has a `Here` interior.
pub const fn is_here(&self) -> bool {
self.parents == 0 && self.interior.len() == 0
}
/// Remove the `NetworkId` value in any interior `Junction`s.
pub fn remove_network_id(&mut self) {
self.interior.remove_network_id();
}
/// Return a reference to the interior field.
pub fn interior(&self) -> &Junctions {
&self.interior
}
/// Return a mutable reference to the interior field.
pub fn interior_mut(&mut self) -> &mut Junctions {
&mut self.interior
}
/// Returns the number of `Parent` junctions at the beginning of `self`.
pub const fn parent_count(&self) -> u8 {
self.parents
}
/// Returns boolean indicating whether `self` contains only the specified amount of
/// parents and no interior junctions.
pub const fn contains_parents_only(&self, count: u8) -> bool {
matches!(self.interior, Junctions::Here) && self.parents == count
}
/// Returns the number of parents and junctions in `self`.
pub const fn len(&self) -> usize {
self.parent_count() as usize + self.interior.len()
}
/// Returns the first interior junction, or `None` if the location is empty or contains only
/// parents.
pub fn first_interior(&self) -> Option<&Junction> {
self.interior.first()
}
/// Returns last junction, or `None` if the location is empty or contains only parents.
pub fn last(&self) -> Option<&Junction> {
self.interior.last()
}
/// Splits off the first interior junction, returning the remaining suffix (first item in tuple)
/// and the first element (second item in tuple) or `None` if it was empty.
pub fn split_first_interior(self) -> (MultiLocation, Option<Junction>) {
let MultiLocation { parents, interior: junctions } = self;
let (suffix, first) = junctions.split_first();
let multilocation = MultiLocation { parents, interior: suffix };
(multilocation, first)
}
/// Splits off the last interior junction, returning the remaining prefix (first item in tuple)
/// and the last element (second item in tuple) or `None` if it was empty or if `self` only
/// contains parents.
pub fn split_last_interior(self) -> (MultiLocation, Option<Junction>) {
let MultiLocation { parents, interior: junctions } = self;
let (prefix, last) = junctions.split_last();
let multilocation = MultiLocation { parents, interior: prefix };
(multilocation, last)
}
/// Mutates `self`, suffixing its interior junctions with `new`. Returns `Err` with `new` in
/// case of overflow.
pub fn push_interior(&mut self, new: impl Into<Junction>) -> result::Result<(), Junction> {
self.interior.push(new)
}
/// Mutates `self`, prefixing its interior junctions with `new`. Returns `Err` with `new` in
/// case of overflow.
pub fn push_front_interior(
&mut self,
new: impl Into<Junction>,
) -> result::Result<(), Junction> {
self.interior.push_front(new)
}
/// Consumes `self` and returns a `MultiLocation` suffixed with `new`, or an `Err` with theoriginal value of
/// `self` in case of overflow.
pub fn pushed_with_interior(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
match self.interior.pushed_with(new) {
Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }),
Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)),
}
}
/// Consumes `self` and returns a `MultiLocation` prefixed with `new`, or an `Err` with the original value of
/// `self` in case of overflow.
pub fn pushed_front_with_interior(
self,
new: impl Into<Junction>,
) -> result::Result<Self, (Self, Junction)> {
match self.interior.pushed_front_with(new) {
Ok(i) => Ok(MultiLocation { interior: i, parents: self.parents }),
Err((i, j)) => Err((MultiLocation { interior: i, parents: self.parents }, j)),
}
}
/// Returns the junction at index `i`, or `None` if the location is a parent or if the location
/// does not contain that many elements.
pub fn at(&self, i: usize) -> Option<&Junction> {
let num_parents = self.parents as usize;
if i < num_parents {
return None
}
self.interior.at(i - num_parents)
}
/// Returns a mutable reference to the junction at index `i`, or `None` if the location is a
/// parent or if it doesn't contain that many elements.
pub fn at_mut(&mut self, i: usize) -> Option<&mut Junction> {
let num_parents = self.parents as usize;
if i < num_parents {
return None
}
self.interior.at_mut(i - num_parents)
}
/// Decrements the parent count by 1.
pub fn dec_parent(&mut self) {
self.parents = self.parents.saturating_sub(1);
}
/// Removes the first interior junction from `self`, returning it
/// (or `None` if it was empty or if `self` contains only parents).
pub fn take_first_interior(&mut self) -> Option<Junction> {
self.interior.take_first()
}
/// Removes the last element from `interior`, returning it (or `None` if it was empty or if
/// `self` only contains parents).
pub fn take_last(&mut self) -> Option<Junction> {
self.interior.take_last()
}
/// Ensures that `self` has the same number of parents as `prefix`, its junctions begins with
/// the junctions of `prefix` and that it has a single `Junction` item following.
/// If so, returns a reference to this `Junction` item.
///
/// # Example
/// ```rust
/// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation};
/// # fn main() {
/// let mut m = MultiLocation::new(1, X2(PalletInstance(3), OnlyChild));
/// assert_eq!(
/// m.match_and_split(&MultiLocation::new(1, X1(PalletInstance(3)))),
/// Some(&OnlyChild),
/// );
/// assert_eq!(m.match_and_split(&MultiLocation::new(1, Here)), None);
/// # }
/// ```
pub fn match_and_split(&self, prefix: &MultiLocation) -> Option<&Junction> {
if self.parents != prefix.parents {
return None
}
self.interior.match_and_split(&prefix.interior)
}
pub fn starts_with(&self, prefix: &MultiLocation) -> bool {
self.parents == prefix.parents && self.interior.starts_with(&prefix.interior)
}
/// Mutate `self` so that it is suffixed with `suffix`.
///
/// Does not modify `self` and returns `Err` with `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// # fn main() {
/// let mut m: MultiLocation = (Parent, Parachain(21), 69u64).into();
/// assert_eq!(m.append_with((Parent, PalletInstance(3))), Ok(()));
/// assert_eq!(m, MultiLocation::new(1, X2(Parachain(21), PalletInstance(3))));
/// # }
/// ```
pub fn append_with(&mut self, suffix: impl Into<Self>) -> Result<(), Self> {
let prefix = core::mem::replace(self, suffix.into());
match self.prepend_with(prefix) {
Ok(()) => Ok(()),
Err(prefix) => Err(core::mem::replace(self, prefix)),
}
}
/// Consume `self` and return its value suffixed with `suffix`.
///
/// Returns `Err` with the original value of `self` and `suffix` in case of overflow.
///
/// # Example
/// ```rust
/// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// # fn main() {
/// let mut m: MultiLocation = (Parent, Parachain(21), 69u64).into();
/// let r = m.appended_with((Parent, PalletInstance(3))).unwrap();
/// assert_eq!(r, MultiLocation::new(1, X2(Parachain(21), PalletInstance(3))));
/// # }
/// ```
pub fn appended_with(mut self, suffix: impl Into<Self>) -> Result<Self, (Self, Self)> {
match self.append_with(suffix) {
Ok(()) => Ok(self),
Err(suffix) => Err((self, suffix)),
}
}
/// Mutate `self` so that it is prefixed with `prefix`.
///
/// Does not modify `self` and returns `Err` with `prefix` in case of overflow.
///
/// # Example
/// ```rust
/// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// # fn main() {
/// let mut m: MultiLocation = (Parent, Parent, PalletInstance(3)).into();
/// assert_eq!(m.prepend_with((Parent, Parachain(21), OnlyChild)), Ok(()));
/// assert_eq!(m, MultiLocation::new(1, X1(PalletInstance(3))));
/// # }
/// ```
pub fn prepend_with(&mut self, prefix: impl Into<Self>) -> Result<(), Self> {
// prefix self (suffix)
// P .. P I .. I p .. p i .. i
let mut prefix = prefix.into();
let prepend_interior = prefix.interior.len().saturating_sub(self.parents as usize);
let final_interior = self.interior.len().saturating_add(prepend_interior);
if final_interior > super::junctions::MAX_JUNCTIONS {
return Err(prefix)
}
let suffix_parents = (self.parents as usize).saturating_sub(prefix.interior.len());
let final_parents = (prefix.parents as usize).saturating_add(suffix_parents);
if final_parents > 255 {
return Err(prefix)
}
// cancel out the final item on the prefix interior for one of the suffix's parents.
while self.parents > 0 && prefix.take_last().is_some() {
self.dec_parent();
}
// now we have either removed all suffix's parents or prefix interior.
// this means we can combine the prefix's and suffix's remaining parents/interior since
// we know that with at least one empty, the overall order will be respected:
// prefix self (suffix)
// P .. P (I) p .. p i .. i => P + p .. (no I) i
// -- or --
// P .. P I .. I (p) i .. i => P (no p) .. I + i
self.parents = self.parents.saturating_add(prefix.parents);
for j in prefix.interior.into_iter().rev() {
self.push_front_interior(j)
.expect("final_interior no greater than MAX_JUNCTIONS; qed");
}
Ok(())
}
/// Consume `self` and return its value prefixed with `prefix`.
///
/// Returns `Err` with the original value of `self` and `prefix` in case of overflow.
///
/// # Example
/// ```rust
/// # use xcm::v3::{Junctions::*, Junction::*, MultiLocation, Parent};
/// # fn main() {
/// let m: MultiLocation = (Parent, Parent, PalletInstance(3)).into();
/// let r = m.prepended_with((Parent, Parachain(21), OnlyChild)).unwrap();
/// assert_eq!(r, MultiLocation::new(1, X1(PalletInstance(3))));
/// # }
/// ```
pub fn prepended_with(mut self, prefix: impl Into<Self>) -> Result<Self, (Self, Self)> {
match self.prepend_with(prefix) {
Ok(()) => Ok(self),
Err(prefix) => Err((self, prefix)),
}
}
/// Mutate `self` so that it represents the same location from the point of view of `target`.
/// The context of `self` is provided as `context`.
///
/// Does not modify `self` in case of overflow.
pub fn reanchor(
&mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<(), ()> {
// TODO: https://github.com/paritytech/polkadot/issues/4489 Optimize this.
// 1. Use our `context` to figure out how the `target` would address us.
let inverted_target = context.invert_target(target)?;
// 2. Prepend `inverted_target` to `self` to get self's location from the perspective of
// `target`.
self.prepend_with(inverted_target).map_err(|_| ())?;
// 3. Given that we know some of `target` context, ensure that any parents in `self` are
// strictly needed.
self.simplify(target.interior());
Ok(())
}
/// Consume `self` and return a new value representing the same location from the point of view
/// of `target`. The context of `self` is provided as `context`.
///
/// Returns the original `self` in case of overflow.
pub fn reanchored(
mut self,
target: &MultiLocation,
context: InteriorMultiLocation,
) -> Result<Self, Self> {
match self.reanchor(target, context) {
Ok(()) => Ok(self),
Err(()) => Err(self),
}
}
/// Remove any unneeded parents/junctions in `self` based on the given context it will be
/// interpreted in.
pub fn simplify(&mut self, context: &Junctions) {
if context.len() < self.parents as usize {
// Not enough context
return
}
while self.parents > 0 {
let maybe = context.at(context.len() - (self.parents as usize));
match (self.interior.first(), maybe) {
(Some(i), Some(j)) if i == j => {
self.interior.take_first();
self.parents -= 1;
},
_ => break,
}
}
}
}
impl TryFrom<OldMultiLocation> for MultiLocation {
type Error = ();
fn try_from(x: OldMultiLocation) -> result::Result<Self, ()> {
Ok(MultiLocation { parents: x.parents, interior: x.interior.try_into()? })
}
}
/// A unit struct which can be converted into a `MultiLocation` of `parents` value 1.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Parent;
impl From<Parent> for MultiLocation {
fn from(_: Parent) -> Self {
MultiLocation { parents: 1, interior: Junctions::Here }
}
}
/// A tuple struct which can be converted into a `MultiLocation` of `parents` value 1 with the inner interior.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct ParentThen(pub Junctions);
impl From<ParentThen> for MultiLocation {
fn from(ParentThen(interior): ParentThen) -> Self {
MultiLocation { parents: 1, interior }
}
}
/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Ancestor(pub u8);
impl From<Ancestor> for MultiLocation {
fn from(Ancestor(parents): Ancestor) -> Self {
MultiLocation { parents, interior: Junctions::Here }
}
}
/// A unit struct which can be converted into a `MultiLocation` of the inner `parents` value and the inner interior.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct AncestorThen<Interior>(pub u8, pub Interior);
impl<Interior: Into<Junctions>> From<AncestorThen<Interior>> for MultiLocation {
fn from(AncestorThen(parents, interior): AncestorThen<Interior>) -> Self {
MultiLocation { parents, interior: interior.into() }
}
}
xcm_procedural::impl_conversion_functions_for_multilocation_v3!();
#[cfg(test)]
mod tests {
use crate::v3::prelude::*;
use parity_scale_codec::{Decode, Encode};
#[test]
fn conversion_works() {
let x: MultiLocation = Parent.into();
assert_eq!(x, MultiLocation { parents: 1, interior: Here });
// let x: MultiLocation = (Parent,).into();
// assert_eq!(x, MultiLocation { parents: 1, interior: Here });
// let x: MultiLocation = (Parent, Parent).into();
// assert_eq!(x, MultiLocation { parents: 2, interior: Here });
let x: MultiLocation = (Parent, Parent, OnlyChild).into();
assert_eq!(x, MultiLocation { parents: 2, interior: OnlyChild.into() });
let x: MultiLocation = OnlyChild.into();
assert_eq!(x, MultiLocation { parents: 0, interior: OnlyChild.into() });
let x: MultiLocation = (OnlyChild,).into();
assert_eq!(x, MultiLocation { parents: 0, interior: OnlyChild.into() });
}
#[test]
fn simplify_basic_works() {
let mut location: MultiLocation =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X2(Parachain(1000), PalletInstance(42));
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into();
let context = X1(PalletInstance(42));
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation = (Parent, PalletInstance(42), GeneralIndex(69)).into();
let context = X2(Parachain(1000), PalletInstance(42));
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X3(OnlyChild, Parachain(1000), PalletInstance(42));
let expected = GeneralIndex(69).into();
location.simplify(&context);
assert_eq!(location, expected);
}
#[test]
fn simplify_incompatible_location_fails() {
let mut location: MultiLocation =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X3(Parachain(1000), PalletInstance(42), GeneralIndex(42));
let expected =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
let mut location: MultiLocation =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
let context = X1(Parachain(1000));
let expected =
(Parent, Parent, Parachain(1000), PalletInstance(42), GeneralIndex(69)).into();
location.simplify(&context);
assert_eq!(location, expected);
}
#[test]
fn reanchor_works() {
let mut id: MultiLocation = (Parent, Parachain(1000), GeneralIndex(42)).into();
let context = Parachain(2000).into();
let target = (Parent, Parachain(1000)).into();
let expected = GeneralIndex(42).into();
id.reanchor(&target, context).unwrap();
assert_eq!(id, expected);
}
#[test]
fn encode_and_decode_works() {
let m = MultiLocation {
parents: 1,
interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 }),
};
let encoded = m.encode();
assert_eq!(encoded, [1, 2, 0, 168, 2, 0, 92].to_vec());
let decoded = MultiLocation::decode(&mut &encoded[..]);
assert_eq!(decoded, Ok(m));
}
#[test]
fn match_and_split_works() {
let m = MultiLocation {
parents: 1,
interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 }),
};
assert_eq!(m.match_and_split(&MultiLocation { parents: 1, interior: Here }), None);
assert_eq!(
m.match_and_split(&MultiLocation { parents: 1, interior: X1(Parachain(42)) }),
Some(&AccountIndex64 { network: None, index: 23 })
);
assert_eq!(m.match_and_split(&m), None);
}
#[test]
fn append_with_works() {
let acc = AccountIndex64 { network: None, index: 23 };
let mut m = MultiLocation { parents: 1, interior: X1(Parachain(42)) };
assert_eq!(m.append_with(X2(PalletInstance(3), acc.clone())), Ok(()));
assert_eq!(
m,
MultiLocation {
parents: 1,
interior: X3(Parachain(42), PalletInstance(3), acc.clone())
}
);
// cannot append to create overly long multilocation
let acc = AccountIndex64 { network: None, index: 23 };
let m = MultiLocation {
parents: 254,
interior: X5(Parachain(42), OnlyChild, OnlyChild, OnlyChild, OnlyChild),
};
let suffix: MultiLocation = (PalletInstance(3), acc.clone(), OnlyChild, OnlyChild).into();
assert_eq!(m.clone().append_with(suffix.clone()), Err(suffix));
}
#[test]
fn prepend_with_works() {
let mut m = MultiLocation {
parents: 1,
interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 }),
};
assert_eq!(m.prepend_with(MultiLocation { parents: 1, interior: X1(OnlyChild) }), Ok(()));
assert_eq!(
m,
MultiLocation {
parents: 1,
interior: X2(Parachain(42), AccountIndex64 { network: None, index: 23 })
}
);
// cannot prepend to create overly long multilocation
let mut m = MultiLocation { parents: 254, interior: X1(Parachain(42)) };
let prefix = MultiLocation { parents: 2, interior: Here };
assert_eq!(m.prepend_with(prefix.clone()), Err(prefix));
let prefix = MultiLocation { parents: 1, interior: Here };
assert_eq!(m.prepend_with(prefix), Ok(()));
assert_eq!(m, MultiLocation { parents: 255, interior: X1(Parachain(42)) });
}
#[test]
fn double_ended_ref_iteration_works() {
let m = X3(Parachain(1000), Parachain(3), PalletInstance(5));
let mut iter = m.iter();
let first = iter.next().unwrap();
assert_eq!(first, &Parachain(1000));
let third = iter.next_back().unwrap();
assert_eq!(third, &PalletInstance(5));
let second = iter.next_back().unwrap();
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
assert_eq!(second, &Parachain(3));
let res = Here
.pushed_with(first.clone())
.unwrap()
.pushed_with(second.clone())
.unwrap()
.pushed_with(third.clone())
.unwrap();
assert_eq!(m, res);
// make sure there's no funny business with the 0 indexing
let m = Here;
let mut iter = m.iter();
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
}
#[test]
fn conversion_from_other_types_works() {
use crate::v2;
use core::convert::TryInto;
fn takes_multilocation<Arg: Into<MultiLocation>>(_arg: Arg) {}
takes_multilocation(Parent);
takes_multilocation(Here);
takes_multilocation(X1(Parachain(42)));
takes_multilocation((Ancestor(255), PalletInstance(8)));
takes_multilocation((Ancestor(5), Parachain(1), PalletInstance(3)));
takes_multilocation((Ancestor(2), Here));
takes_multilocation(AncestorThen(
3,
X2(Parachain(43), AccountIndex64 { network: None, index: 155 }),
));
takes_multilocation((Parent, AccountId32 { network: None, id: [0; 32] }));
takes_multilocation((Parent, Here));
takes_multilocation(ParentThen(X1(Parachain(75))));
takes_multilocation([Parachain(100), PalletInstance(3)]);
assert_eq!(
v2::MultiLocation::from(v2::Junctions::Here).try_into(),
Ok(MultiLocation::here())
);
assert_eq!(v2::MultiLocation::from(v2::Parent).try_into(), Ok(MultiLocation::parent()));
assert_eq!(
v2::MultiLocation::from((v2::Parent, v2::Parent, v2::Junction::GeneralIndex(42u128),))
.try_into(),
Ok(MultiLocation { parents: 2, interior: X1(GeneralIndex(42u128)) }),
);
}
}
+541
View File
@@ -0,0 +1,541 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.
//! Cross-Consensus Message format data structures.
use crate::v2::Error as OldError;
use core::result;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
pub use sp_weights::Weight;
use super::*;
/// Error codes used in XCM. The first errors codes have explicit indices and are part of the XCM
/// format. Those trailing are merely part of the XCM implementation; there is no expectation that
/// they will retain the same index over time.
#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
pub enum Error {
// Errors that happen due to instructions being executed. These alone are defined in the
// XCM specification.
/// An arithmetic overflow happened.
#[codec(index = 0)]
Overflow,
/// The instruction is intentionally unsupported.
#[codec(index = 1)]
Unimplemented,
/// Origin Register does not contain a value value for a reserve transfer notification.
#[codec(index = 2)]
UntrustedReserveLocation,
/// Origin Register does not contain a value value for a teleport notification.
#[codec(index = 3)]
UntrustedTeleportLocation,
/// `MultiLocation` value too large to descend further.
#[codec(index = 4)]
LocationFull,
/// `MultiLocation` value ascend more parents than known ancestors of local location.
#[codec(index = 5)]
LocationNotInvertible,
/// The Origin Register does not contain a valid value for instruction.
#[codec(index = 6)]
BadOrigin,
/// The location parameter is not a valid value for the instruction.
#[codec(index = 7)]
InvalidLocation,
/// The given asset is not handled.
#[codec(index = 8)]
AssetNotFound,
/// An asset transaction (like withdraw or deposit) failed (typically due to type conversions).
#[codec(index = 9)]
FailedToTransactAsset(#[codec(skip)] &'static str),
/// An asset cannot be withdrawn, potentially due to lack of ownership, availability or rights.
#[codec(index = 10)]
NotWithdrawable,
/// An asset cannot be deposited under the ownership of a particular location.
#[codec(index = 11)]
LocationCannotHold,
/// Attempt to send a message greater than the maximum supported by the transport protocol.
#[codec(index = 12)]
ExceedsMaxMessageSize,
/// The given message cannot be translated into a format supported by the destination.
#[codec(index = 13)]
DestinationUnsupported,
/// Destination is routable, but there is some issue with the transport mechanism.
#[codec(index = 14)]
Transport(#[codec(skip)] &'static str),
/// Destination is known to be unroutable.
#[codec(index = 15)]
Unroutable,
/// Used by `ClaimAsset` when the given claim could not be recognized/found.
#[codec(index = 16)]
UnknownClaim,
/// Used by `Transact` when the functor cannot be decoded.
#[codec(index = 17)]
FailedToDecode,
/// Used by `Transact` to indicate that the given weight limit could be breached by the functor.
#[codec(index = 18)]
MaxWeightInvalid,
/// Used by `BuyExecution` when the Holding Register does not contain payable fees.
#[codec(index = 19)]
NotHoldingFees,
/// Used by `BuyExecution` when the fees declared to purchase weight are insufficient.
#[codec(index = 20)]
TooExpensive,
/// Used by the `Trap` instruction to force an error intentionally. Its code is included.
#[codec(index = 21)]
Trap(u64),
/// Used by `ExpectAsset`, `ExpectError` and `ExpectOrigin` when the expectation was not true.
#[codec(index = 22)]
ExpectationFalse,
/// The provided pallet index was not found.
#[codec(index = 23)]
PalletNotFound,
/// The given pallet's name is different to that expected.
#[codec(index = 24)]
NameMismatch,
/// The given pallet's version has an incompatible version to that expected.
#[codec(index = 25)]
VersionIncompatible,
/// The given operation would lead to an overflow of the Holding Register.
#[codec(index = 26)]
HoldingWouldOverflow,
/// The message was unable to be exported.
#[codec(index = 27)]
ExportError,
/// `MultiLocation` value failed to be reanchored.
#[codec(index = 28)]
ReanchorFailed,
/// No deal is possible under the given constraints.
#[codec(index = 29)]
NoDeal,
/// Fees were required which the origin could not pay.
#[codec(index = 30)]
FeesNotMet,
/// Some other error with locking.
#[codec(index = 31)]
LockError,
/// The state was not in a condition where the operation was valid to make.
#[codec(index = 32)]
NoPermission,
/// The universal location of the local consensus is improper.
#[codec(index = 33)]
Unanchored,
/// An asset cannot be deposited, probably because (too much of) it already exists.
#[codec(index = 34)]
NotDepositable,
// Errors that happen prior to instructions being executed. These fall outside of the XCM spec.
/// XCM version not able to be handled.
UnhandledXcmVersion,
/// Execution of the XCM would potentially result in a greater weight used than weight limit.
WeightLimitReached(Weight),
/// The XCM did not pass the barrier condition for execution.
///
/// The barrier condition differs on different chains and in different circumstances, but
/// generally it means that the conditions surrounding the message were not such that the chain
/// considers the message worth spending time executing. Since most chains lift the barrier to
/// execution on appropriate payment, presentation of an NFT voucher, or based on the message
/// origin, it means that none of those were the case.
Barrier,
/// The weight of an XCM message is not computable ahead of execution.
WeightNotComputable,
/// Recursion stack limit reached
ExceedsStackLimit,
}
impl MaxEncodedLen for Error {
fn max_encoded_len() -> usize {
// TODO: max_encoded_len doesn't quite work here as it tries to take notice of the fields
// marked `codec(skip)`. We can hard-code it with the right answer for now.
1
}
}
impl TryFrom<OldError> for Error {
type Error = ();
fn try_from(old_error: OldError) -> result::Result<Error, ()> {
use OldError::*;
Ok(match old_error {
Overflow => Self::Overflow,
Unimplemented => Self::Unimplemented,
UntrustedReserveLocation => Self::UntrustedReserveLocation,
UntrustedTeleportLocation => Self::UntrustedTeleportLocation,
MultiLocationFull => Self::LocationFull,
MultiLocationNotInvertible => Self::LocationNotInvertible,
BadOrigin => Self::BadOrigin,
InvalidLocation => Self::InvalidLocation,
AssetNotFound => Self::AssetNotFound,
FailedToTransactAsset(s) => Self::FailedToTransactAsset(s),
NotWithdrawable => Self::NotWithdrawable,
LocationCannotHold => Self::LocationCannotHold,
ExceedsMaxMessageSize => Self::ExceedsMaxMessageSize,
DestinationUnsupported => Self::DestinationUnsupported,
Transport(s) => Self::Transport(s),
Unroutable => Self::Unroutable,
UnknownClaim => Self::UnknownClaim,
FailedToDecode => Self::FailedToDecode,
MaxWeightInvalid => Self::MaxWeightInvalid,
NotHoldingFees => Self::NotHoldingFees,
TooExpensive => Self::TooExpensive,
Trap(i) => Self::Trap(i),
_ => return Err(()),
})
}
}
impl From<SendError> for Error {
fn from(e: SendError) -> Self {
match e {
SendError::NotApplicable | SendError::Unroutable | SendError::MissingArgument =>
Error::Unroutable,
SendError::Transport(s) => Error::Transport(s),
SendError::DestinationUnsupported => Error::DestinationUnsupported,
SendError::ExceedsMaxMessageSize => Error::ExceedsMaxMessageSize,
SendError::Fees => Error::FeesNotMet,
}
}
}
pub type Result = result::Result<(), Error>;
/// Outcome of an XCM execution.
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
pub enum Outcome {
/// Execution completed successfully; given weight was used.
Complete(Weight),
/// Execution started, but did not complete successfully due to the given error; given weight was used.
Incomplete(Weight, Error),
/// Execution did not start due to the given error.
Error(Error),
}
impl Outcome {
pub fn ensure_complete(self) -> Result {
match self {
Outcome::Complete(_) => Ok(()),
Outcome::Incomplete(_, e) => Err(e),
Outcome::Error(e) => Err(e),
}
}
pub fn ensure_execution(self) -> result::Result<Weight, Error> {
match self {
Outcome::Complete(w) => Ok(w),
Outcome::Incomplete(w, _) => Ok(w),
Outcome::Error(e) => Err(e),
}
}
/// How much weight was used by the XCM execution attempt.
pub fn weight_used(&self) -> Weight {
match self {
Outcome::Complete(w) => *w,
Outcome::Incomplete(w, _) => *w,
Outcome::Error(_) => Weight::zero(),
}
}
}
pub trait PreparedMessage {
fn weight_of(&self) -> Weight;
}
/// Type of XCM message executor.
pub trait ExecuteXcm<Call> {
type Prepared: PreparedMessage;
fn prepare(message: Xcm<Call>) -> result::Result<Self::Prepared, Xcm<Call>>;
fn execute(
origin: impl Into<MultiLocation>,
pre: Self::Prepared,
hash: XcmHash,
weight_credit: Weight,
) -> Outcome;
/// Execute some XCM `message` with the message `hash` from `origin` using no more than `weight_limit` weight.
/// The weight limit is a basic hard-limit and the implementation may place further restrictions or requirements
/// on weight and other aspects.
fn execute_xcm(
origin: impl Into<MultiLocation>,
message: Xcm<Call>,
hash: XcmHash,
weight_limit: Weight,
) -> Outcome {
let origin = origin.into();
log::debug!(
target: "xcm::execute_xcm",
"origin: {:?}, message: {:?}, weight_limit: {:?}",
origin,
message,
weight_limit,
);
Self::execute_xcm_in_credit(origin, message, hash, weight_limit, Weight::zero())
}
/// Execute some XCM `message` with the message `hash` from `origin` using no more than `weight_limit` weight.
///
/// Some amount of `weight_credit` may be provided which, depending on the implementation, may allow
/// execution without associated payment.
fn execute_xcm_in_credit(
origin: impl Into<MultiLocation>,
message: Xcm<Call>,
hash: XcmHash,
weight_limit: Weight,
weight_credit: Weight,
) -> Outcome {
let pre = match Self::prepare(message) {
Ok(x) => x,
Err(_) => return Outcome::Error(Error::WeightNotComputable),
};
let xcm_weight = pre.weight_of();
if xcm_weight.any_gt(weight_limit) {
return Outcome::Error(Error::WeightLimitReached(xcm_weight))
}
Self::execute(origin, pre, hash, weight_credit)
}
/// Deduct some `fees` to the sovereign account of the given `location` and place them as per
/// the convention for fees.
fn charge_fees(location: impl Into<MultiLocation>, fees: MultiAssets) -> Result;
}
pub enum Weightless {}
impl PreparedMessage for Weightless {
fn weight_of(&self) -> Weight {
unreachable!()
}
}
impl<C> ExecuteXcm<C> for () {
type Prepared = Weightless;
fn prepare(message: Xcm<C>) -> result::Result<Self::Prepared, Xcm<C>> {
Err(message)
}
fn execute(_: impl Into<MultiLocation>, _: Self::Prepared, _: XcmHash, _: Weight) -> Outcome {
unreachable!()
}
fn charge_fees(_location: impl Into<MultiLocation>, _fees: MultiAssets) -> Result {
Err(Error::Unimplemented)
}
}
/// Error result value when attempting to send an XCM message.
#[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, scale_info::TypeInfo)]
pub enum SendError {
/// The message and destination combination was not recognized as being reachable.
///
/// This is not considered fatal: if there are alternative transport routes available, then
/// they may be attempted.
NotApplicable,
/// Destination is routable, but there is some issue with the transport mechanism. This is
/// considered fatal.
/// A human-readable explanation of the specific issue is provided.
Transport(#[codec(skip)] &'static str),
/// Destination is known to be unroutable. This is considered fatal.
Unroutable,
/// The given message cannot be translated into a format that the destination can be expected
/// to interpret.
DestinationUnsupported,
/// Message could not be sent due to its size exceeding the maximum allowed by the transport
/// layer.
ExceedsMaxMessageSize,
/// A needed argument is `None` when it should be `Some`.
MissingArgument,
/// Fees needed to be paid in order to send the message and they were unavailable.
Fees,
}
/// A hash type for identifying messages.
pub type XcmHash = [u8; 32];
/// Result value when attempting to send an XCM message.
pub type SendResult<T> = result::Result<(T, MultiAssets), SendError>;
pub trait Unwrappable {
type Inner;
fn none() -> Self;
fn some(i: Self::Inner) -> Self;
fn take(self) -> Option<Self::Inner>;
}
impl<T> Unwrappable for Option<T> {
type Inner = T;
fn none() -> Self {
None
}
fn some(i: Self::Inner) -> Self {
Some(i)
}
fn take(self) -> Option<Self::Inner> {
self
}
}
/// Utility for sending an XCM message to a given location.
///
/// These can be amalgamated in tuples to form sophisticated routing systems. In tuple format, each
/// router might return `NotApplicable` to pass the execution to the next sender item. Note that
/// each `NotApplicable` might alter the destination and the XCM message for to the next router.
///
/// # Example
/// ```rust
/// # use parity_scale_codec::Encode;
/// # use xcm::v3::{prelude::*, Weight};
/// # use xcm::VersionedXcm;
/// # use std::convert::Infallible;
///
/// /// A sender that only passes the message through and does nothing.
/// struct Sender1;
/// impl SendXcm for Sender1 {
/// type Ticket = Infallible;
/// fn validate(_: &mut Option<MultiLocation>, _: &mut Option<Xcm<()>>) -> SendResult<Infallible> {
/// Err(SendError::NotApplicable)
/// }
/// fn deliver(_: Infallible) -> Result<XcmHash, SendError> {
/// unreachable!()
/// }
/// }
///
/// /// A sender that accepts a message that has an X2 junction, otherwise stops the routing.
/// struct Sender2;
/// impl SendXcm for Sender2 {
/// type Ticket = ();
/// fn validate(destination: &mut Option<MultiLocation>, message: &mut Option<Xcm<()>>) -> SendResult<()> {
/// match destination.as_ref().ok_or(SendError::MissingArgument)? {
/// MultiLocation { parents: 0, interior: X2(j1, j2) } => Ok(((), MultiAssets::new())),
/// _ => Err(SendError::Unroutable),
/// }
/// }
/// fn deliver(_: ()) -> Result<XcmHash, SendError> {
/// Ok([0; 32])
/// }
/// }
///
/// /// A sender that accepts a message from a parent, passing through otherwise.
/// struct Sender3;
/// impl SendXcm for Sender3 {
/// type Ticket = ();
/// fn validate(destination: &mut Option<MultiLocation>, message: &mut Option<Xcm<()>>) -> SendResult<()> {
/// match destination.as_ref().ok_or(SendError::MissingArgument)? {
/// MultiLocation { parents: 1, interior: Here } => Ok(((), MultiAssets::new())),
/// _ => Err(SendError::NotApplicable),
/// }
/// }
/// fn deliver(_: ()) -> Result<XcmHash, SendError> {
/// Ok([0; 32])
/// }
/// }
///
/// // A call to send via XCM. We don't really care about this.
/// # fn main() {
/// let call: Vec<u8> = ().encode();
/// let message = Xcm(vec![Instruction::Transact {
/// origin_kind: OriginKind::Superuser,
/// require_weight_at_most: Weight::zero(),
/// call: call.into(),
/// }]);
/// let message_hash = message.using_encoded(sp_io::hashing::blake2_256);
///
/// // Sender2 will block this.
/// assert!(send_xcm::<(Sender1, Sender2, Sender3)>(Parent.into(), message.clone()).is_err());
///
/// // Sender3 will catch this.
/// assert!(send_xcm::<(Sender1, Sender3)>(Parent.into(), message.clone()).is_ok());
/// # }
/// ```
pub trait SendXcm {
/// Intermediate value which connects the two phaases of the send operation.
type Ticket;
/// Check whether the given `_message` is deliverable to the given `_destination` and if so
/// determine the cost which will be paid by this chain to do so, returning a `Validated` token
/// which can be used to enact delivery.
///
/// The `destination` and `message` must be `Some` (or else an error will be returned) and they
/// may only be consumed if the `Err` is not `NotApplicable`.
///
/// If it is not a destination which can be reached with this type but possibly could by others,
/// then this *MUST* return `NotApplicable`. Any other error will cause the tuple
/// implementation to exit early without trying other type fields.
fn validate(
destination: &mut Option<MultiLocation>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket>;
/// Actually carry out the delivery operation for a previously validated message sending.
fn deliver(ticket: Self::Ticket) -> result::Result<XcmHash, SendError>;
}
#[impl_trait_for_tuples::impl_for_tuples(30)]
impl SendXcm for Tuple {
for_tuples! { type Ticket = (#( Option<Tuple::Ticket> ),* ); }
fn validate(
destination: &mut Option<MultiLocation>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
let mut maybe_cost: Option<MultiAssets> = None;
let one_ticket: Self::Ticket = (for_tuples! { #(
if maybe_cost.is_some() {
None
} else {
match Tuple::validate(destination, message) {
Err(SendError::NotApplicable) => None,
Err(e) => { return Err(e) },
Ok((v, c)) => {
maybe_cost = Some(c);
Some(v)
},
}
}
),* });
if let Some(cost) = maybe_cost {
Ok((one_ticket, cost))
} else {
Err(SendError::NotApplicable)
}
}
fn deliver(one_ticket: Self::Ticket) -> result::Result<XcmHash, SendError> {
for_tuples!( #(
if let Some(validated) = one_ticket.Tuple {
return Tuple::deliver(validated);
}
)* );
Err(SendError::Unroutable)
}
}
/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
/// both in `Some` before passing them as as mutable references into `T::send_xcm`.
pub fn validate_send<T: SendXcm>(dest: MultiLocation, msg: Xcm<()>) -> SendResult<T::Ticket> {
T::validate(&mut Some(dest), &mut Some(msg))
}
/// Convenience function for using a `SendXcm` implementation. Just interprets the `dest` and wraps
/// both in `Some` before passing them as as mutable references into `T::send_xcm`.
///
/// Returns either `Ok` with the price of the delivery, or `Err` with the reason why the message
/// could not be sent.
///
/// Generally you'll want to validate and get the price first to ensure that the sender can pay it
/// before actually doing the delivery.
pub fn send_xcm<T: SendXcm>(
dest: MultiLocation,
msg: Xcm<()>,
) -> result::Result<(XcmHash, MultiAssets), SendError> {
let (ticket, price) = T::validate(&mut Some(dest), &mut Some(msg))?;
let hash = T::deliver(ticket)?;
Ok((hash, price))
}
+4
View File
@@ -6,6 +6,7 @@ edition.workspace = true
version.workspace = true
[dependencies]
impl-trait-for-tuples = "0.2.1"
parity-scale-codec = { version = "3.1.5", default-features = false, features = ["derive"] }
scale-info = { version = "2.1.2", default-features = false, features = ["derive"] }
xcm = { path = "..", default-features = false }
@@ -27,6 +28,8 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-xcm = { path = "../pallet-xcm" }
polkadot-runtime-parachains = { path = "../../runtime/parachains" }
assert_matches = "1.5.0"
[features]
default = ["std"]
runtime-benchmarks = [
@@ -44,6 +47,7 @@ std = [
"sp-io/std",
"sp-runtime/std",
"frame-support/std",
"frame-system/std",
"polkadot-parachain/std",
"pallet-transaction-payment/std",
]
@@ -0,0 +1,149 @@
// Copyright 2022 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/>.
//! Adapters to work with `frame_support::traits::tokens::fungibles` through XCM.
use frame_support::traits::Get;
use sp_std::{borrow::Borrow, marker::PhantomData, prelude::*, result};
use xcm::latest::prelude::*;
use xcm_executor::traits::{Convert, Error as MatchError, MatchesFungibles, MatchesNonFungibles};
/// Converter struct implementing `AssetIdConversion` converting a numeric asset ID (must be `TryFrom/TryInto<u128>`) into
/// a `GeneralIndex` junction, prefixed by some `MultiLocation` value. The `MultiLocation` value will typically be a
/// `PalletInstance` junction.
pub struct AsPrefixedGeneralIndex<Prefix, AssetId, ConvertAssetId>(
PhantomData<(Prefix, AssetId, ConvertAssetId)>,
);
impl<Prefix: Get<MultiLocation>, AssetId: Clone, ConvertAssetId: Convert<u128, AssetId>>
Convert<MultiLocation, AssetId> for AsPrefixedGeneralIndex<Prefix, AssetId, ConvertAssetId>
{
fn convert_ref(id: impl Borrow<MultiLocation>) -> result::Result<AssetId, ()> {
let prefix = Prefix::get();
let id = id.borrow();
if prefix.parent_count() != id.parent_count() ||
prefix
.interior()
.iter()
.enumerate()
.any(|(index, junction)| id.interior().at(index) != Some(junction))
{
return Err(())
}
match id.interior().at(prefix.interior().len()) {
Some(Junction::GeneralIndex(id)) => ConvertAssetId::convert_ref(id),
_ => Err(()),
}
}
fn reverse_ref(what: impl Borrow<AssetId>) -> result::Result<MultiLocation, ()> {
let mut location = Prefix::get();
let id = ConvertAssetId::reverse_ref(what)?;
location.push_interior(Junction::GeneralIndex(id)).map_err(|_| ())?;
Ok(location)
}
}
pub struct ConvertedConcreteId<AssetId, Balance, ConvertAssetId, ConvertOther>(
PhantomData<(AssetId, Balance, ConvertAssetId, ConvertOther)>,
);
impl<
AssetId: Clone,
Balance: Clone,
ConvertAssetId: Convert<MultiLocation, AssetId>,
ConvertBalance: Convert<u128, Balance>,
> MatchesFungibles<AssetId, Balance>
for ConvertedConcreteId<AssetId, Balance, ConvertAssetId, ConvertBalance>
{
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
let (amount, id) = match (&a.fun, &a.id) {
(Fungible(ref amount), Concrete(ref id)) => (amount, id),
_ => return Err(MatchError::AssetNotFound),
};
let what =
ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?;
let amount = ConvertBalance::convert_ref(amount)
.map_err(|_| MatchError::AmountToBalanceConversionFailed)?;
Ok((what, amount))
}
}
impl<
ClassId: Clone,
InstanceId: Clone,
ConvertClassId: Convert<MultiLocation, ClassId>,
ConvertInstanceId: Convert<AssetInstance, InstanceId>,
> MatchesNonFungibles<ClassId, InstanceId>
for ConvertedConcreteId<ClassId, InstanceId, ConvertClassId, ConvertInstanceId>
{
fn matches_nonfungibles(a: &MultiAsset) -> result::Result<(ClassId, InstanceId), MatchError> {
let (instance, class) = match (&a.fun, &a.id) {
(NonFungible(ref instance), Concrete(ref class)) => (instance, class),
_ => return Err(MatchError::AssetNotFound),
};
let what =
ConvertClassId::convert_ref(class).map_err(|_| MatchError::AssetIdConversionFailed)?;
let instance = ConvertInstanceId::convert_ref(instance)
.map_err(|_| MatchError::InstanceConversionFailed)?;
Ok((what, instance))
}
}
pub struct ConvertedAbstractId<AssetId, Balance, ConvertAssetId, ConvertOther>(
PhantomData<(AssetId, Balance, ConvertAssetId, ConvertOther)>,
);
impl<
AssetId: Clone,
Balance: Clone,
ConvertAssetId: Convert<[u8; 32], AssetId>,
ConvertBalance: Convert<u128, Balance>,
> MatchesFungibles<AssetId, Balance>
for ConvertedAbstractId<AssetId, Balance, ConvertAssetId, ConvertBalance>
{
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
let (amount, id) = match (&a.fun, &a.id) {
(Fungible(ref amount), Abstract(ref id)) => (amount, id),
_ => return Err(MatchError::AssetNotFound),
};
let what =
ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?;
let amount = ConvertBalance::convert_ref(amount)
.map_err(|_| MatchError::AmountToBalanceConversionFailed)?;
Ok((what, amount))
}
}
impl<
ClassId: Clone,
InstanceId: Clone,
ConvertClassId: Convert<[u8; 32], ClassId>,
ConvertInstanceId: Convert<AssetInstance, InstanceId>,
> MatchesNonFungibles<ClassId, InstanceId>
for ConvertedAbstractId<ClassId, InstanceId, ConvertClassId, ConvertInstanceId>
{
fn matches_nonfungibles(a: &MultiAsset) -> result::Result<(ClassId, InstanceId), MatchError> {
let (instance, class) = match (&a.fun, &a.id) {
(NonFungible(ref instance), Abstract(ref class)) => (instance, class),
_ => return Err(MatchError::AssetNotFound),
};
let what =
ConvertClassId::convert_ref(class).map_err(|_| MatchError::AssetIdConversionFailed)?;
let instance = ConvertInstanceId::convert_ref(instance)
.map_err(|_| MatchError::InstanceConversionFailed)?;
Ok((what, instance))
}
}
#[deprecated = "Use `ConvertedConcreteId` instead"]
pub type ConvertedConcreteAssetId<A, B, C, O> = ConvertedConcreteId<A, B, C, O>;
#[deprecated = "Use `ConvertedAbstractId` instead"]
pub type ConvertedAbstractAssetId<A, B, C, O> = ConvertedAbstractId<A, B, C, O>;
+167 -29
View File
@@ -16,11 +16,18 @@
//! Various implementations for `ShouldExecute`.
use frame_support::{ensure, traits::Contains};
use frame_support::{
ensure,
traits::{Contains, Get},
};
use polkadot_parachain::primitives::IsSystem;
use sp_std::{marker::PhantomData, result::Result};
use xcm::latest::{
Instruction::*, Junction, Junctions, MultiLocation, Weight, WeightLimit::*, Xcm,
Instruction::{self, *},
InteriorMultiLocation, Junction, Junctions,
Junctions::X1,
MultiLocation, Weight,
WeightLimit::*,
};
use xcm_executor::traits::{OnResponse, ShouldExecute};
@@ -33,16 +40,16 @@ pub struct TakeWeightCredit;
impl ShouldExecute for TakeWeightCredit {
fn should_execute<RuntimeCall>(
_origin: &MultiLocation,
_message: &mut Xcm<RuntimeCall>,
_instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
weight_credit: &mut Weight,
) -> Result<(), ()> {
log::trace!(
target: "xcm::barriers",
"TakeWeightCredit origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}",
_origin, _message, max_weight, weight_credit,
"TakeWeightCredit origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
_origin, _instructions, max_weight, weight_credit,
);
*weight_credit = weight_credit.checked_sub(max_weight).ok_or(())?;
*weight_credit = weight_credit.checked_sub(&max_weight).ok_or(())?;
Ok(())
}
}
@@ -56,17 +63,21 @@ pub struct AllowTopLevelPaidExecutionFrom<T>(PhantomData<T>);
impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFrom<T> {
fn should_execute<RuntimeCall>(
origin: &MultiLocation,
message: &mut Xcm<RuntimeCall>,
instructions: &mut [Instruction<RuntimeCall>],
max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ()> {
log::trace!(
target: "xcm::barriers",
"AllowTopLevelPaidExecutionFrom origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, message, max_weight, _weight_credit,
"AllowTopLevelPaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, instructions, max_weight, _weight_credit,
);
ensure!(T::contains(origin), ());
let mut iter = message.0.iter_mut();
// We will read up to 5 instructions. This allows up to 3 `ClearOrigin` instructions. We
// allow for more than one since anything beyond the first is a no-op and it's conceivable
// that composition of operations might result in more than one being appended.
let mut iter = instructions.iter_mut().take(5);
let i = iter.next().ok_or(())?;
match i {
ReceiveTeleportedAsset(..) |
@@ -80,8 +91,10 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFro
i = iter.next().ok_or(())?;
}
match i {
BuyExecution { weight_limit: Limited(ref mut weight), .. } if *weight >= max_weight => {
*weight = max_weight;
BuyExecution { weight_limit: Limited(ref mut weight), .. }
if weight.all_gte(max_weight) =>
{
*weight = weight.max(max_weight);
Ok(())
},
BuyExecution { ref mut weight_limit, .. } if weight_limit == &Unlimited => {
@@ -93,26 +106,150 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFro
}
}
/// Allows execution from any origin that is contained in `T` (i.e. `T::Contains(origin)`) without any payments.
/// Use only for executions from trusted origin groups.
/// A derivative barrier, which scans the first `MaxPrefixes` instructions for origin-alterers and
/// then evaluates `should_execute` of the `InnerBarrier` based on the remaining instructions and
/// the newly computed origin.
///
/// This effectively allows for the possibility of distinguishing an origin which is acting as a
/// router for its derivative locations (or as a bridge for a remote location) and an origin which
/// is actually trying to send a message for itself. In the former case, the message will be
/// prefixed with origin-mutating instructions.
///
/// Any barriers which should be interpreted based on the computed origin rather than the original
/// message origin should be subject to this. This is the case for most barriers since the
/// effective origin is generally more important than the routing origin. Any other barriers, and
/// especially those which should be interpreted only the routing origin should not be subject to
/// this.
///
/// E.g.
/// ```nocompile
/// type MyBarrier = (
/// TakeWeightCredit,
/// AllowTopLevelPaidExecutionFrom<DirectCustomerLocations>,
/// WithComputedOrigin<(
/// AllowTopLevelPaidExecutionFrom<DerivativeCustomerLocations>,
/// AllowUnpaidExecutionFrom<ParentLocation>,
/// AllowSubscriptionsFrom<AllowedSubscribers>,
/// AllowKnownQueryResponses<TheResponseHandler>,
/// )>,
/// );
/// ```
///
/// In the above example, `AllowUnpaidExecutionFrom` appears once underneath
/// `WithComputedOrigin`. This is in order to distinguish between messages which are notionally
/// from a derivative location of `ParentLocation` but that just happened to be sent via
/// `ParentLocaction` rather than messages that were sent by the parent.
///
/// Similarly `AllowTopLevelPaidExecutionFrom` appears twice: once inside of `WithComputedOrigin`
/// where we provide the list of origins which are derivative origins, and then secondly outside
/// of `WithComputedOrigin` where we provide the list of locations which are direct origins. It's
/// reasonable for these lists to be merged into one and that used both inside and out.
///
/// Finally, we see `AllowSubscriptionsFrom` and `AllowKnownQueryResponses` are both inside of
/// `WithComputedOrigin`. This means that if a message begins with origin-mutating instructions,
/// then it must be the finally computed origin which we accept subscriptions or expect a query
/// response from. For example, even if an origin appeared in the `AllowedSubscribers` list, we
/// would ignore this rule if it began with origin mutators and they changed the origin to something
/// which was not on the list.
pub struct WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>(
PhantomData<(InnerBarrier, LocalUniversal, MaxPrefixes)>,
);
impl<
InnerBarrier: ShouldExecute,
LocalUniversal: Get<InteriorMultiLocation>,
MaxPrefixes: Get<u32>,
> ShouldExecute for WithComputedOrigin<InnerBarrier, LocalUniversal, MaxPrefixes>
{
fn should_execute<Call>(
origin: &MultiLocation,
instructions: &mut [Instruction<Call>],
max_weight: Weight,
weight_credit: &mut Weight,
) -> Result<(), ()> {
log::trace!(
target: "xcm::barriers",
"WithComputedOrigin origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, instructions, max_weight, weight_credit,
);
let mut actual_origin = *origin;
let mut skipped = 0;
// NOTE: We do not check the validity of `UniversalOrigin` here, meaning that a malicious
// origin could place a `UniversalOrigin` in order to spoof some location which gets free
// execution. This technical could get it past the barrier condition, but the execution
// would instantly fail since the first instruction would cause an error with the
// invalid UniversalOrigin.
while skipped < MaxPrefixes::get() as usize {
match instructions.get(skipped) {
Some(UniversalOrigin(new_global)) => {
// Note the origin is *relative to local consensus*! So we need to escape local
// consensus with the `parents` before diving in into the `universal_location`.
actual_origin = X1(*new_global).relative_to(&LocalUniversal::get());
},
Some(DescendOrigin(j)) => {
actual_origin.append_with(*j).map_err(|_| ())?;
},
_ => break,
}
skipped += 1;
}
InnerBarrier::should_execute(
&actual_origin,
&mut instructions[skipped..],
max_weight,
weight_credit,
)
}
}
/// Allows execution from any origin that is contained in `T` (i.e. `T::Contains(origin)`).
///
/// Use only for executions from completely trusted origins, from which no unpermissioned messages
/// can be sent.
pub struct AllowUnpaidExecutionFrom<T>(PhantomData<T>);
impl<T: Contains<MultiLocation>> ShouldExecute for AllowUnpaidExecutionFrom<T> {
fn should_execute<RuntimeCall>(
origin: &MultiLocation,
_message: &mut Xcm<RuntimeCall>,
instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ()> {
log::trace!(
target: "xcm::barriers",
"AllowUnpaidExecutionFrom origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, _message, _max_weight, _weight_credit,
"AllowUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, instructions, _max_weight, _weight_credit,
);
ensure!(T::contains(origin), ());
Ok(())
}
}
/// Allows execution from any origin that is contained in `T` (i.e. `T::Contains(origin)`) if the
/// message begins with the instruction `UnpaidExecution`.
///
/// Use only for executions from trusted origin groups.
pub struct AllowExplicitUnpaidExecutionFrom<T>(PhantomData<T>);
impl<T: Contains<MultiLocation>> ShouldExecute for AllowExplicitUnpaidExecutionFrom<T> {
fn should_execute<Call>(
origin: &MultiLocation,
instructions: &mut [Instruction<Call>],
max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ()> {
log::trace!(
target: "xcm::barriers",
"AllowExplicitUnpaidExecutionFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, instructions, max_weight, _weight_credit,
);
ensure!(T::contains(origin), ());
match instructions.first() {
Some(UnpaidExecution { weight_limit: Limited(m), .. }) if m.all_gte(max_weight) =>
Ok(()),
Some(UnpaidExecution { weight_limit: Unlimited, .. }) => Ok(()),
_ => Err(()),
}
}
}
/// Allows a message only if it is from a system-level child parachain.
pub struct IsChildSystemParachain<ParaId>(PhantomData<ParaId>);
impl<ParaId: IsSystem + From<u32>> Contains<MultiLocation> for IsChildSystemParachain<ParaId> {
@@ -130,42 +267,43 @@ pub struct AllowKnownQueryResponses<ResponseHandler>(PhantomData<ResponseHandler
impl<ResponseHandler: OnResponse> ShouldExecute for AllowKnownQueryResponses<ResponseHandler> {
fn should_execute<RuntimeCall>(
origin: &MultiLocation,
message: &mut Xcm<RuntimeCall>,
instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ()> {
log::trace!(
target: "xcm::barriers",
"AllowKnownQueryResponses origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, message, _max_weight, _weight_credit,
"AllowKnownQueryResponses origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, instructions, _max_weight, _weight_credit,
);
match message.0.first() {
Some(QueryResponse { query_id, .. })
if ResponseHandler::expecting_response(origin, *query_id) =>
ensure!(instructions.len() == 1, ());
match instructions.first() {
Some(QueryResponse { query_id, querier, .. })
if ResponseHandler::expecting_response(origin, *query_id, querier.as_ref()) =>
Ok(()),
_ => Err(()),
}
}
}
/// Allows execution from `origin` if it is just a straight `SubscribeVerison` or
/// Allows execution from `origin` if it is just a straight `SubscribeVersion` or
/// `UnsubscribeVersion` instruction.
pub struct AllowSubscriptionsFrom<T>(PhantomData<T>);
impl<T: Contains<MultiLocation>> ShouldExecute for AllowSubscriptionsFrom<T> {
fn should_execute<RuntimeCall>(
origin: &MultiLocation,
message: &mut Xcm<RuntimeCall>,
instructions: &mut [Instruction<RuntimeCall>],
_max_weight: Weight,
_weight_credit: &mut Weight,
) -> Result<(), ()> {
log::trace!(
target: "xcm::barriers",
"AllowSubscriptionsFrom origin: {:?}, message: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, message, _max_weight, _weight_credit,
"AllowSubscriptionsFrom origin: {:?}, instructions: {:?}, max_weight: {:?}, weight_credit: {:?}",
origin, instructions, _max_weight, _weight_credit,
);
ensure!(T::contains(origin), ());
match (message.0.len(), message.0.first()) {
(1, Some(SubscribeVersion { .. })) | (1, Some(UnsubscribeVersion)) => Ok(()),
match instructions {
&mut [SubscribeVersion { .. } | UnsubscribeVersion] => Ok(()),
_ => Err(()),
}
}
@@ -16,10 +16,11 @@
//! Adapters to work with `frame_support::traits::Currency` through XCM.
use super::MintLocation;
use frame_support::traits::{ExistenceRequirement::AllowDeath, Get, WithdrawReasons};
use sp_runtime::traits::CheckedSub;
use sp_std::{marker::PhantomData, result};
use xcm::latest::{Error as XcmError, MultiAsset, MultiLocation, Result};
use xcm::latest::{Error as XcmError, MultiAsset, MultiLocation, Result, XcmContext};
use xcm_executor::{
traits::{Convert, MatchesFungible, TransactAsset},
Assets,
@@ -67,10 +68,13 @@ impl From<Error> for XcmError {
/// /// messages from the parent (relay chain).
/// pub type LocationConverter = (ParentIsPreset<AccountId>);
///
/// /// Just a dummy implementation of `Currency`. Normally this would be `Balances`.
/// pub type CurrencyImpl = ();
///
/// /// Final currency adapter. This can be used in `xcm::Config` to specify how asset related transactions happen.
/// pub type AssetTransactor = CurrencyAdapter<
/// // Use this balance type:
/// u128,
/// // Use this `Currency` impl instance:
/// CurrencyImpl,
/// // The matcher: use the currency when the asset is a concrete asset in our relay chain.
/// IsConcrete<RelayChain>,
/// // The local converter: default account of the parent relay chain.
@@ -86,67 +90,108 @@ pub struct CurrencyAdapter<Currency, Matcher, AccountIdConverter, AccountId, Che
);
impl<
Currency: frame_support::traits::Currency<AccountId>,
Matcher: MatchesFungible<Currency::Balance>,
AccountIdConverter: Convert<MultiLocation, AccountId>,
Currency: frame_support::traits::Currency<AccountId>,
AccountId: Clone, // can't get away without it since Currency is generic over it.
CheckedAccount: Get<Option<AccountId>>,
CheckedAccount: Get<Option<(AccountId, MintLocation)>>,
> CurrencyAdapter<Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount>
{
fn can_accrue_checked(_checked_account: AccountId, _amount: Currency::Balance) -> Result {
Ok(())
}
fn can_reduce_checked(checked_account: AccountId, amount: Currency::Balance) -> Result {
let new_balance = Currency::free_balance(&checked_account)
.checked_sub(&amount)
.ok_or(XcmError::NotWithdrawable)?;
Currency::ensure_can_withdraw(
&checked_account,
amount,
WithdrawReasons::TRANSFER,
new_balance,
)
.map_err(|_| XcmError::NotWithdrawable)
}
fn accrue_checked(checked_account: AccountId, amount: Currency::Balance) {
Currency::deposit_creating(&checked_account, amount);
Currency::deactivate(amount);
}
fn reduce_checked(checked_account: AccountId, amount: Currency::Balance) {
let ok =
Currency::withdraw(&checked_account, amount, WithdrawReasons::TRANSFER, AllowDeath)
.is_ok();
if ok {
Currency::reactivate(amount);
} else {
frame_support::defensive!(
"`can_check_in` must have returned `true` immediately prior; qed"
);
}
}
}
impl<
Currency: frame_support::traits::Currency<AccountId>,
Matcher: MatchesFungible<Currency::Balance>,
AccountIdConverter: Convert<MultiLocation, AccountId>,
AccountId: Clone, // can't get away without it since Currency is generic over it.
CheckedAccount: Get<Option<(AccountId, MintLocation)>>,
> TransactAsset
for CurrencyAdapter<Currency, Matcher, AccountIdConverter, AccountId, CheckedAccount>
{
fn can_check_in(_origin: &MultiLocation, what: &MultiAsset) -> Result {
fn can_check_in(_origin: &MultiLocation, what: &MultiAsset, _context: &XcmContext) -> Result {
log::trace!(target: "xcm::currency_adapter", "can_check_in origin: {:?}, what: {:?}", _origin, what);
// Check we handle this asset.
let amount: Currency::Balance =
Matcher::matches_fungible(what).ok_or(Error::AssetNotFound)?;
if let Some(checked_account) = CheckedAccount::get() {
let new_balance = Currency::free_balance(&checked_account)
.checked_sub(&amount)
.ok_or(XcmError::NotWithdrawable)?;
Currency::ensure_can_withdraw(
&checked_account,
amount,
WithdrawReasons::TRANSFER,
new_balance,
)
.map_err(|_| XcmError::NotWithdrawable)?;
match CheckedAccount::get() {
Some((checked_account, MintLocation::Local)) =>
Self::can_reduce_checked(checked_account, amount),
Some((checked_account, MintLocation::NonLocal)) =>
Self::can_accrue_checked(checked_account, amount),
None => Ok(()),
}
Ok(())
}
fn check_in(_origin: &MultiLocation, what: &MultiAsset) {
fn check_in(_origin: &MultiLocation, what: &MultiAsset, _context: &XcmContext) {
log::trace!(target: "xcm::currency_adapter", "check_in origin: {:?}, what: {:?}", _origin, what);
if let Some(amount) = Matcher::matches_fungible(what) {
if let Some(checked_account) = CheckedAccount::get() {
let ok = Currency::withdraw(
&checked_account,
amount,
WithdrawReasons::TRANSFER,
AllowDeath,
)
.is_ok();
if ok {
Currency::reactivate(amount);
}
debug_assert!(
ok,
"`can_check_in` must have returned `true` immediately prior; qed"
);
match CheckedAccount::get() {
Some((checked_account, MintLocation::Local)) =>
Self::reduce_checked(checked_account, amount),
Some((checked_account, MintLocation::NonLocal)) =>
Self::accrue_checked(checked_account, amount),
None => (),
}
}
}
fn check_out(_dest: &MultiLocation, what: &MultiAsset) {
fn can_check_out(_dest: &MultiLocation, what: &MultiAsset, _context: &XcmContext) -> Result {
log::trace!(target: "xcm::currency_adapter", "check_out dest: {:?}, what: {:?}", _dest, what);
let amount = Matcher::matches_fungible(what).ok_or(Error::AssetNotFound)?;
match CheckedAccount::get() {
Some((checked_account, MintLocation::Local)) =>
Self::can_accrue_checked(checked_account, amount),
Some((checked_account, MintLocation::NonLocal)) =>
Self::can_reduce_checked(checked_account, amount),
None => Ok(()),
}
}
fn check_out(_dest: &MultiLocation, what: &MultiAsset, _context: &XcmContext) {
log::trace!(target: "xcm::currency_adapter", "check_out dest: {:?}, what: {:?}", _dest, what);
if let Some(amount) = Matcher::matches_fungible(what) {
if let Some(checked_account) = CheckedAccount::get() {
Currency::deposit_creating(&checked_account, amount);
Currency::deactivate(amount);
match CheckedAccount::get() {
Some((checked_account, MintLocation::Local)) =>
Self::accrue_checked(checked_account, amount),
Some((checked_account, MintLocation::NonLocal)) =>
Self::reduce_checked(checked_account, amount),
None => (),
}
}
}
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result {
fn deposit_asset(what: &MultiAsset, who: &MultiLocation, _context: &XcmContext) -> Result {
log::trace!(target: "xcm::currency_adapter", "deposit_asset what: {:?}, who: {:?}", what, who);
// Check we handle this asset.
let amount = Matcher::matches_fungible(&what).ok_or(Error::AssetNotFound)?;
@@ -156,7 +201,11 @@ impl<
Ok(())
}
fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> result::Result<Assets, XcmError> {
fn withdraw_asset(
what: &MultiAsset,
who: &MultiLocation,
_maybe_context: Option<&XcmContext>,
) -> result::Result<Assets, XcmError> {
log::trace!(target: "xcm::currency_adapter", "withdraw_asset what: {:?}, who: {:?}", what, who);
// Check we handle this asset.
let amount = Matcher::matches_fungible(what).ok_or(Error::AssetNotFound)?;
@@ -171,6 +220,7 @@ impl<
asset: &MultiAsset,
from: &MultiLocation,
to: &MultiLocation,
_context: &XcmContext,
) -> result::Result<Assets, XcmError> {
log::trace!(target: "xcm::currency_adapter", "internal_transfer_asset asset: {:?}, from: {:?}, to: {:?}", asset, from, to);
let amount = Matcher::matches_fungible(asset).ok_or(Error::AssetNotFound)?;
@@ -14,28 +14,29 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Various implementations of `FilterAssetLocation`.
//! Various implementations of `ContainsPair<MultiAsset, MultiLocation>`.
use frame_support::traits::Get;
use frame_support::traits::{ContainsPair, Get};
use sp_std::marker::PhantomData;
use xcm::latest::{AssetId::Concrete, MultiAsset, MultiAssetFilter, MultiLocation};
use xcm_executor::traits::FilterAssetLocation;
/// Accepts an asset iff it is a native asset.
pub struct NativeAsset;
impl FilterAssetLocation for NativeAsset {
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
log::trace!(target: "xcm::filter_asset_location", "NativeAsset asset: {:?}, origin: {:?}", asset, origin);
impl ContainsPair<MultiAsset, MultiLocation> for NativeAsset {
fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool {
log::trace!(target: "xcm::contains", "NativeAsset asset: {:?}, origin: {:?}", asset, origin);
matches!(asset.id, Concrete(ref id) if id == origin)
}
}
/// Accepts an asset if it is contained in the given `T`'s `Get` implementation.
pub struct Case<T>(PhantomData<T>);
impl<T: Get<(MultiAssetFilter, MultiLocation)>> FilterAssetLocation for Case<T> {
fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
log::trace!(target: "xcm::filter_asset_location", "Case asset: {:?}, origin: {:?}", asset, origin);
impl<T: Get<(MultiAssetFilter, MultiLocation)>> ContainsPair<MultiAsset, MultiLocation>
for Case<T>
{
fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool {
log::trace!(target: "xcm::contains", "Case asset: {:?}, origin: {:?}", asset, origin);
let (a, o) = T::get();
a.contains(asset) && &o == origin
a.matches(asset) && &o == origin
}
}
+190 -123
View File
@@ -17,97 +17,11 @@
//! Adapters to work with `frame_support::traits::tokens::fungibles` through XCM.
use frame_support::traits::{tokens::fungibles, Contains, Get};
use sp_std::{borrow::Borrow, marker::PhantomData, prelude::*, result};
use xcm::latest::{
AssetId::{Abstract, Concrete},
Error as XcmError,
Fungibility::Fungible,
Junction, MultiAsset, MultiLocation, Result,
};
use sp_std::{marker::PhantomData, prelude::*, result};
use xcm::latest::prelude::*;
use xcm_executor::traits::{Convert, Error as MatchError, MatchesFungibles, TransactAsset};
/// Converter struct implementing `AssetIdConversion` converting a numeric asset ID (must be `TryFrom/TryInto<u128>`) into
/// a `GeneralIndex` junction, prefixed by some `MultiLocation` value. The `MultiLocation` value will typically be a
/// `PalletInstance` junction.
pub struct AsPrefixedGeneralIndex<Prefix, AssetId, ConvertAssetId>(
PhantomData<(Prefix, AssetId, ConvertAssetId)>,
);
impl<Prefix: Get<MultiLocation>, AssetId: Clone, ConvertAssetId: Convert<u128, AssetId>>
Convert<MultiLocation, AssetId> for AsPrefixedGeneralIndex<Prefix, AssetId, ConvertAssetId>
{
fn convert_ref(id: impl Borrow<MultiLocation>) -> result::Result<AssetId, ()> {
let prefix = Prefix::get();
let id = id.borrow();
if prefix.parent_count() != id.parent_count() ||
prefix
.interior()
.iter()
.enumerate()
.any(|(index, junction)| id.interior().at(index) != Some(junction))
{
return Err(())
}
match id.interior().at(prefix.interior().len()) {
Some(Junction::GeneralIndex(id)) => ConvertAssetId::convert_ref(id),
_ => Err(()),
}
}
fn reverse_ref(what: impl Borrow<AssetId>) -> result::Result<MultiLocation, ()> {
let mut location = Prefix::get();
let id = ConvertAssetId::reverse_ref(what)?;
location.push_interior(Junction::GeneralIndex(id)).map_err(|_| ())?;
Ok(location)
}
}
pub struct ConvertedConcreteAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>(
PhantomData<(AssetId, Balance, ConvertAssetId, ConvertBalance)>,
);
impl<
AssetId: Clone,
Balance: Clone,
ConvertAssetId: Convert<MultiLocation, AssetId>,
ConvertBalance: Convert<u128, Balance>,
> MatchesFungibles<AssetId, Balance>
for ConvertedConcreteAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>
{
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
let (amount, id) = match (&a.fun, &a.id) {
(Fungible(ref amount), Concrete(ref id)) => (amount, id),
_ => return Err(MatchError::AssetNotFound),
};
let what =
ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?;
let amount = ConvertBalance::convert_ref(amount)
.map_err(|_| MatchError::AmountToBalanceConversionFailed)?;
Ok((what, amount))
}
}
pub struct ConvertedAbstractAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>(
PhantomData<(AssetId, Balance, ConvertAssetId, ConvertBalance)>,
);
impl<
AssetId: Clone,
Balance: Clone,
ConvertAssetId: Convert<Vec<u8>, AssetId>,
ConvertBalance: Convert<u128, Balance>,
> MatchesFungibles<AssetId, Balance>
for ConvertedAbstractAssetId<AssetId, Balance, ConvertAssetId, ConvertBalance>
{
fn matches_fungibles(a: &MultiAsset) -> result::Result<(AssetId, Balance), MatchError> {
let (amount, id) = match (&a.fun, &a.id) {
(Fungible(ref amount), Abstract(ref id)) => (amount, id),
_ => return Err(MatchError::AssetNotFound),
};
let what =
ConvertAssetId::convert_ref(id).map_err(|_| MatchError::AssetIdConversionFailed)?;
let amount = ConvertBalance::convert_ref(amount)
.map_err(|_| MatchError::AmountToBalanceConversionFailed)?;
Ok((what, amount))
}
}
/// `TransactAsset` implementation to convert a `fungibles` implementation to become usable in XCM.
pub struct FungiblesTransferAdapter<Assets, Matcher, AccountIdConverter, AccountId>(
PhantomData<(Assets, Matcher, AccountIdConverter, AccountId)>,
);
@@ -122,6 +36,7 @@ impl<
what: &MultiAsset,
from: &MultiLocation,
to: &MultiLocation,
_context: &XcmContext,
) -> result::Result<xcm_executor::Assets, XcmError> {
log::trace!(
target: "xcm::fungibles_adapter",
@@ -140,6 +55,83 @@ impl<
}
}
/// The location which is allowed to mint a particular asset.
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum MintLocation {
/// This chain is allowed to mint the asset. When we track teleports of the asset we ensure that
/// no more of the asset returns back to the chain than has been sent out.
Local,
/// This chain is not allowed to mint the asset. When we track teleports of the asset we ensure
/// that no more of the asset is sent out from the chain than has been previously received.
NonLocal,
}
/// Simple trait to indicate whether an asset is subject to having its teleportation into and out of
/// this chain recorded and if so in what `MintLocation`.
///
/// The overall purpose of asset-checking is to ensure either no more assets are teleported into a
/// chain than the outstanding balance of assets which were previously teleported out (as in the
/// case of locally-minted assets); or that no more assets are teleported out of a chain than the
/// outstanding balance of assets which have previously been teleported in (as in the case of chains
/// where the `asset` is not minted locally).
pub trait AssetChecking<AssetId> {
/// Return the teleportation asset-checking policy for the given `asset`. `None` implies no
/// checking. Otherwise the policy detailed by the inner `MintLocation` should be respected by
/// teleportation.
fn asset_checking(asset: &AssetId) -> Option<MintLocation>;
}
/// Implementation of `AssetChecking` which subjects no assets to having their teleportations
/// recorded.
pub struct NoChecking;
impl<AssetId> AssetChecking<AssetId> for NoChecking {
fn asset_checking(_: &AssetId) -> Option<MintLocation> {
None
}
}
/// Implementation of `AssetChecking` which subjects a given set of assets `T` to having their
/// teleportations recorded with a `MintLocation::Local`.
pub struct LocalMint<T>(sp_std::marker::PhantomData<T>);
impl<AssetId, T: Contains<AssetId>> AssetChecking<AssetId> for LocalMint<T> {
fn asset_checking(asset: &AssetId) -> Option<MintLocation> {
match T::contains(asset) {
true => Some(MintLocation::Local),
false => None,
}
}
}
/// Implementation of `AssetChecking` which subjects a given set of assets `T` to having their
/// teleportations recorded with a `MintLocation::NonLocal`.
pub struct NonLocalMint<T>(sp_std::marker::PhantomData<T>);
impl<AssetId, T: Contains<AssetId>> AssetChecking<AssetId> for NonLocalMint<T> {
fn asset_checking(asset: &AssetId) -> Option<MintLocation> {
match T::contains(asset) {
true => Some(MintLocation::NonLocal),
false => None,
}
}
}
/// Implementation of `AssetChecking` which subjects a given set of assets `L` to having their
/// teleportations recorded with a `MintLocation::Local` and a second set of assets `R` to having
/// their teleportations recorded with a `MintLocation::NonLocal`.
pub struct DualMint<L, R>(sp_std::marker::PhantomData<(L, R)>);
impl<AssetId, L: Contains<AssetId>, R: Contains<AssetId>> AssetChecking<AssetId>
for DualMint<L, R>
{
fn asset_checking(asset: &AssetId) -> Option<MintLocation> {
if L::contains(asset) {
Some(MintLocation::Local)
} else if R::contains(asset) {
Some(MintLocation::NonLocal)
} else {
None
}
}
}
pub struct FungiblesMutateAdapter<
Assets,
Matcher,
@@ -148,12 +140,48 @@ pub struct FungiblesMutateAdapter<
CheckAsset,
CheckingAccount,
>(PhantomData<(Assets, Matcher, AccountIdConverter, AccountId, CheckAsset, CheckingAccount)>);
impl<
Assets: fungibles::Mutate<AccountId>,
Matcher: MatchesFungibles<Assets::AssetId, Assets::Balance>,
AccountIdConverter: Convert<MultiLocation, AccountId>,
AccountId: Clone, // can't get away without it since Currency is generic over it.
CheckAsset: Contains<Assets::AssetId>,
CheckAsset: AssetChecking<Assets::AssetId>,
CheckingAccount: Get<AccountId>,
>
FungiblesMutateAdapter<Assets, Matcher, AccountIdConverter, AccountId, CheckAsset, CheckingAccount>
{
fn can_accrue_checked(asset_id: Assets::AssetId, amount: Assets::Balance) -> XcmResult {
let checking_account = CheckingAccount::get();
Assets::can_deposit(asset_id, &checking_account, amount, true)
.into_result()
.map_err(|_| XcmError::NotDepositable)
}
fn can_reduce_checked(asset_id: Assets::AssetId, amount: Assets::Balance) -> XcmResult {
let checking_account = CheckingAccount::get();
Assets::can_withdraw(asset_id, &checking_account, amount)
.into_result()
.map_err(|_| XcmError::NotWithdrawable)
.map(|_| ())
}
fn accrue_checked(asset_id: Assets::AssetId, amount: Assets::Balance) {
let checking_account = CheckingAccount::get();
let ok = Assets::mint_into(asset_id, &checking_account, amount).is_ok();
debug_assert!(ok, "`can_accrue_checked` must have returned `true` immediately prior; qed");
}
fn reduce_checked(asset_id: Assets::AssetId, amount: Assets::Balance) {
let checking_account = CheckingAccount::get();
let ok = Assets::burn_from(asset_id, &checking_account, amount).is_ok();
debug_assert!(ok, "`can_reduce_checked` must have returned `true` immediately prior; qed");
}
}
impl<
Assets: fungibles::Mutate<AccountId>,
Matcher: MatchesFungibles<Assets::AssetId, Assets::Balance>,
AccountIdConverter: Convert<MultiLocation, AccountId>,
AccountId: Clone, // can't get away without it since Currency is generic over it.
CheckAsset: AssetChecking<Assets::AssetId>,
CheckingAccount: Get<AccountId>,
> TransactAsset
for FungiblesMutateAdapter<
@@ -165,7 +193,11 @@ impl<
CheckingAccount,
>
{
fn can_check_in(_origin: &MultiLocation, what: &MultiAsset) -> Result {
fn can_check_in(
_origin: &MultiLocation,
what: &MultiAsset,
_context: &XcmContext,
) -> XcmResult {
log::trace!(
target: "xcm::fungibles_adapter",
"can_check_in origin: {:?}, what: {:?}",
@@ -173,50 +205,71 @@ impl<
);
// Check we handle this asset.
let (asset_id, amount) = Matcher::matches_fungibles(what)?;
if CheckAsset::contains(&asset_id) {
// This is an asset whose teleports we track.
let checking_account = CheckingAccount::get();
Assets::can_withdraw(asset_id, &checking_account, amount)
.into_result()
.map_err(|_| XcmError::NotWithdrawable)?;
match CheckAsset::asset_checking(&asset_id) {
// We track this asset's teleports to ensure no more come in than have gone out.
Some(MintLocation::Local) => Self::can_reduce_checked(asset_id, amount),
// We track this asset's teleports to ensure no more go out than have come in.
Some(MintLocation::NonLocal) => Self::can_accrue_checked(asset_id, amount),
_ => Ok(()),
}
Ok(())
}
fn check_in(_origin: &MultiLocation, what: &MultiAsset) {
fn check_in(_origin: &MultiLocation, what: &MultiAsset, _context: &XcmContext) {
log::trace!(
target: "xcm::fungibles_adapter",
"check_in origin: {:?}, what: {:?}",
_origin, what
);
if let Ok((asset_id, amount)) = Matcher::matches_fungibles(what) {
if CheckAsset::contains(&asset_id) {
let checking_account = CheckingAccount::get();
let ok = Assets::burn_from(asset_id, &checking_account, amount).is_ok();
debug_assert!(
ok,
"`can_check_in` must have returned `true` immediately prior; qed"
);
match CheckAsset::asset_checking(&asset_id) {
// We track this asset's teleports to ensure no more come in than have gone out.
Some(MintLocation::Local) => Self::reduce_checked(asset_id, amount),
// We track this asset's teleports to ensure no more go out than have come in.
Some(MintLocation::NonLocal) => Self::accrue_checked(asset_id, amount),
_ => (),
}
}
}
fn check_out(_dest: &MultiLocation, what: &MultiAsset) {
fn can_check_out(
_origin: &MultiLocation,
what: &MultiAsset,
_context: &XcmContext,
) -> XcmResult {
log::trace!(
target: "xcm::fungibles_adapter",
"can_check_in origin: {:?}, what: {:?}",
_origin, what
);
// Check we handle this asset.
let (asset_id, amount) = Matcher::matches_fungibles(what)?;
match CheckAsset::asset_checking(&asset_id) {
// We track this asset's teleports to ensure no more come in than have gone out.
Some(MintLocation::Local) => Self::can_accrue_checked(asset_id, amount),
// We track this asset's teleports to ensure no more go out than have come in.
Some(MintLocation::NonLocal) => Self::can_reduce_checked(asset_id, amount),
_ => Ok(()),
}
}
fn check_out(_dest: &MultiLocation, what: &MultiAsset, _context: &XcmContext) {
log::trace!(
target: "xcm::fungibles_adapter",
"check_out dest: {:?}, what: {:?}",
_dest, what
);
if let Ok((asset_id, amount)) = Matcher::matches_fungibles(what) {
if CheckAsset::contains(&asset_id) {
let checking_account = CheckingAccount::get();
let ok = Assets::mint_into(asset_id, &checking_account, amount).is_ok();
debug_assert!(ok, "`mint_into` cannot generally fail; qed");
match CheckAsset::asset_checking(&asset_id) {
// We track this asset's teleports to ensure no more come in than have gone out.
Some(MintLocation::Local) => Self::accrue_checked(asset_id, amount),
// We track this asset's teleports to ensure no more go out than have come in.
Some(MintLocation::NonLocal) => Self::reduce_checked(asset_id, amount),
_ => (),
}
}
}
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result {
fn deposit_asset(what: &MultiAsset, who: &MultiLocation, _context: &XcmContext) -> XcmResult {
log::trace!(
target: "xcm::fungibles_adapter",
"deposit_asset what: {:?}, who: {:?}",
@@ -233,6 +286,7 @@ impl<
fn withdraw_asset(
what: &MultiAsset,
who: &MultiLocation,
_maybe_context: Option<&XcmContext>,
) -> result::Result<xcm_executor::Assets, XcmError> {
log::trace!(
target: "xcm::fungibles_adapter",
@@ -262,12 +316,12 @@ impl<
Matcher: MatchesFungibles<Assets::AssetId, Assets::Balance>,
AccountIdConverter: Convert<MultiLocation, AccountId>,
AccountId: Clone, // can't get away without it since Currency is generic over it.
CheckAsset: Contains<Assets::AssetId>,
CheckAsset: AssetChecking<Assets::AssetId>,
CheckingAccount: Get<AccountId>,
> TransactAsset
for FungiblesAdapter<Assets, Matcher, AccountIdConverter, AccountId, CheckAsset, CheckingAccount>
{
fn can_check_in(origin: &MultiLocation, what: &MultiAsset) -> Result {
fn can_check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult {
FungiblesMutateAdapter::<
Assets,
Matcher,
@@ -275,10 +329,10 @@ impl<
AccountId,
CheckAsset,
CheckingAccount,
>::can_check_in(origin, what)
>::can_check_in(origin, what, context)
}
fn check_in(origin: &MultiLocation, what: &MultiAsset) {
fn check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) {
FungiblesMutateAdapter::<
Assets,
Matcher,
@@ -286,10 +340,10 @@ impl<
AccountId,
CheckAsset,
CheckingAccount,
>::check_in(origin, what)
>::check_in(origin, what, context)
}
fn check_out(dest: &MultiLocation, what: &MultiAsset) {
fn can_check_out(dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult {
FungiblesMutateAdapter::<
Assets,
Matcher,
@@ -297,10 +351,10 @@ impl<
AccountId,
CheckAsset,
CheckingAccount,
>::check_out(dest, what)
>::can_check_out(dest, what, context)
}
fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result {
fn check_out(dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) {
FungiblesMutateAdapter::<
Assets,
Matcher,
@@ -308,12 +362,24 @@ impl<
AccountId,
CheckAsset,
CheckingAccount,
>::deposit_asset(what, who)
>::check_out(dest, what, context)
}
fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult {
FungiblesMutateAdapter::<
Assets,
Matcher,
AccountIdConverter,
AccountId,
CheckAsset,
CheckingAccount,
>::deposit_asset(what, who, context)
}
fn withdraw_asset(
what: &MultiAsset,
who: &MultiLocation,
maybe_context: Option<&XcmContext>,
) -> result::Result<xcm_executor::Assets, XcmError> {
FungiblesMutateAdapter::<
Assets,
@@ -322,16 +388,17 @@ impl<
AccountId,
CheckAsset,
CheckingAccount,
>::withdraw_asset(what, who)
>::withdraw_asset(what, who, maybe_context)
}
fn internal_transfer_asset(
what: &MultiAsset,
from: &MultiLocation,
to: &MultiLocation,
context: &XcmContext,
) -> result::Result<xcm_executor::Assets, XcmError> {
FungiblesTransferAdapter::<Assets, Matcher, AccountIdConverter, AccountId>::internal_transfer_asset(
what, from, to,
what, from, to, context
)
}
}
+25 -11
View File
@@ -20,8 +20,6 @@
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
@@ -31,7 +29,7 @@ pub mod test_utils;
mod location_conversion;
pub use location_conversion::{
Account32Hash, AccountId32Aliases, AccountKey20Aliases, ChildParachainConvertsVia,
LocationInverter, ParentIsPreset, SiblingParachainConvertsVia,
ParentIsPreset, SiblingParachainConvertsVia,
};
mod origin_conversion;
@@ -42,10 +40,16 @@ pub use origin_conversion::{
SignedToAccountId32, SovereignSignedViaLocation,
};
mod asset_conversion;
pub use asset_conversion::{AsPrefixedGeneralIndex, ConvertedAbstractId, ConvertedConcreteId};
#[allow(deprecated)]
pub use asset_conversion::{ConvertedAbstractAssetId, ConvertedConcreteAssetId};
mod barriers;
pub use barriers::{
AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom,
AllowUnpaidExecutionFrom, IsChildSystemParachain, TakeWeightCredit,
AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, IsChildSystemParachain,
TakeWeightCredit, WithComputedOrigin,
};
mod currency_adapter;
@@ -53,19 +57,29 @@ pub use currency_adapter::CurrencyAdapter;
mod fungibles_adapter;
pub use fungibles_adapter::{
AsPrefixedGeneralIndex, ConvertedAbstractAssetId, ConvertedConcreteAssetId, FungiblesAdapter,
FungiblesMutateAdapter, FungiblesTransferAdapter,
AssetChecking, DualMint, FungiblesAdapter, FungiblesMutateAdapter, FungiblesTransferAdapter,
LocalMint, MintLocation, NoChecking, NonLocalMint,
};
mod nonfungibles_adapter;
pub use nonfungibles_adapter::{
NonFungiblesAdapter, NonFungiblesMutateAdapter, NonFungiblesTransferAdapter,
};
mod weight;
#[allow(deprecated)]
pub use weight::FixedRateOfConcreteFungible;
pub use weight::{
FixedRateOfFungible, FixedWeightBounds, TakeRevenue, UsingComponents, WeightInfoBounds,
};
mod matches_fungible;
pub use matches_fungible::{IsAbstract, IsConcrete};
mod matches_token;
pub use matches_token::{IsAbstract, IsConcrete};
mod filter_asset_location;
pub use filter_asset_location::{Case, NativeAsset};
mod universal_exports;
pub use universal_exports::{
BridgeBlobDispatcher, BridgeMessage, DispatchBlob, DispatchBlobError, ExporterFor, HaulBlob,
HaulBlobExporter, NetworkExportTable, SovereignPaidRemoteExporter, UnpaidLocalExporter,
UnpaidRemoteExporter,
};
@@ -19,11 +19,11 @@ use parity_scale_codec::{Decode, Encode};
use sp_io::hashing::blake2_256;
use sp_runtime::traits::{AccountIdConversion, TrailingZeroInput};
use sp_std::{borrow::Borrow, marker::PhantomData};
use xcm::latest::{Junction::*, Junctions::*, MultiLocation, NetworkId, Parent};
use xcm_executor::traits::{Convert, InvertLocation};
use xcm::latest::prelude::*;
use xcm_executor::traits::Convert;
pub struct Account32Hash<Network, AccountId>(PhantomData<(Network, AccountId)>);
impl<Network: Get<NetworkId>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
impl<Network: Get<Option<NetworkId>>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
Convert<MultiLocation, AccountId> for Account32Hash<Network, AccountId>
{
fn convert_ref(location: impl Borrow<MultiLocation>) -> Result<AccountId, ()> {
@@ -107,15 +107,12 @@ impl<ParaId: From<u32> + Into<u32> + AccountIdConversion<AccountId>, AccountId:
/// Extracts the `AccountId32` from the passed `location` if the network matches.
pub struct AccountId32Aliases<Network, AccountId>(PhantomData<(Network, AccountId)>);
impl<Network: Get<NetworkId>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
impl<Network: Get<Option<NetworkId>>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone>
Convert<MultiLocation, AccountId> for AccountId32Aliases<Network, AccountId>
{
fn convert(location: MultiLocation) -> Result<AccountId, MultiLocation> {
let id = match location {
MultiLocation {
parents: 0,
interior: X1(AccountId32 { id, network: NetworkId::Any }),
} => id,
MultiLocation { parents: 0, interior: X1(AccountId32 { id, network: None }) } => id,
MultiLocation { parents: 0, interior: X1(AccountId32 { id, network }) }
if network == Network::get() =>
id,
@@ -130,15 +127,12 @@ impl<Network: Get<NetworkId>, AccountId: From<[u8; 32]> + Into<[u8; 32]> + Clone
}
pub struct AccountKey20Aliases<Network, AccountId>(PhantomData<(Network, AccountId)>);
impl<Network: Get<NetworkId>, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone>
impl<Network: Get<Option<NetworkId>>, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone>
Convert<MultiLocation, AccountId> for AccountKey20Aliases<Network, AccountId>
{
fn convert(location: MultiLocation) -> Result<AccountId, MultiLocation> {
let key = match location {
MultiLocation {
parents: 0,
interior: X1(AccountKey20 { key, network: NetworkId::Any }),
} => key,
MultiLocation { parents: 0, interior: X1(AccountKey20 { key, network: None }) } => key,
MultiLocation { parents: 0, interior: X1(AccountKey20 { key, network }) }
if network == Network::get() =>
key,
@@ -153,69 +147,19 @@ impl<Network: Get<NetworkId>, AccountId: From<[u8; 20]> + Into<[u8; 20]> + Clone
}
}
/// Simple location inverter; give it this location's ancestry and it'll figure out the inverted
/// location.
///
/// # Example
/// ## Network Topology
/// ```txt
/// v Source
/// Relay -> Para 1 -> Account20
/// -> Para 2 -> Account32
/// ^ Target
/// ```
/// ```rust
/// # use frame_support::parameter_types;
/// # use xcm::latest::{MultiLocation, Junction::*, Junctions::{self, *}, NetworkId::Any};
/// # use xcm_builder::LocationInverter;
/// # use xcm_executor::traits::InvertLocation;
/// # fn main() {
/// parameter_types!{
/// pub Ancestry: MultiLocation = X2(
/// Parachain(1),
/// AccountKey20 { network: Any, key: Default::default() },
/// ).into();
/// }
///
/// let input = MultiLocation::new(2, X2(Parachain(2), AccountId32 { network: Any, id: Default::default() }));
/// let inverted = LocationInverter::<Ancestry>::invert_location(&input);
/// assert_eq!(inverted, Ok(MultiLocation::new(
/// 2,
/// X2(Parachain(1), AccountKey20 { network: Any, key: Default::default() }),
/// )));
/// # }
/// ```
pub struct LocationInverter<Ancestry>(PhantomData<Ancestry>);
impl<Ancestry: Get<MultiLocation>> InvertLocation for LocationInverter<Ancestry> {
fn ancestry() -> MultiLocation {
Ancestry::get()
}
fn invert_location(location: &MultiLocation) -> Result<MultiLocation, ()> {
let mut ancestry = Ancestry::get();
let mut junctions = Here;
for _ in 0..location.parent_count() {
junctions = junctions
.pushed_with(ancestry.take_first_interior().unwrap_or(OnlyChild))
.map_err(|_| ())?;
}
let parents = location.interior().len() as u8;
Ok(MultiLocation::new(parents, junctions))
}
}
#[cfg(test)]
mod tests {
use super::*;
use frame_support::parameter_types;
use xcm::latest::{Junction, NetworkId::Any};
use xcm::latest::Junction;
fn account20() -> Junction {
AccountKey20 { network: Any, key: Default::default() }
AccountKey20 { network: None, key: Default::default() }
}
fn account32() -> Junction {
AccountId32 { network: Any, id: Default::default() }
AccountId32 { network: None, id: Default::default() }
}
// Network Topology
@@ -227,17 +171,17 @@ mod tests {
// Inputs and outputs written as file paths:
//
// input location (source to target): ../../../para_2/account32_default
// ancestry (root to source): para_1/account20_default/account20_default
// context (root to source): para_1/account20_default/account20_default
// =>
// output (target to source): ../../para_1/account20_default/account20_default
#[test]
fn inverter_works_in_tree() {
parameter_types! {
pub Ancestry: MultiLocation = X3(Parachain(1), account20(), account20()).into();
pub UniversalLocation: InteriorMultiLocation = X3(Parachain(1), account20(), account20());
}
let input = MultiLocation::new(3, X2(Parachain(2), account32()));
let inverted = LocationInverter::<Ancestry>::invert_location(&input).unwrap();
let inverted = UniversalLocation::get().invert_target(&input).unwrap();
assert_eq!(inverted, MultiLocation::new(2, X3(Parachain(1), account20(), account20())));
}
@@ -246,13 +190,13 @@ mod tests {
// Relay -> Para 1 -> SmartContract -> Account
// ^ Target
#[test]
fn inverter_uses_ancestry_as_inverted_location() {
fn inverter_uses_context_as_inverted_location() {
parameter_types! {
pub Ancestry: MultiLocation = X2(account20(), account20()).into();
pub UniversalLocation: InteriorMultiLocation = X2(account20(), account20());
}
let input = MultiLocation::grandparent();
let inverted = LocationInverter::<Ancestry>::invert_location(&input).unwrap();
let inverted = UniversalLocation::get().invert_target(&input).unwrap();
assert_eq!(inverted, X2(account20(), account20()).into());
}
@@ -261,24 +205,24 @@ mod tests {
// Relay -> Para 1 -> CollectivePallet -> Plurality
// ^ Target
#[test]
fn inverter_uses_only_child_on_missing_ancestry() {
fn inverter_uses_only_child_on_missing_context() {
parameter_types! {
pub Ancestry: MultiLocation = X1(PalletInstance(5)).into();
pub UniversalLocation: InteriorMultiLocation = PalletInstance(5).into();
}
let input = MultiLocation::grandparent();
let inverted = LocationInverter::<Ancestry>::invert_location(&input).unwrap();
assert_eq!(inverted, X2(PalletInstance(5), OnlyChild).into());
let inverted = UniversalLocation::get().invert_target(&input).unwrap();
assert_eq!(inverted, (OnlyChild, PalletInstance(5)).into());
}
#[test]
fn inverter_errors_when_location_is_too_large() {
parameter_types! {
pub Ancestry: MultiLocation = Here.into();
pub UniversalLocation: InteriorMultiLocation = Here;
}
let input = MultiLocation { parents: 99, interior: X1(Parachain(88)) };
let inverted = LocationInverter::<Ancestry>::invert_location(&input);
let inverted = UniversalLocation::get().invert_target(&input);
assert_eq!(inverted, Err(()));
}
}

Some files were not shown because too many files have changed in this diff Show More