pallet-xcm: enhance reserve_transfer_assets to support remote reserves (#1672)

## Motivation

`pallet-xcm` is the main user-facing interface for XCM functionality,
including assets manipulation functions like `teleportAssets()` and
`reserve_transfer_assets()` calls.

While `teleportAsset()` works both ways, `reserve_transfer_assets()`
works only for sending reserve-based assets to a remote destination and
beneficiary when the reserve is the _local chain_.

## Solution

This PR enhances `pallet_xcm::(limited_)reserve_withdraw_assets` to
support transfers when reserves are other chains.
This will allow complete, **bi-directional** reserve-based asset
transfers user stories using `pallet-xcm`.

Enables following scenarios:
- transferring assets with local reserve (was previously supported iff
asset used as fee also had local reserve - now it works in all cases),
- transferring assets with reserve on destination,
- transferring assets with reserve on remote/third-party chain (iff
assets and fees have same remote reserve),
- transferring assets with reserve different than the reserve of the
asset to be used as fees - meaning can be used to transfer random asset
with local/dest reserve while using DOT for fees on all involved chains,
even if DOT local/dest reserve doesn't match asset reserve,
- transferring assets with any type of local/dest reserve while using
fees which can be teleported between involved chains.

All of the above is done by pallet inner logic without the user having
to specify which scenario/reserves/teleports/etc. The correct scenario
and corresponding XCM programs are identified, and respectively, built
automatically based on runtime configuration of trusted teleporters and
trusted reserves.

#### Current limitations:
- while `fees` and "non-fee" `assets` CAN have different reserves (or
fees CAN be teleported), the remaining "non-fee" `assets` CANNOT, among
themselves, have different reserve locations (this is also implicitly
enforced by `MAX_ASSETS_FOR_TRANSFER=2`, but this can be safely
increased in the future).
- `fees` and "non-fee" `assets` CANNOT have **different remote**
reserves (this could also be supported in the future, but adds even more
complexity while possibly not being worth it - we'll see what the future
holds).

Fixes https://github.com/paritytech/polkadot-sdk/issues/1584
Fixes https://github.com/paritytech/polkadot-sdk/issues/2055

---------

Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
This commit is contained in:
Adrian Catangiu
2023-11-13 17:16:55 +02:00
committed by GitHub
parent 29654a4d71
commit 18257373b3
80 changed files with 3679 additions and 1466 deletions
Generated
+5
View File
@@ -901,9 +901,11 @@ dependencies = [
"pallet-asset-conversion",
"pallet-assets",
"pallet-balances",
"pallet-message-queue",
"pallet-xcm",
"parachains-common",
"parity-scale-codec",
"penpal-runtime",
"rococo-runtime",
"rococo-system-emulated-network",
"sp-runtime",
@@ -11183,6 +11185,7 @@ dependencies = [
"frame-support",
"frame-system",
"log",
"pallet-assets",
"pallet-balances",
"parity-scale-codec",
"polkadot-parachain-primitives",
@@ -11630,6 +11633,7 @@ dependencies = [
"frame-support",
"parachains-common",
"penpal-runtime",
"rococo-emulated-chain",
"serde_json",
"sp-core",
"sp-runtime",
@@ -18416,6 +18420,7 @@ dependencies = [
"impl-trait-for-tuples",
"log",
"parity-scale-codec",
"scale-info",
"sp-arithmetic",
"sp-core",
"sp-io",
@@ -150,11 +150,6 @@ pub type XcmRouter = WithUniqueTopic<(
XcmpQueue,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
@@ -180,8 +175,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -21,7 +21,7 @@ use frame_support::traits::OnInitialize;
// Cumulus
use emulated_integration_tests_common::{
impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain,
impl_assets_helpers_for_system_parachain, xcm_emulator::decl_test_parachains,
impl_assets_helpers_for_parachain, xcm_emulator::decl_test_parachains,
};
use rococo_emulated_chain::Rococo;
@@ -51,5 +51,5 @@ decl_test_parachains! {
// AssetHubRococo implementation
impl_accounts_helpers_for_parachain!(AssetHubRococo);
impl_assert_events_helpers_for_parachain!(AssetHubRococo);
impl_assets_helpers_for_system_parachain!(AssetHubRococo, Rococo);
impl_assert_events_helpers_for_parachain!(AssetHubRococo, false);
impl_assets_helpers_for_parachain!(AssetHubRococo, Rococo);
@@ -21,7 +21,7 @@ use frame_support::traits::OnInitialize;
// Cumulus
use emulated_integration_tests_common::{
impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain,
impl_assets_helpers_for_system_parachain, xcm_emulator::decl_test_parachains,
impl_assets_helpers_for_parachain, xcm_emulator::decl_test_parachains,
};
use westend_emulated_chain::Westend;
@@ -51,5 +51,5 @@ decl_test_parachains! {
// AssetHubWestend implementation
impl_accounts_helpers_for_parachain!(AssetHubWestend);
impl_assert_events_helpers_for_parachain!(AssetHubWestend);
impl_assets_helpers_for_system_parachain!(AssetHubWestend, Westend);
impl_assert_events_helpers_for_parachain!(AssetHubWestend, false);
impl_assets_helpers_for_parachain!(AssetHubWestend, Westend);
@@ -19,7 +19,7 @@ use frame_support::traits::OnInitialize;
// Cumulus
use emulated_integration_tests_common::{
impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain,
impl_assets_helpers_for_system_parachain, xcm_emulator::decl_test_parachains,
impl_assets_helpers_for_parachain, xcm_emulator::decl_test_parachains,
};
use wococo_emulated_chain::Wococo;
@@ -49,5 +49,5 @@ decl_test_parachains! {
// AssetHubWococo implementation
impl_accounts_helpers_for_parachain!(AssetHubWococo);
impl_assert_events_helpers_for_parachain!(AssetHubWococo);
impl_assets_helpers_for_system_parachain!(AssetHubWococo, Wococo);
impl_assert_events_helpers_for_parachain!(AssetHubWococo, false);
impl_assets_helpers_for_parachain!(AssetHubWococo, Wococo);
@@ -46,4 +46,4 @@ decl_test_parachains! {
// BridgeHubRococo implementation
impl_accounts_helpers_for_parachain!(BridgeHubRococo);
impl_assert_events_helpers_for_parachain!(BridgeHubRococo);
impl_assert_events_helpers_for_parachain!(BridgeHubRococo, false);
@@ -46,4 +46,4 @@ decl_test_parachains! {
// BridgeHubWestend implementation
impl_accounts_helpers_for_parachain!(BridgeHubWestend);
impl_assert_events_helpers_for_parachain!(BridgeHubWestend);
impl_assert_events_helpers_for_parachain!(BridgeHubWestend, false);
@@ -44,4 +44,4 @@ decl_test_parachains! {
// BridgeHubWococo implementation
impl_accounts_helpers_for_parachain!(BridgeHubWococo);
impl_assert_events_helpers_for_parachain!(BridgeHubWococo);
impl_assert_events_helpers_for_parachain!(BridgeHubWococo, false);
@@ -22,3 +22,4 @@ parachains-common = { path = "../../../../../../../parachains/common" }
cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false }
emulated-integration-tests-common = { path = "../../../../common", default-features = false }
penpal-runtime = { path = "../../../../../../runtimes/testing/penpal" }
rococo-emulated-chain = { path = "../../../relays/rococo" }
@@ -21,8 +21,10 @@ use frame_support::traits::OnInitialize;
// Cumulus
use emulated_integration_tests_common::{
impl_assert_events_helpers_for_parachain, xcm_emulator::decl_test_parachains,
impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain,
impl_assets_helpers_for_parachain, xcm_emulator::decl_test_parachains,
};
use rococo_emulated_chain::Rococo;
// Penpal Parachain declaration
decl_test_parachains! {
@@ -40,6 +42,7 @@ decl_test_parachains! {
pallets = {
PolkadotXcm: penpal_runtime::PolkadotXcm,
Assets: penpal_runtime::Assets,
Balances: penpal_runtime::Balances,
}
},
pub struct PenpalB {
@@ -56,10 +59,13 @@ decl_test_parachains! {
pallets = {
PolkadotXcm: penpal_runtime::PolkadotXcm,
Assets: penpal_runtime::Assets,
Balances: penpal_runtime::Balances,
}
},
}
// Penpal implementation
impl_assert_events_helpers_for_parachain!(PenpalA);
impl_assert_events_helpers_for_parachain!(PenpalB);
impl_accounts_helpers_for_parachain!(PenpalA);
impl_assets_helpers_for_parachain!(PenpalA, Rococo);
impl_assert_events_helpers_for_parachain!(PenpalA, true);
impl_assert_events_helpers_for_parachain!(PenpalB, true);
@@ -30,7 +30,7 @@ decl_test_relay_chains! {
on_init = (),
runtime = westend_runtime,
core = {
SovereignAccountOf: westend_runtime::xcm_config::LocationConverter, //TODO: rename to SovereignAccountOf,
SovereignAccountOf: westend_runtime::xcm_config::LocationConverter,
},
pallets = {
XcmPallet: westend_runtime::XcmPallet,
@@ -399,7 +399,7 @@ macro_rules! impl_accounts_helpers_for_parachain {
#[macro_export]
macro_rules! impl_assert_events_helpers_for_parachain {
( $chain:ident ) => {
( $chain:ident, $ignore_weight:expr ) => {
$crate::impls::paste::paste! {
type [<$chain RuntimeEvent>]<N> = <$chain<N> as $crate::impls::Chain>::RuntimeEvent;
@@ -412,7 +412,7 @@ macro_rules! impl_assert_events_helpers_for_parachain {
[<$chain RuntimeEvent>]::<N>::PolkadotXcm(
$crate::impls::pallet_xcm::Event::Attempted { outcome: $crate::impls::Outcome::Complete(weight) }
) => {
weight: $crate::impls::weight_within_threshold(
weight: $ignore_weight || $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
@@ -434,7 +434,7 @@ macro_rules! impl_assert_events_helpers_for_parachain {
[<$chain RuntimeEvent>]::<N>::PolkadotXcm(
$crate::impls::pallet_xcm::Event::Attempted { outcome: $crate::impls::Outcome::Incomplete(weight, error) }
) => {
weight: $crate::impls::weight_within_threshold(
weight: $ignore_weight || $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
@@ -490,7 +490,7 @@ macro_rules! impl_assert_events_helpers_for_parachain {
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pallet_message_queue::Event::Processed {
success: true, weight_used: weight, ..
}) => {
weight: $crate::impls::weight_within_threshold(
weight: $ignore_weight || $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
@@ -510,7 +510,7 @@ macro_rules! impl_assert_events_helpers_for_parachain {
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pallet_message_queue::Event::Processed {
success: false, weight_used: weight, ..
}) => {
weight: $crate::impls::weight_within_threshold(
weight: $ignore_weight || $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
@@ -541,7 +541,7 @@ macro_rules! impl_assert_events_helpers_for_parachain {
vec![
[<$chain RuntimeEvent>]::<N>::MessageQueue($crate::impls::pallet_message_queue::Event::Processed { success: true, weight_used: weight, .. }
) => {
weight: $crate::impls::weight_within_threshold(
weight: $ignore_weight || $crate::impls::weight_within_threshold(
($crate::impls::REF_TIME_THRESHOLD, $crate::impls::PROOF_SIZE_THRESHOLD),
expected_weight.unwrap_or(*weight),
*weight
@@ -556,7 +556,7 @@ macro_rules! impl_assert_events_helpers_for_parachain {
}
#[macro_export]
macro_rules! impl_assets_helpers_for_system_parachain {
macro_rules! impl_assets_helpers_for_parachain {
( $chain:ident, $relay_chain:ident ) => {
$crate::impls::paste::paste! {
impl<N: $crate::impls::Network> $chain<N> {
@@ -17,6 +17,7 @@ frame-support = { path = "../../../../../../../substrate/frame/support", default
pallet-balances = { path = "../../../../../../../substrate/frame/balances", default-features = false}
pallet-assets = { path = "../../../../../../../substrate/frame/assets", default-features = false}
pallet-asset-conversion = { path = "../../../../../../../substrate/frame/asset-conversion", default-features = false}
pallet-message-queue = { path = "../../../../../../../substrate/frame/message-queue", default-features = false }
# Polkadot
xcm = { package = "staging-xcm", path = "../../../../../../../polkadot/xcm", default-features = false}
@@ -28,5 +29,6 @@ rococo-runtime = { path = "../../../../../../../polkadot/runtime/rococo" }
asset-test-utils = { path = "../../../../../runtimes/assets/test-utils" }
parachains-common = { path = "../../../../../../parachains/common" }
asset-hub-rococo-runtime = { path = "../../../../../runtimes/assets/asset-hub-rococo" }
emulated-integration-tests-common = { path = "../../../common", default-features = false}
emulated-integration-tests-common = { path = "../../../common", default-features = false }
penpal-runtime = { path = "../../../../../runtimes/testing/penpal" }
rococo-system-emulated-network ={ path = "../../../networks/rococo-system" }
@@ -61,18 +61,20 @@ pub const ASSET_MIN_BALANCE: u128 = 1000;
pub const ASSETS_PALLET_ID: u8 = 50;
pub type RelayToSystemParaTest = Test<Rococo, AssetHubRococo>;
pub type RelayToParaTest = Test<Rococo, PenpalA>;
pub type SystemParaToRelayTest = Test<AssetHubRococo, Rococo>;
pub type SystemParaToParaTest = Test<AssetHubRococo, PenpalA>;
pub type ParaToSystemParaTest = Test<PenpalA, AssetHubRococo>;
/// Returns a `TestArgs` instance to de used for the Relay Chain accross integraton tests
pub fn relay_test_args(amount: Balance) -> TestArgs {
/// Returns a `TestArgs` instance to be used for the Relay Chain across integration tests
pub fn relay_test_args(
dest: MultiLocation,
beneficiary_id: AccountId32,
amount: Balance,
) -> TestArgs {
TestArgs {
dest: Rococo::child_location_of(AssetHubRococo::para_id()),
beneficiary: AccountId32Junction {
network: None,
id: AssetHubRococoReceiver::get().into(),
}
.into(),
dest,
beneficiary: AccountId32Junction { network: None, id: beneficiary_id.into() }.into(),
amount,
assets: (Here, amount).into(),
asset_id: None,
@@ -81,13 +83,14 @@ pub fn relay_test_args(amount: Balance) -> TestArgs {
}
}
/// Returns a `TestArgs` instance to de used for the System Parachain accross integraton tests
pub fn system_para_test_args(
/// Returns a `TestArgs` instance to be used by parachains across integration tests
pub fn para_test_args(
dest: MultiLocation,
beneficiary_id: AccountId32,
amount: Balance,
assets: MultiAssets,
asset_id: Option<u32>,
fee_asset_item: u32,
) -> TestArgs {
TestArgs {
dest,
@@ -95,7 +98,7 @@ pub fn system_para_test_args(
amount,
assets,
asset_id,
fee_asset_item: 0,
fee_asset_item,
weight_limit: WeightLimit::Unlimited,
}
}
@@ -15,37 +15,45 @@
use crate::*;
use asset_hub_rococo_runtime::xcm_config::XcmConfig as AssetHubRococoXcmConfig;
use penpal_runtime::xcm_config::XcmConfig as PenpalRococoXcmConfig;
use rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig;
fn relay_origin_assertions(t: RelayToSystemParaTest) {
fn relay_to_para_sender_assertions(t: RelayToParaTest) {
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
Rococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(630_092_000, 6_196)));
Rococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8_799)));
assert_expected_events!(
Rococo,
vec![
// Amount to reserve transfer is transferred to System Parachain's Sovereign account
RuntimeEvent::Balances(pallet_balances::Event::Transfer { from, to, amount }) => {
// Amount to reserve transfer is transferred to Parachain's Sovereign account
RuntimeEvent::Balances(
pallet_balances::Event::Transfer { from, to, amount }
) => {
from: *from == t.sender.account_id,
to: *to == Rococo::sovereign_account_id_of(
t.args.dest
),
amount: *amount == t.args.amount,
amount: *amount == t.args.amount,
},
]
);
}
fn system_para_dest_assertions_incomplete(_t: RelayToSystemParaTest) {
AssetHubRococo::assert_dmp_queue_incomplete(Some(Weight::from_parts(57_185_000, 3504)));
fn relay_to_para_receiver_assertions<Test>(_: Test) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
assert_expected_events!(
PenpalA,
vec![
RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {},
RuntimeEvent::MessageQueue(
pallet_message_queue::Event::Processed { success: true, .. }
) => {},
]
);
}
fn system_para_to_relay_assertions(_t: SystemParaToRelayTest) {
AssetHubRococo::assert_xcm_pallet_attempted_error(Some(XcmError::Barrier))
}
fn system_para_to_para_assertions(t: SystemParaToParaTest) {
fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
AssetHubRococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
@@ -56,7 +64,7 @@ fn system_para_to_para_assertions(t: SystemParaToParaTest) {
assert_expected_events!(
AssetHubRococo,
vec![
// Amount to reserve transfer is transferred to Parachain's Sovereing account
// Amount to reserve transfer is transferred to Parachain's Sovereign account
RuntimeEvent::Balances(
pallet_balances::Event::Transfer { from, to, amount }
) => {
@@ -70,7 +78,64 @@ fn system_para_to_para_assertions(t: SystemParaToParaTest) {
);
}
fn system_para_to_para_assets_assertions(t: SystemParaToParaTest) {
fn system_para_to_para_receiver_assertions<Test>(_: Test) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
assert_expected_events!(
PenpalA,
vec![
RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {},
RuntimeEvent::MessageQueue(
pallet_message_queue::Event::Processed { success: true, .. }
) => {},
]
);
}
fn para_to_system_para_sender_assertions(t: ParaToSystemParaTest) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
PenpalA::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(864_610_000, 8_799)));
assert_expected_events!(
PenpalA,
vec![
// Amount to reserve transfer is transferred to Parachain's Sovereign account
RuntimeEvent::Balances(
pallet_balances::Event::Withdraw { who, amount }
) => {
who: *who == t.sender.account_id,
amount: *amount == t.args.amount,
},
]
);
}
fn para_to_system_para_receiver_assertions(t: ParaToSystemParaTest) {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(
AssetHubRococo::sibling_location_of(PenpalA::para_id()),
);
assert_expected_events!(
AssetHubRococo,
vec![
// Amount to reserve transfer is transferred to Parachain's Sovereign account
RuntimeEvent::Balances(
pallet_balances::Event::Withdraw { who, amount }
) => {
who: *who == sov_penpal_on_ahr.clone().into(),
amount: *amount == t.args.amount,
},
RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {},
RuntimeEvent::MessageQueue(
pallet_message_queue::Event::Processed { success: true, .. }
) => {},
]
);
}
fn system_para_to_para_assets_sender_assertions(t: SystemParaToParaTest) {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
AssetHubRococo::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
@@ -81,7 +146,7 @@ fn system_para_to_para_assets_assertions(t: SystemParaToParaTest) {
assert_expected_events!(
AssetHubRococo,
vec![
// Amount to reserve transfer is transferred to Parachain's Sovereing account
// Amount to reserve transfer is transferred to Parachain's Sovereign account
RuntimeEvent::Assets(
pallet_assets::Event::Transferred { asset_id, from, to, amount }
) => {
@@ -96,7 +161,21 @@ fn system_para_to_para_assets_assertions(t: SystemParaToParaTest) {
);
}
fn relay_limited_reserve_transfer_assets(t: RelayToSystemParaTest) -> DispatchResult {
fn system_para_to_para_assets_receiver_assertions<Test>(_: Test) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
assert_expected_events!(
PenpalA,
vec![
RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {},
RuntimeEvent::Assets(pallet_assets::Event::Issued { .. }) => {},
RuntimeEvent::MessageQueue(
pallet_message_queue::Event::Processed { success: true, .. }
) => {},
]
);
}
fn relay_to_para_limited_reserve_transfer_assets(t: RelayToParaTest) -> DispatchResult {
<Rococo as RococoPallet>::XcmPallet::limited_reserve_transfer_assets(
t.signed_origin,
bx!(t.args.dest.into()),
@@ -107,37 +186,6 @@ fn relay_limited_reserve_transfer_assets(t: RelayToSystemParaTest) -> DispatchRe
)
}
fn relay_reserve_transfer_assets(t: RelayToSystemParaTest) -> DispatchResult {
<Rococo as RococoPallet>::XcmPallet::reserve_transfer_assets(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
t.args.fee_asset_item,
)
}
fn system_para_limited_reserve_transfer_assets(t: SystemParaToRelayTest) -> DispatchResult {
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::limited_reserve_transfer_assets(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
t.args.fee_asset_item,
t.args.weight_limit,
)
}
fn system_para_reserve_transfer_assets(t: SystemParaToRelayTest) -> DispatchResult {
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::reserve_transfer_assets(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
t.args.fee_asset_item,
)
}
fn system_para_to_para_limited_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::limited_reserve_transfer_assets(
t.signed_origin,
@@ -149,183 +197,123 @@ fn system_para_to_para_limited_reserve_transfer_assets(t: SystemParaToParaTest)
)
}
fn system_para_to_para_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::reserve_transfer_assets(
fn para_to_system_para_limited_reserve_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult {
<PenpalA as PenpalAPallet>::PolkadotXcm::limited_reserve_transfer_assets(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
t.args.fee_asset_item,
t.args.weight_limit,
)
}
/// Limited Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't
/// work
#[test]
fn limited_reserve_transfer_native_asset_from_relay_to_system_para_fails() {
// Init values for Relay Chain
let amount_to_send: Balance = ROCOCO_ED * 1000;
let test_args = TestContext {
sender: RococoSender::get(),
receiver: AssetHubRococoReceiver::get(),
args: relay_test_args(amount_to_send),
};
let mut test = RelayToSystemParaTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
test.set_assertion::<Rococo>(relay_origin_assertions);
test.set_assertion::<AssetHubRococo>(system_para_dest_assertions_incomplete);
test.set_dispatchable::<Rococo>(relay_limited_reserve_transfer_assets);
test.assert();
let delivery_fees = Rococo::execute_with(|| {
xcm_helpers::transfer_assets_delivery_fees::<
<RococoXcmConfig as xcm_executor::Config>::XcmSender,
>(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest)
});
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
assert_eq!(receiver_balance_before, receiver_balance_after);
}
/// Limited Reserve Transfers of native asset from System Parachain to Relay Chain shoudln't work
#[test]
fn limited_reserve_transfer_native_asset_from_system_para_to_relay_fails() {
// Init values for System Parachain
let destination = AssetHubRococo::parent_location();
let beneficiary_id = RococoReceiver::get();
let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 1000;
let assets = (Parent, amount_to_send).into();
let test_args = TestContext {
sender: AssetHubRococoSender::get(),
receiver: RococoReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
};
let mut test = SystemParaToRelayTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
test.set_assertion::<AssetHubRococo>(system_para_to_relay_assertions);
test.set_dispatchable::<AssetHubRococo>(system_para_limited_reserve_transfer_assets);
test.assert();
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
assert_eq!(sender_balance_before, sender_balance_after);
assert_eq!(receiver_balance_before, receiver_balance_after);
}
/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work
#[test]
fn reserve_transfer_native_asset_from_relay_to_system_para_fails() {
// Init values for Relay Chain
let signed_origin = <Rococo as Chain>::RuntimeOrigin::signed(RococoSender::get().into());
let destination = Rococo::child_location_of(AssetHubRococo::para_id());
let beneficiary: MultiLocation =
AccountId32Junction { network: None, id: AssetHubRococoReceiver::get().into() }.into();
let amount_to_send: Balance = ROCOCO_ED * 1000;
let test_args = TestContext {
sender: RococoSender::get(),
receiver: AssetHubRococoReceiver::get(),
args: relay_test_args(amount_to_send),
};
let assets: MultiAssets = (Here, amount_to_send).into();
let fee_asset_item = 0;
let mut test = RelayToSystemParaTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
test.set_assertion::<Rococo>(relay_origin_assertions);
test.set_assertion::<AssetHubRococo>(system_para_dest_assertions_incomplete);
test.set_dispatchable::<Rococo>(relay_reserve_transfer_assets);
test.assert();
let delivery_fees = Rococo::execute_with(|| {
xcm_helpers::transfer_assets_delivery_fees::<
<RococoXcmConfig as xcm_executor::Config>::XcmSender,
>(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest)
// this should fail
Rococo::execute_with(|| {
let result = <Rococo as RococoPallet>::XcmPallet::limited_reserve_transfer_assets(
signed_origin,
bx!(destination.into()),
bx!(beneficiary.into()),
bx!(assets.into()),
fee_asset_item,
WeightLimit::Unlimited,
);
assert_err!(
result,
DispatchError::Module(sp_runtime::ModuleError {
index: 99,
error: [2, 0, 0, 0],
message: Some("Filtered")
})
);
});
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
assert_eq!(receiver_balance_before, receiver_balance_after);
}
/// Reserve Transfers of native asset from System Parachain to Relay Chain shouldn't work
#[test]
fn reserve_transfer_native_asset_from_system_para_to_relay_fails() {
// Init values for System Parachain
let signed_origin =
<AssetHubRococo as Chain>::RuntimeOrigin::signed(AssetHubRococoSender::get().into());
let destination = AssetHubRococo::parent_location();
let beneficiary_id = RococoReceiver::get();
let beneficiary: MultiLocation =
AccountId32Junction { network: None, id: beneficiary_id.into() }.into();
let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 1000;
let assets = (Parent, amount_to_send).into();
let assets: MultiAssets = (Parent, amount_to_send).into();
let fee_asset_item = 0;
// this should fail
AssetHubRococo::execute_with(|| {
let result =
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::limited_reserve_transfer_assets(
signed_origin,
bx!(destination.into()),
bx!(beneficiary.into()),
bx!(assets.into()),
fee_asset_item,
WeightLimit::Unlimited,
);
assert_err!(
result,
DispatchError::Module(sp_runtime::ModuleError {
index: 31,
error: [2, 0, 0, 0],
message: Some("Filtered")
})
);
});
}
/// Reserve Transfers of native asset from Relay to Parachain should work
#[test]
fn reserve_transfer_native_asset_from_relay_to_para() {
// Init values for Relay
let destination = Rococo::child_location_of(PenpalA::para_id());
let beneficiary_id = PenpalAReceiver::get();
let amount_to_send: Balance = ROCOCO_ED * 1000;
let test_args = TestContext {
sender: AssetHubRococoSender::get(),
receiver: RococoReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
sender: RococoSender::get(),
receiver: PenpalAReceiver::get(),
args: relay_test_args(destination, beneficiary_id, amount_to_send),
};
let mut test = SystemParaToRelayTest::new(test_args);
let mut test = RelayToParaTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
test.set_assertion::<AssetHubRococo>(system_para_to_relay_assertions);
test.set_dispatchable::<AssetHubRococo>(system_para_reserve_transfer_assets);
test.set_assertion::<Rococo>(relay_to_para_sender_assertions);
test.set_assertion::<PenpalA>(relay_to_para_receiver_assertions);
test.set_dispatchable::<Rococo>(relay_to_para_limited_reserve_transfer_assets);
test.assert();
let delivery_fees = Rococo::execute_with(|| {
xcm_helpers::transfer_assets_delivery_fees::<
<RococoXcmConfig as xcm_executor::Config>::XcmSender,
>(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest)
});
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
assert_eq!(sender_balance_before, sender_balance_after);
assert_eq!(receiver_balance_before, receiver_balance_after);
}
/// Limited Reserve Transfers of native asset from System Parachain to Parachain should work
#[test]
fn limited_reserve_transfer_native_asset_from_system_para_to_para() {
// Init values for System Parachain
let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id());
let beneficiary_id = PenpalAReceiver::get();
let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 1000;
let assets = (Parent, amount_to_send).into();
let test_args = TestContext {
sender: AssetHubRococoSender::get(),
receiver: PenpalAReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
};
let mut test = SystemParaToParaTest::new(test_args);
let sender_balance_before = test.sender.balance;
test.set_assertion::<AssetHubRococo>(system_para_to_para_assertions);
// TODO: Add assertion for Penpal runtime. Right now message is failing with
// `UntrustedReserveLocation`
test.set_dispatchable::<AssetHubRococo>(system_para_to_para_limited_reserve_transfer_assets);
test.assert();
let sender_balance_after = test.sender.balance;
let delivery_fees = AssetHubRococo::execute_with(|| {
xcm_helpers::transfer_assets_delivery_fees::<
<AssetHubRococoXcmConfig as xcm_executor::Config>::XcmSender,
>(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest)
});
// Sender's balance is reduced
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
// TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve
// transfers
// Receiver's balance is increased
assert!(receiver_balance_after > receiver_balance_before);
}
/// Reserve Transfers of native asset from System Parachain to Parachain should work
@@ -340,20 +328,21 @@ fn reserve_transfer_native_asset_from_system_para_to_para() {
let test_args = TestContext {
sender: AssetHubRococoSender::get(),
receiver: PenpalAReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
args: para_test_args(destination, beneficiary_id, amount_to_send, assets, None, 0),
};
let mut test = SystemParaToParaTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
test.set_assertion::<AssetHubRococo>(system_para_to_para_assertions);
// TODO: Add assertion for Penpal runtime. Right now message is failing with
// `UntrustedReserveLocation`
test.set_dispatchable::<AssetHubRococo>(system_para_to_para_reserve_transfer_assets);
test.set_assertion::<AssetHubRococo>(system_para_to_para_sender_assertions);
test.set_assertion::<PenpalA>(system_para_to_para_receiver_assertions);
test.set_dispatchable::<AssetHubRococo>(system_para_to_para_limited_reserve_transfer_assets);
test.assert();
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
let delivery_fees = AssetHubRococo::execute_with(|| {
xcm_helpers::transfer_assets_delivery_fees::<
@@ -361,79 +350,153 @@ fn reserve_transfer_native_asset_from_system_para_to_para() {
>(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest)
});
// Sender's balance is reduced
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
// TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve
// transfers
// Receiver's balance is increased
assert!(receiver_balance_after > receiver_balance_before);
}
/// Limited Reserve Transfers of a local asset from System Parachain to Parachain should work
/// Reserve Transfers of native asset from Parachain to System Parachain should work
#[test]
fn limited_reserve_transfer_asset_from_system_para_to_para() {
// Force create asset from Relay Chain and mint assets for System Parachain's sender account
fn reserve_transfer_native_asset_from_para_to_system_para() {
// Init values for Penpal Parachain
let destination = PenpalA::sibling_location_of(AssetHubRococo::para_id());
let beneficiary_id = AssetHubRococoReceiver::get();
let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 1000;
let assets = (Parent, amount_to_send).into();
let test_args = TestContext {
sender: PenpalASender::get(),
receiver: AssetHubRococoReceiver::get(),
args: para_test_args(destination, beneficiary_id, amount_to_send, assets, None, 0),
};
let mut test = ParaToSystemParaTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
let penpal_location_as_seen_by_ahr = AssetHubRococo::sibling_location_of(PenpalA::para_id());
let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location_as_seen_by_ahr);
// fund the Penpal's SA on AHR with the native tokens held in reserve
AssetHubRococo::fund_accounts(vec![(sov_penpal_on_ahr.into(), amount_to_send * 2)]);
test.set_assertion::<PenpalA>(para_to_system_para_sender_assertions);
test.set_assertion::<AssetHubRococo>(para_to_system_para_receiver_assertions);
test.set_dispatchable::<PenpalA>(para_to_system_para_limited_reserve_transfer_assets);
test.assert();
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
let delivery_fees = PenpalA::execute_with(|| {
xcm_helpers::transfer_assets_delivery_fees::<
<PenpalRococoXcmConfig as xcm_executor::Config>::XcmSender,
>(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest)
});
// Sender's balance is reduced
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
// Receiver's balance is increased
assert!(receiver_balance_after > receiver_balance_before);
}
/// Reserve Transfers of a local asset and native asset from System Parachain to Parachain should
/// work
#[test]
fn reserve_transfer_assets_from_system_para_to_para() {
// Force create asset on AssetHubRococo and PenpalA from Relay Chain
AssetHubRococo::force_create_and_mint_asset(
ASSET_ID,
ASSET_MIN_BALANCE,
true,
false,
AssetHubRococoSender::get(),
Some(Weight::from_parts(1_019_445_000, 200_000)),
ASSET_MIN_BALANCE * 1000000,
ASSET_MIN_BALANCE * 1_000_000,
);
PenpalA::force_create_and_mint_asset(
ASSET_ID,
ASSET_MIN_BALANCE,
false,
PenpalASender::get(),
Some(Weight::from_parts(1_019_445_000, 200_000)),
0,
);
// Init values for System Parachain
let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id());
let beneficiary_id = PenpalAReceiver::get();
let amount_to_send = ASSET_MIN_BALANCE * 1000;
let assets =
(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), amount_to_send)
.into();
let fee_amount_to_send = ASSET_HUB_ROCOCO_ED * 1000;
let asset_amount_to_send = ASSET_MIN_BALANCE * 1000;
let assets: MultiAssets = vec![
(Parent, fee_amount_to_send).into(),
(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), asset_amount_to_send)
.into(),
]
.into();
let fee_asset_index = assets
.inner()
.iter()
.position(|r| r == &(Parent, fee_amount_to_send).into())
.unwrap() as u32;
let system_para_test_args = TestContext {
let para_test_args = TestContext {
sender: AssetHubRococoSender::get(),
receiver: PenpalAReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
args: para_test_args(
destination,
beneficiary_id,
asset_amount_to_send,
assets,
None,
fee_asset_index,
),
};
let mut system_para_test = SystemParaToParaTest::new(system_para_test_args);
let mut test = SystemParaToParaTest::new(para_test_args);
system_para_test.set_assertion::<AssetHubRococo>(system_para_to_para_assets_assertions);
// TODO: Add assertions when Penpal is able to manage assets
system_para_test
.set_dispatchable::<AssetHubRococo>(system_para_to_para_limited_reserve_transfer_assets);
system_para_test.assert();
}
/// Reserve Transfers of a local asset from System Parachain to Parachain should work
#[test]
fn reserve_transfer_asset_from_system_para_to_para() {
// Force create asset from Relay Chain and mint assets for System Parachain's sender account
AssetHubRococo::force_create_and_mint_asset(
ASSET_ID,
ASSET_MIN_BALANCE,
true,
AssetHubRococoSender::get(),
Some(Weight::from_parts(1_019_445_000, 200_000)),
ASSET_MIN_BALANCE * 1000000,
);
// Init values for System Parachain
let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id());
let beneficiary_id = PenpalAReceiver::get();
let amount_to_send = ASSET_MIN_BALANCE * 1000;
let assets =
(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), amount_to_send)
.into();
let system_para_test_args = TestContext {
sender: AssetHubRococoSender::get(),
receiver: PenpalAReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
};
let mut system_para_test = SystemParaToParaTest::new(system_para_test_args);
system_para_test.set_assertion::<AssetHubRococo>(system_para_to_para_assets_assertions);
// TODO: Add assertions when Penpal is able to manage assets
system_para_test
.set_dispatchable::<AssetHubRococo>(system_para_to_para_reserve_transfer_assets);
system_para_test.assert();
// Create SA-of-Penpal-on-AHR with ED.
let penpal_location = AssetHubRococo::sibling_location_of(PenpalA::para_id());
let sov_penpal_on_ahr = AssetHubRococo::sovereign_account_id_of(penpal_location);
AssetHubRococo::fund_accounts(vec![(sov_penpal_on_ahr.into(), ROCOCO_ED)]);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
let sender_assets_before = AssetHubRococo::execute_with(|| {
type Assets = <AssetHubRococo as AssetHubRococoPallet>::Assets;
<Assets as Inspect<_>>::balance(ASSET_ID, &AssetHubRococoSender::get())
});
let receiver_assets_before = PenpalA::execute_with(|| {
type Assets = <PenpalA as PenpalAPallet>::Assets;
<Assets as Inspect<_>>::balance(ASSET_ID, &PenpalAReceiver::get())
});
test.set_assertion::<AssetHubRococo>(system_para_to_para_assets_sender_assertions);
test.set_assertion::<PenpalA>(system_para_to_para_assets_receiver_assertions);
test.set_dispatchable::<AssetHubRococo>(system_para_to_para_limited_reserve_transfer_assets);
test.assert();
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
// Sender's balance is reduced
assert!(sender_balance_after < sender_balance_before);
// Receiver's balance is increased
assert!(receiver_balance_after > receiver_balance_before);
let sender_assets_after = AssetHubRococo::execute_with(|| {
type Assets = <AssetHubRococo as AssetHubRococoPallet>::Assets;
<Assets as Inspect<_>>::balance(ASSET_ID, &AssetHubRococoSender::get())
});
let receiver_assets_after = PenpalA::execute_with(|| {
type Assets = <PenpalA as PenpalAPallet>::Assets;
<Assets as Inspect<_>>::balance(ASSET_ID, &PenpalAReceiver::get())
});
// Sender's balance is reduced
assert_eq!(sender_assets_before - asset_amount_to_send, sender_assets_after);
// Receiver's balance is increased
assert!(receiver_assets_after > receiver_assets_before);
}
@@ -51,7 +51,7 @@ fn relay_dest_assertions(t: SystemParaToRelayTest) {
assert_expected_events!(
Rococo,
vec![
// Amount is witdrawn from Relay Chain's `CheckAccount`
// Amount is withdrawn from Relay Chain's `CheckAccount`
RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
who: *who == <Rococo as RococoPallet>::XcmPallet::check_account(),
amount: *amount == t.args.amount,
@@ -157,10 +157,12 @@ fn system_para_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult {
fn limited_teleport_native_assets_from_relay_to_system_para_works() {
// Init values for Relay Chain
let amount_to_send: Balance = ROCOCO_ED * 1000;
let dest = Rococo::child_location_of(AssetHubRococo::para_id());
let beneficiary_id = AssetHubRococoReceiver::get();
let test_args = TestContext {
sender: RococoSender::get(),
receiver: AssetHubRococoReceiver::get(),
args: relay_test_args(amount_to_send),
args: relay_test_args(dest, beneficiary_id, amount_to_send),
};
let mut test = RelayToSystemParaTest::new(test_args);
@@ -204,7 +206,7 @@ fn limited_teleport_native_assets_back_from_system_para_to_relay_works() {
let test_args = TestContext {
sender: AssetHubRococoSender::get(),
receiver: RococoReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
args: para_test_args(destination, beneficiary_id, amount_to_send, assets, None, 0),
};
let mut test = SystemParaToRelayTest::new(test_args);
@@ -245,7 +247,7 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() {
let test_args = TestContext {
sender: AssetHubRococoSender::get(),
receiver: RococoReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
args: para_test_args(destination, beneficiary_id, amount_to_send, assets, None, 0),
};
let mut test = SystemParaToRelayTest::new(test_args);
@@ -278,10 +280,12 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() {
fn teleport_native_assets_from_relay_to_system_para_works() {
// Init values for Relay Chain
let amount_to_send: Balance = ROCOCO_ED * 1000;
let dest = Rococo::child_location_of(AssetHubRococo::para_id());
let beneficiary_id = AssetHubRococoReceiver::get();
let test_args = TestContext {
sender: RococoSender::get(),
receiver: AssetHubRococoReceiver::get(),
args: relay_test_args(amount_to_send),
args: relay_test_args(dest, beneficiary_id, amount_to_send),
};
let mut test = RelayToSystemParaTest::new(test_args);
@@ -325,7 +329,7 @@ fn teleport_native_assets_back_from_system_para_to_relay_works() {
let test_args = TestContext {
sender: AssetHubRococoSender::get(),
receiver: RococoReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
args: para_test_args(destination, beneficiary_id, amount_to_send, assets, None, 0),
};
let mut test = SystemParaToRelayTest::new(test_args);
@@ -366,7 +370,7 @@ fn teleport_native_assets_from_system_para_to_relay_fails() {
let test_args = TestContext {
sender: AssetHubRococoSender::get(),
receiver: RococoReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
args: para_test_args(destination, beneficiary_id, amount_to_send, assets, None, 0),
};
let mut test = SystemParaToRelayTest::new(test_args);
@@ -65,7 +65,7 @@ pub type RelayToSystemParaTest = Test<Westend, AssetHubWestend>;
pub type SystemParaToRelayTest = Test<AssetHubWestend, Westend>;
pub type SystemParaToParaTest = Test<AssetHubWestend, PenpalA>;
/// Returns a `TestArgs` instance to de used for the Relay Chain accross integraton tests
/// Returns a `TestArgs` instance to be used for the Relay Chain across integration tests
pub fn relay_test_args(amount: Balance) -> TestArgs {
TestArgs {
dest: Westend::child_location_of(AssetHubWestend::para_id()),
@@ -82,7 +82,7 @@ pub fn relay_test_args(amount: Balance) -> TestArgs {
}
}
/// Returns a `TestArgs` instance to de used for the System Parachain accross integraton tests
/// Returns a `TestArgs` instance to be used for the System Parachain across integration tests
pub fn system_para_test_args(
dest: MultiLocation,
beneficiary_id: AccountId32,
@@ -15,37 +15,8 @@
use crate::*;
use asset_hub_westend_runtime::xcm_config::XcmConfig;
use westend_runtime::xcm_config::XcmConfig as WestendXcmConfig;
fn relay_origin_assertions(t: RelayToSystemParaTest) {
type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
Westend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(629_384_000, 6_196)));
assert_expected_events!(
Westend,
vec![
// Amount to reserve transfer is transferred to System Parachain's Sovereign account
RuntimeEvent::Balances(pallet_balances::Event::Transfer { from, to, amount }) => {
from: *from == t.sender.account_id,
to: *to == Westend::sovereign_account_id_of(
t.args.dest
),
amount: *amount == t.args.amount,
},
]
);
}
fn system_para_dest_assertions(_t: RelayToSystemParaTest) {
AssetHubWestend::assert_dmp_queue_incomplete(Some(Weight::from_parts(31_352_000, 1489)));
}
fn system_para_to_relay_assertions(_t: SystemParaToRelayTest) {
AssetHubWestend::assert_xcm_pallet_attempted_error(Some(XcmError::Barrier))
}
fn system_para_to_para_assertions(t: SystemParaToParaTest) {
fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) {
type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
AssetHubWestend::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
@@ -56,7 +27,7 @@ fn system_para_to_para_assertions(t: SystemParaToParaTest) {
assert_expected_events!(
AssetHubWestend,
vec![
// Amount to reserve transfer is transferred to Parachain's Sovereing account
// Amount to reserve transfer is transferred to Parachain's Sovereign account
RuntimeEvent::Balances(
pallet_balances::Event::Transfer { from, to, amount }
) => {
@@ -70,6 +41,19 @@ fn system_para_to_para_assertions(t: SystemParaToParaTest) {
);
}
fn para_receiver_assertions<Test>(_: Test) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
assert_expected_events!(
PenpalA,
vec![
RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {},
RuntimeEvent::MessageQueue(
pallet_message_queue::Event::Processed { success: true, .. }
) => {},
]
);
}
fn system_para_to_para_assets_assertions(t: SystemParaToParaTest) {
type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
@@ -81,7 +65,7 @@ fn system_para_to_para_assets_assertions(t: SystemParaToParaTest) {
assert_expected_events!(
AssetHubWestend,
vec![
// Amount to reserve transfer is transferred to Parachain's Sovereing account
// Amount to reserve transfer is transferred to Parachain's Sovereign account
RuntimeEvent::Assets(
pallet_assets::Event::Transferred { asset_id, from, to, amount }
) => {
@@ -96,48 +80,6 @@ fn system_para_to_para_assets_assertions(t: SystemParaToParaTest) {
);
}
fn relay_limited_reserve_transfer_assets(t: RelayToSystemParaTest) -> DispatchResult {
<Westend as WestendPallet>::XcmPallet::limited_reserve_transfer_assets(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
t.args.fee_asset_item,
t.args.weight_limit,
)
}
fn relay_reserve_transfer_assets(t: RelayToSystemParaTest) -> DispatchResult {
<Westend as WestendPallet>::XcmPallet::reserve_transfer_assets(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
t.args.fee_asset_item,
)
}
fn system_para_limited_reserve_transfer_assets(t: SystemParaToRelayTest) -> DispatchResult {
<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::limited_reserve_transfer_assets(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
t.args.fee_asset_item,
t.args.weight_limit,
)
}
fn system_para_reserve_transfer_assets(t: SystemParaToRelayTest) -> DispatchResult {
<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::reserve_transfer_assets(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
t.args.fee_asset_item,
)
}
fn system_para_to_para_limited_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::limited_reserve_transfer_assets(
t.signed_origin,
@@ -149,187 +91,72 @@ fn system_para_to_para_limited_reserve_transfer_assets(t: SystemParaToParaTest)
)
}
fn system_para_to_para_reserve_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::reserve_transfer_assets(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
t.args.fee_asset_item,
)
}
/// Limited Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't
/// work
#[test]
fn limited_reserve_transfer_native_asset_from_relay_to_system_para_fails() {
// Init values for Relay Chain
let amount_to_send: Balance = WESTEND_ED * 1000;
let test_args = TestContext {
sender: WestendSender::get(),
receiver: AssetHubWestendReceiver::get(),
args: relay_test_args(amount_to_send),
};
let mut test = RelayToSystemParaTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
test.set_assertion::<Westend>(relay_origin_assertions);
test.set_assertion::<AssetHubWestend>(system_para_dest_assertions);
test.set_dispatchable::<Westend>(relay_limited_reserve_transfer_assets);
test.assert();
let delivery_fees = Westend::execute_with(|| {
xcm_helpers::transfer_assets_delivery_fees::<
<WestendXcmConfig as xcm_executor::Config>::XcmSender,
>(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest)
});
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
assert_eq!(receiver_balance_before, receiver_balance_after);
}
/// Limited Reserve Transfers of native asset from System Parachain to Relay Chain shoudln't work
#[test]
fn limited_reserve_transfer_native_asset_from_system_para_to_relay_fails() {
// Init values for System Parachain
let destination = AssetHubWestend::parent_location();
let beneficiary_id = WestendReceiver::get();
let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
let assets = (Parent, amount_to_send).into();
let test_args = TestContext {
sender: AssetHubWestendSender::get(),
receiver: WestendReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
};
let mut test = SystemParaToRelayTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
test.set_assertion::<AssetHubWestend>(system_para_to_relay_assertions);
test.set_dispatchable::<AssetHubWestend>(system_para_limited_reserve_transfer_assets);
test.assert();
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
assert_eq!(sender_balance_before, sender_balance_after);
assert_eq!(receiver_balance_before, receiver_balance_after);
}
/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work
#[test]
fn reserve_transfer_native_asset_from_relay_to_system_para_fails() {
// Init values for Relay Chain
let signed_origin = <Westend as Chain>::RuntimeOrigin::signed(WestendSender::get().into());
let destination = Westend::child_location_of(AssetHubWestend::para_id());
let beneficiary: MultiLocation =
AccountId32Junction { network: None, id: AssetHubWestendReceiver::get().into() }.into();
let amount_to_send: Balance = WESTEND_ED * 1000;
let test_args = TestContext {
sender: WestendSender::get(),
receiver: AssetHubWestendReceiver::get(),
args: relay_test_args(amount_to_send),
};
let assets: MultiAssets = (Here, amount_to_send).into();
let fee_asset_item = 0;
let mut test = RelayToSystemParaTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
test.set_assertion::<Westend>(relay_origin_assertions);
test.set_assertion::<AssetHubWestend>(system_para_dest_assertions);
test.set_dispatchable::<Westend>(relay_reserve_transfer_assets);
test.assert();
let delivery_fees = Westend::execute_with(|| {
xcm_helpers::transfer_assets_delivery_fees::<
<WestendXcmConfig as xcm_executor::Config>::XcmSender,
>(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest)
// this should fail
Westend::execute_with(|| {
let result = <Westend as WestendPallet>::XcmPallet::limited_reserve_transfer_assets(
signed_origin,
bx!(destination.into()),
bx!(beneficiary.into()),
bx!(assets.into()),
fee_asset_item,
WeightLimit::Unlimited,
);
assert_err!(
result,
DispatchError::Module(sp_runtime::ModuleError {
index: 99,
error: [2, 0, 0, 0],
message: Some("Filtered")
})
);
});
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
assert_eq!(receiver_balance_before, receiver_balance_after);
}
/// Reserve Transfers of native asset from System Parachain to Relay Chain shouldn't work
#[test]
fn reserve_transfer_native_asset_from_system_para_to_relay_fails() {
// Init values for System Parachain
let signed_origin =
<AssetHubWestend as Chain>::RuntimeOrigin::signed(AssetHubWestendSender::get().into());
let destination = AssetHubWestend::parent_location();
let beneficiary_id = WestendReceiver::get();
let beneficiary: MultiLocation =
AccountId32Junction { network: None, id: beneficiary_id.into() }.into();
let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
let assets = (Parent, amount_to_send).into();
let assets: MultiAssets = (Parent, amount_to_send).into();
let fee_asset_item = 0;
let test_args = TestContext {
sender: AssetHubWestendSender::get(),
receiver: WestendReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
};
let mut test = SystemParaToRelayTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
test.set_assertion::<AssetHubWestend>(system_para_to_relay_assertions);
test.set_dispatchable::<AssetHubWestend>(system_para_reserve_transfer_assets);
test.assert();
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
assert_eq!(sender_balance_before, sender_balance_after);
assert_eq!(receiver_balance_before, receiver_balance_after);
}
/// Limited Reserve Transfers of native asset from System Parachain to Parachain should work
#[test]
fn limited_reserve_transfer_native_asset_from_system_para_to_para() {
// Init values for System Parachain
let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id());
let beneficiary_id = PenpalAReceiver::get();
let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
let assets = (Parent, amount_to_send).into();
let test_args = TestContext {
sender: AssetHubWestendSender::get(),
receiver: PenpalAReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
};
let mut test = SystemParaToParaTest::new(test_args);
let sender_balance_before = test.sender.balance;
test.set_assertion::<AssetHubWestend>(system_para_to_para_assertions);
// TODO: Add assertion for Penpal runtime. Right now message is failing with
// `UntrustedReserveLocation`
test.set_dispatchable::<AssetHubWestend>(system_para_to_para_limited_reserve_transfer_assets);
test.assert();
let sender_balance_after = test.sender.balance;
let delivery_fees = AssetHubWestend::execute_with(|| {
xcm_helpers::transfer_assets_delivery_fees::<<XcmConfig as xcm_executor::Config>::XcmSender>(
test.args.assets.clone(),
0,
test.args.weight_limit,
test.args.beneficiary,
test.args.dest,
)
// this should fail
AssetHubWestend::execute_with(|| {
let result =
<AssetHubWestend as AssetHubWestendPallet>::PolkadotXcm::limited_reserve_transfer_assets(
signed_origin,
bx!(destination.into()),
bx!(beneficiary.into()),
bx!(assets.into()),
fee_asset_item,
WeightLimit::Unlimited,
);
assert_err!(
result,
DispatchError::Module(sp_runtime::ModuleError {
index: 31,
error: [2, 0, 0, 0],
message: Some("Filtered")
})
);
});
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
// TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve
// transfers
}
/// Reserve Transfers of native asset from System Parachain to Parachain should work
@@ -350,14 +177,15 @@ fn reserve_transfer_native_asset_from_system_para_to_para() {
let mut test = SystemParaToParaTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
test.set_assertion::<AssetHubWestend>(system_para_to_para_assertions);
// TODO: Add assertion for Penpal runtime. Right now message is failing with
// `UntrustedReserveLocation`
test.set_dispatchable::<AssetHubWestend>(system_para_to_para_reserve_transfer_assets);
test.set_assertion::<AssetHubWestend>(system_para_to_para_sender_assertions);
test.set_assertion::<PenpalA>(para_receiver_assertions);
test.set_dispatchable::<AssetHubWestend>(system_para_to_para_limited_reserve_transfer_assets);
test.assert();
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
let delivery_fees = AssetHubWestend::execute_with(|| {
xcm_helpers::transfer_assets_delivery_fees::<<XcmConfig as xcm_executor::Config>::XcmSender>(
@@ -369,45 +197,10 @@ fn reserve_transfer_native_asset_from_system_para_to_para() {
)
});
// Sender's balance is reduced
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
// TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve
// transfers
}
/// Limited Reserve Transfers of a local asset from System Parachain to Parachain should work
#[test]
fn limited_reserve_transfer_asset_from_system_para_to_para() {
// Force create asset from Relay Chain and mint assets for System Parachain's sender account
AssetHubWestend::force_create_and_mint_asset(
ASSET_ID,
ASSET_MIN_BALANCE,
true,
AssetHubWestendSender::get(),
Some(Weight::from_parts(1_019_445_000, 200_000)),
ASSET_MIN_BALANCE * 1000000,
);
// Init values for System Parachain
let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id());
let beneficiary_id = PenpalAReceiver::get();
let amount_to_send = ASSET_MIN_BALANCE * 1000;
let assets =
(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), amount_to_send)
.into();
let system_para_test_args = TestContext {
sender: AssetHubWestendSender::get(),
receiver: PenpalAReceiver::get(),
args: system_para_test_args(destination, beneficiary_id, amount_to_send, assets, None),
};
let mut system_para_test = SystemParaToParaTest::new(system_para_test_args);
system_para_test.set_assertion::<AssetHubWestend>(system_para_to_para_assets_assertions);
// TODO: Add assertions when Penpal is able to manage assets
system_para_test
.set_dispatchable::<AssetHubWestend>(system_para_to_para_limited_reserve_transfer_assets);
system_para_test.assert();
// Receiver's balance is increased
assert!(receiver_balance_after > receiver_balance_before);
}
/// Reserve Transfers of a local asset from System Parachain to Parachain should work
@@ -442,6 +235,6 @@ fn reserve_transfer_asset_from_system_para_to_para() {
system_para_test.set_assertion::<AssetHubWestend>(system_para_to_para_assets_assertions);
// TODO: Add assertions when Penpal is able to manage assets
system_para_test
.set_dispatchable::<AssetHubWestend>(system_para_to_para_reserve_transfer_assets);
.set_dispatchable::<AssetHubWestend>(system_para_to_para_limited_reserve_transfer_assets);
system_para_test.assert();
}
@@ -51,7 +51,7 @@ fn relay_dest_assertions(t: SystemParaToRelayTest) {
assert_expected_events!(
Westend,
vec![
// Amount is witdrawn from Relay Chain's `CheckAccount`
// Amount is withdrawn from Relay Chain's `CheckAccount`
RuntimeEvent::Balances(pallet_balances::Event::Withdraw { who, amount }) => {
who: *who == <Westend as WestendPallet>::XcmPallet::check_account(),
amount: *amount == t.args.amount,
@@ -962,7 +962,7 @@ mod benches {
[cumulus_pallet_xcmp_queue, XcmpQueue]
[cumulus_pallet_dmp_queue, DmpQueue]
// XCM
[pallet_xcm, PolkadotXcm]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
// NOTE: Make sure you point to the individual modules below.
[pallet_xcm_benchmarks::fungible, XcmBalances]
[pallet_xcm_benchmarks::generic, XcmGeneric]
@@ -1200,6 +1200,7 @@ impl_runtime_apis! {
use frame_support::traits::StorageInfoTrait;
use frame_system_benchmarking::Pallet as SystemBench;
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
// This is defined once again in dispatch_benchmark, because list_benchmarks!
// and add_benchmarks! are macros exported by define_benchmarks! macros and those types
@@ -1243,6 +1244,39 @@ impl_runtime_apis! {
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(Parent.into())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported between AH and Relay.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
Parent.into(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// AH can reserve transfer native token to some random parachain.
let random_para_id = 43211234;
ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(
random_para_id.into()
);
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
ParentThen(Parachain(random_para_id).into()).into(),
))
}
}
use xcm::latest::prelude::*;
use xcm_config::{KsmLocation, MaxAssetsIntoHolding};
use pallet_xcm_benchmarks::asset_instance_from;
@@ -552,11 +552,6 @@ pub type XcmRouter = WithUniqueTopic<(
XcmpQueue,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
// We want to disallow users sending (arbitrary) XCMs from this chain.
@@ -586,8 +581,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -18,13 +18,14 @@
//! Tests for the Statemine (Kusama Assets Hub) chain.
use asset_hub_kusama_runtime::xcm_config::{
AssetFeeAsExistentialDepositMultiplierFeeCharger, KsmLocation, TrustBackedAssetsPalletLocation,
AssetFeeAsExistentialDepositMultiplierFeeCharger, KsmLocation, LocationToAccountId,
TrustBackedAssetsPalletLocation,
};
pub use asset_hub_kusama_runtime::{
xcm_config::{CheckingAccount, ForeignCreatorsSovereignAccountOf, XcmConfig},
AllPalletsWithoutSystem, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets,
ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime,
RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance,
RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance, XcmpQueue,
};
use asset_test_utils::{CollatorSessionKeys, ExtBuilder};
use codec::{Decode, Encode};
@@ -518,12 +519,6 @@ asset_test_utils::include_teleports_for_native_asset_works!(
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
1000
);
@@ -632,3 +627,32 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p
assert_eq!(ForeignAssets::asset_ids().collect::<Vec<_>>().len(), 1);
})
);
#[test]
fn reserve_transfer_native_asset_to_non_teleport_para_works() {
asset_test_utils::test_cases::reserve_transfer_native_asset_to_non_teleport_para_works::<
Runtime,
AllPalletsWithoutSystem,
XcmConfig,
ParachainSystem,
XcmpQueue,
LocationToAccountId,
>(
collator_session_keys(),
ExistentialDeposit::get(),
AccountId::from(ALICE),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event),
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
WeightLimit::Unlimited,
);
}
@@ -868,7 +868,7 @@ mod benches {
[cumulus_pallet_xcmp_queue, XcmpQueue]
[cumulus_pallet_dmp_queue, DmpQueue]
// XCM
[pallet_xcm, PolkadotXcm]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
// NOTE: Make sure you point to the individual modules below.
[pallet_xcm_benchmarks::fungible, XcmBalances]
[pallet_xcm_benchmarks::generic, XcmGeneric]
@@ -1082,6 +1082,7 @@ impl_runtime_apis! {
use frame_support::traits::StorageInfoTrait;
use frame_system_benchmarking::Pallet as SystemBench;
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
// This is defined once again in dispatch_benchmark, because list_benchmarks!
// and add_benchmarks! are macros exported by define_benchmarks! macros and those types
@@ -1124,6 +1125,39 @@ impl_runtime_apis! {
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(Parent.into())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported between AH and Relay.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
Parent.into(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// AH can reserve transfer native token to some random parachain.
let random_para_id = 43211234;
ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(
random_para_id.into()
);
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
ParentThen(Parachain(random_para_id).into()).into(),
))
}
}
use xcm::latest::prelude::*;
use xcm_config::{DotLocation, MaxAssetsIntoHolding};
use pallet_xcm_benchmarks::asset_instance_from;
@@ -476,11 +476,6 @@ pub type XcmRouter = WithUniqueTopic<(
XcmpQueue,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
// We want to disallow users sending (arbitrary) XCMs from this chain.
@@ -510,8 +505,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -19,12 +19,13 @@
use asset_hub_polkadot_runtime::xcm_config::{
AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, DotLocation,
ForeignCreatorsSovereignAccountOf, TrustBackedAssetsPalletLocation, XcmConfig,
ForeignCreatorsSovereignAccountOf, LocationToAccountId, TrustBackedAssetsPalletLocation,
XcmConfig,
};
pub use asset_hub_polkadot_runtime::{
AllPalletsWithoutSystem, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets,
ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime,
RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance,
RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance, XcmpQueue,
};
use asset_test_utils::{CollatorSessionKeys, ExtBuilder};
use codec::{Decode, Encode};
@@ -531,12 +532,6 @@ asset_test_utils::include_teleports_for_native_asset_works!(
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
1000
);
@@ -657,3 +652,32 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p
assert_eq!(ForeignAssets::asset_ids().collect::<Vec<_>>().len(), 1);
})
);
#[test]
fn reserve_transfer_native_asset_to_non_teleport_para_works() {
asset_test_utils::test_cases::reserve_transfer_native_asset_to_non_teleport_para_works::<
Runtime,
AllPalletsWithoutSystem,
XcmConfig,
ParachainSystem,
XcmpQueue,
LocationToAccountId,
>(
collator_session_keys(),
ExistentialDeposit::get(),
AccountId::from(ALICE),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event),
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
WeightLimit::Unlimited,
);
}
@@ -1077,7 +1077,7 @@ mod benches {
[pallet_xcm_bridge_hub_router, ToWestend]
[pallet_xcm_bridge_hub_router, ToRococo]
// XCM
[pallet_xcm, PolkadotXcm]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
// NOTE: Make sure you point to the individual modules below.
[pallet_xcm_benchmarks::fungible, XcmBalances]
[pallet_xcm_benchmarks::generic, XcmGeneric]
@@ -1315,6 +1315,7 @@ impl_runtime_apis! {
use frame_support::traits::StorageInfoTrait;
use frame_system_benchmarking::Pallet as SystemBench;
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench;
// This is defined once again in dispatch_benchmark, because list_benchmarks!
@@ -1368,6 +1369,39 @@ impl_runtime_apis! {
Config as XcmBridgeHubRouterConfig,
};
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(Parent.into())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported between AH and Relay.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
Parent.into(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// AH can reserve transfer native token to some random parachain.
let random_para_id = 43211234;
ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(
random_para_id.into()
);
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
ParentThen(Parachain(random_para_id).into()).into(),
))
}
}
impl XcmBridgeHubRouterConfig<ToWococoXcmRouterInstance> for Runtime {
fn make_congested() {
cumulus_pallet_xcmp_queue::bridging::suspend_channel_for_benchmarks::<Runtime>(
@@ -667,11 +667,6 @@ pub type XcmRouter = WithUniqueTopic<(
ToRococoXcmRouter,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
// We want to disallow users sending (arbitrary) XCMs from this chain.
@@ -701,8 +696,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -529,12 +529,6 @@ asset_test_utils::include_teleports_for_native_asset_works!(
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
1000
);
@@ -930,6 +924,35 @@ mod asset_hub_rococo_tests {
actual
);
}
#[test]
fn reserve_transfer_native_asset_to_non_teleport_para_works() {
asset_test_utils::test_cases::reserve_transfer_native_asset_to_non_teleport_para_works::<
Runtime,
AllPalletsWithoutSystem,
XcmConfig,
ParachainSystem,
XcmpQueue,
LocationToAccountId,
>(
collator_session_keys(),
ExistentialDeposit::get(),
AccountId::from(ALICE),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event),
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
WeightLimit::Unlimited,
);
}
}
mod asset_hub_wococo_tests {
@@ -1013,7 +1013,7 @@ mod benches {
[cumulus_pallet_dmp_queue, DmpQueue]
[pallet_xcm_bridge_hub_router, ToRococo]
// XCM
[pallet_xcm, PolkadotXcm]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
// NOTE: Make sure you point to the individual modules below.
[pallet_xcm_benchmarks::fungible, XcmBalances]
[pallet_xcm_benchmarks::generic, XcmGeneric]
@@ -1297,6 +1297,7 @@ impl_runtime_apis! {
use frame_support::traits::StorageInfoTrait;
use frame_system_benchmarking::Pallet as SystemBench;
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench;
// This is defined once again in dispatch_benchmark, because list_benchmarks!
@@ -1343,6 +1344,39 @@ impl_runtime_apis! {
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(Parent.into())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported between AH and Relay.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
Parent.into(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// AH can reserve transfer native token to some random parachain.
let random_para_id = 43211234;
ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(
random_para_id.into()
);
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
ParentThen(Parachain(random_para_id).into()).into(),
))
}
}
use pallet_xcm_bridge_hub_router::benchmarking::{
Pallet as XcmBridgeHubRouterBench,
Config as XcmBridgeHubRouterConfig,
@@ -636,11 +636,6 @@ pub type XcmRouter = WithUniqueTopic<(
ToRococoXcmRouter,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
@@ -666,8 +661,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -525,12 +525,6 @@ asset_test_utils::include_teleports_for_native_asset_works!(
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
1000
);
@@ -815,3 +809,32 @@ fn change_xcm_bridge_hub_router_byte_fee_by_governance_works() {
},
)
}
#[test]
fn reserve_transfer_native_asset_to_non_teleport_para_works() {
asset_test_utils::test_cases::reserve_transfer_native_asset_to_non_teleport_para_works::<
Runtime,
AllPalletsWithoutSystem,
XcmConfig,
ParachainSystem,
XcmpQueue,
LocationToAccountId,
>(
collator_session_keys(),
ExistentialDeposit::get(),
AccountId::from(ALICE),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event),
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
WeightLimit::Unlimited,
);
}
@@ -27,6 +27,21 @@ use std::fmt::Debug;
use xcm::latest::prelude::*;
use xcm_builder::{CreateMatcher, MatchXcm};
/// Given a message, a sender, and a destination, it returns the delivery fees
fn get_fungible_delivery_fees<S: SendXcm>(destination: MultiLocation, message: Xcm<()>) -> u128 {
let Ok((_, delivery_fees)) = validate_send::<S>(destination, message) else {
unreachable!("message can be sent; qed")
};
if let Some(delivery_fee) = delivery_fees.inner().first() {
let Fungible(delivery_fee_amount) = delivery_fee.fun else {
unreachable!("asset is fungible; qed");
};
delivery_fee_amount
} else {
0
}
}
/// Helper function to verify `xcm` contains all relevant instructions expected on destination
/// chain as part of a reserve-asset-transfer.
pub(crate) fn assert_matches_reserve_asset_deposited_instructions<RuntimeCall: Debug>(
@@ -16,25 +16,28 @@
//! Module contains predefined test-case scenarios for `Runtime` with various assets.
use super::xcm_helpers;
use crate::{assert_matches_reserve_asset_deposited_instructions, get_fungible_delivery_fees};
use codec::Encode;
use cumulus_primitives_core::XcmpMessageSource;
use frame_support::{
assert_noop, assert_ok,
traits::{
fungible::Mutate, fungibles::InspectEnumerable, Get, OnFinalize, OnInitialize, OriginTrait,
fungible::Mutate, fungibles::InspectEnumerable, Currency, Get, OnFinalize, OnInitialize,
OriginTrait,
},
weights::Weight,
};
use frame_system::pallet_prelude::BlockNumberFor;
use parachains_common::{AccountId, Balance};
use parachains_runtimes_test_utils::{
assert_metadata, assert_total, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder,
ValidatorIdOf, XcmReceivedFrom,
assert_metadata, assert_total, mock_open_hrmp_channel, AccountIdOf, BalanceOf,
CollatorSessionKeys, ExtBuilder, ValidatorIdOf, XcmReceivedFrom,
};
use sp_runtime::{
traits::{MaybeEquivalence, StaticLookup, Zero},
DispatchError, Saturating,
};
use xcm::latest::prelude::*;
use xcm::{latest::prelude::*, VersionedMultiAssets};
use xcm_executor::{traits::ConvertLocation, XcmExecutor};
type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
@@ -43,8 +46,8 @@ type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
// Re-export test_case from `parachains-runtimes-test-utils`
pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
/// Test-case makes sure that `Runtime` can receive native asset from relay chain
/// and can teleport it back and to the other parachains
/// Test-case makes sure that `Runtime` can receive native asset from relay chain and can teleport
/// it back
pub fn teleports_for_native_asset_works<
Runtime,
AllPalletsWithoutSystem,
@@ -57,9 +60,6 @@ pub fn teleports_for_native_asset_works<
existential_deposit: BalanceOf<Runtime>,
target_account: AccountIdOf<Runtime>,
unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
unwrap_xcmp_queue_event: Box<
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
>,
runtime_para_id: u32,
) where
Runtime: frame_system::Config
@@ -164,12 +164,13 @@ pub fn teleports_for_native_asset_works<
// 2. try to teleport asset back to the relaychain
{
let dest = MultiLocation::parent();
let dest_beneficiary = MultiLocation::parent()
let mut dest_beneficiary = MultiLocation::parent()
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
let target_account_balance_before_teleport =
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
@@ -223,65 +224,53 @@ pub fn teleports_for_native_asset_works<
);
}
// 3. try to teleport asset away to other parachain (1234)
// 3. try to teleport assets away to other parachain (2345): should not work as we don't
// trust `IsTeleporter` for `(relay-native-asset, para(2345))` pair
{
let other_para_id = 1234;
let other_para_id = 2345;
let dest = MultiLocation::new(1, X1(Parachain(other_para_id)));
let dest_beneficiary = MultiLocation::new(1, X1(Parachain(other_para_id)))
let mut dest_beneficiary = MultiLocation::new(1, X1(Parachain(other_para_id)))
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
let target_account_balance_before_teleport =
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
let native_asset_to_teleport_away = native_asset_amount_unit * 3.into();
assert!(
native_asset_to_teleport_away <
target_account_balance_before_teleport - existential_deposit
);
assert_ok!(RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
dest,
dest_beneficiary,
(native_asset_id, native_asset_to_teleport_away.into()),
Some((runtime_para_id, other_para_id)),
included_head,
&alice,
));
let delivery_fees =
xcm_helpers::transfer_assets_delivery_fees::<XcmConfig::XcmSender>(
(native_asset_id, native_asset_to_teleport_away.into()).into(),
0,
Unlimited,
dest_beneficiary,
assert_eq!(
RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
dest,
);
dest_beneficiary,
(native_asset_id, native_asset_to_teleport_away.into()),
Some((runtime_para_id, other_para_id)),
included_head,
&alice,
),
Err(DispatchError::Module(sp_runtime::ModuleError {
index: 31,
error: [2, 0, 0, 0,],
message: Some("Filtered",),
},),)
);
// check balances
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
target_account_balance_before_teleport -
native_asset_to_teleport_away -
delivery_fees.into()
target_account_balance_before_teleport
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
0.into()
);
// check events
RuntimeHelper::<Runtime>::assert_pallet_xcm_event_outcome(
&unwrap_pallet_xcm_event,
|outcome| {
assert_ok!(outcome.ensure_complete());
},
);
assert!(RuntimeHelper::<Runtime>::xcmp_queue_message_sent(unwrap_xcmp_queue_event)
.is_some());
}
})
}
@@ -298,7 +287,6 @@ macro_rules! include_teleports_for_native_asset_works(
$collator_session_key:expr,
$existential_deposit:expr,
$unwrap_pallet_xcm_event:expr,
$unwrap_xcmp_queue_event:expr,
$runtime_para_id:expr
) => {
#[test]
@@ -318,15 +306,14 @@ macro_rules! include_teleports_for_native_asset_works(
$existential_deposit,
target_account,
$unwrap_pallet_xcm_event,
$unwrap_xcmp_queue_event,
$runtime_para_id
)
}
}
);
/// Test-case makes sure that `Runtime` can receive teleported assets from sibling parachain relay
/// chain
/// Test-case makes sure that `Runtime` can receive teleported assets from sibling parachain, and
/// can teleport it back
pub fn teleports_for_foreign_assets_works<
Runtime,
AllPalletsWithoutSystem,
@@ -381,7 +368,7 @@ pub fn teleports_for_foreign_assets_works<
<Runtime as frame_system::Config>::AccountId: From<AccountId>,
ForeignAssetsPalletInstance: 'static,
{
// foreign parachain with the same consenus currency as asset
// foreign parachain with the same consensus currency as asset
let foreign_para_id = 2222;
let foreign_asset_id_multilocation = MultiLocation {
parents: 1,
@@ -473,7 +460,7 @@ pub fn teleports_for_foreign_assets_works<
>(foreign_asset_id_multilocation, 0, 0);
assert!(teleported_foreign_asset_amount > asset_minimum_asset_balance);
// 1. process received teleported assets from relaychain
// 1. process received teleported assets from sibling parachain (foreign_para_id)
let xcm = Xcm(vec![
// BuyExecution with relaychain native token
WithdrawAsset(buy_execution_fee.clone().into()),
@@ -551,12 +538,13 @@ pub fn teleports_for_foreign_assets_works<
// 2. try to teleport asset back to source parachain (foreign_para_id)
{
let dest = MultiLocation::new(1, X1(Parachain(foreign_para_id)));
let dest_beneficiary = MultiLocation::new(1, X1(Parachain(foreign_para_id)))
let mut dest_beneficiary = MultiLocation::new(1, X1(Parachain(foreign_para_id)))
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
let target_account_balance_before_teleport =
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
@@ -1108,7 +1096,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor
AssetId: Clone + Copy,
AssetIdConverter: MaybeEquivalence<MultiLocation, AssetId>,
{
// foreign parachain with the same consenus currency as asset
// foreign parachain with the same consensus currency as asset
let foreign_asset_id_multilocation =
MultiLocation { parents: 1, interior: X2(Parachain(2222), GeneralIndex(1234567)) };
let asset_id = AssetIdConverter::convert(&foreign_asset_id_multilocation).unwrap();
@@ -1388,3 +1376,199 @@ macro_rules! include_create_and_manage_foreign_assets_for_local_consensus_parach
}
}
);
/// Test-case makes sure that `Runtime` can reserve-transfer asset to other parachains (where
/// teleport is not trusted)
pub fn reserve_transfer_native_asset_to_non_teleport_para_works<
Runtime,
AllPalletsWithoutSystem,
XcmConfig,
HrmpChannelOpener,
HrmpChannelSource,
LocationToAccountId,
>(
collator_session_keys: CollatorSessionKeys<Runtime>,
existential_deposit: BalanceOf<Runtime>,
alice_account: AccountIdOf<Runtime>,
unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
unwrap_xcmp_queue_event: Box<
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
>,
weight_limit: WeightLimit,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config
+ cumulus_pallet_xcmp_queue::Config,
AllPalletsWithoutSystem:
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
AccountIdOf<Runtime>: Into<[u8; 32]>,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
BalanceOf<Runtime>: From<Balance>,
<Runtime as pallet_balances::Config>::Balance: From<Balance> + Into<u128>,
XcmConfig: xcm_executor::Config,
LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
<Runtime as frame_system::Config>::AccountId:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
From<<Runtime as frame_system::Config>::AccountId>,
<Runtime as frame_system::Config>::AccountId: From<AccountId>,
HrmpChannelOpener: frame_support::inherent::ProvideInherent<
Call = cumulus_pallet_parachain_system::Call<Runtime>,
>,
HrmpChannelSource: XcmpMessageSource,
{
let runtime_para_id = 1000;
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_keys.collators())
.with_session_keys(collator_session_keys.session_keys())
.with_tracing()
.with_safe_xcm_version(3)
.with_para_id(runtime_para_id.into())
.build()
.execute_with(|| {
let mut alice = [0u8; 32];
alice[0] = 1;
let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
2,
AccountId::from(alice).into(),
);
// reserve-transfer native asset with local reserve to remote parachain (2345)
let other_para_id = 2345;
let native_asset = MultiLocation::parent();
let dest = MultiLocation::new(1, X1(Parachain(other_para_id)));
let mut dest_beneficiary = MultiLocation::new(1, X1(Parachain(other_para_id)))
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
let reserve_account = LocationToAccountId::convert_location(&dest)
.expect("Sovereign account for reserves");
let balance_to_transfer = 1_000_000_000_000_u128;
// open HRMP to other parachain
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
runtime_para_id.into(),
other_para_id.into(),
included_head,
&alice,
);
// we calculate exact delivery fees _after_ sending the message by weighing the sent
// xcm, and this delivery fee varies for different runtimes, so just add enough buffer,
// then verify the arithmetics check out on final balance.
let delivery_fees_buffer = 40_000_000_000u128;
// drip 2xED + transfer_amount + delivery_fees_buffer to Alice account
let alice_account_init_balance = existential_deposit.saturating_mul(2.into()) +
balance_to_transfer.into() +
delivery_fees_buffer.into();
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
&alice_account,
alice_account_init_balance,
);
// SA of target location needs to have at least ED, otherwise making reserve fails
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
&reserve_account,
existential_deposit,
);
// we just check here, that user retains enough balance after withdrawal
// and also we check if `balance_to_transfer` is more than `existential_deposit`,
assert!(
(<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account) -
balance_to_transfer.into()) >=
existential_deposit
);
// SA has just ED
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
existential_deposit
);
// local native asset (pallet_balances)
let asset_to_transfer = MultiAsset {
fun: Fungible(balance_to_transfer.into()),
id: Concrete(native_asset),
};
// pallet_xcm call reserve transfer
assert_ok!(<pallet_xcm::Pallet<Runtime>>::limited_reserve_transfer_assets(
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(alice_account.clone()),
Box::new(dest.into_versioned()),
Box::new(dest_beneficiary.into_versioned()),
Box::new(VersionedMultiAssets::from(MultiAssets::from(asset_to_transfer))),
0,
weight_limit,
));
// check events
// check pallet_xcm attempted
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::assert_pallet_xcm_event_outcome(
&unwrap_pallet_xcm_event,
|outcome| {
assert_ok!(outcome.ensure_complete());
},
);
// check that xcm was sent
let xcm_sent_message_hash = <frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
.find_map(|e| match e {
cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } =>
Some(message_hash),
_ => None,
});
// read xcm
let xcm_sent = RuntimeHelper::<HrmpChannelSource, AllPalletsWithoutSystem>::take_xcm(
other_para_id.into(),
)
.unwrap();
let delivery_fees = get_fungible_delivery_fees::<
<XcmConfig as xcm_executor::Config>::XcmSender,
>(dest, Xcm::try_from(xcm_sent.clone()).unwrap());
assert_eq!(
xcm_sent_message_hash,
Some(xcm_sent.using_encoded(sp_io::hashing::blake2_256))
);
let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm");
// check sent XCM Program to other parachain
println!("reserve_transfer_native_asset_works sent xcm: {:?}", xcm_sent);
let reserve_assets_deposited = MultiAssets::from(vec![MultiAsset {
id: Concrete(MultiLocation { parents: 1, interior: Here }),
fun: Fungible(1000000000000),
}]);
assert_matches_reserve_asset_deposited_instructions(
&mut xcm_sent,
&reserve_assets_deposited,
&dest_beneficiary,
);
// check alice account decreased by balance_to_transfer ( + delivery_fees)
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
alice_account_init_balance - balance_to_transfer.into() - delivery_fees.into()
);
// check reserve account
// check reserve account increased by balance_to_transfer
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
existential_deposit + balance_to_transfer.into()
);
})
}
@@ -16,7 +16,7 @@
//! Module contains predefined test-case scenarios for `Runtime` with various assets transferred
//! over a bridge.
use crate::assert_matches_reserve_asset_deposited_instructions;
use crate::{assert_matches_reserve_asset_deposited_instructions, get_fungible_delivery_fees};
use codec::Encode;
use cumulus_primitives_core::XcmpMessageSource;
use frame_support::{
@@ -32,10 +32,7 @@ use parachains_runtimes_test_utils::{
use sp_runtime::{traits::StaticLookup, Saturating};
use xcm::{latest::prelude::*, VersionedMultiAssets};
use xcm_builder::{CreateMatcher, MatchXcm};
use xcm_executor::{
traits::{ConvertLocation, TransactAsset},
XcmExecutor,
};
use xcm_executor::{traits::ConvertLocation, XcmExecutor};
pub struct TestBridgingConfig {
pub bridged_network: NetworkId,
@@ -129,8 +126,13 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
&alice,
);
// drip ED to account
let alice_account_init_balance = existential_deposit + balance_to_transfer.into();
// we calculate exact delivery fees _after_ sending the message by weighing the sent
// xcm, and this delivery fee varies for different runtimes, so just add enough buffer,
// then verify the arithmetics check out on final balance.
let delivery_fees_buffer = 8_000_000_000_000u128;
// drip ED + transfer_amount + delivery_fees_buffer to Alice account
let alice_account_init_balance =
existential_deposit + balance_to_transfer.into() + delivery_fees_buffer.into();
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
&alice_account,
alice_account_init_balance,
@@ -183,56 +185,6 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
let expected_beneficiary = target_destination_account;
// Make sure sender has enough funds for paying delivery fees
let handling_delivery_fees = {
// Probable XCM with `ReserveAssetDeposited`.
let mut expected_reserve_asset_deposited_message = Xcm(vec![
ReserveAssetDeposited(MultiAssets::from(expected_assets.clone())),
ClearOrigin,
BuyExecution {
fees: MultiAsset {
id: Concrete(Default::default()),
fun: Fungible(balance_to_transfer),
},
weight_limit: Unlimited,
},
DepositAsset { assets: Wild(AllCounted(1)), beneficiary: expected_beneficiary },
SetTopic([
220, 188, 144, 32, 213, 83, 111, 175, 44, 210, 111, 19, 90, 165, 191, 112,
140, 247, 192, 124, 42, 17, 153, 141, 114, 34, 189, 20, 83, 69, 237, 173,
]),
]);
assert_matches_reserve_asset_deposited_instructions(
&mut expected_reserve_asset_deposited_message,
&expected_assets,
&expected_beneficiary,
);
// Call `SendXcm::validate` to get delivery fees.
let (_, delivery_fees): (_, MultiAssets) = XcmConfig::XcmSender::validate(
&mut Some(target_location_from_different_consensus),
&mut Some(expected_reserve_asset_deposited_message),
)
.expect("validate passes");
// Drip delivery fee to Alice account.
let mut delivery_fees_added = false;
for delivery_fee in delivery_fees.inner() {
assert_ok!(<XcmConfig::AssetTransactor as TransactAsset>::deposit_asset(
&delivery_fee,
&MultiLocation {
parents: 0,
interior: X1(AccountId32 {
network: None,
id: alice_account.clone().into(),
}),
},
None,
));
delivery_fees_added = true;
}
delivery_fees_added
};
// do pallet_xcm call reserve transfer
assert_ok!(<pallet_xcm::Pallet<Runtime>>::limited_reserve_transfer_assets(
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(alice_account.clone()),
@@ -275,6 +227,7 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
// check sent XCM ExportMessage to BridgeHub
let mut delivery_fees = 0;
// 1. check paid or unpaid
if let Some(expected_fee_asset_id) = maybe_paid_export_message {
xcm_sent
@@ -315,6 +268,10 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
.split_global()
.expect("split works");
assert_eq!(destination, &target_location_junctions_without_global_consensus);
// Call `SendXcm::validate` to get delivery fees.
delivery_fees = get_fungible_delivery_fees::<
<XcmConfig as xcm_executor::Config>::XcmSender,
>(target_location_from_different_consensus, inner_xcm.clone());
assert_matches_reserve_asset_deposited_instructions(
inner_xcm,
&expected_assets,
@@ -330,8 +287,8 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
alice_account_init_balance
.saturating_sub(existential_deposit)
.saturating_sub(balance_to_transfer.into())
.saturating_sub(delivery_fees.into())
);
// check reserve account increased by balance_to_transfer
@@ -341,14 +298,13 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
);
// check dedicated account increased by delivery fees (if configured)
if handling_delivery_fees {
if let Some(delivery_fees_account) = delivery_fees_account {
let delivery_fees_account_balance_after =
<pallet_balances::Pallet<Runtime>>::free_balance(&delivery_fees_account);
assert!(
delivery_fees_account_balance_after > delivery_fees_account_balance_before
);
}
if let Some(delivery_fees_account) = delivery_fees_account {
let delivery_fees_account_balance_after =
<pallet_balances::Pallet<Runtime>>::free_balance(&delivery_fees_account);
assert!(
delivery_fees_account_balance_after - delivery_fees.into() >=
delivery_fees_account_balance_before
);
}
})
}
@@ -270,7 +270,7 @@ cd <polkadot-sdk-git-repo-dir>
### Send messages - transfer asset over bridge (ROCs/WNDs)
Do (asset) transfers:
Do reserve-backed transfers:
```
cd <polkadot-sdk-git-repo-dir>
@@ -291,6 +291,20 @@ cd <polkadot-sdk-git-repo-dir>
- AssetHubWestend (see `foreignAssets.Issued`, `xcmpQueue.Success`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9010#/explorer
- BridgeHubRocococ (see `bridgeWestendMessages.MessagesDelivered`) https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:8943#/explorer
Do reserve withdraw transfers: (when previous is finished)
```
cd <polkadot-sdk-git-repo-dir>
# wrappedWNDs from Rococo's Asset Hub to Westend's.
./cumulus/scripts/bridges_rococo_westend.sh withdraw-reserve-assets-from-asset-hub-rococo-local
```
```
cd <polkadot-sdk-git-repo-dir>
# wrappedROCs from Westend's Asset Hub to Rococo's.
./cumulus/scripts/bridges_rococo_westend.sh withdraw-reserve-assets-from-asset-hub-westend-local
```
### Claim relayer's rewards on BridgeHubRococo and BridgeHubWestend
**Accounts of BridgeHub parachains:**
@@ -490,7 +490,7 @@ mod benches {
[cumulus_pallet_xcmp_queue, XcmpQueue]
[cumulus_pallet_dmp_queue, DmpQueue]
// XCM
[pallet_xcm, PolkadotXcm]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
// NOTE: Make sure you point to the individual modules below.
[pallet_xcm_benchmarks::fungible, XcmBalances]
[pallet_xcm_benchmarks::generic, XcmGeneric]
@@ -670,6 +670,7 @@ impl_runtime_apis! {
use frame_support::traits::StorageInfoTrait;
use frame_system_benchmarking::Pallet as SystemBench;
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
// This is defined once again in dispatch_benchmark, because list_benchmarks!
// and add_benchmarks! are macros exported by define_benchmarks! macros and those types
@@ -705,6 +706,29 @@ impl_runtime_apis! {
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(Parent.into())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported between BH and Relay.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
Parent.into(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Reserve transfers are disabled on BH.
None
}
}
use xcm::latest::prelude::*;
use xcm_config::KsmRelayLocation;
@@ -241,11 +241,6 @@ pub type XcmRouter = WithUniqueTopic<(
XcmpQueue,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
// We want to disallow users sending (arbitrary) XCMs from this chain.
@@ -274,8 +269,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -47,11 +47,5 @@ bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!(
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
1002
);
@@ -491,7 +491,7 @@ mod benches {
[cumulus_pallet_xcmp_queue, XcmpQueue]
[cumulus_pallet_dmp_queue, DmpQueue]
// XCM
[pallet_xcm, PolkadotXcm]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
// NOTE: Make sure you point to the individual modules below.
[pallet_xcm_benchmarks::fungible, XcmBalances]
[pallet_xcm_benchmarks::generic, XcmGeneric]
@@ -671,6 +671,7 @@ impl_runtime_apis! {
use frame_support::traits::StorageInfoTrait;
use frame_system_benchmarking::Pallet as SystemBench;
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
// This is defined once again in dispatch_benchmark, because list_benchmarks!
// and add_benchmarks! are macros exported by define_benchmarks! macros and those types
@@ -706,6 +707,29 @@ impl_runtime_apis! {
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(Parent.into())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported between BH and Relay.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
Parent.into(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Reserve transfers are disabled on BH.
None
}
}
use xcm::latest::prelude::*;
use xcm_config::DotRelayLocation;
@@ -245,11 +245,6 @@ pub type XcmRouter = WithUniqueTopic<(
XcmpQueue,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
// We want to disallow users sending (arbitrary) XCMs from this chain.
@@ -278,8 +273,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -47,11 +47,5 @@ bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!(
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
1002
);
@@ -595,7 +595,7 @@ mod benches {
[cumulus_pallet_xcmp_queue, XcmpQueue]
[cumulus_pallet_dmp_queue, DmpQueue]
// XCM
[pallet_xcm, PolkadotXcm]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
// NOTE: Make sure you point to the individual modules below.
[pallet_xcm_benchmarks::fungible, XcmBalances]
[pallet_xcm_benchmarks::generic, XcmGeneric]
@@ -933,6 +933,7 @@ impl_runtime_apis! {
use frame_support::traits::StorageInfoTrait;
use frame_system_benchmarking::Pallet as SystemBench;
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
// This is defined once again in dispatch_benchmark, because list_benchmarks!
// and add_benchmarks! are macros exported by define_benchmarks! macros and those types
@@ -980,6 +981,29 @@ impl_runtime_apis! {
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(Parent.into())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported between BH and Relay.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
Parent.into(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Reserve transfers are disabled on BH.
None
}
}
use xcm::latest::prelude::*;
use xcm_config::TokenLocation;
@@ -349,11 +349,6 @@ pub type XcmRouter = WithUniqueTopic<(
XcmpQueue,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type XcmRouter = XcmRouter;
@@ -381,8 +376,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -133,12 +133,6 @@ mod bridge_hub_rococo_tests {
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID
);
@@ -517,12 +511,6 @@ mod bridge_hub_wococo_tests {
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
bp_bridge_hub_wococo::BRIDGE_HUB_WOCOCO_PARACHAIN_ID
);
@@ -544,7 +544,7 @@ mod benches {
[pallet_collator_selection, CollatorSelection]
[cumulus_pallet_xcmp_queue, XcmpQueue]
// XCM
[pallet_xcm, PolkadotXcm]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
// NOTE: Make sure you point to the individual modules below.
[pallet_xcm_benchmarks::fungible, XcmBalances]
[pallet_xcm_benchmarks::generic, XcmGeneric]
@@ -772,6 +772,7 @@ impl_runtime_apis! {
use frame_support::traits::StorageInfoTrait;
use frame_system_benchmarking::Pallet as SystemBench;
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
// This is defined once again in dispatch_benchmark, because list_benchmarks!
// and add_benchmarks! are macros exported by define_benchmarks! macros and those types
@@ -813,6 +814,29 @@ impl_runtime_apis! {
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(Parent.into())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported between BH and Relay.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
Parent.into(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Reserve transfers are disabled on BH.
None
}
}
use xcm::latest::prelude::*;
use xcm_config::WestendLocation;
@@ -284,11 +284,6 @@ pub type XcmRouter = WithUniqueTopic<(
XcmpQueue,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type XcmRouter = XcmRouter;
@@ -316,8 +311,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -118,12 +118,6 @@ bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!(
_ => None,
}
}),
Box::new(|runtime_event_encoded: Vec<u8>| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
_ => None,
}
}),
bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID
);
@@ -751,7 +751,7 @@ mod benches {
[cumulus_pallet_dmp_queue, DmpQueue]
[pallet_alliance, Alliance]
[pallet_collective, AllianceMotion]
[pallet_xcm, PolkadotXcm]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
[pallet_preimage, Preimage]
[pallet_scheduler, Scheduler]
[pallet_referenda, FellowshipReferenda]
@@ -939,6 +939,7 @@ impl_runtime_apis! {
use frame_support::traits::StorageInfoTrait;
use frame_system_benchmarking::Pallet as SystemBench;
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
let mut list = Vec::<BenchmarkList>::new();
list_benchmarks!(list, extra);
@@ -968,6 +969,29 @@ impl_runtime_apis! {
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(Parent.into())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported between Collectives and Relay.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
}.into(),
Parent.into(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Reserve transfers are disabled on Collectives.
None
}
}
let whitelist: Vec<TrackedStorageKey> = vec![
// Block Number
hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),
@@ -293,11 +293,6 @@ pub type XcmRouter = WithUniqueTopic<(
XcmpQueue,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
/// Type to convert the Fellows origin to a Plurality `MultiLocation` value.
pub type FellowsToPlurality = OriginToPluralityVoice<RuntimeOrigin, Fellows, FellowsBodyId>;
@@ -325,8 +320,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -433,7 +433,7 @@ mod benches {
[pallet_timestamp, Timestamp]
[pallet_collator_selection, CollatorSelection]
[pallet_contracts, Contracts]
[pallet_xcm, PolkadotXcm]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
);
}
@@ -678,6 +678,7 @@ impl_runtime_apis! {
use frame_support::traits::StorageInfoTrait;
use frame_system_benchmarking::Pallet as SystemBench;
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
let mut list = Vec::<BenchmarkList>::new();
list_benchmarks!(list, extra);
@@ -707,6 +708,30 @@ impl_runtime_apis! {
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
use xcm::latest::prelude::*;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(Parent.into())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported between Contracts-System-Para and Relay.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Parent.into())
},
Parent.into(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Reserve transfers are disabled on Contracts-System-Para.
None
}
}
let whitelist: Vec<TrackedStorageKey> = vec![
// Block Number
hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),
@@ -227,11 +227,6 @@ pub type XcmRouter = WithUniqueTopic<(
XcmpQueue,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
// We want to disallow users sending (arbitrary) XCMs from this chain.
@@ -258,8 +253,6 @@ impl pallet_xcm::Config for Runtime {
type MaxLockers = ConstU32<8>;
// FIXME: Replace with benchmarked weight info
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -434,7 +434,7 @@ parameter_types! {
// pub type AssetsForceOrigin =
// EnsureOneOf<EnsureRoot<AccountId>, EnsureXcm<IsMajorityOfBody<KsmLocation, ExecutiveBody>>>;
impl pallet_assets::Config for Runtime {
impl pallet_assets::Config<pallet_assets::Instance1> for Runtime {
type RuntimeEvent = RuntimeEvent;
type Balance = Balance;
type AssetId = AssetId;
@@ -577,7 +577,12 @@ impl pallet_asset_tx_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Fungibles = Assets;
type OnChargeAssetTransaction = pallet_asset_tx_payment::FungiblesAdapter<
pallet_assets::BalanceToAssetBalance<Balances, Runtime, ConvertInto>,
pallet_assets::BalanceToAssetBalance<
Balances,
Runtime,
ConvertInto,
pallet_assets::Instance1,
>,
AssetsToBlockAuthor<Runtime>,
>;
}
@@ -619,7 +624,7 @@ construct_runtime!(
MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event<T>} = 34,
// The main stage.
Assets: pallet_assets::{Pallet, Call, Storage, Event<T>} = 50,
Assets: pallet_assets::<Instance1>::{Pallet, Call, Storage, Event<T>} = 50,
Sudo: pallet_sudo::{Pallet, Call, Storage, Event<T>, Config<T>} = 255,
}
@@ -38,6 +38,7 @@ use frame_support::{
};
use frame_system::EnsureRoot;
use pallet_asset_tx_payment::HandleCredit;
use pallet_assets::Instance1;
use pallet_xcm::XcmPassthrough;
use polkadot_parachain_primitives::primitives::Sibling;
use polkadot_runtime_common::impls::ToAuthor;
@@ -48,9 +49,10 @@ use xcm_builder::{
AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex,
ConvertedConcreteId, CurrencyAdapter, DenyReserveTransferToRelayChain, DenyThenTry,
EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset,
ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic,
ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents,
WithComputedOrigin, WithUniqueTopic,
};
use xcm_executor::{traits::JustTry, XcmExecutor};
@@ -126,6 +128,9 @@ pub type XcmOriginToTransactDispatchOrigin = (
// Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
// recognized.
SiblingParachainAsNative<cumulus_pallet_xcm::Origin, RuntimeOrigin>,
// Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a
// transaction from the Root origin.
ParentAsSuperuser<RuntimeOrigin>,
// Native signed account converter; this just converts an `AccountId32` origin into a normal
// `RuntimeOrigin::Signed` origin of the same 32-byte value.
SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>,
@@ -182,14 +187,25 @@ pub type Barrier = TrailingSetTopicAsId<
/// Type alias to conveniently refer to `frame_system`'s `Config::AccountId`.
pub type AccountIdOf<R> = <R as frame_system::Config>::AccountId;
/// Asset filter that allows all assets from a certain location.
/// Asset filter that allows all assets from a certain location matching asset id.
pub struct AssetsFrom<T>(PhantomData<T>);
impl<T: Get<MultiLocation>> ContainsPair<MultiAsset, MultiLocation> for AssetsFrom<T> {
fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool {
let loc = T::get();
&loc == origin &&
matches!(asset, MultiAsset { id: AssetId::Concrete(asset_loc), fun: Fungible(_a) }
if asset_loc.match_and_split(&loc).is_some())
if asset_loc.starts_with(&loc))
}
}
/// Asset filter that allows native/relay asset if coming from a certain location.
pub struct NativeAssetFrom<T>(PhantomData<T>);
impl<T: Get<MultiLocation>> ContainsPair<MultiAsset, MultiLocation> for NativeAssetFrom<T> {
fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool {
let loc = T::get();
&loc == origin &&
matches!(asset, MultiAsset { id: AssetId::Concrete(asset_loc), fun: Fungible(_a) }
if *asset_loc == MultiLocation::from(Parent))
}
}
@@ -208,56 +224,19 @@ where
/// A `HandleCredit` implementation that naively transfers the fees to the block author.
/// Will drop and burn the assets in case the transfer fails.
pub struct AssetsToBlockAuthor<R>(PhantomData<R>);
impl<R> HandleCredit<AccountIdOf<R>, pallet_assets::Pallet<R>> for AssetsToBlockAuthor<R>
impl<R> HandleCredit<AccountIdOf<R>, pallet_assets::Pallet<R, Instance1>> for AssetsToBlockAuthor<R>
where
R: pallet_authorship::Config + pallet_assets::Config,
R: pallet_authorship::Config + pallet_assets::Config<Instance1>,
AccountIdOf<R>: From<polkadot_primitives::AccountId> + Into<polkadot_primitives::AccountId>,
{
fn handle_credit(credit: Credit<AccountIdOf<R>, pallet_assets::Pallet<R>>) {
fn handle_credit(credit: Credit<AccountIdOf<R>, pallet_assets::Pallet<R, Instance1>>) {
if let Some(author) = pallet_authorship::Pallet::<R>::author() {
// In case of error: Will drop the result triggering the `OnDrop` of the imbalance.
let _ = pallet_assets::Pallet::<R>::resolve(&author, credit);
let _ = pallet_assets::Pallet::<R, Instance1>::resolve(&author, credit);
}
}
}
pub trait Reserve {
/// Returns assets reserve location.
fn reserve(&self) -> Option<MultiLocation>;
}
// Takes the chain part of a MultiAsset
impl Reserve for MultiAsset {
fn reserve(&self) -> Option<MultiLocation> {
if let AssetId::Concrete(location) = self.id {
let first_interior = location.first_interior();
let parents = location.parent_count();
match (parents, first_interior) {
(0, Some(Parachain(id))) => Some(MultiLocation::new(0, X1(Parachain(*id)))),
(1, Some(Parachain(id))) => Some(MultiLocation::new(1, X1(Parachain(*id)))),
(1, _) => Some(MultiLocation::parent()),
_ => None,
}
} else {
None
}
}
}
/// A `FilterAssetLocation` implementation. Filters multi native assets whose
/// reserve is same with `origin`.
pub struct MultiNativeAsset;
impl ContainsPair<MultiAsset, MultiLocation> for MultiNativeAsset {
fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool {
if let Some(ref reserve) = asset.reserve() {
if reserve == origin {
return true
}
}
false
}
}
parameter_types! {
/// The location that this chain recognizes as the Relay network's Asset Hub.
pub SystemAssetHubLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(1000)));
@@ -268,7 +247,8 @@ parameter_types! {
pub CheckingAccount: AccountId = PolkadotXcm::check_account();
}
pub type Reserves = (NativeAsset, AssetsFrom<SystemAssetHubLocation>);
pub type Reserves =
(NativeAsset, AssetsFrom<SystemAssetHubLocation>, NativeAssetFrom<SystemAssetHubLocation>);
pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
@@ -277,7 +257,8 @@ impl xcm_executor::Config for XcmConfig {
// How to withdraw and deposit an asset.
type AssetTransactor = AssetTransactors;
type OriginConverter = XcmOriginToTransactDispatchOrigin;
type IsReserve = MultiNativeAsset; // TODO: maybe needed to be replaced by Reserves
type IsReserve = Reserves;
// no teleport trust established with other chains
type IsTeleporter = NativeAsset;
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
@@ -312,11 +293,6 @@ pub type XcmRouter = WithUniqueTopic<(
XcmpQueue,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
@@ -342,8 +318,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
@@ -492,11 +492,6 @@ pub type XcmRouter = WithUniqueTopic<(
XcmpQueue,
)>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
@@ -518,8 +513,6 @@ impl pallet_xcm::Config for Runtime {
type SovereignAccountOf = LocationToAccountId;
type MaxLockers = ConstU32<8>;
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
+27 -1
View File
@@ -301,9 +301,21 @@ case "$1" in
0 \
"Unlimited"
;;
withdraw-reserve-assets-from-asset-hub-rococo-local)
ensure_polkadot_js_api
# send back only 100000000000 wrappedWNDs to Alice account on AHW
limited_reserve_transfer_assets \
"ws://127.0.0.1:9910" \
"//Alice" \
"$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Westend" }, { "Parachain": 1000 } ] } } }')" \
"$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] } } } } }')" \
"$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Westend" } } } }, "fun": { "Fungible": 140000000000 } } ] }')" \
0 \
"Unlimited"
;;
reserve-transfer-assets-from-asset-hub-westend-local)
ensure_polkadot_js_api
# send WOCs to Alice account on AHR
# send WNDs to Alice account on AHR
limited_reserve_transfer_assets \
"ws://127.0.0.1:9010" \
"//Alice" \
@@ -313,6 +325,18 @@ case "$1" in
0 \
"Unlimited"
;;
withdraw-reserve-assets-from-asset-hub-westend-local)
ensure_polkadot_js_api
# send back only 100000000000 wrappedROCs to Alice account on AHR
limited_reserve_transfer_assets \
"ws://127.0.0.1:9010" \
"//Alice" \
"$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Rococo" }, { "Parachain": 1000 } ] } } }')" \
"$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] } } } } }')" \
"$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Rococo" } } } }, "fun": { "Fungible": 100000000000 } } ] }')" \
0 \
"Unlimited"
;;
claim-rewards-bridge-hub-rococo-local)
ensure_polkadot_js_api
# bhwd -> [62, 68, 77, 64] -> 0x62687764
@@ -360,7 +384,9 @@ case "$1" in
- init-asset-hub-westend-local
- init-bridge-hub-westend-local
- reserve-transfer-assets-from-asset-hub-rococo-local
- withdraw-reserve-assets-from-asset-hub-rococo-local
- reserve-transfer-assets-from-asset-hub-westend-local
- withdraw-reserve-assets-from-asset-hub-westend-local
- claim-rewards-bridge-hub-rococo-local
- claim-rewards-bridge-hub-westend-local";
exit 1
+4 -4
View File
@@ -1443,9 +1443,9 @@ pub struct TestContext<T, Origin: Chain, Destination: Chain> {
/// These arguments can be easily reused and shared between the assertion functions
/// and dispatchable functions, which are also stored in `Test`.
/// `Origin` corresponds to the chain where the XCM interaction starts with an initial execution.
/// `Destination` corresponds to the last chain where an effect of the intial execution is expected
/// happen. `Hops` refer all the ordered intermediary chains an initial XCM execution can provoke
/// some effect.
/// `Destination` corresponds to the last chain where an effect of the initial execution is expected
/// to happen. `Hops` refer to all the ordered intermediary chains an initial XCM execution can
/// provoke some effect on.
#[derive(Clone)]
pub struct Test<Origin, Destination, Hops = (), Args = TestArgs>
where
@@ -1499,7 +1499,7 @@ where
let chain_name = std::any::type_name::<Hop>();
self.hops_assertion.insert(chain_name.to_string(), assertion);
}
/// Stores an assertion in a particular Chain
/// Stores a dispatchable in a particular Chain
pub fn set_dispatchable<Hop>(&mut self, dispatchable: fn(Self) -> DispatchResult) {
let chain_name = std::any::type_name::<Hop>();
self.hops_dispatchable.insert(chain_name.to_string(), dispatchable);
+1 -1
View File
@@ -2064,7 +2064,7 @@ impl<T: Config> Pallet<T> {
}
/// Submits a given PVF check statement with corresponding signature as an unsigned transaction
/// into the memory pool. Ultimately, that disseminates the transaction accross the network.
/// into the memory pool. Ultimately, that disseminates the transaction across the network.
///
/// This function expects an offchain context and cannot be callable from the on-chain logic.
///
+31 -1
View File
@@ -1588,7 +1588,7 @@ mod benches {
[pallet_asset_rate, AssetRate]
[pallet_whitelist, Whitelist]
// XCM
[pallet_xcm, XcmPallet]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
[pallet_xcm_benchmarks::fungible, pallet_xcm_benchmarks::fungible::Pallet::<Runtime>]
[pallet_xcm_benchmarks::generic, pallet_xcm_benchmarks::generic::Pallet::<Runtime>]
);
@@ -2064,6 +2064,8 @@ sp_api::impl_runtime_apis! {
use frame_system_benchmarking::Pallet as SystemBench;
use frame_benchmarking::baseline::Pallet as Baseline;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
let mut list = Vec::<BenchmarkList>::new();
list_benchmarks!(list, extra);
@@ -2081,6 +2083,7 @@ sp_api::impl_runtime_apis! {
use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError};
use frame_system_benchmarking::Pallet as SystemBench;
use frame_benchmarking::baseline::Pallet as Baseline;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
use sp_storage::TrackedStorageKey;
use xcm::latest::prelude::*;
use xcm_config::{
@@ -2097,6 +2100,33 @@ sp_api::impl_runtime_apis! {
impl frame_system_benchmarking::Config for Runtime {}
impl frame_benchmarking::baseline::Config for Runtime {}
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(crate::xcm_config::AssetHub::get())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported to/from AH.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Here.into())
},
crate::xcm_config::AssetHub::get(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay can reserve transfer native token to some random parachain.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Here.into())
},
Parachain(43211234).into(),
))
}
}
impl pallet_xcm_benchmarks::Config for Runtime {
type XcmConfig = XcmConfig;
type AccountIdConverter = LocationConverter;
@@ -211,11 +211,6 @@ parameter_types! {
pub const FellowsBodyId: BodyId = BodyId::Technical;
}
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parachain(ASSET_HUB_ID).into());
}
/// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior
/// location of this chain.
pub type LocalOriginToLocation = (
@@ -269,7 +264,5 @@ impl pallet_xcm::Config for Runtime {
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
}
@@ -127,11 +127,6 @@ impl xcm_executor::Config for XcmConfig {
type Aliasers = Nothing;
}
#[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.
@@ -157,7 +152,5 @@ impl pallet_xcm::Config for crate::Runtime {
type MaxRemoteLockConsumers = frame_support::traits::ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<crate::AccountId>;
}
+27 -1
View File
@@ -1626,7 +1626,7 @@ mod benches {
[pallet_whitelist, Whitelist]
[pallet_asset_rate, AssetRate]
// XCM
[pallet_xcm, XcmPallet]
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
// NOTE: Make sure you point to the individual modules below.
[pallet_xcm_benchmarks::fungible, XcmBalances]
[pallet_xcm_benchmarks::generic, XcmGeneric]
@@ -2145,6 +2145,7 @@ sp_api::impl_runtime_apis! {
use pallet_session_benchmarking::Pallet as SessionBench;
use pallet_offences_benchmarking::Pallet as OffencesBench;
use pallet_election_provider_support_benchmarking::Pallet as ElectionProviderBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
use frame_system_benchmarking::Pallet as SystemBench;
use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench;
@@ -2172,12 +2173,37 @@ sp_api::impl_runtime_apis! {
use pallet_session_benchmarking::Pallet as SessionBench;
use pallet_offences_benchmarking::Pallet as OffencesBench;
use pallet_election_provider_support_benchmarking::Pallet as ElectionProviderBench;
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
use frame_system_benchmarking::Pallet as SystemBench;
use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench;
impl pallet_session_benchmarking::Config for Runtime {}
impl pallet_offences_benchmarking::Config for Runtime {}
impl pallet_election_provider_support_benchmarking::Config for Runtime {}
impl pallet_xcm::benchmarking::Config for Runtime {
fn reachable_dest() -> Option<MultiLocation> {
Some(crate::xcm_config::AssetHub::get())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay/native token can be teleported to/from AH.
Some((
MultiAsset { fun: Fungible(EXISTENTIAL_DEPOSIT), id: Concrete(Here.into()) },
crate::xcm_config::AssetHub::get(),
))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
// Relay can reserve transfer native token to some random parachain.
Some((
MultiAsset {
fun: Fungible(EXISTENTIAL_DEPOSIT),
id: Concrete(Here.into())
},
crate::Junction::Parachain(43211234).into(),
))
}
}
impl frame_system_benchmarking::Config for Runtime {}
impl pallet_nomination_pools_benchmarking::Config for Runtime {}
impl runtime_parachains::disputes::slashing::benchmarking::Config for Runtime {}
@@ -119,11 +119,6 @@ parameter_types! {
pub const MaxAssetsIntoHolding: u32 = 64;
}
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parachain(ASSET_HUB_ID).into());
}
pub type TrustedTeleporters = (
xcm_builder::Case<WndForAssetHub>,
xcm_builder::Case<WndForCollectives>,
@@ -265,7 +260,5 @@ impl pallet_xcm::Config for Runtime {
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
}
+8 -2
View File
@@ -13,7 +13,6 @@ scale-info = { version = "2.10.0", default-features = false, features = ["derive
serde = { version = "1.0.188", optional = true, features = ["derive"] }
log = { version = "0.4.17", default-features = false }
frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true }
frame-support = { path = "../../../substrate/frame/support", default-features = false}
frame-system = { path = "../../../substrate/frame/system", default-features = false}
sp-core = { path = "../../../substrate/primitives/core", default-features = false}
@@ -25,8 +24,12 @@ xcm = { package = "staging-xcm", path = "..", default-features = false }
xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false }
xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", default-features = false }
# marked optional, used in benchmarking
frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true }
pallet-balances = { path = "../../../substrate/frame/balances", default-features = false, optional = true }
[dev-dependencies]
pallet-balances = { path = "../../../substrate/frame/balances" }
pallet-assets = { path = "../../../substrate/frame/assets" }
polkadot-runtime-parachains = { path = "../../runtime/parachains" }
polkadot-parachain-primitives = { path = "../../parachain" }
@@ -39,6 +42,7 @@ std = [
"frame-support/std",
"frame-system/std",
"log/std",
"pallet-balances/std",
"scale-info/std",
"serde",
"sp-core/std",
@@ -53,6 +57,7 @@ runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-assets/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"polkadot-parachain-primitives/runtime-benchmarks",
"polkadot-runtime-parachains/runtime-benchmarks",
@@ -63,6 +68,7 @@ runtime-benchmarks = [
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-assets/try-runtime",
"pallet-balances/try-runtime",
"polkadot-runtime-parachains/try-runtime",
"sp-runtime/try-runtime",
+122 -43
View File
@@ -16,15 +16,56 @@
use super::*;
use bounded_collections::{ConstU32, WeakBoundedVec};
use frame_benchmarking::{benchmarks, BenchmarkError, BenchmarkResult};
use frame_support::weights::Weight;
use frame_benchmarking::{benchmarks, whitelisted_caller, BenchmarkError, BenchmarkResult};
use frame_support::{traits::Currency, weights::Weight};
use frame_system::RawOrigin;
use sp_std::prelude::*;
use xcm::{latest::prelude::*, v2};
type RuntimeOrigin<T> = <T as frame_system::Config>::RuntimeOrigin;
// existential deposit multiplier
const ED_MULTIPLIER: u32 = 100;
/// Pallet we're benchmarking here.
pub struct Pallet<T: Config>(crate::Pallet<T>);
/// Trait that must be implemented by runtime to be able to benchmark pallet properly.
pub trait Config: crate::Config {
/// A `MultiLocation` that can be reached via `XcmRouter`. Used only in benchmarks.
///
/// If `None`, the benchmarks that depend on a reachable destination will be skipped.
fn reachable_dest() -> Option<MultiLocation> {
None
}
/// A `(MultiAsset, MultiLocation)` pair representing asset and the destination it can be
/// teleported to. Used only in benchmarks.
///
/// Implementation should also make sure `dest` is reachable/connected.
///
/// If `None`, the benchmarks that depend on this will be skipped.
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
None
}
/// A `(MultiAsset, MultiLocation)` pair representing asset and the destination it can be
/// reserve-transferred to. Used only in benchmarks.
///
/// Implementation should also make sure `dest` is reachable/connected.
///
/// If `None`, the benchmarks that depend on this will be skipped.
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
None
}
}
benchmarks! {
where_clause {
where
T: pallet_balances::Config,
<T as pallet_balances::Config>::Balance: From<u128> + Into<u128>,
}
send {
let send_origin =
T::SendXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
@@ -32,7 +73,7 @@ benchmarks! {
return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))
}
let msg = Xcm(vec![ClearOrigin]);
let versioned_dest: VersionedMultiLocation = T::ReachableDest::get().ok_or(
let versioned_dest: VersionedMultiLocation = T::reachable_dest().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?
.into();
@@ -40,44 +81,82 @@ benchmarks! {
}: _<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::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone())
let (asset, destination) = T::teleportable_asset_and_dest().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?;
let transferred_amount = match &asset.fun {
Fungible(amount) => *amount,
_ => return Err(BenchmarkError::Stop("Benchmark asset not fungible")),
}.into();
let assets: MultiAssets = asset.into();
let existential_deposit = T::ExistentialDeposit::get();
let caller = whitelisted_caller();
// Give some multiple of the existential deposit
let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
assert!(balance >= transferred_amount);
let _ = <pallet_balances::Pallet<T> as Currency<_>>::make_free_balance_be(&caller, balance);
// verify initial balance
assert_eq!(pallet_balances::Pallet::<T>::free_balance(&caller), balance);
let send_origin = RawOrigin::Signed(caller.clone());
let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone().into())
.map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
if !T::XcmTeleportFilter::contains(&(origin_location, vec![asset.clone()])) {
if !T::XcmTeleportFilter::contains(&(origin_location, assets.clone().into_inner())) {
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_dest: VersionedMultiLocation = destination.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)
let versioned_assets: VersionedMultiAssets = assets.into();
}: _<RuntimeOrigin<T>>(send_origin.into(), Box::new(versioned_dest), Box::new(versioned_beneficiary), Box::new(versioned_assets), 0)
verify {
// verify balance after transfer, decreased by transferred amount (+ maybe XCM delivery fees)
assert!(pallet_balances::Pallet::<T>::free_balance(&caller) <= balance - transferred_amount);
}
reserve_transfer_assets {
let asset: MultiAsset = (Here, 10).into();
let send_origin =
T::ExecuteXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone())
let (asset, destination) = T::reserve_transferable_asset_and_dest().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?;
let transferred_amount = match &asset.fun {
Fungible(amount) => *amount,
_ => return Err(BenchmarkError::Stop("Benchmark asset not fungible")),
}.into();
let assets: MultiAssets = asset.into();
let existential_deposit = T::ExistentialDeposit::get();
let caller = whitelisted_caller();
// Give some multiple of the existential deposit
let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
assert!(balance >= transferred_amount);
let _ = <pallet_balances::Pallet<T> as Currency<_>>::make_free_balance_be(&caller, balance);
// verify initial balance
assert_eq!(pallet_balances::Pallet::<T>::free_balance(&caller), balance);
let send_origin = RawOrigin::Signed(caller.clone());
let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone().into())
.map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
if !T::XcmReserveTransferFilter::contains(&(origin_location, vec![asset.clone()])) {
if !T::XcmReserveTransferFilter::contains(&(origin_location, assets.clone().into_inner())) {
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_dest: VersionedMultiLocation = destination.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)
let versioned_assets: VersionedMultiAssets = assets.into();
}: _<RuntimeOrigin<T>>(send_origin.into(), Box::new(versioned_dest), Box::new(versioned_beneficiary), Box::new(versioned_assets), 0)
verify {
// verify balance after transfer, decreased by transferred amount (+ maybe XCM delivery fees)
assert!(pallet_balances::Pallet::<T>::free_balance(&caller) <= balance - transferred_amount);
}
execute {
let execute_origin =
@@ -92,7 +171,7 @@ benchmarks! {
}: _<RuntimeOrigin<T>>(execute_origin, Box::new(versioned_msg), Weight::zero())
force_xcm_version {
let loc = T::ReachableDest::get().ok_or(
let loc = T::reachable_dest().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?;
let xcm_version = 2;
@@ -101,18 +180,18 @@ benchmarks! {
force_default_xcm_version {}: _(RawOrigin::Root, Some(2))
force_subscribe_version_notify {
let versioned_loc: VersionedMultiLocation = T::ReachableDest::get().ok_or(
let versioned_loc: VersionedMultiLocation = T::reachable_dest().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(
let loc = T::reachable_dest().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?;
let versioned_loc: VersionedMultiLocation = loc.into();
let _ = Pallet::<T>::request_version_notify(loc);
let _ = crate::Pallet::<T>::request_version_notify(loc);
}: _(RawOrigin::Root, Box::new(versioned_loc))
force_suspension {}: _(RawOrigin::Root, true)
@@ -122,7 +201,7 @@ benchmarks! {
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());
crate::Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateSupportedVersion, Weight::zero());
}
migrate_version_notifiers {
@@ -130,22 +209,22 @@ benchmarks! {
let loc = VersionedMultiLocation::from(MultiLocation::from(Parent));
VersionNotifiers::<T>::insert(old_version, loc, 0);
}: {
Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateVersionNotifiers, Weight::zero());
crate::Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateVersionNotifiers, Weight::zero());
}
already_notified_target {
let loc = T::ReachableDest::get().ok_or(
let loc = T::reachable_dest().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());
crate::Pallet::<T>::check_xcm_version_change(VersionMigrationStage::NotifyCurrentTargets(None), Weight::zero());
}
notify_current_targets {
let loc = T::ReachableDest::get().ok_or(
let loc = T::reachable_dest().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(T::DbWeight::get().reads_writes(1, 3))),
)?;
let loc = VersionedMultiLocation::from(loc);
@@ -153,7 +232,7 @@ benchmarks! {
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());
crate::Pallet::<T>::check_xcm_version_change(VersionMigrationStage::NotifyCurrentTargets(None), Weight::zero());
}
notify_target_migration_fail {
@@ -167,7 +246,7 @@ benchmarks! {
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());
crate::Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateAndNotifyOldTargets, Weight::zero());
}
migrate_version_notify_targets {
@@ -176,18 +255,18 @@ benchmarks! {
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());
crate::Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateAndNotifyOldTargets, Weight::zero());
}
migrate_and_notify_old_targets {
let loc = T::ReachableDest::get().ok_or(
let loc = T::reachable_dest().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());
crate::Pallet::<T>::check_xcm_version_change(VersionMigrationStage::MigrateAndNotifyOldTargets, Weight::zero());
}
new_query {
@@ -195,14 +274,14 @@ benchmarks! {
let timeout = 1u32.into();
let match_querier = MultiLocation::from(Here);
}: {
Pallet::<T>::new_query(responder, timeout, match_querier);
crate::Pallet::<T>::new_query(responder, timeout, match_querier);
}
take_response {
let responder = MultiLocation::from(Parent);
let timeout = 1u32.into();
let match_querier = MultiLocation::from(Here);
let query_id = Pallet::<T>::new_query(responder, timeout, match_querier);
let query_id = crate::Pallet::<T>::new_query(responder, timeout, match_querier);
let infos = (0 .. xcm::v3::MaxPalletsInfo::get()).map(|_| PalletInfo::new(
u32::MAX,
(0..xcm::v3::MaxPalletNameLen::get()).map(|_| 97u8).collect::<Vec<_>>().try_into().unwrap(),
@@ -211,10 +290,10 @@ benchmarks! {
u32::MAX,
u32::MAX,
).unwrap()).collect::<Vec<_>>();
Pallet::<T>::expect_response(query_id, Response::PalletsInfo(infos.try_into().unwrap()));
crate::Pallet::<T>::expect_response(query_id, Response::PalletsInfo(infos.try_into().unwrap()));
}: {
<Pallet::<T> as QueryHandler>::take_response(query_id);
<crate::Pallet::<T> as QueryHandler>::take_response(query_id);
}
impl_benchmark_test_suite!(
+492 -59
View File
@@ -19,7 +19,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod benchmarking;
#[cfg(test)]
mod mock;
#[cfg(test)]
@@ -55,9 +55,9 @@ use xcm_builder::{
};
use xcm_executor::{
traits::{
CheckSuspension, ClaimAssets, ConvertLocation, ConvertOrigin, DropAssets, MatchesFungible,
OnResponse, Properties, QueryHandler, QueryResponseStatus, VersionChangeNotifier,
WeightBounds,
AssetTransferError, CheckSuspension, ClaimAssets, ConvertLocation, ConvertOrigin,
DropAssets, MatchesFungible, OnResponse, Properties, QueryHandler, QueryResponseStatus,
TransactAsset, TransferType, VersionChangeNotifier, WeightBounds, XcmAssetTransfers,
},
Assets,
};
@@ -222,7 +222,7 @@ pub mod pallet {
type XcmExecuteFilter: Contains<(MultiLocation, Xcm<<Self as Config>::RuntimeCall>)>;
/// Something to execute an XCM message.
type XcmExecutor: ExecuteXcm<<Self as Config>::RuntimeCall>;
type XcmExecutor: ExecuteXcm<<Self as Config>::RuntimeCall> + XcmAssetTransfers;
/// Our XCM filter which messages to be teleported using the dedicated extrinsic must pass.
type XcmTeleportFilter: Contains<(MultiLocation, Vec<MultiAsset>)>;
@@ -275,12 +275,6 @@ pub mod pallet {
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
/// A `MultiLocation` that can be reached via `XcmRouter`. Used only in benchmarks.
///
/// If `None`, the benchmarks that depend on a reachable destination will be skipped.
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest: Get<Option<MultiLocation>>;
}
impl<T: Config> ExecuteControllerWeightInfo for Pallet<T> {
@@ -531,8 +525,8 @@ pub mod pallet {
NoSubscription,
/// The location is invalid since it already has a subscription from us.
AlreadySubscribed,
/// Invalid asset for the operation.
InvalidAsset,
/// Could not check-out the assets for teleportation to the destination chain.
CannotCheckOutTeleport,
/// The owner does not own (all) of the asset that they wish to do the operation on.
LowBalance,
/// The asset owner has too many locks on the asset.
@@ -545,6 +539,16 @@ pub mod pallet {
LockNotFound,
/// The unlock operation cannot succeed because there are still consumers of the lock.
InUse,
/// Invalid non-concrete asset.
InvalidAssetNotConcrete,
/// Invalid asset, reserve chain could not be determined for it.
InvalidAssetUnknownReserve,
/// Invalid asset, do not support remote asset reserves with different fees reserves.
InvalidAssetUnsupportedReserve,
/// Too many assets with different reserve locations have been attempted for transfer.
TooManyReserves,
/// Local XCM execution of asset transfer incomplete.
LocalExecutionIncomplete,
}
impl<T: Config> From<SendError> for Error<T> {
@@ -557,6 +561,15 @@ pub mod pallet {
}
}
impl<T: Config> From<AssetTransferError> for Error<T> {
fn from(e: AssetTransferError) -> Self {
match e {
AssetTransferError::NotConcrete => Error::<T>::InvalidAssetNotConcrete,
AssetTransferError::UnknownReserve => Error::<T>::InvalidAssetUnknownReserve,
}
}
}
/// The status of a query.
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
pub enum QueryStatus<BlockNumber> {
@@ -907,11 +920,7 @@ pub mod pallet {
let mut message = Xcm(vec![
WithdrawAsset(assets),
SetFeesMode { jit_withdraw: true },
InitiateTeleport {
assets: Wild(AllCounted(count)),
dest,
xcm: Xcm(vec![]),
},
InitiateTeleport { assets: Wild(AllCounted(count)), dest, xcm: Xcm(vec![]) },
]);
T::Weigher::weight(&mut message).map_or(Weight::MAX, |w| T::WeightInfo::teleport_assets().saturating_add(w))
}
@@ -954,6 +963,8 @@ pub mod pallet {
match (maybe_assets, maybe_dest) {
(Ok(assets), Ok(dest)) => {
use sp_std::vec;
// heaviest version of locally executed XCM program: equivalent in weight to
// transfer assets to SA, reanchor them, extend XCM program, and send onward XCM
let mut message = Xcm(vec![
SetFeesMode { jit_withdraw: true },
TransferReserveAsset { assets, dest, xcm: Xcm(vec![]) }
@@ -1114,6 +1125,8 @@ pub mod pallet {
match (maybe_assets, maybe_dest) {
(Ok(assets), Ok(dest)) => {
use sp_std::vec;
// heaviest version of locally executed XCM program: equivalent in weight to
// transfer assets to SA, reanchor them, extend XCM program, and send onward XCM
let mut message = Xcm(vec![
SetFeesMode { jit_withdraw: true },
TransferReserveAsset { assets, dest, xcm: Xcm(vec![]) }
@@ -1273,6 +1286,33 @@ impl<T: Config> QueryHandler for Pallet<T> {
}
impl<T: Config> Pallet<T> {
/// Validate `assets` to be reserve-transferred and return their reserve location.
fn validate_assets_and_find_reserve(
assets: &[MultiAsset],
dest: &MultiLocation,
) -> Result<TransferType, Error<T>> {
let mut reserve = None;
for asset in assets.iter() {
if let Fungible(x) = asset.fun {
// If fungible asset, ensure non-zero amount.
ensure!(!x.is_zero(), Error::<T>::Empty);
}
let transfer_type =
T::XcmExecutor::determine_for(&asset, dest).map_err(Error::<T>::from)?;
// Ensure asset is not teleportable to `dest`.
ensure!(transfer_type != TransferType::Teleport, Error::<T>::Filtered);
if let Some(reserve) = reserve.as_ref() {
// Ensure transfer for multiple assets uses same reserve location (only fee may have
// different reserve location)
ensure!(reserve == &transfer_type, Error::<T>::TooManyReserves);
} else {
// asset reserve identified
reserve = Some(transfer_type);
}
}
reserve.ok_or(Error::<T>::Empty)
}
fn do_reserve_transfer_assets(
origin: OriginFor<T>,
dest: Box<VersionedMultiLocation>,
@@ -1286,35 +1326,75 @@ impl<T: Config> Pallet<T> {
let beneficiary: MultiLocation =
(*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
let assets: MultiAssets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
log::trace!(
target: "xcm::pallet_xcm::do_reserve_transfer_assets",
"origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, fee-idx {:?}",
origin_location, dest, beneficiary, assets, fee_asset_item,
);
ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
let value = (origin_location, assets.into_inner());
ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
let (origin_location, assets) = value;
let context = T::UniversalLocation::get();
let fees = assets
.get(fee_asset_item as usize)
.ok_or(Error::<T>::Empty)?
.clone()
.reanchored(&dest, context)
.map_err(|_| Error::<T>::CannotReanchor)?;
let max_assets = assets.len() as u32;
let assets: MultiAssets = assets.into();
let xcm = Xcm(vec![
BuyExecution { fees, weight_limit },
DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary },
]);
let mut message = Xcm(vec![
SetFeesMode { jit_withdraw: true },
TransferReserveAsset { assets, dest, xcm },
]);
let weight =
T::Weigher::weight(&mut message).map_err(|()| Error::<T>::UnweighableMessage)?;
let hash = message.using_encoded(sp_io::hashing::blake2_256);
let outcome =
T::XcmExecutor::execute_xcm_in_credit(origin_location, message, hash, weight, weight);
Self::deposit_event(Event::Attempted { outcome });
Ok(())
let (origin_location, mut assets) = value;
if fee_asset_item as usize >= assets.len() {
return Err(Error::<T>::Empty.into())
}
let fees = assets.swap_remove(fee_asset_item as usize);
let fees_transfer_type =
T::XcmExecutor::determine_for(&fees, &dest).map_err(Error::<T>::from)?;
let assets_transfer_type = if assets.is_empty() {
// Single asset to transfer (one used for fees where transfer type is determined above).
ensure!(fees_transfer_type != TransferType::Teleport, Error::<T>::Filtered);
fees_transfer_type
} else {
// Find reserve for non-fee assets.
Self::validate_assets_and_find_reserve(&assets, &dest)?
};
// local and remote XCM programs to potentially handle fees separately
let separate_fees_instructions: Option<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>)>;
if fees_transfer_type == assets_transfer_type {
// Same reserve location (fees not teleportable), we can batch together fees and assets
// in same reserve-based-transfer.
assets.push(fees.clone());
// no need for custom fees instructions, fees are batched with assets
separate_fees_instructions = None;
} else {
// Disallow _remote reserves_ unless assets & fees have same remote reserve (covered by
// branch above). The reason for this is that we'd need to send XCMs to separate chains
// with no guarantee of delivery order on final destination; therefore we cannot
// guarantee to have fees in place on final destination chain to pay for assets
// transfer.
ensure!(
!matches!(assets_transfer_type, TransferType::RemoteReserve(_)),
Error::<T>::InvalidAssetUnsupportedReserve
);
let fees = fees.clone();
let weight_limit = weight_limit.clone();
// build fees transfer instructions to be added to assets transfers XCM programs
separate_fees_instructions = Some(match fees_transfer_type {
TransferType::LocalReserve =>
Self::local_reserve_fees_instructions(dest, fees, weight_limit)?,
TransferType::DestinationReserve =>
Self::destination_reserve_fees_instructions(dest, fees, weight_limit)?,
TransferType::Teleport =>
Self::teleport_fees_instructions(dest, fees, weight_limit)?,
TransferType::RemoteReserve(_) =>
return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
});
};
Self::build_and_execute_xcm_transfer_type(
origin_location,
dest,
beneficiary,
assets,
assets_transfer_type,
fees,
separate_fees_instructions,
weight_limit,
)
}
fn do_teleport_assets(
@@ -1335,31 +1415,384 @@ impl<T: Config> Pallet<T> {
let value = (origin_location, assets.into_inner());
ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
let (origin_location, assets) = value;
for asset in assets.iter() {
let transfer_type =
T::XcmExecutor::determine_for(asset, &dest).map_err(Error::<T>::from)?;
ensure!(matches!(transfer_type, TransferType::Teleport), Error::<T>::Filtered);
}
let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
Self::build_and_execute_xcm_transfer_type(
origin_location,
dest,
beneficiary,
assets,
TransferType::Teleport,
fees,
None,
weight_limit,
)
}
fn build_and_execute_xcm_transfer_type(
origin: MultiLocation,
dest: MultiLocation,
beneficiary: MultiLocation,
assets: Vec<MultiAsset>,
transfer_type: TransferType,
fees: MultiAsset,
separate_fees_instructions: Option<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>)>,
weight_limit: WeightLimit,
) -> DispatchResult {
log::trace!(
target: "xcm::pallet_xcm::build_and_execute_xcm_transfer_type",
"origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, transfer_type {:?}, \
fees {:?}, fees_xcm: {:?}, weight_limit: {:?}",
origin, dest, beneficiary, assets, transfer_type, fees, separate_fees_instructions, weight_limit,
);
let (mut local_xcm, remote_xcm) = match transfer_type {
TransferType::LocalReserve => {
let (local, remote) = Self::local_reserve_transfer_programs(
dest,
beneficiary,
assets,
fees,
separate_fees_instructions,
weight_limit,
)?;
(local, Some(remote))
},
TransferType::DestinationReserve => {
let (local, remote) = Self::destination_reserve_transfer_programs(
dest,
beneficiary,
assets,
fees,
separate_fees_instructions,
weight_limit,
)?;
(local, Some(remote))
},
TransferType::RemoteReserve(reserve) => (
Self::remote_reserve_transfer_program(
reserve,
dest,
beneficiary,
assets,
fees,
weight_limit,
)?,
None,
),
TransferType::Teleport => (
Self::teleport_assets_program(dest, beneficiary, assets, fees, weight_limit)?,
None,
),
};
let weight =
T::Weigher::weight(&mut local_xcm).map_err(|()| Error::<T>::UnweighableMessage)?;
let hash = local_xcm.using_encoded(sp_io::hashing::blake2_256);
let outcome =
T::XcmExecutor::execute_xcm_in_credit(origin, local_xcm, hash, weight, weight);
Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
if let Some(remote_xcm) = remote_xcm {
outcome.ensure_complete().map_err(|_| Error::<T>::LocalExecutionIncomplete)?;
let (ticket, price) = validate_send::<T::XcmRouter>(dest, remote_xcm.clone())
.map_err(Error::<T>::from)?;
if origin != Here.into_location() {
Self::charge_fees(origin, price).map_err(|_| Error::<T>::FeesNotMet)?;
}
let message_id = T::XcmRouter::deliver(ticket).map_err(Error::<T>::from)?;
let e = Event::Sent { origin, destination: dest, message: remote_xcm, message_id };
Self::deposit_event(e);
}
Ok(())
}
fn local_reserve_fees_instructions(
dest: MultiLocation,
fees: MultiAsset,
weight_limit: WeightLimit,
) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
let context = T::UniversalLocation::get();
let fees = assets
.get(fee_asset_item as usize)
.ok_or(Error::<T>::Empty)?
let reanchored_fees = fees
.clone()
.reanchored(&dest, context)
.map_err(|_| Error::<T>::CannotReanchor)?;
let max_assets = assets.len() as u32;
let local_execute_xcm = Xcm(vec![
// move `fees` to `dest`s local sovereign account
TransferAsset { assets: fees.into(), beneficiary: dest },
]);
let xcm_on_dest = Xcm(vec![
// let (dest) chain know `fees` are in its SA on reserve
ReserveAssetDeposited(reanchored_fees.clone().into()),
// buy exec using `fees` in holding deposited in above instruction
BuyExecution { fees: reanchored_fees, weight_limit },
]);
Ok((local_execute_xcm, xcm_on_dest))
}
fn local_reserve_transfer_programs(
dest: MultiLocation,
beneficiary: MultiLocation,
assets: Vec<MultiAsset>,
fees: MultiAsset,
separate_fees_instructions: Option<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>)>,
weight_limit: WeightLimit,
) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
// max assets is `assets` (+ potentially separately handled fee)
let max_assets =
assets.len() as u32 + separate_fees_instructions.as_ref().map(|_| 1).unwrap_or(0);
let assets: MultiAssets = assets.into();
let xcm = Xcm(vec![
let context = T::UniversalLocation::get();
let mut reanchored_assets = assets.clone();
reanchored_assets
.reanchor(&dest, context)
.map_err(|_| Error::<T>::CannotReanchor)?;
// fees are either handled through dedicated instructions, or batched together with assets
let fees_already_handled = separate_fees_instructions.is_some();
let (fees_local_xcm, fees_remote_xcm) = separate_fees_instructions
.map(|(local, remote)| (local.into_inner(), remote.into_inner()))
.unwrap_or_default();
// start off with any necessary local fees specific instructions
let mut local_execute_xcm = fees_local_xcm;
// move `assets` to `dest`s local sovereign account
local_execute_xcm.push(TransferAsset { assets, beneficiary: dest });
// on destination chain, start off with custom fee instructions
let mut xcm_on_dest = fees_remote_xcm;
// continue with rest of assets
xcm_on_dest.extend_from_slice(&[
// let (dest) chain know assets are in its SA on reserve
ReserveAssetDeposited(reanchored_assets),
// following instructions are not exec'ed on behalf of origin chain anymore
ClearOrigin,
]);
if !fees_already_handled {
// no custom fees instructions, they are batched together with `assets` transfer;
// BuyExecution happens after receiving all `assets`
let reanchored_fees =
fees.reanchored(&dest, context).map_err(|_| Error::<T>::CannotReanchor)?;
// buy execution using `fees` batched together with above `reanchored_assets`
xcm_on_dest.push(BuyExecution { fees: reanchored_fees, weight_limit });
}
// deposit all remaining assets in holding to `beneficiary` location
xcm_on_dest.push(DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary });
Ok((Xcm(local_execute_xcm), Xcm(xcm_on_dest)))
}
fn destination_reserve_fees_instructions(
dest: MultiLocation,
fees: MultiAsset,
weight_limit: WeightLimit,
) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
let context = T::UniversalLocation::get();
let reanchored_fees = fees
.clone()
.reanchored(&dest, context)
.map_err(|_| Error::<T>::CannotReanchor)?;
let fees: MultiAssets = fees.into();
let local_execute_xcm = Xcm(vec![
// withdraw reserve-based fees (derivatives)
WithdrawAsset(fees.clone()),
// burn derivatives
BurnAsset(fees),
]);
let xcm_on_dest = Xcm(vec![
// withdraw `fees` from origin chain's sovereign account
WithdrawAsset(reanchored_fees.clone().into()),
// buy exec using `fees` in holding withdrawn in above instruction
BuyExecution { fees: reanchored_fees, weight_limit },
]);
Ok((local_execute_xcm, xcm_on_dest))
}
fn destination_reserve_transfer_programs(
dest: MultiLocation,
beneficiary: MultiLocation,
assets: Vec<MultiAsset>,
fees: MultiAsset,
separate_fees_instructions: Option<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>)>,
weight_limit: WeightLimit,
) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
// max assets is `assets` (+ potentially separately handled fee)
let max_assets =
assets.len() as u32 + separate_fees_instructions.as_ref().map(|_| 1).unwrap_or(0);
let assets: MultiAssets = assets.into();
let context = T::UniversalLocation::get();
let mut reanchored_assets = assets.clone();
reanchored_assets
.reanchor(&dest, context)
.map_err(|_| Error::<T>::CannotReanchor)?;
// fees are either handled through dedicated instructions, or batched together with assets
let fees_already_handled = separate_fees_instructions.is_some();
let (fees_local_xcm, fees_remote_xcm) = separate_fees_instructions
.map(|(local, remote)| (local.into_inner(), remote.into_inner()))
.unwrap_or_default();
// start off with any necessary local fees specific instructions
let mut local_execute_xcm = fees_local_xcm;
// continue with rest of assets
local_execute_xcm.extend_from_slice(&[
// withdraw reserve-based assets
WithdrawAsset(assets.clone()),
// burn reserve-based assets
BurnAsset(assets),
]);
// on destination chain, start off with custom fee instructions
let mut xcm_on_dest = fees_remote_xcm;
// continue with rest of assets
xcm_on_dest.extend_from_slice(&[
// withdraw `assets` from origin chain's sovereign account
WithdrawAsset(reanchored_assets),
// following instructions are not exec'ed on behalf of origin chain anymore
ClearOrigin,
]);
if !fees_already_handled {
// no custom fees instructions, they are batched together with `assets` transfer;
// BuyExecution happens after receiving all `assets`
let reanchored_fees =
fees.reanchored(&dest, context).map_err(|_| Error::<T>::CannotReanchor)?;
// buy execution using `fees` batched together with above `reanchored_assets`
xcm_on_dest.push(BuyExecution { fees: reanchored_fees, weight_limit });
}
// deposit all remaining assets in holding to `beneficiary` location
xcm_on_dest.push(DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary });
Ok((Xcm(local_execute_xcm), Xcm(xcm_on_dest)))
}
// function assumes fees and assets have the same remote reserve
fn remote_reserve_transfer_program(
reserve: MultiLocation,
dest: MultiLocation,
beneficiary: MultiLocation,
assets: Vec<MultiAsset>,
fees: MultiAsset,
weight_limit: WeightLimit,
) -> Result<Xcm<<T as Config>::RuntimeCall>, Error<T>> {
let max_assets = assets.len() as u32;
let context = T::UniversalLocation::get();
// we spend up to half of fees for execution on reserve and other half for execution on
// destination
let (fees_half_1, fees_half_2) = Self::halve_fees(fees)?;
// identifies fee item as seen by `reserve` - to be used at reserve chain
let reserve_fees = fees_half_1
.reanchored(&reserve, context)
.map_err(|_| Error::<T>::CannotReanchor)?;
// identifies fee item as seen by `dest` - to be used at destination chain
let dest_fees =
fees_half_2.reanchored(&dest, context).map_err(|_| Error::<T>::CannotReanchor)?;
// identifies `dest` as seen by `reserve`
let dest = dest.reanchored(&reserve, context).map_err(|_| Error::<T>::CannotReanchor)?;
// xcm to be executed at dest
let xcm_on_dest = Xcm(vec![
BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() },
DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary },
]);
// xcm to be executed on reserve
let xcm_on_reserve = Xcm(vec![
BuyExecution { fees: reserve_fees, weight_limit },
DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
]);
Ok(Xcm(vec![
WithdrawAsset(assets.into()),
InitiateReserveWithdraw {
assets: Wild(AllCounted(max_assets)),
reserve,
xcm: xcm_on_reserve,
},
]))
}
fn teleport_fees_instructions(
dest: MultiLocation,
fees: MultiAsset,
weight_limit: WeightLimit,
) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
let context = T::UniversalLocation::get();
let reanchored_fees = fees
.clone()
.reanchored(&dest, context)
.map_err(|_| Error::<T>::CannotReanchor)?;
// XcmContext irrelevant in teleports checks
let dummy_context =
XcmContext { origin: None, message_id: Default::default(), topic: None };
// We should check that the asset can actually be teleported out (for this to
// be in error, there would need to be an accounting violation by ourselves,
// so it's unlikely, but we don't want to allow that kind of bug to leak into
// a trusted chain.
<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
&dest,
&fees,
&dummy_context,
)
.map_err(|_| Error::<T>::CannotCheckOutTeleport)?;
<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
&dest,
&fees,
&dummy_context,
);
let fees: MultiAssets = fees.into();
let local_execute_xcm = Xcm(vec![
// withdraw fees
WithdrawAsset(fees.clone()),
// burn fees
BurnAsset(fees),
]);
let xcm_on_dest = Xcm(vec![
// (dest) chain receive teleported assets burned on origin chain
ReceiveTeleportedAsset(reanchored_fees.clone().into()),
// buy exec using `fees` in holding received in above instruction
BuyExecution { fees: reanchored_fees, weight_limit },
]);
Ok((local_execute_xcm, xcm_on_dest))
}
fn teleport_assets_program(
dest: MultiLocation,
beneficiary: MultiLocation,
assets: Vec<MultiAsset>,
mut fees: MultiAsset,
weight_limit: WeightLimit,
) -> Result<Xcm<<T as Config>::RuntimeCall>, Error<T>> {
let context = T::UniversalLocation::get();
fees.reanchor(&dest, context).map_err(|_| Error::<T>::CannotReanchor)?;
let max_assets = assets.len() as u32;
let xcm_on_dest = Xcm(vec![
BuyExecution { fees, weight_limit },
DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary },
]);
let mut message = Xcm(vec![
WithdrawAsset(assets),
Ok(Xcm(vec![
WithdrawAsset(assets.into()),
SetFeesMode { jit_withdraw: true },
InitiateTeleport { assets: Wild(AllCounted(max_assets)), dest, xcm },
]);
let weight =
T::Weigher::weight(&mut message).map_err(|()| Error::<T>::UnweighableMessage)?;
let hash = message.using_encoded(sp_io::hashing::blake2_256);
let outcome =
T::XcmExecutor::execute_xcm_in_credit(origin_location, message, hash, weight, weight);
Self::deposit_event(Event::Attempted { outcome });
Ok(())
InitiateTeleport { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
]))
}
/// Halve `fees` fungible amount.
pub(crate) fn halve_fees(fees: MultiAsset) -> Result<(MultiAsset, MultiAsset), Error<T>> {
match fees.fun {
Fungible(amount) => {
let fee1 = amount.saturating_div(2);
let fee2 = amount.saturating_sub(fee1);
ensure!(fee1 > 0, Error::<T>::FeesNotMet);
ensure!(fee2 > 0, Error::<T>::FeesNotMet);
Ok((MultiAsset::from((fees.id, fee1)), MultiAsset::from((fees.id, fee2))))
},
NonFungible(_) => Err(Error::<T>::FeesNotMet),
}
}
/// Will always make progress, and will do its best not to use much more than `weight_cutoff`
+175 -28
View File
@@ -17,7 +17,9 @@
use codec::Encode;
use frame_support::{
construct_runtime, match_types, parameter_types,
traits::{ConstU32, Everything, EverythingBut, Nothing},
traits::{
AsEnsureOriginWithArg, ConstU128, ConstU32, Equals, Everything, EverythingBut, Nothing,
},
weights::Weight,
};
use frame_system::EnsureRoot;
@@ -32,11 +34,15 @@ use xcm::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia,
ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible,
FixedWeightBounds, IsConcrete, SignedAccountId32AsNative, SignedToAccountId32,
ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, DescribeAllTerminal,
FixedRateOfFungible, FixedWeightBounds, FungiblesAdapter, HashedDescription, IsConcrete,
MatchedConvertedConcreteId, NoChecking, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, XcmFeeManagerFromComponents, XcmFeeToAccount,
};
use xcm_executor::XcmExecutor;
use xcm_executor::{
traits::{Identity, JustTry},
XcmExecutor,
};
use crate::{self as pallet_xcm, TestWeightInfo};
@@ -137,6 +143,7 @@ construct_runtime!(
{
System: frame_system::{Pallet, Call, Storage, Config<T>, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
Assets: pallet_assets::{Pallet, Call, Storage, Config<T>, Event<T>},
ParasOrigin: origin::{Pallet, Origin},
XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event<T>, Origin, Config<T>},
TestNotifier: pallet_test_notifier::{Pallet, Call, Event<T>},
@@ -179,13 +186,13 @@ impl SendXcm for TestSendXcmErrX8 {
type Ticket = (MultiLocation, Xcm<()>);
fn validate(
dest: &mut Option<MultiLocation>,
msg: &mut Option<Xcm<()>>,
_: &mut Option<Xcm<()>>,
) -> SendResult<(MultiLocation, Xcm<()>)> {
let (dest, msg) = (dest.take().unwrap(), msg.take().unwrap());
if dest.len() == 8 {
if dest.as_ref().unwrap().len() == 8 {
dest.take();
Err(SendError::Transport("Destination location full"))
} else {
Ok(((dest, msg), MultiAssets::new()))
Err(SendError::NotApplicable)
}
}
fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<XcmHash, SendError> {
@@ -280,18 +287,135 @@ impl pallet_balances::Config for Test {
type MaxFreezes = ConstU32<0>;
}
#[cfg(feature = "runtime-benchmarks")]
/// Simple conversion of `u32` into an `AssetId` for use in benchmarking.
pub struct XcmBenchmarkHelper;
#[cfg(feature = "runtime-benchmarks")]
impl pallet_assets::BenchmarkHelper<MultiLocation> for XcmBenchmarkHelper {
fn create_asset_id_parameter(id: u32) -> MultiLocation {
MultiLocation { parents: 1, interior: X1(Parachain(id)) }
}
}
impl pallet_assets::Config for Test {
type RuntimeEvent = RuntimeEvent;
type Balance = Balance;
type AssetId = MultiLocation;
type AssetIdParameter = MultiLocation;
type Currency = Balances;
type CreateOrigin = AsEnsureOriginWithArg<frame_system::EnsureSigned<AccountId>>;
type ForceOrigin = EnsureRoot<AccountId>;
type AssetDeposit = ConstU128<1>;
type AssetAccountDeposit = ConstU128<10>;
type MetadataDepositBase = ConstU128<1>;
type MetadataDepositPerByte = ConstU128<1>;
type ApprovalDeposit = ConstU128<1>;
type StringLimit = ConstU32<50>;
type Freezer = ();
type WeightInfo = ();
type CallbackHandle = ();
type Extra = ();
type RemoveItemsLimit = ConstU32<5>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = XcmBenchmarkHelper;
}
// This child parachain is a system parachain trusted to teleport native token.
pub const SOME_SYSTEM_PARA: u32 = 1001;
// This child parachain acts as trusted reserve for its assets in tests.
// USDT allowed to teleport to/from here.
pub const FOREIGN_ASSET_RESERVE_PARA_ID: u32 = 2001;
// Inner junction of reserve asset on `FOREIGN_ASSET_RESERVE_PARA_ID`.
pub const FOREIGN_ASSET_INNER_JUNCTION: Junction = GeneralIndex(1234567);
// This child parachain acts as trusted reserve for say.. USDC that can be used for fees.
pub const USDC_RESERVE_PARA_ID: u32 = 2002;
// Inner junction of reserve asset on `USDC_RESERVE_PARA_ID`.
pub const USDC_INNER_JUNCTION: Junction = PalletInstance(42);
// This child parachain is a trusted teleporter for say.. USDT (T from Teleport :)).
// We'll use USDT in tests that teleport fees.
pub const USDT_PARA_ID: u32 = 2003;
// This child parachain is not configured as trusted reserve or teleport location for any assets.
pub const OTHER_PARA_ID: u32 = 2009;
parameter_types! {
pub const RelayLocation: MultiLocation = Here.into_location();
pub const NativeAsset: MultiAsset = MultiAsset {
fun: Fungible(10),
id: Concrete(Here.into_location()),
};
pub const SystemParachainLocation: MultiLocation = MultiLocation {
parents: 0,
interior: X1(Parachain(SOME_SYSTEM_PARA))
};
pub const ForeignReserveLocation: MultiLocation = MultiLocation {
parents: 0,
interior: X1(Parachain(FOREIGN_ASSET_RESERVE_PARA_ID))
};
pub const ForeignAsset: MultiAsset = MultiAsset {
fun: Fungible(10),
id: Concrete(MultiLocation {
parents: 0,
interior: X2(Parachain(FOREIGN_ASSET_RESERVE_PARA_ID), FOREIGN_ASSET_INNER_JUNCTION),
}),
};
pub const UsdcReserveLocation: MultiLocation = MultiLocation {
parents: 0,
interior: X1(Parachain(USDC_RESERVE_PARA_ID))
};
pub const Usdc: MultiAsset = MultiAsset {
fun: Fungible(10),
id: Concrete(MultiLocation {
parents: 0,
interior: X2(Parachain(USDC_RESERVE_PARA_ID), USDC_INNER_JUNCTION),
}),
};
pub const UsdtTeleportLocation: MultiLocation = MultiLocation {
parents: 0,
interior: X1(Parachain(USDT_PARA_ID))
};
pub const Usdt: MultiAsset = MultiAsset {
fun: Fungible(10),
id: Concrete(MultiLocation {
parents: 0,
interior: X1(Parachain(USDT_PARA_ID)),
}),
};
pub const AnyNetwork: Option<NetworkId> = None;
pub UniversalLocation: InteriorMultiLocation = Here;
pub UnitWeightCost: u64 = 1_000;
pub CheckingAccount: AccountId = XcmPallet::check_account();
}
pub type SovereignAccountOf =
(ChildParachainConvertsVia<ParaId, AccountId>, AccountId32Aliases<AnyNetwork, AccountId>);
pub type SovereignAccountOf = (
ChildParachainConvertsVia<ParaId, AccountId>,
AccountId32Aliases<AnyNetwork, AccountId>,
HashedDescription<AccountId, DescribeAllTerminal>,
);
pub type LocalAssetTransactor =
XcmCurrencyAdapter<Balances, IsConcrete<RelayLocation>, SovereignAccountOf, AccountId, ()>;
pub type ForeignAssetsConvertedConcreteId = MatchedConvertedConcreteId<
MultiLocation,
Balance,
// Excludes relay/parent chain currency
EverythingBut<(Equals<RelayLocation>,)>,
Identity,
JustTry,
>;
pub type AssetTransactors = (
XcmCurrencyAdapter<Balances, IsConcrete<RelayLocation>, SovereignAccountOf, AccountId, ()>,
FungiblesAdapter<
Assets,
ForeignAssetsConvertedConcreteId,
SovereignAccountOf,
AccountId,
NoChecking,
CheckingAccount,
>,
);
type LocalOriginConverter = (
SovereignSignedViaLocation<SovereignAccountOf, RuntimeOrigin>,
@@ -303,7 +427,12 @@ type LocalOriginConverter = (
parameter_types! {
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 TrustedLocal: (MultiAssetFilter, MultiLocation) = (All.into(), Here.into());
pub TrustedSystemPara: (MultiAssetFilter, MultiLocation) = (NativeAsset::get().into(), SystemParachainLocation::get());
pub TrustedUsdt: (MultiAssetFilter, MultiLocation) = (Usdt::get().into(), UsdtTeleportLocation::get());
pub TeleportUsdtToForeign: (MultiAssetFilter, MultiLocation) = (Usdt::get().into(), ForeignReserveLocation::get());
pub TrustedForeign: (MultiAssetFilter, MultiLocation) = (ForeignAsset::get().into(), ForeignReserveLocation::get());
pub TrustedUsdc: (MultiAssetFilter, MultiLocation) = (Usdc::get().into(), UsdcReserveLocation::get());
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 64;
pub XcmFeesTargetAccount: AccountId = AccountId::new([167u8; 32]);
@@ -323,14 +452,21 @@ pub type Barrier = (
AllowSubscriptionsFrom<Everything>,
);
pub type XcmRouter = (TestPaidForPara3000SendXcm, TestSendXcmErrX8, TestSendXcm);
pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
type XcmSender = (TestPaidForPara3000SendXcm, TestSendXcm);
type AssetTransactor = LocalAssetTransactor;
type XcmSender = XcmRouter;
type AssetTransactor = AssetTransactors;
type OriginConverter = LocalOriginConverter;
type IsReserve = ();
type IsTeleporter = Case<TrustedAssets>;
type IsReserve = (Case<TrustedForeign>, Case<TrustedUsdc>);
type IsTeleporter = (
Case<TrustedLocal>,
Case<TrustedSystemPara>,
Case<TrustedUsdt>,
Case<TeleportUsdtToForeign>,
);
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
@@ -360,15 +496,10 @@ parameter_types! {
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 {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type XcmRouter = (TestSendXcmErrX8, TestPaidForPara3000SendXcm, TestSendXcm);
type XcmRouter = XcmRouter;
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type XcmExecuteFilter = Everything;
type XcmExecutor = XcmExecutor<XcmConfig>;
@@ -380,6 +511,7 @@ impl pallet_xcm::Config for Test {
type RuntimeCall = RuntimeCall;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = AdvertisedXcmVersion;
type AdminOrigin = EnsureRoot<AccountId>;
type TrustedLockers = ();
type SovereignAccountOf = AccountId32Aliases<(), AccountId32>;
type Currency = Balances;
@@ -388,9 +520,6 @@ impl pallet_xcm::Config for Test {
type MaxRemoteLockConsumers = frame_support::traits::ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
}
impl origin::Config for Test {}
@@ -401,6 +530,24 @@ impl pallet_test_notifier::Config for Test {
type RuntimeCall = RuntimeCall;
}
#[cfg(feature = "runtime-benchmarks")]
impl super::benchmarking::Config for Test {
fn reachable_dest() -> Option<MultiLocation> {
Some(Parachain(1000).into())
}
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
Some((NativeAsset::get(), SystemParachainLocation::get()))
}
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
Some((
MultiAsset { fun: Fungible(10), id: Concrete(Here.into_location()) },
Parachain(OTHER_PARA_ID).into(),
))
}
}
pub(crate) fn last_event() -> RuntimeEvent {
System::events().pop().expect("RuntimeEvent expected").event
}
@@ -416,10 +563,10 @@ pub(crate) fn buy_execution<C>(fees: impl Into<MultiAsset>) -> Instruction<C> {
pub(crate) fn buy_limited_execution<C>(
fees: impl Into<MultiAsset>,
weight: Weight,
weight_limit: WeightLimit,
) -> Instruction<C> {
use xcm::latest::prelude::*;
BuyExecution { fees: fees.into(), weight_limit: Limited(weight) }
BuyExecution { fees: fees.into(), weight_limit }
}
pub(crate) fn new_test_ext_with_balances(
File diff suppressed because it is too large Load Diff
@@ -14,6 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
#![cfg(test)]
mod assets_transfer;
use crate::{
mock::*, AssetTraps, CurrentMigration, Error, LatestVersionedMultiLocation, Queries,
QueryStatus, VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers,
@@ -35,15 +39,15 @@ use xcm_executor::{
const ALICE: AccountId = AccountId::new([0u8; 32]);
const BOB: AccountId = AccountId::new([1u8; 32]);
const PARA_ID: u32 = 2000;
const INITIAL_BALANCE: u128 = 100;
const SEND_AMOUNT: u128 = 10;
const FEE_AMOUNT: u128 = 2;
#[test]
fn report_outcome_notify_works() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
(ParaId::from(OTHER_PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
let sender: MultiLocation = AccountId32 { network: None, id: ALICE.into() }.into();
let mut message =
@@ -56,7 +60,7 @@ fn report_outcome_notify_works() {
new_test_ext_with_balances(balances).execute_with(|| {
XcmPallet::report_outcome_notify(
&mut message,
Parachain(PARA_ID).into_location(),
Parachain(OTHER_PARA_ID).into_location(),
notify,
100,
)
@@ -74,8 +78,8 @@ fn report_outcome_notify_works() {
);
let querier: MultiLocation = Here.into();
let status = QueryStatus::Pending {
responder: MultiLocation::from(Parachain(PARA_ID)).into(),
maybe_notify: Some((4, 2)),
responder: MultiLocation::from(Parachain(OTHER_PARA_ID)).into(),
maybe_notify: Some((5, 2)),
timeout: 100,
maybe_match_querier: Some(querier.into()),
};
@@ -89,7 +93,7 @@ fn report_outcome_notify_works() {
}]);
let hash = fake_message_hash(&message);
let r = XcmExecutor::<XcmConfig>::execute_xcm(
Parachain(PARA_ID),
Parachain(OTHER_PARA_ID),
message,
hash,
Weight::from_parts(1_000_000_000, 1_000_000_000),
@@ -99,13 +103,13 @@ fn report_outcome_notify_works() {
last_events(2),
vec![
RuntimeEvent::TestNotifier(pallet_test_notifier::Event::ResponseReceived(
Parachain(PARA_ID).into(),
Parachain(OTHER_PARA_ID).into(),
0,
Response::ExecutionResult(None),
)),
RuntimeEvent::XcmPallet(crate::Event::Notified {
query_id: 0,
pallet_index: 4,
pallet_index: 5,
call_index: 2
}),
]
@@ -118,13 +122,14 @@ fn report_outcome_notify_works() {
fn report_outcome_works() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
(ParaId::from(OTHER_PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
let sender: MultiLocation = AccountId32 { network: None, id: ALICE.into() }.into();
let mut message =
Xcm(vec![TransferAsset { assets: (Here, SEND_AMOUNT).into(), beneficiary: sender }]);
new_test_ext_with_balances(balances).execute_with(|| {
XcmPallet::report_outcome(&mut message, Parachain(PARA_ID).into_location(), 100).unwrap();
XcmPallet::report_outcome(&mut message, Parachain(OTHER_PARA_ID).into_location(), 100)
.unwrap();
assert_eq!(
message,
Xcm(vec![
@@ -138,7 +143,7 @@ fn report_outcome_works() {
);
let querier: MultiLocation = Here.into();
let status = QueryStatus::Pending {
responder: MultiLocation::from(Parachain(PARA_ID)).into(),
responder: MultiLocation::from(Parachain(OTHER_PARA_ID)).into(),
maybe_notify: None,
timeout: 100,
maybe_match_querier: Some(querier.into()),
@@ -153,7 +158,7 @@ fn report_outcome_works() {
}]);
let hash = fake_message_hash(&message);
let r = XcmExecutor::<XcmConfig>::execute_xcm(
Parachain(PARA_ID),
Parachain(OTHER_PARA_ID),
message,
hash,
Weight::from_parts(1_000_000_000, 1_000_000_000),
@@ -177,7 +182,7 @@ fn report_outcome_works() {
fn custom_querier_works() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
(ParaId::from(OTHER_PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let querier: MultiLocation =
@@ -281,7 +286,7 @@ fn custom_querier_works() {
fn send_works() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
(ParaId::from(OTHER_PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let sender: MultiLocation = AccountId32 { network: None, id: ALICE.into() }.into();
@@ -325,7 +330,7 @@ fn send_works() {
fn send_fails_when_xcm_router_blocks() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
(ParaId::from(OTHER_PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let sender: MultiLocation =
@@ -346,344 +351,6 @@ fn send_fails_when_xcm_router_blocks() {
});
}
/// Test `teleport_assets`
///
/// Asserts that the sender's balance is decreased as a result of execution of
/// local effects.
#[test]
fn teleport_assets_works() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let weight = BaseXcmWeight::get() * 3;
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into();
assert_ok!(XcmPallet::teleport_assets(
RuntimeOrigin::signed(ALICE),
Box::new(RelayLocation::get().into()),
Box::new(dest.into()),
Box::new((Here, SEND_AMOUNT).into()),
0,
));
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
assert_eq!(
sent_xcm(),
vec![(
RelayLocation::get().into(),
Xcm(vec![
ReceiveTeleportedAsset((Here, SEND_AMOUNT).into()),
ClearOrigin,
buy_execution((Here, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]),
)]
);
let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1);
let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
assert_eq!(
last_event(),
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
);
});
}
/// Test `limited_teleport_assets`
///
/// Asserts that the sender's balance is decreased as a result of execution of
/// local effects.
#[test]
fn limited_teleport_assets_works() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let weight = BaseXcmWeight::get() * 3;
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into();
assert_ok!(XcmPallet::limited_teleport_assets(
RuntimeOrigin::signed(ALICE),
Box::new(RelayLocation::get().into()),
Box::new(dest.into()),
Box::new((Here, SEND_AMOUNT).into()),
0,
WeightLimit::Limited(Weight::from_parts(5000, 5000)),
));
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
assert_eq!(
sent_xcm(),
vec![(
RelayLocation::get().into(),
Xcm(vec![
ReceiveTeleportedAsset((Here, SEND_AMOUNT).into()),
ClearOrigin,
buy_limited_execution((Here, SEND_AMOUNT), Weight::from_parts(5000, 5000)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]),
)]
);
let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1);
let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
assert_eq!(
last_event(),
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
);
});
}
/// Test `limited_teleport_assets` with unlimited weight
///
/// Asserts that the sender's balance is decreased as a result of execution of
/// local effects.
#[test]
fn unlimited_teleport_assets_works() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let weight = BaseXcmWeight::get() * 3;
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into();
assert_ok!(XcmPallet::limited_teleport_assets(
RuntimeOrigin::signed(ALICE),
Box::new(RelayLocation::get().into()),
Box::new(dest.into()),
Box::new((Here, SEND_AMOUNT).into()),
0,
WeightLimit::Unlimited,
));
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
assert_eq!(
sent_xcm(),
vec![(
RelayLocation::get().into(),
Xcm(vec![
ReceiveTeleportedAsset((Here, SEND_AMOUNT).into()),
ClearOrigin,
buy_execution((Here, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]),
)]
);
assert_eq!(
last_event(),
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
);
});
}
/// Test `reserve_transfer_assets`
///
/// Asserts that the sender's balance is decreased and the beneficiary's balance
/// is increased. Verifies the correct message is sent and event is emitted.
#[test]
fn reserve_transfer_assets_works() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let weight = BaseXcmWeight::get() * 2;
let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into();
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
assert_ok!(XcmPallet::reserve_transfer_assets(
RuntimeOrigin::signed(ALICE),
Box::new(Parachain(PARA_ID).into()),
Box::new(dest.into()),
Box::new((Here, SEND_AMOUNT).into()),
0,
));
// Alice spent amount
assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE - SEND_AMOUNT);
// Destination account (parachain account) has amount
let para_acc: AccountId = ParaId::from(PARA_ID).into_account_truncating();
assert_eq!(Balances::free_balance(para_acc), INITIAL_BALANCE + SEND_AMOUNT);
assert_eq!(
sent_xcm(),
vec![(
Parachain(PARA_ID).into(),
Xcm(vec![
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
ClearOrigin,
buy_execution((Parent, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]),
)]
);
let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1);
let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
assert_eq!(
last_event(),
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
);
});
}
/// Test `reserve_transfer_assets_with_paid_router_works`
///
/// Asserts that the sender's balance is decreased and the beneficiary's balance
/// is increased. Verifies the correct message is sent and event is emitted.
/// Verifies that XCM router fees (`SendXcm::validate` -> `MultiAssets`) are withdrawn from correct
/// user account and deposited to a correct target account (`XcmFeesTargetAccount`).
#[test]
fn reserve_transfer_assets_with_paid_router_works() {
let user_account = AccountId::from(XCM_FEES_NOT_WAIVED_USER_ACCOUNT);
let paid_para_id = Para3000::get();
let balances = vec![
(user_account.clone(), INITIAL_BALANCE),
(ParaId::from(paid_para_id).into_account_truncating(), INITIAL_BALANCE),
(XcmFeesTargetAccount::get(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let xcm_router_fee_amount = Para3000PaymentAmount::get();
let weight = BaseXcmWeight::get() * 2;
let dest: MultiLocation =
Junction::AccountId32 { network: None, id: user_account.clone().into() }.into();
assert_eq!(Balances::total_balance(&user_account), INITIAL_BALANCE);
assert_ok!(XcmPallet::reserve_transfer_assets(
RuntimeOrigin::signed(user_account.clone()),
Box::new(Parachain(paid_para_id).into()),
Box::new(dest.into()),
Box::new((Here, SEND_AMOUNT).into()),
0,
));
// check event
assert_eq!(
last_event(),
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
);
// XCM_FEES_NOT_WAIVED_USER_ACCOUNT spent amount
assert_eq!(
Balances::free_balance(user_account),
INITIAL_BALANCE - SEND_AMOUNT - xcm_router_fee_amount
);
// Destination account (parachain account) has amount
let para_acc: AccountId = ParaId::from(paid_para_id).into_account_truncating();
assert_eq!(Balances::free_balance(para_acc), INITIAL_BALANCE + SEND_AMOUNT);
// XcmFeesTargetAccount where should lend xcm_router_fee_amount
assert_eq!(
Balances::free_balance(XcmFeesTargetAccount::get()),
INITIAL_BALANCE + xcm_router_fee_amount
);
assert_eq!(
sent_xcm(),
vec![(
Parachain(paid_para_id).into(),
Xcm(vec![
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
ClearOrigin,
buy_execution((Parent, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]),
)]
);
let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1);
let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
assert_eq!(
last_event(),
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
);
});
}
/// Test `limited_reserve_transfer_assets`
///
/// Asserts that the sender's balance is decreased and the beneficiary's balance
/// is increased. Verifies the correct message is sent and event is emitted.
#[test]
fn limited_reserve_transfer_assets_works() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let weight = BaseXcmWeight::get() * 2;
let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into();
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
assert_ok!(XcmPallet::limited_reserve_transfer_assets(
RuntimeOrigin::signed(ALICE),
Box::new(Parachain(PARA_ID).into()),
Box::new(dest.into()),
Box::new((Here, SEND_AMOUNT).into()),
0,
WeightLimit::Limited(Weight::from_parts(5000, 5000)),
));
// Alice spent amount
assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE - SEND_AMOUNT);
// Destination account (parachain account) has amount
let para_acc: AccountId = ParaId::from(PARA_ID).into_account_truncating();
assert_eq!(Balances::free_balance(para_acc), INITIAL_BALANCE + SEND_AMOUNT);
assert_eq!(
sent_xcm(),
vec![(
Parachain(PARA_ID).into(),
Xcm(vec![
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
ClearOrigin,
buy_limited_execution((Parent, SEND_AMOUNT), Weight::from_parts(5000, 5000)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]),
)]
);
let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1);
let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
assert_eq!(
last_event(),
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
);
});
}
/// Test `limited_reserve_transfer_assets` with unlimited weight purchasing
///
/// Asserts that the sender's balance is decreased and the beneficiary's balance
/// is increased. Verifies the correct message is sent and event is emitted.
#[test]
fn unlimited_reserve_transfer_assets_works() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let weight = BaseXcmWeight::get() * 2;
let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into();
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
assert_ok!(XcmPallet::limited_reserve_transfer_assets(
RuntimeOrigin::signed(ALICE),
Box::new(Parachain(PARA_ID).into()),
Box::new(dest.into()),
Box::new((Here, SEND_AMOUNT).into()),
0,
WeightLimit::Unlimited,
));
// Alice spent amount
assert_eq!(Balances::free_balance(ALICE), INITIAL_BALANCE - SEND_AMOUNT);
// Destination account (parachain account) has amount
let para_acc: AccountId = ParaId::from(PARA_ID).into_account_truncating();
assert_eq!(Balances::free_balance(para_acc), INITIAL_BALANCE + SEND_AMOUNT);
assert_eq!(
sent_xcm(),
vec![(
Parachain(PARA_ID).into(),
Xcm(vec![
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
ClearOrigin,
buy_execution((Parent, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]),
)]
);
assert_eq!(
last_event(),
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
);
});
}
/// Test local execution of XCM
///
/// Asserts that the sender's balance is decreased and the beneficiary's balance
@@ -692,7 +359,7 @@ fn unlimited_reserve_transfer_assets_works() {
fn execute_withdraw_to_deposit_works() {
let balances = vec![
(ALICE, INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
(ParaId::from(OTHER_PARA_ID).into_account_truncating(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let weight = BaseXcmWeight::get() * 3;
+66
View File
@@ -444,6 +444,21 @@ impl MultiLocation {
}
}
}
/// Return the MultiLocation subsection identifying the chain that `self` points to.
pub fn chain_location(&self) -> MultiLocation {
let mut clone = *self;
// start popping junctions until we reach chain identifier
while let Some(j) = clone.last() {
if matches!(j, Junction::Parachain(_) | Junction::GlobalConsensus(_)) {
// return chain subsection
return clone
} else {
(clone, _) = clone.split_last_interior();
}
}
MultiLocation::new(clone.parents, Junctions::Here)
}
}
impl TryFrom<OldMultiLocation> for MultiLocation {
@@ -674,6 +689,57 @@ mod tests {
assert_eq!(iter.next_back(), None);
}
#[test]
fn chain_location_works() {
// Relay-chain or parachain context pointing to local resource,
let relay_to_local = MultiLocation::new(0, (PalletInstance(42), GeneralIndex(42)));
assert_eq!(relay_to_local.chain_location(), MultiLocation::here());
// Relay-chain context pointing to child parachain,
let relay_to_child =
MultiLocation::new(0, (Parachain(42), PalletInstance(42), GeneralIndex(42)));
let expected = MultiLocation::new(0, Parachain(42));
assert_eq!(relay_to_child.chain_location(), expected);
// Relay-chain context pointing to different consensus relay,
let relay_to_remote_relay =
MultiLocation::new(1, (GlobalConsensus(Kusama), PalletInstance(42), GeneralIndex(42)));
let expected = MultiLocation::new(1, GlobalConsensus(Kusama));
assert_eq!(relay_to_remote_relay.chain_location(), expected);
// Relay-chain context pointing to different consensus parachain,
let relay_to_remote_para = MultiLocation::new(
1,
(GlobalConsensus(Kusama), Parachain(42), PalletInstance(42), GeneralIndex(42)),
);
let expected = MultiLocation::new(1, (GlobalConsensus(Kusama), Parachain(42)));
assert_eq!(relay_to_remote_para.chain_location(), expected);
// Parachain context pointing to relay chain,
let para_to_relay = MultiLocation::new(1, (PalletInstance(42), GeneralIndex(42)));
assert_eq!(para_to_relay.chain_location(), MultiLocation::parent());
// Parachain context pointing to sibling parachain,
let para_to_sibling =
MultiLocation::new(1, (Parachain(42), PalletInstance(42), GeneralIndex(42)));
let expected = MultiLocation::new(1, Parachain(42));
assert_eq!(para_to_sibling.chain_location(), expected);
// Parachain context pointing to different consensus relay,
let para_to_remote_relay =
MultiLocation::new(2, (GlobalConsensus(Kusama), PalletInstance(42), GeneralIndex(42)));
let expected = MultiLocation::new(2, GlobalConsensus(Kusama));
assert_eq!(para_to_remote_relay.chain_location(), expected);
// Parachain context pointing to different consensus parachain,
let para_to_remote_para = MultiLocation::new(
2,
(GlobalConsensus(Kusama), Parachain(42), PalletInstance(42), GeneralIndex(42)),
);
let expected = MultiLocation::new(2, (GlobalConsensus(Kusama), Parachain(42)));
assert_eq!(para_to_remote_para.chain_location(), expected);
}
#[test]
fn conversion_from_other_types_works() {
use crate::v2;
+9 -4
View File
@@ -81,10 +81,15 @@ impl<T: Contains<MultiLocation>> ShouldExecute for AllowTopLevelPaidExecutionFro
instructions[..end]
.matcher()
.match_next_inst(|inst| match inst {
ReceiveTeleportedAsset(..) | ReserveAssetDeposited(..) => Ok(()),
WithdrawAsset(ref assets) if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION => Ok(()),
ClaimAsset { ref assets, .. } if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION =>
Ok(()),
ReceiveTeleportedAsset(ref assets) |
ReserveAssetDeposited(ref assets) |
WithdrawAsset(ref assets) |
ClaimAsset { ref assets, .. } =>
if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION {
Ok(())
} else {
Err(ProcessMessageError::BadFormat)
},
_ => Err(ProcessMessageError::BadFormat),
})?
.skip_inst_while(|inst| matches!(inst, ClearOrigin))?
@@ -246,11 +246,6 @@ type SovereignAccountOf = (
HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>,
);
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parachain(1000).into());
}
impl pallet_xcm::Config for Test {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
@@ -274,8 +269,6 @@ impl pallet_xcm::Config for Test {
type MaxRemoteLockConsumers = frame_support::traits::ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
}
@@ -210,11 +210,6 @@ impl xcm_executor::Config for XcmConfig {
pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, KusamaNetwork>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Here.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type UniversalLocation = UniversalLocation;
@@ -239,8 +234,6 @@ impl pallet_xcm::Config for Runtime {
type MaxRemoteLockConsumers = frame_support::traits::ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
}
+2
View File
@@ -10,6 +10,7 @@ version = "1.0.0"
impl-trait-for-tuples = "0.2.2"
environmental = { version = "1.1.4", default-features = false }
parity-scale-codec = { version = "3.6.1", default-features = false, features = ["derive"] }
scale-info = { version = "2.5.0", default-features = false, features = ["derive", "serde"] }
xcm = { package = "staging-xcm", path = "..", default-features = false }
sp-std = { path = "../../../substrate/primitives/std", default-features = false }
sp-io = { path = "../../../substrate/primitives/io", default-features = false }
@@ -34,6 +35,7 @@ std = [
"frame-support/std",
"log/std",
"parity-scale-codec/std",
"scale-info/std",
"sp-arithmetic/std",
"sp-core/std",
"sp-io/std",
+7 -1
View File
@@ -32,7 +32,7 @@ pub mod traits;
use traits::{
validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin,
DropAssets, Enact, ExportXcm, FeeManager, FeeReason, OnResponse, Properties, ShouldExecute,
TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader,
TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, XcmAssetTransfers,
};
mod assets;
@@ -254,6 +254,12 @@ impl<Config: config::Config> ExecuteXcm<Config::RuntimeCall> for XcmExecutor<Con
}
}
impl<Config: config::Config> XcmAssetTransfers for XcmExecutor<Config> {
type IsReserve = Config::IsReserve;
type IsTeleporter = Config::IsTeleporter;
type AssetTransactor = Config::AssetTransactor;
}
#[derive(Debug)]
pub struct ExecutorError {
pub index: u32,
@@ -0,0 +1,90 @@
// Copyright (C) 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::traits::TransactAsset;
use frame_support::traits::ContainsPair;
use scale_info::TypeInfo;
use sp_runtime::codec::{Decode, Encode};
use xcm::prelude::*;
/// Errors related to determining asset transfer support.
#[derive(Copy, Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo)]
pub enum Error {
/// Invalid non-concrete asset.
NotConcrete,
/// Reserve chain could not be determined for assets.
UnknownReserve,
}
/// Specify which type of asset transfer is required for a particular `(asset, dest)` combination.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum TransferType {
/// should teleport `asset` to `dest`
Teleport,
/// should reserve-transfer `asset` to `dest`, using local chain as reserve
LocalReserve,
/// should reserve-transfer `asset` to `dest`, using `dest` as reserve
DestinationReserve,
/// should reserve-transfer `asset` to `dest`, using remote chain `MultiLocation` as reserve
RemoteReserve(MultiLocation),
}
/// A trait for identifying asset transfer type based on `IsTeleporter` and `IsReserve`
/// configurations.
pub trait XcmAssetTransfers {
/// Combinations of (Asset, Location) pairs which we trust as reserves. Meaning
/// reserve-based-transfers are to be used for assets matching this filter.
type IsReserve: ContainsPair<MultiAsset, MultiLocation>;
/// Combinations of (Asset, Location) pairs which we trust as teleporters. Meaning teleports are
/// to be used for assets matching this filter.
type IsTeleporter: ContainsPair<MultiAsset, MultiLocation>;
/// How to withdraw and deposit an asset.
type AssetTransactor: TransactAsset;
/// Determine transfer type to be used for transferring `asset` from local chain to `dest`.
fn determine_for(asset: &MultiAsset, dest: &MultiLocation) -> Result<TransferType, Error> {
if Self::IsTeleporter::contains(asset, dest) {
// we trust destination for teleporting asset
return Ok(TransferType::Teleport)
} else if Self::IsReserve::contains(asset, dest) {
// we trust destination as asset reserve location
return Ok(TransferType::DestinationReserve)
}
// try to determine reserve location based on asset id/location
let asset_location = match asset.id {
Concrete(location) => Ok(location.chain_location()),
_ => Err(Error::NotConcrete),
}?;
if asset_location == MultiLocation::here() ||
Self::IsTeleporter::contains(asset, &asset_location)
{
// if the asset is local, then it's a local reserve
// it's also a local reserve if the asset's location is not `here` but it's a location
// where it can be teleported to `here` => local reserve
Ok(TransferType::LocalReserve)
} else if Self::IsReserve::contains(asset, &asset_location) {
// remote location that is recognized as reserve location for asset
Ok(TransferType::RemoteReserve(asset_location))
} else {
// remote location that is not configured either as teleporter or reserve => cannot
// determine asset reserve
Err(Error::UnknownReserve)
}
}
}
+4 -2
View File
@@ -20,10 +20,12 @@ mod conversion;
pub use conversion::{CallDispatcher, ConvertLocation, ConvertOrigin, WithOriginFilter};
mod drop_assets;
pub use drop_assets::{ClaimAssets, DropAssets};
mod asset_lock;
pub use asset_lock::{AssetLock, Enact, LockError};
mod asset_exchange;
pub use asset_exchange::AssetExchange;
mod asset_lock;
pub use asset_lock::{AssetLock, Enact, LockError};
mod asset_transfer;
pub use asset_transfer::{Error as AssetTransferError, TransferType, XcmAssetTransfers};
mod export;
pub use export::{export_xcm, validate_export, ExportXcm};
mod fee_manager;
@@ -399,11 +399,6 @@ impl mock_msg_queue::Config for Runtime {
pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
pub struct TrustedLockerCase<T>(PhantomData<T>);
impl<T: Get<(MultiLocation, MultiAssetFilter)>> ContainsPair<MultiLocation, MultiAsset>
for TrustedLockerCase<T>
@@ -443,8 +438,6 @@ impl pallet_xcm::Config for Runtime {
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
}
@@ -199,11 +199,6 @@ impl Config for XcmConfig {
pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parachain(1).into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
@@ -228,8 +223,6 @@ impl pallet_xcm::Config for Runtime {
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
}
@@ -313,11 +313,6 @@ impl mock_msg_queue::Config for Runtime {
pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
@@ -341,8 +336,6 @@ impl pallet_xcm::Config for Runtime {
type MaxRemoteLockConsumers = frame_support::traits::ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
}
@@ -163,11 +163,6 @@ impl Config for XcmConfig {
pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, ThisNetwork>;
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub ReachableDest: Option<MultiLocation> = Some(Parachain(1).into());
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
@@ -192,8 +187,6 @@ impl pallet_xcm::Config for Runtime {
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = pallet_xcm::TestWeightInfo;
#[cfg(feature = "runtime-benchmarks")]
type ReachableDest = ReachableDest;
type AdminOrigin = EnsureRoot<AccountId>;
}