Contracts expose pallet-xcm (#1248)

This PR introduces:
- XCM  host functions `xcm_send`, `xcm_execute`
- An Xcm trait into the config. that proxy these functions to to
`pallet_xcm`, or disable their usage by using `()`.
- A mock_network and xcm_test files to test the newly added xcm-related
functions.

---------

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
Co-authored-by: command-bot <>
Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Co-authored-by: Alexander Theißen <alex.theissen@me.com>
This commit is contained in:
PG Herveou
2023-11-14 21:32:14 +01:00
committed by GitHub
parent fc12f435e3
commit f517900a48
23 changed files with 1797 additions and 15 deletions
@@ -0,0 +1,151 @@
// Copyright Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
pub mod mocks;
pub mod parachain;
pub mod primitives;
pub mod relay_chain;
#[cfg(test)]
mod tests;
use crate::primitives::{AccountId, UNITS};
use sp_runtime::BuildStorage;
use xcm::latest::{prelude::*, MultiLocation};
use xcm_executor::traits::ConvertLocation;
use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt};
// Accounts
pub const ADMIN: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]);
pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([1u8; 32]);
pub const BOB: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([2u8; 32]);
// Balances
pub const INITIAL_BALANCE: u128 = 1_000_000_000 * UNITS;
decl_test_parachain! {
pub struct ParaA {
Runtime = parachain::Runtime,
XcmpMessageHandler = parachain::MsgQueue,
DmpMessageHandler = parachain::MsgQueue,
new_ext = para_ext(1),
}
}
decl_test_relay_chain! {
pub struct Relay {
Runtime = relay_chain::Runtime,
RuntimeCall = relay_chain::RuntimeCall,
RuntimeEvent = relay_chain::RuntimeEvent,
XcmConfig = relay_chain::XcmConfig,
MessageQueue = relay_chain::MessageQueue,
System = relay_chain::System,
new_ext = relay_ext(),
}
}
decl_test_network! {
pub struct MockNet {
relay_chain = Relay,
parachains = vec![
(1, ParaA),
],
}
}
pub fn relay_sovereign_account_id() -> AccountId {
let location: MultiLocation = (Parent,).into();
parachain::SovereignAccountOf::convert_location(&location).unwrap()
}
pub fn parachain_sovereign_account_id(para: u32) -> AccountId {
let location: MultiLocation = (Parachain(para),).into();
relay_chain::SovereignAccountOf::convert_location(&location).unwrap()
}
pub fn parachain_account_sovereign_account_id(
para: u32,
who: sp_runtime::AccountId32,
) -> AccountId {
let location: MultiLocation = (
Parachain(para),
AccountId32 { network: Some(relay_chain::RelayNetwork::get()), id: who.into() },
)
.into();
relay_chain::SovereignAccountOf::convert_location(&location).unwrap()
}
pub fn para_ext(para_id: u32) -> sp_io::TestExternalities {
use parachain::{MsgQueue, Runtime, System};
let mut t = frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
pallet_balances::GenesisConfig::<Runtime> {
balances: vec![
(ALICE, INITIAL_BALANCE),
(relay_sovereign_account_id(), INITIAL_BALANCE),
(BOB, INITIAL_BALANCE),
],
}
.assimilate_storage(&mut t)
.unwrap();
pallet_assets::GenesisConfig::<Runtime> {
assets: vec![
(0u128, ADMIN, false, 1u128), // Create derivative asset for relay's native token
],
metadata: Default::default(),
accounts: vec![
(0u128, ALICE, INITIAL_BALANCE),
(0u128, relay_sovereign_account_id(), INITIAL_BALANCE),
],
}
.assimilate_storage(&mut t)
.unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| {
sp_tracing::try_init_simple();
System::set_block_number(1);
MsgQueue::set_para_id(para_id.into());
});
ext
}
pub fn relay_ext() -> sp_io::TestExternalities {
use relay_chain::{Runtime, System};
let mut t = frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
pallet_balances::GenesisConfig::<Runtime> {
balances: vec![
(ALICE, INITIAL_BALANCE),
(parachain_sovereign_account_id(1), INITIAL_BALANCE),
(parachain_account_sovereign_account_id(1, ALICE), INITIAL_BALANCE),
],
}
.assimilate_storage(&mut t)
.unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| {
System::set_block_number(1);
});
ext
}
pub type ParachainPalletXcm = pallet_xcm::Pallet<parachain::Runtime>;
pub type ParachainBalances = pallet_balances::Pallet<parachain::Runtime>;
@@ -0,0 +1,18 @@
// Copyright Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
pub mod msg_queue;
pub mod relay_message_queue;
@@ -0,0 +1,168 @@
// Copyright 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/>.
//! Parachain runtime mock.
use codec::{Decode, Encode};
use frame_support::weights::Weight;
use polkadot_parachain_primitives::primitives::{
DmpMessageHandler, Id as ParaId, XcmpMessageFormat, XcmpMessageHandler,
};
use polkadot_primitives::BlockNumber as RelayBlockNumber;
use sp_runtime::traits::{Get, Hash};
use sp_std::prelude::*;
use xcm::{latest::prelude::*, VersionedXcm};
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type XcmExecutor: ExecuteXcm<Self::RuntimeCall>;
}
#[pallet::call]
impl<T: Config> Pallet<T> {}
#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);
#[pallet::storage]
#[pallet::getter(fn parachain_id)]
pub(super) type ParachainId<T: Config> = StorageValue<_, ParaId, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn received_dmp)]
/// A queue of received DMP messages
pub(super) type ReceivedDmp<T: Config> = StorageValue<_, Vec<Xcm<T::RuntimeCall>>, ValueQuery>;
impl<T: Config> Get<ParaId> for Pallet<T> {
fn get() -> ParaId {
Self::parachain_id()
}
}
pub type MessageId = [u8; 32];
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Some XCM was executed OK.
Success(Option<T::Hash>),
/// Some XCM failed.
Fail(Option<T::Hash>, XcmError),
/// Bad XCM version used.
BadVersion(Option<T::Hash>),
/// Bad XCM format used.
BadFormat(Option<T::Hash>),
// DMP
/// Downward message is invalid XCM.
InvalidFormat(MessageId),
/// Downward message is unsupported version of XCM.
UnsupportedVersion(MessageId),
/// Downward message executed with the given outcome.
ExecutedDownward(MessageId, Outcome),
}
impl<T: Config> Pallet<T> {
pub fn set_para_id(para_id: ParaId) {
ParachainId::<T>::put(para_id);
}
fn handle_xcmp_message(
sender: ParaId,
_sent_at: RelayBlockNumber,
xcm: VersionedXcm<T::RuntimeCall>,
max_weight: Weight,
) -> Result<Weight, XcmError> {
let hash = Encode::using_encoded(&xcm, T::Hashing::hash);
let message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256);
let (result, event) = match Xcm::<T::RuntimeCall>::try_from(xcm) {
Ok(xcm) => {
let location = (Parent, Parachain(sender.into()));
match T::XcmExecutor::execute_xcm(location, xcm, message_hash, max_weight) {
Outcome::Error(e) => (Err(e), Event::Fail(Some(hash), e)),
Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))),
// As far as the caller is concerned, this was dispatched without error, so
// we just report the weight used.
Outcome::Incomplete(w, e) => (Ok(w), Event::Fail(Some(hash), e)),
}
},
Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))),
};
Self::deposit_event(event);
result
}
}
impl<T: Config> XcmpMessageHandler for Pallet<T> {
fn handle_xcmp_messages<'a, I: Iterator<Item = (ParaId, RelayBlockNumber, &'a [u8])>>(
iter: I,
max_weight: Weight,
) -> Weight {
for (sender, sent_at, data) in iter {
let mut data_ref = data;
let _ = XcmpMessageFormat::decode(&mut data_ref)
.expect("Simulator encodes with versioned xcm format; qed");
let mut remaining_fragments = data_ref;
while !remaining_fragments.is_empty() {
if let Ok(xcm) =
VersionedXcm::<T::RuntimeCall>::decode(&mut remaining_fragments)
{
let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight);
} else {
debug_assert!(false, "Invalid incoming XCMP message data");
}
}
}
max_weight
}
}
impl<T: Config> DmpMessageHandler for Pallet<T> {
fn handle_dmp_messages(
iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>,
limit: Weight,
) -> Weight {
for (_i, (_sent_at, data)) in iter.enumerate() {
let id = sp_io::hashing::blake2_256(&data[..]);
let maybe_versioned = VersionedXcm::<T::RuntimeCall>::decode(&mut &data[..]);
match maybe_versioned {
Err(_) => {
Self::deposit_event(Event::InvalidFormat(id));
},
Ok(versioned) => match Xcm::try_from(versioned) {
Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)),
Ok(x) => {
let outcome = T::XcmExecutor::execute_xcm(Parent, x.clone(), id, limit);
<ReceivedDmp<T>>::append(x);
Self::deposit_event(Event::ExecutedDownward(id, outcome));
},
},
}
}
limit
}
}
}
@@ -0,0 +1,52 @@
// Copyright 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 frame_support::{parameter_types, weights::Weight};
use xcm::latest::prelude::*;
use xcm_simulator::{
AggregateMessageOrigin, ProcessMessage, ProcessMessageError, UmpQueueId, WeightMeter,
};
use crate::relay_chain::{RuntimeCall, XcmConfig};
parameter_types! {
/// Amount of weight that can be spent per block to service messages.
pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000);
pub const MessageQueueHeapSize: u32 = 65_536;
pub const MessageQueueMaxStale: u32 = 16;
}
/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet.
pub struct MessageProcessor;
impl ProcessMessage for MessageProcessor {
type Origin = AggregateMessageOrigin;
fn process_message(
message: &[u8],
origin: Self::Origin,
meter: &mut WeightMeter,
id: &mut [u8; 32],
) -> Result<bool, ProcessMessageError> {
let para = match origin {
AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para,
};
xcm_builder::ProcessXcmMessage::<
Junction,
xcm_executor::XcmExecutor<XcmConfig>,
RuntimeCall,
>::process_message(message, Junction::Parachain(para.into()), meter, id)
}
}
@@ -0,0 +1,353 @@
// Copyright 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/>.
//! Parachain runtime mock.
mod contracts_config;
use crate::{
mocks::msg_queue::pallet as mock_msg_queue,
primitives::{AccountId, AssetIdForAssets, Balance},
};
use core::marker::PhantomData;
use frame_support::{
construct_runtime, parameter_types,
traits::{AsEnsureOriginWithArg, Contains, ContainsPair, Everything, EverythingBut, Nothing},
weights::{
constants::{WEIGHT_PROOF_SIZE_PER_MB, WEIGHT_REF_TIME_PER_SECOND},
Weight,
},
};
use frame_system::{EnsureRoot, EnsureSigned};
use pallet_xcm::XcmPassthrough;
use sp_core::{ConstU32, ConstU64, H256};
use sp_runtime::traits::{Get, IdentityLookup, MaybeEquivalence};
use sp_std::prelude::*;
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom,
ConvertedConcreteId, CurrencyAdapter as XcmCurrencyAdapter, EnsureXcmOrigin,
FixedRateOfFungible, FixedWeightBounds, FungiblesAdapter, IsConcrete, NativeAsset, NoChecking,
ParentAsSuperuser, ParentIsPreset, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, WithComputedOrigin,
};
use xcm_executor::{traits::JustTry, Config, XcmExecutor};
pub type SovereignAccountOf =
(AccountId32Aliases<RelayNetwork, AccountId>, ParentIsPreset<AccountId>);
parameter_types! {
pub const BlockHashCount: u64 = 250;
}
impl frame_system::Config for Runtime {
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Nonce = u64;
type Block = Block;
type Hash = H256;
type Hashing = ::sp_runtime::traits::BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type BlockWeights = ();
type BlockLength = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type DbWeight = ();
type BaseCallFilter = Everything;
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}
parameter_types! {
pub ExistentialDeposit: Balance = 1;
pub const MaxLocks: u32 = 50;
pub const MaxReserves: u32 = 50;
}
impl pallet_balances::Config for Runtime {
type AccountStore = System;
type Balance = Balance;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type MaxHolds = ConstU32<1>;
type MaxLocks = MaxLocks;
type MaxReserves = MaxReserves;
type ReserveIdentifier = [u8; 8];
type RuntimeEvent = RuntimeEvent;
type RuntimeHoldReason = RuntimeHoldReason;
type RuntimeFreezeReason = RuntimeFreezeReason;
type WeightInfo = ();
}
parameter_types! {
pub const AssetDeposit: u128 = 1_000_000;
pub const MetadataDepositBase: u128 = 1_000_000;
pub const MetadataDepositPerByte: u128 = 100_000;
pub const AssetAccountDeposit: u128 = 1_000_000;
pub const ApprovalDeposit: u128 = 1_000_000;
pub const AssetsStringLimit: u32 = 50;
pub const RemoveItemsLimit: u32 = 50;
}
impl pallet_assets::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Balance = Balance;
type AssetId = AssetIdForAssets;
type Currency = Balances;
type CreateOrigin = AsEnsureOriginWithArg<EnsureSigned<AccountId>>;
type ForceOrigin = EnsureRoot<AccountId>;
type AssetDeposit = AssetDeposit;
type MetadataDepositBase = MetadataDepositBase;
type MetadataDepositPerByte = MetadataDepositPerByte;
type AssetAccountDeposit = AssetAccountDeposit;
type ApprovalDeposit = ApprovalDeposit;
type StringLimit = AssetsStringLimit;
type Freezer = ();
type Extra = ();
type WeightInfo = ();
type RemoveItemsLimit = RemoveItemsLimit;
type AssetIdParameter = AssetIdForAssets;
type CallbackHandle = ();
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
parameter_types! {
pub const ReservedXcmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0);
pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0);
}
parameter_types! {
pub const KsmLocation: MultiLocation = MultiLocation::parent();
pub const TokenLocation: MultiLocation = Here.into_location();
pub const RelayNetwork: NetworkId = ByGenesis([0; 32]);
pub UniversalLocation: InteriorMultiLocation = Parachain(MsgQueue::parachain_id().into()).into();
}
pub type XcmOriginToCallOrigin = (
SovereignSignedViaLocation<SovereignAccountOf, RuntimeOrigin>,
ParentAsSuperuser<RuntimeOrigin>,
SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>,
XcmPassthrough<RuntimeOrigin>,
);
parameter_types! {
pub const XcmInstructionWeight: Weight = Weight::from_parts(1_000, 1_000);
pub TokensPerSecondPerMegabyte: (AssetId, u128, u128) = (Concrete(Parent.into()), 1_000_000_000_000, 1024 * 1024);
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 64;
pub ForeignPrefix: MultiLocation = (Parent,).into();
pub CheckingAccount: AccountId = PolkadotXcm::check_account();
pub TrustedLockPairs: (MultiLocation, MultiAssetFilter) =
(Parent.into(), Wild(AllOf { id: Concrete(Parent.into()), fun: WildFungible }));
}
pub fn estimate_message_fee(number_of_instructions: u64) -> u128 {
let weight = estimate_weight(number_of_instructions);
estimate_fee_for_weight(weight)
}
pub fn estimate_weight(number_of_instructions: u64) -> Weight {
XcmInstructionWeight::get().saturating_mul(number_of_instructions)
}
pub fn estimate_fee_for_weight(weight: Weight) -> u128 {
let (_, units_per_second, units_per_mb) = TokensPerSecondPerMegabyte::get();
units_per_second * (weight.ref_time() as u128) / (WEIGHT_REF_TIME_PER_SECOND as u128) +
units_per_mb * (weight.proof_size() as u128) / (WEIGHT_PROOF_SIZE_PER_MB as u128)
}
pub type LocalBalancesTransactor =
XcmCurrencyAdapter<Balances, IsConcrete<TokenLocation>, SovereignAccountOf, AccountId, ()>;
pub struct FromMultiLocationToAsset<MultiLocation, AssetId>(PhantomData<(MultiLocation, AssetId)>);
impl MaybeEquivalence<MultiLocation, AssetIdForAssets>
for FromMultiLocationToAsset<MultiLocation, AssetIdForAssets>
{
fn convert(value: &MultiLocation) -> Option<AssetIdForAssets> {
match *value {
MultiLocation { parents: 1, interior: Here } => Some(0 as AssetIdForAssets),
MultiLocation { parents: 1, interior: X1(Parachain(para_id)) } =>
Some(para_id as AssetIdForAssets),
_ => None,
}
}
fn convert_back(_id: &AssetIdForAssets) -> Option<MultiLocation> {
None
}
}
pub type ForeignAssetsTransactor = FungiblesAdapter<
Assets,
ConvertedConcreteId<
AssetIdForAssets,
Balance,
FromMultiLocationToAsset<MultiLocation, AssetIdForAssets>,
JustTry,
>,
SovereignAccountOf,
AccountId,
NoChecking,
CheckingAccount,
>;
/// Means for transacting assets on this chain
pub type AssetTransactors = (LocalBalancesTransactor, ForeignAssetsTransactor);
pub struct ParentRelay;
impl Contains<MultiLocation> for ParentRelay {
fn contains(location: &MultiLocation) -> bool {
location.contains_parents_only(1)
}
}
pub struct ThisParachain;
impl Contains<MultiLocation> for ThisParachain {
fn contains(location: &MultiLocation) -> bool {
matches!(
location,
MultiLocation { parents: 0, interior: Junctions::X1(Junction::AccountId32 { .. }) }
)
}
}
pub type XcmRouter = crate::ParachainXcmRouter<MsgQueue>;
pub type Barrier = (
xcm_builder::AllowUnpaidExecutionFrom<ThisParachain>,
WithComputedOrigin<
(AllowExplicitUnpaidExecutionFrom<ParentRelay>, AllowTopLevelPaidExecutionFrom<Everything>),
UniversalLocation,
ConstU32<1>,
>,
);
parameter_types! {
pub NftCollectionOne: MultiAssetFilter
= Wild(AllOf { fun: WildNonFungible, id: Concrete((Parent, GeneralIndex(1)).into()) });
pub NftCollectionOneForRelay: (MultiAssetFilter, MultiLocation)
= (NftCollectionOne::get(), Parent.into());
pub RelayNativeAsset: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete((Parent, Here).into()) });
pub RelayNativeAssetForRelay: (MultiAssetFilter, MultiLocation) = (RelayNativeAsset::get(), Parent.into());
}
pub type TrustedTeleporters =
(xcm_builder::Case<NftCollectionOneForRelay>, xcm_builder::Case<RelayNativeAssetForRelay>);
pub type TrustedReserves = EverythingBut<xcm_builder::Case<NftCollectionOneForRelay>>;
pub struct XcmConfig;
impl Config for XcmConfig {
type RuntimeCall = RuntimeCall;
type XcmSender = XcmRouter;
type AssetTransactor = AssetTransactors;
type OriginConverter = XcmOriginToCallOrigin;
type IsReserve = (NativeAsset, TrustedReserves);
type IsTeleporter = TrustedTeleporters;
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<XcmInstructionWeight, RuntimeCall, MaxInstructions>;
type Trader = FixedRateOfFungible<TokensPerSecondPerMegabyte, ()>;
type ResponseHandler = PolkadotXcm;
type AssetTrap = PolkadotXcm;
type AssetLocker = PolkadotXcm;
type AssetExchanger = ();
type AssetClaims = PolkadotXcm;
type SubscriptionService = PolkadotXcm;
type PalletInstancesInfo = AllPalletsWithSystem;
type FeeManager = ();
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = RuntimeCall;
type SafeCallFilter = Everything;
type Aliasers = Nothing;
}
impl mock_msg_queue::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type XcmExecutor = XcmExecutor<XcmConfig>;
}
pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>;
pub struct TrustedLockerCase<T>(PhantomData<T>);
impl<T: Get<(MultiLocation, MultiAssetFilter)>> ContainsPair<MultiLocation, MultiAsset>
for TrustedLockerCase<T>
{
fn contains(origin: &MultiLocation, asset: &MultiAsset) -> bool {
let (o, a) = T::get();
a.matches(asset) && &o == origin
}
}
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type XcmRouter = XcmRouter;
type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type XcmExecuteFilter = Everything;
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = Nothing;
type XcmReserveTransferFilter = Everything;
type Weigher = FixedWeightBounds<XcmInstructionWeight, RuntimeCall, MaxInstructions>;
type UniversalLocation = UniversalLocation;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
type Currency = Balances;
type CurrencyMatcher = IsConcrete<TokenLocation>;
type TrustedLockers = TrustedLockerCase<TrustedLockPairs>;
type SovereignAccountOf = SovereignAccountOf;
type MaxLockers = ConstU32<8>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = pallet_xcm::TestWeightInfo;
type AdminOrigin = EnsureRoot<AccountId>;
}
type Block = frame_system::mocking::MockBlock<Runtime>;
impl pallet_timestamp::Config for Runtime {
type Moment = u64;
type OnTimestampSet = ();
type MinimumPeriod = ConstU64<1>;
type WeightInfo = ();
}
construct_runtime!(
pub enum Runtime
{
System: frame_system,
Balances: pallet_balances,
Timestamp: pallet_timestamp,
MsgQueue: mock_msg_queue,
PolkadotXcm: pallet_xcm,
Contracts: pallet_contracts,
Assets: pallet_assets,
}
);
@@ -0,0 +1,98 @@
// Copyright Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use super::{Balances, Runtime, RuntimeCall, RuntimeEvent};
use crate::{
parachain,
parachain::RuntimeHoldReason,
primitives::{Balance, CENTS},
};
use frame_support::{
parameter_types,
traits::{ConstBool, ConstU32, Contains, Randomness},
weights::Weight,
};
use frame_system::pallet_prelude::BlockNumberFor;
use pallet_xcm::BalanceOf;
use sp_runtime::{traits::Convert, Perbill};
pub const fn deposit(items: u32, bytes: u32) -> Balance {
items as Balance * 1 * CENTS + (bytes as Balance) * 1 * CENTS
}
parameter_types! {
pub const DepositPerItem: Balance = deposit(1, 0);
pub const DepositPerByte: Balance = deposit(0, 1);
pub const DefaultDepositLimit: Balance = deposit(1024, 1024 * 1024);
pub Schedule: pallet_contracts::Schedule<Runtime> = Default::default();
pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
pub const MaxDelegateDependencies: u32 = 32;
}
pub struct DummyRandomness<T: pallet_contracts::Config>(sp_std::marker::PhantomData<T>);
impl<T: pallet_contracts::Config> Randomness<T::Hash, BlockNumberFor<T>> for DummyRandomness<T> {
fn random(_subject: &[u8]) -> (T::Hash, BlockNumberFor<T>) {
(Default::default(), Default::default())
}
}
impl Convert<Weight, BalanceOf<Self>> for Runtime {
fn convert(w: Weight) -> BalanceOf<Self> {
w.ref_time().into()
}
}
#[derive(Clone, Default)]
pub struct Filters;
impl Contains<RuntimeCall> for Filters {
fn contains(call: &RuntimeCall) -> bool {
match call {
parachain::RuntimeCall::Contracts(_) => true,
_ => false,
}
}
}
impl pallet_contracts::Config for Runtime {
type AddressGenerator = pallet_contracts::DefaultAddressGenerator;
type CallFilter = Filters;
type CallStack = [pallet_contracts::Frame<Self>; 5];
type ChainExtension = ();
type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
type Currency = Balances;
type DefaultDepositLimit = DefaultDepositLimit;
type DepositPerByte = DepositPerByte;
type DepositPerItem = DepositPerItem;
type MaxCodeLen = ConstU32<{ 123 * 1024 }>;
type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>;
type MaxDelegateDependencies = MaxDelegateDependencies;
type MaxStorageKeyLen = ConstU32<128>;
type Migrations = ();
type Randomness = DummyRandomness<Self>;
type RuntimeCall = RuntimeCall;
type RuntimeEvent = RuntimeEvent;
type RuntimeHoldReason = RuntimeHoldReason;
type Schedule = Schedule;
type Time = super::Timestamp;
type UnsafeUnstableInterface = ConstBool<true>;
type WeightInfo = ();
type WeightPrice = Self;
type Debug = ();
type Environment = ();
type Xcm = pallet_xcm::Pallet<Self>;
}
@@ -0,0 +1,23 @@
// 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/>.
pub type Balance = u128;
pub const UNITS: Balance = 10_000_000_000;
pub const CENTS: Balance = UNITS / 100; // 100_000_000
pub type AccountId = sp_runtime::AccountId32;
pub type AssetIdForAssets = u128;
@@ -0,0 +1,236 @@
// Copyright 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/>.
//! Relay chain runtime mock.
use frame_support::{
construct_runtime, parameter_types,
traits::{Contains, Everything, Nothing},
weights::Weight,
};
use frame_system::EnsureRoot;
use sp_core::{ConstU32, H256};
use sp_runtime::traits::IdentityLookup;
use polkadot_parachain_primitives::primitives::Id as ParaId;
use polkadot_runtime_parachains::{configuration, origin, shared};
use xcm::latest::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia,
ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, DescribeAllTerminal,
DescribeFamily, FixedRateOfFungible, FixedWeightBounds, HashedDescription, IsConcrete,
SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, WithComputedOrigin,
};
use xcm_executor::{Config, XcmExecutor};
use super::{
mocks::relay_message_queue::*,
primitives::{AccountId, Balance},
};
parameter_types! {
pub const BlockHashCount: u64 = 250;
}
impl frame_system::Config for Runtime {
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Block = Block;
type Nonce = u64;
type Hash = H256;
type Hashing = ::sp_runtime::traits::BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type BlockWeights = ();
type BlockLength = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type DbWeight = ();
type BaseCallFilter = Everything;
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}
parameter_types! {
pub ExistentialDeposit: Balance = 1;
pub const MaxLocks: u32 = 50;
pub const MaxReserves: u32 = 50;
}
impl pallet_balances::Config for Runtime {
type MaxLocks = MaxLocks;
type Balance = Balance;
type RuntimeEvent = RuntimeEvent;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type WeightInfo = ();
type MaxReserves = MaxReserves;
type ReserveIdentifier = [u8; 8];
type FreezeIdentifier = ();
type MaxHolds = ConstU32<0>;
type MaxFreezes = ConstU32<0>;
type RuntimeHoldReason = RuntimeHoldReason;
type RuntimeFreezeReason = RuntimeFreezeReason;
}
impl shared::Config for Runtime {}
impl configuration::Config for Runtime {
type WeightInfo = configuration::TestWeightInfo;
}
parameter_types! {
pub RelayNetwork: NetworkId = ByGenesis([0; 32]);
pub const TokenLocation: MultiLocation = Here.into_location();
pub UniversalLocation: InteriorMultiLocation = Here;
pub UnitWeightCost: u64 = 1_000;
}
pub type SovereignAccountOf = (
HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>,
AccountId32Aliases<RelayNetwork, AccountId>,
ChildParachainConvertsVia<ParaId, AccountId>,
);
pub type LocalBalancesTransactor =
XcmCurrencyAdapter<Balances, IsConcrete<TokenLocation>, SovereignAccountOf, AccountId, ()>;
pub type AssetTransactors = LocalBalancesTransactor;
type LocalOriginConverter = (
SovereignSignedViaLocation<SovereignAccountOf, RuntimeOrigin>,
ChildParachainAsNative<origin::Origin, RuntimeOrigin>,
SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>,
ChildSystemParachainAsSuperuser<ParaId, RuntimeOrigin>,
);
parameter_types! {
pub const XcmInstructionWeight: Weight = Weight::from_parts(1_000, 1_000);
pub TokensPerSecondPerMegabyte: (AssetId, u128, u128) =
(Concrete(TokenLocation::get()), 1_000_000_000_000, 1024 * 1024);
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 64;
}
pub struct ChildrenParachains;
impl Contains<MultiLocation> for ChildrenParachains {
fn contains(location: &MultiLocation) -> bool {
matches!(location, MultiLocation { parents: 0, interior: X1(Parachain(_)) })
}
}
pub type XcmRouter = crate::RelayChainXcmRouter;
pub type Barrier = WithComputedOrigin<
(
AllowExplicitUnpaidExecutionFrom<ChildrenParachains>,
AllowTopLevelPaidExecutionFrom<Everything>,
AllowSubscriptionsFrom<Everything>,
),
UniversalLocation,
ConstU32<1>,
>;
pub struct XcmConfig;
impl Config for XcmConfig {
type RuntimeCall = RuntimeCall;
type XcmSender = XcmRouter;
type AssetTransactor = AssetTransactors;
type OriginConverter = LocalOriginConverter;
type IsReserve = ();
type IsTeleporter = ();
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
type Weigher = FixedWeightBounds<XcmInstructionWeight, RuntimeCall, MaxInstructions>;
type Trader = FixedRateOfFungible<TokensPerSecondPerMegabyte, ()>;
type ResponseHandler = XcmPallet;
type AssetTrap = XcmPallet;
type AssetLocker = XcmPallet;
type AssetExchanger = ();
type AssetClaims = XcmPallet;
type SubscriptionService = XcmPallet;
type PalletInstancesInfo = AllPalletsWithSystem;
type FeeManager = ();
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = RuntimeCall;
type SafeCallFilter = Everything;
type Aliasers = Nothing;
}
pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>;
impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type XcmRouter = XcmRouter;
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type XcmExecuteFilter = Everything;
type XcmExecutor = XcmExecutor<XcmConfig>;
type XcmTeleportFilter = Everything;
type XcmReserveTransferFilter = Everything;
type Weigher = FixedWeightBounds<XcmInstructionWeight, RuntimeCall, MaxInstructions>;
type UniversalLocation = UniversalLocation;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
type Currency = Balances;
type CurrencyMatcher = IsConcrete<TokenLocation>;
type TrustedLockers = ();
type SovereignAccountOf = SovereignAccountOf;
type MaxLockers = ConstU32<8>;
type MaxRemoteLockConsumers = ConstU32<0>;
type RemoteLockConsumerIdentifier = ();
type WeightInfo = pallet_xcm::TestWeightInfo;
type AdminOrigin = EnsureRoot<AccountId>;
}
impl origin::Config for Runtime {}
type Block = frame_system::mocking::MockBlock<Runtime>;
impl pallet_message_queue::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Size = u32;
type HeapSize = MessageQueueHeapSize;
type MaxStale = MessageQueueMaxStale;
type ServiceWeight = MessageQueueServiceWeight;
type MessageProcessor = MessageProcessor;
type QueueChangeHandler = ();
type WeightInfo = ();
type QueuePausedQuery = ();
}
construct_runtime!(
pub enum Runtime {
System: frame_system::{Pallet, Call, Storage, Config<T>, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
ParasOrigin: origin::{Pallet, Origin},
XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event<T>, Origin},
MessageQueue: pallet_message_queue::{Pallet, Event<T>},
}
);
@@ -0,0 +1,239 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
parachain::{self, Runtime},
parachain_account_sovereign_account_id,
primitives::{AccountId, CENTS},
relay_chain, MockNet, ParaA, ParachainBalances, Relay, ALICE, BOB, INITIAL_BALANCE,
};
use assert_matches::assert_matches;
use codec::{Decode, Encode};
use frame_support::{
assert_err,
pallet_prelude::Weight,
traits::{fungibles::Mutate, Currency},
};
use pallet_balances::{BalanceLock, Reasons};
use pallet_contracts::{CollectEvents, DebugInfo, Determinism};
use pallet_contracts_fixtures::compile_module;
use pallet_contracts_primitives::Code;
use xcm::{v3::prelude::*, VersionedMultiLocation, VersionedXcm};
use xcm_simulator::TestExt;
type ParachainContracts = pallet_contracts::Pallet<parachain::Runtime>;
/// Instantiate the tests contract, and fund it with some balance and assets.
fn instantiate_test_contract(name: &str) -> AccountId {
let (wasm, _) = compile_module::<Runtime>(name).unwrap();
// Instantiate contract.
let contract_addr = ParaA::execute_with(|| {
ParachainContracts::bare_instantiate(
ALICE,
0,
Weight::MAX,
None,
Code::Upload(wasm),
vec![],
vec![],
DebugInfo::UnsafeDebug,
CollectEvents::Skip,
)
.result
.unwrap()
.account_id
});
// Funds contract account with some balance and assets.
ParaA::execute_with(|| {
parachain::Balances::make_free_balance_be(&contract_addr, INITIAL_BALANCE);
parachain::Assets::mint_into(0u32.into(), &contract_addr, INITIAL_BALANCE).unwrap();
});
Relay::execute_with(|| {
let sovereign_account = parachain_account_sovereign_account_id(1u32, contract_addr.clone());
relay_chain::Balances::make_free_balance_be(&sovereign_account, INITIAL_BALANCE);
});
contract_addr
}
#[test]
fn test_xcm_execute() {
MockNet::reset();
let contract_addr = instantiate_test_contract("xcm_execute");
// Execute XCM instructions through the contract.
ParaA::execute_with(|| {
let amount: u128 = 10 * CENTS;
// The XCM used to transfer funds to Bob.
let message: xcm_simulator::Xcm<()> = Xcm(vec![
WithdrawAsset(vec![(Here, amount).into()].into()),
DepositAsset {
assets: All.into(),
beneficiary: AccountId32 { network: None, id: BOB.clone().into() }.into(),
},
]);
let result = ParachainContracts::bare_call(
ALICE,
contract_addr.clone(),
0,
Weight::MAX,
None,
VersionedXcm::V3(message).encode(),
DebugInfo::UnsafeDebug,
CollectEvents::UnsafeCollect,
Determinism::Enforced,
)
.result
.unwrap();
let mut data = &result.data[..];
let outcome = Outcome::decode(&mut data).expect("Failed to decode xcm_execute Outcome");
assert_matches!(outcome, Outcome::Complete(_));
// Check if the funds are subtracted from the account of Alice and added to the account of
// Bob.
let initial = INITIAL_BALANCE;
assert_eq!(parachain::Assets::balance(0, contract_addr), initial);
assert_eq!(ParachainBalances::free_balance(BOB), initial + amount);
});
}
#[test]
fn test_xcm_execute_filtered_call() {
MockNet::reset();
let contract_addr = instantiate_test_contract("xcm_execute");
ParaA::execute_with(|| {
// `remark` should be rejected, as it is not allowed by our CallFilter.
let call = parachain::RuntimeCall::System(frame_system::Call::remark { remark: vec![] });
let message: Xcm<parachain::RuntimeCall> = Xcm(vec![Transact {
origin_kind: OriginKind::Native,
require_weight_at_most: Weight::MAX,
call: call.encode().into(),
}]);
let result = ParachainContracts::bare_call(
ALICE,
contract_addr.clone(),
0,
Weight::MAX,
None,
VersionedXcm::V3(message).encode(),
DebugInfo::UnsafeDebug,
CollectEvents::UnsafeCollect,
Determinism::Enforced,
);
assert_err!(result.result, frame_system::Error::<parachain::Runtime>::CallFiltered);
});
}
#[test]
fn test_xcm_execute_reentrant_call() {
MockNet::reset();
let contract_addr = instantiate_test_contract("xcm_execute");
ParaA::execute_with(|| {
let transact_call = parachain::RuntimeCall::Contracts(pallet_contracts::Call::call {
dest: contract_addr.clone(),
gas_limit: 1_000_000.into(),
storage_deposit_limit: None,
data: vec![],
value: 0u128,
});
// The XCM used to transfer funds to Bob.
let message: Xcm<parachain::RuntimeCall> = Xcm(vec![
Transact {
origin_kind: OriginKind::Native,
require_weight_at_most: 1_000_000_000.into(),
call: transact_call.encode().into(),
},
ExpectTransactStatus(MaybeErrorCode::Success),
]);
let result = ParachainContracts::bare_call(
ALICE,
contract_addr.clone(),
0,
Weight::MAX,
None,
VersionedXcm::V3(message).encode(),
DebugInfo::UnsafeDebug,
CollectEvents::UnsafeCollect,
Determinism::Enforced,
)
.result
.unwrap();
let mut data = &result.data[..];
let outcome = Outcome::decode(&mut data).expect("Failed to decode xcm_execute Outcome");
assert_matches!(outcome, Outcome::Incomplete(_, XcmError::ExpectationFalse));
// Funds should not change hands as the XCM transact failed.
assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE);
});
}
#[test]
fn test_xcm_send() {
MockNet::reset();
let contract_addr = instantiate_test_contract("xcm_send");
let fee = parachain::estimate_message_fee(4); // Accounts for the `DescendOrigin` instruction added by `send_xcm`
// Send XCM instructions through the contract, to lock some funds on the relay chain.
ParaA::execute_with(|| {
let dest = MultiLocation::from(Parent);
let dest = VersionedMultiLocation::V3(dest);
let message: xcm_simulator::Xcm<()> = Xcm(vec![
WithdrawAsset((Here, fee).into()),
BuyExecution { fees: (Here, fee).into(), weight_limit: WeightLimit::Unlimited },
LockAsset { asset: (Here, 5 * CENTS).into(), unlocker: (Parachain(1)).into() },
]);
let message = VersionedXcm::V3(message);
let exec = ParachainContracts::bare_call(
ALICE,
contract_addr.clone(),
0,
Weight::MAX,
None,
(dest, message).encode(),
DebugInfo::UnsafeDebug,
CollectEvents::UnsafeCollect,
Determinism::Enforced,
);
let mut data = &exec.result.unwrap().data[..];
XcmHash::decode(&mut data).expect("Failed to decode xcm_send message_id");
});
Relay::execute_with(|| {
// Check if the funds are locked on the relay chain.
assert_eq!(
relay_chain::Balances::locks(&parachain_account_sovereign_account_id(1, contract_addr)),
vec![BalanceLock { id: *b"py/xcmlk", amount: 5 * CENTS, reasons: Reasons::All }]
);
});
}