mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 18:11:10 +00:00
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:
Generated
+43
@@ -9699,11 +9699,13 @@ dependencies = [
|
||||
"frame-system",
|
||||
"impl-trait-for-tuples",
|
||||
"log",
|
||||
"pallet-assets",
|
||||
"pallet-balances",
|
||||
"pallet-contracts-fixtures",
|
||||
"pallet-contracts-primitives",
|
||||
"pallet-contracts-proc-macro",
|
||||
"pallet-insecure-randomness-collective-flip",
|
||||
"pallet-message-queue",
|
||||
"pallet-proxy",
|
||||
"pallet-timestamp",
|
||||
"pallet-utility",
|
||||
@@ -9720,6 +9722,9 @@ dependencies = [
|
||||
"sp-keystore",
|
||||
"sp-runtime",
|
||||
"sp-std 8.0.0",
|
||||
"sp-tracing 10.0.0",
|
||||
"staging-xcm",
|
||||
"staging-xcm-builder",
|
||||
"wasm-instrument 0.4.0",
|
||||
"wasmi",
|
||||
"wat",
|
||||
@@ -9734,6 +9739,44 @@ dependencies = [
|
||||
"wat",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-contracts-mock-network"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"pallet-assets",
|
||||
"pallet-balances",
|
||||
"pallet-contracts",
|
||||
"pallet-contracts-fixtures",
|
||||
"pallet-contracts-primitives",
|
||||
"pallet-contracts-proc-macro",
|
||||
"pallet-insecure-randomness-collective-flip",
|
||||
"pallet-message-queue",
|
||||
"pallet-proxy",
|
||||
"pallet-timestamp",
|
||||
"pallet-utility",
|
||||
"pallet-xcm",
|
||||
"parity-scale-codec",
|
||||
"polkadot-parachain-primitives",
|
||||
"polkadot-primitives",
|
||||
"polkadot-runtime-parachains",
|
||||
"pretty_assertions",
|
||||
"scale-info",
|
||||
"sp-api",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-keystore",
|
||||
"sp-runtime",
|
||||
"sp-std 8.0.0",
|
||||
"sp-tracing 10.0.0",
|
||||
"staging-xcm",
|
||||
"staging-xcm-builder",
|
||||
"staging-xcm-executor",
|
||||
"xcm-simulator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-contracts-primitives"
|
||||
version = "24.0.0"
|
||||
|
||||
@@ -293,6 +293,7 @@ members = [
|
||||
"substrate/frame/contracts/fixtures",
|
||||
"substrate/frame/contracts/primitives",
|
||||
"substrate/frame/contracts/proc-macro",
|
||||
"substrate/frame/contracts/mock-network",
|
||||
"substrate/frame/conviction-voting",
|
||||
"substrate/frame/core-fellowship",
|
||||
"substrate/frame/democracy",
|
||||
|
||||
@@ -72,4 +72,5 @@ impl Config for Runtime {
|
||||
type RuntimeHoldReason = RuntimeHoldReason;
|
||||
type Debug = ();
|
||||
type Environment = ();
|
||||
type Xcm = pallet_xcm::Pallet<Self>;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
use frame_support::pallet_prelude::DispatchError;
|
||||
use sp_std::boxed::Box;
|
||||
use xcm::prelude::*;
|
||||
use xcm_executor::traits::QueryHandler;
|
||||
pub use xcm_executor::traits::QueryHandler;
|
||||
|
||||
/// Umbrella trait for all Controller traits.
|
||||
pub trait Controller<Origin, RuntimeCall, Timeout>:
|
||||
|
||||
@@ -119,5 +119,5 @@ pub use pay::{FixedLocation, LocatableAssetId, PayAccountId32OnChainOverXcm, Pay
|
||||
mod controller;
|
||||
pub use controller::{
|
||||
Controller, ExecuteController, ExecuteControllerWeightInfo, QueryController,
|
||||
QueryControllerWeightInfo, SendController, SendControllerWeightInfo,
|
||||
QueryControllerWeightInfo, QueryHandler, SendController, SendControllerWeightInfo,
|
||||
};
|
||||
|
||||
@@ -1353,6 +1353,7 @@ impl pallet_contracts::Config for Runtime {
|
||||
type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
|
||||
type Debug = ();
|
||||
type Environment = ();
|
||||
type Xcm = ();
|
||||
}
|
||||
|
||||
impl pallet_sudo::Config for Runtime {
|
||||
|
||||
@@ -48,6 +48,9 @@ sp-io = { path = "../../primitives/io", default-features = false}
|
||||
sp-runtime = { path = "../../primitives/runtime", default-features = false}
|
||||
sp-std = { path = "../../primitives/std", default-features = false}
|
||||
|
||||
xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false}
|
||||
xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false}
|
||||
|
||||
[dev-dependencies]
|
||||
array-bytes = "6.1"
|
||||
assert_matches = "1"
|
||||
@@ -56,13 +59,19 @@ pretty_assertions = "1"
|
||||
wat = "1"
|
||||
pallet-contracts-fixtures = { path = "./fixtures" }
|
||||
|
||||
# Polkadot Dependencies
|
||||
xcm-builder = {package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder"}
|
||||
|
||||
# Substrate Dependencies
|
||||
pallet-balances = { path = "../balances" }
|
||||
pallet-timestamp = { path = "../timestamp" }
|
||||
pallet-message-queue = { path = "../message-queue" }
|
||||
pallet-insecure-randomness-collective-flip = { path = "../insecure-randomness-collective-flip" }
|
||||
pallet-utility = { path = "../utility" }
|
||||
pallet-assets = { path = "../assets" }
|
||||
pallet-proxy = { path = "../proxy" }
|
||||
sp-keystore = { path = "../../primitives/keystore" }
|
||||
sp-tracing = { path = "../../primitives/tracing" }
|
||||
|
||||
[features]
|
||||
default = [ "std" ]
|
||||
@@ -92,12 +101,16 @@ std = [
|
||||
"sp-std/std",
|
||||
"wasm-instrument/std",
|
||||
"wasmi/std",
|
||||
"xcm-builder/std",
|
||||
"xcm/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"pallet-assets/runtime-benchmarks",
|
||||
"pallet-balances/runtime-benchmarks",
|
||||
"pallet-message-queue/runtime-benchmarks",
|
||||
"pallet-proxy/runtime-benchmarks",
|
||||
"pallet-timestamp/runtime-benchmarks",
|
||||
"pallet-utility/runtime-benchmarks",
|
||||
@@ -105,12 +118,15 @@ runtime-benchmarks = [
|
||||
"rand_pcg",
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
"wasm-instrument",
|
||||
"xcm-builder/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"frame-support/try-runtime",
|
||||
"frame-system/try-runtime",
|
||||
"pallet-assets/try-runtime",
|
||||
"pallet-balances/try-runtime",
|
||||
"pallet-insecure-randomness-collective-flip/try-runtime",
|
||||
"pallet-message-queue/try-runtime",
|
||||
"pallet-proxy/try-runtime",
|
||||
"pallet-timestamp/try-runtime",
|
||||
"pallet-utility/try-runtime",
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
;; This passes its input to `seal_xcm_execute` and returns the return value to its caller.
|
||||
(module
|
||||
(import "seal0" "xcm_execute" (func $xcm_execute (param i32 i32 i32) (result i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; 0x1000 = 4k in little endian
|
||||
;; Size of input buffer
|
||||
(data (i32.const 0) "\00\10")
|
||||
|
||||
(func $assert (param i32)
|
||||
(block $ok
|
||||
(br_if $ok
|
||||
(get_local 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "call")
|
||||
;; Receive the encoded call
|
||||
(call $seal_input
|
||||
(i32.const 4) ;; Pointer to the input buffer
|
||||
(i32.const 0) ;; Pointer to the buffer length (before call) and to the copied data length (after call)
|
||||
)
|
||||
;; Input data layout.
|
||||
;; [0..4) - size of the call
|
||||
;; [4..) - message
|
||||
|
||||
;; Call xcm_execute with provided input.
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(call $xcm_execute
|
||||
(i32.const 4) ;; Pointer where the message is stored
|
||||
(i32.load (i32.const 0)) ;; Size of the message
|
||||
(i32.const 100) ;; Pointer to the where the outcome is stored
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
|
||||
(call $seal_return
|
||||
(i32.const 0) ;; flags
|
||||
(i32.const 100) ;; Pointer to returned value
|
||||
(i32.const 10) ;; length of returned value
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
)
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
;; This passes its input to `seal_xcm_send` and returns the return value to its caller.
|
||||
(module
|
||||
(import "seal0" "xcm_send" (func $xcm_send (param i32 i32 i32 i32) (result i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "seal0" "seal_return" (func $seal_return (param i32 i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
;; 0x1000 = 4k in little endian
|
||||
;; size of input buffer
|
||||
(data (i32.const 0) "\00\10")
|
||||
|
||||
(func $assert (param i32)
|
||||
(block $ok
|
||||
(br_if $ok
|
||||
(get_local 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "call")
|
||||
|
||||
;; Receive the encoded call
|
||||
(call $seal_input
|
||||
(i32.const 4) ;; Pointer to the input buffer
|
||||
(i32.const 0) ;; Size of the length buffer
|
||||
)
|
||||
|
||||
;; Input data layout.
|
||||
;; [0..4) - size of the call
|
||||
;; [4..7) - dest
|
||||
;; [7..) - message
|
||||
|
||||
;; Call xcm_send with provided input.
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(call $xcm_send
|
||||
(i32.const 4) ;; Pointer where the dest is stored
|
||||
(i32.const 7) ;; Pointer where the message is stored
|
||||
(i32.sub
|
||||
(i32.load (i32.const 0)) ;; length of the input buffer
|
||||
(i32.const 3) ;; Size of the XCM dest
|
||||
)
|
||||
(i32.const 100) ;; Pointer to the where the message_id is stored
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
|
||||
;; Return the the message_id
|
||||
(call $seal_return
|
||||
(i32.const 0) ;; flags
|
||||
(i32.const 100) ;; Pointer to returned value
|
||||
(i32.const 32) ;; length of returned value
|
||||
)
|
||||
)
|
||||
|
||||
(func (export "deploy"))
|
||||
)
|
||||
@@ -23,6 +23,8 @@ fn fixtures_root_dir() -> PathBuf {
|
||||
// When `CARGO_MANIFEST_DIR` is not set, Rust resolves relative paths from the root folder
|
||||
(Err(_), _) => "substrate/frame/contracts/fixtures/data".into(),
|
||||
(Ok(path), Ok(s)) if s == "pallet-contracts" => PathBuf::from(path).join("fixtures/data"),
|
||||
(Ok(path), Ok(s)) if s == "pallet-contracts-mock-network" =>
|
||||
PathBuf::from(path).parent().unwrap().join("fixtures/data"),
|
||||
(Ok(_), pkg_name) => panic!("Failed to resolve fixture dir for tests from {pkg_name:?}."),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
[package]
|
||||
name = "pallet-contracts-mock-network"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage = "https://substrate.io"
|
||||
repository.workspace = true
|
||||
description = "A mock network for testing pallet-contracts"
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", "max-encoded-len"] }
|
||||
|
||||
frame-support = { path = "../../support", default-features = false}
|
||||
frame-system = { path = "../../system", default-features = false}
|
||||
pallet-assets = { path = "../../assets" }
|
||||
pallet-balances = { path = "../../balances" }
|
||||
pallet-contracts = { path = ".." }
|
||||
pallet-contracts-primitives = { path = "../primitives", default-features = false}
|
||||
pallet-contracts-proc-macro = { path = "../proc-macro" }
|
||||
pallet-insecure-randomness-collective-flip = { path = "../../insecure-randomness-collective-flip" }
|
||||
pallet-message-queue = { path = "../../message-queue" }
|
||||
pallet-proxy = { path = "../../proxy" }
|
||||
pallet-timestamp = { path = "../../timestamp" }
|
||||
pallet-utility = { path = "../../utility" }
|
||||
pallet-xcm = { path = "../../../../polkadot/xcm/pallet-xcm", default-features = false}
|
||||
polkadot-parachain-primitives = { path = "../../../../polkadot/parachain" }
|
||||
polkadot-primitives = { path = "../../../../polkadot/primitives" }
|
||||
polkadot-runtime-parachains = {path = "../../../../polkadot/runtime/parachains"}
|
||||
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
|
||||
sp-api = { path = "../../../primitives/api", default-features = false}
|
||||
sp-core = { path = "../../../primitives/core", default-features = false}
|
||||
sp-io = { path = "../../../primitives/io", default-features = false}
|
||||
sp-keystore = { path = "../../../primitives/keystore" }
|
||||
sp-runtime = { path = "../../../primitives/runtime", default-features = false}
|
||||
sp-std = { path = "../../../primitives/std", default-features = false}
|
||||
sp-tracing = { path = "../../../primitives/tracing" }
|
||||
xcm = { package = "staging-xcm", path = "../../../../polkadot/xcm", default-features = false}
|
||||
xcm-builder = {package = "staging-xcm-builder", path = "../../../../polkadot/xcm/xcm-builder"}
|
||||
xcm-executor = { package = "staging-xcm-executor", path = "../../../../polkadot/xcm/xcm-executor", default-features = false}
|
||||
xcm-simulator = {path = "../../../../polkadot/xcm/xcm-simulator"}
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1"
|
||||
pretty_assertions = "1"
|
||||
pallet-contracts-fixtures = { path = "../fixtures" }
|
||||
|
||||
[features]
|
||||
default = [ "std" ]
|
||||
std = [
|
||||
"codec/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"pallet-balances/std",
|
||||
"pallet-contracts-primitives/std",
|
||||
"pallet-contracts-proc-macro/full",
|
||||
"pallet-contracts/std",
|
||||
"pallet-insecure-randomness-collective-flip/std",
|
||||
"pallet-proxy/std",
|
||||
"pallet-timestamp/std",
|
||||
"pallet-utility/std",
|
||||
"pallet-xcm/std",
|
||||
"scale-info/std",
|
||||
"sp-api/std",
|
||||
"sp-core/std",
|
||||
"sp-io/std",
|
||||
"sp-keystore/std",
|
||||
"sp-runtime/std",
|
||||
"sp-std/std",
|
||||
"xcm-executor/std",
|
||||
"xcm/std",
|
||||
]
|
||||
|
||||
runtime-benchmarks = [
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"pallet-assets/runtime-benchmarks",
|
||||
"pallet-balances/runtime-benchmarks",
|
||||
"pallet-contracts/runtime-benchmarks",
|
||||
"pallet-message-queue/runtime-benchmarks",
|
||||
"pallet-proxy/runtime-benchmarks",
|
||||
"pallet-timestamp/runtime-benchmarks",
|
||||
"pallet-utility/runtime-benchmarks",
|
||||
"pallet-xcm/runtime-benchmarks",
|
||||
"polkadot-parachain-primitives/runtime-benchmarks",
|
||||
"polkadot-primitives/runtime-benchmarks",
|
||||
"polkadot-runtime-parachains/runtime-benchmarks",
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
"xcm-builder/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
]
|
||||
@@ -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(¶chain_account_sovereign_account_id(1, contract_addr)),
|
||||
vec![BalanceLock { id: *b"py/xcmlk", amount: 5 * CENTS, reasons: Reasons::All }]
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -403,6 +403,14 @@ pub mod pallet {
|
||||
/// its type appears in the metadata. Only valid value is `()`.
|
||||
#[pallet::constant]
|
||||
type Environment: Get<Environment<Self>>;
|
||||
|
||||
/// A type that exposes XCM APIs, allowing contracts to interact with other parachains, and
|
||||
/// execute XCM programs.
|
||||
type Xcm: xcm_builder::Controller<
|
||||
OriginFor<Self>,
|
||||
<Self as frame_system::Config>::RuntimeCall,
|
||||
BlockNumberFor<Self>,
|
||||
>;
|
||||
}
|
||||
|
||||
#[pallet::hooks]
|
||||
@@ -1004,6 +1012,8 @@ pub mod pallet {
|
||||
/// in this error. Note that this usually shouldn't happen as deploying such contracts
|
||||
/// is rejected.
|
||||
NoChainExtension,
|
||||
/// Failed to decode the XCM program.
|
||||
XCMDecodeFailed,
|
||||
/// A contract with the same AccountId already exists.
|
||||
DuplicateContract,
|
||||
/// A contract self destructed in its constructor.
|
||||
|
||||
@@ -486,6 +486,7 @@ impl Config for Test {
|
||||
type MaxDelegateDependencies = MaxDelegateDependencies;
|
||||
type Debug = TestDebug;
|
||||
type Environment = ();
|
||||
type Xcm = ();
|
||||
}
|
||||
|
||||
pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]);
|
||||
|
||||
@@ -23,10 +23,16 @@ use crate::{
|
||||
schedule::HostFnWeights,
|
||||
BalanceOf, CodeHash, Config, DebugBufferVec, Error, SENTINEL,
|
||||
};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen};
|
||||
use frame_support::{ensure, traits::Get, weights::Weight};
|
||||
use frame_support::{
|
||||
dispatch::DispatchInfo,
|
||||
ensure,
|
||||
pallet_prelude::{DispatchResult, DispatchResultWithPostInfo},
|
||||
parameter_types,
|
||||
traits::Get,
|
||||
weights::Weight,
|
||||
};
|
||||
use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags};
|
||||
use pallet_contracts_proc_macro::define_env;
|
||||
use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256};
|
||||
@@ -36,6 +42,9 @@ use sp_runtime::{
|
||||
};
|
||||
use sp_std::{fmt, prelude::*};
|
||||
use wasmi::{core::HostError, errors::LinkerError, Linker, Memory, Store};
|
||||
use xcm::VersionedXcm;
|
||||
|
||||
type CallOf<T> = <T as frame_system::Config>::RuntimeCall;
|
||||
|
||||
/// The maximum nesting depth a contract can use when encoding types.
|
||||
const MAX_DECODE_NESTING: u32 = 256;
|
||||
@@ -113,6 +122,17 @@ pub enum ReturnCode {
|
||||
EcdsaRecoverFailed = 11,
|
||||
/// sr25519 signature verification failed.
|
||||
Sr25519VerifyFailed = 12,
|
||||
/// The `xcm_execute` call failed.
|
||||
XcmExecutionFailed = 13,
|
||||
/// The `xcm_send` call failed.
|
||||
XcmSendFailed = 14,
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
/// Getter types used by [`crate::api_doc::Current::call_runtime`]
|
||||
const CallRuntimeFailed: ReturnCode = ReturnCode::CallRuntimeFailed;
|
||||
/// Getter types used by [`crate::api_doc::Current::xcm_execute`]
|
||||
const XcmExecutionFailed: ReturnCode = ReturnCode::XcmExecutionFailed;
|
||||
}
|
||||
|
||||
impl From<ExecReturnValue> for ReturnCode {
|
||||
@@ -461,6 +481,29 @@ fn already_charged(_: u32) -> Option<RuntimeCosts> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Ensure that the XCM program is executable, by checking that it does not contain any [`Transact`]
|
||||
/// instruction with a call that is not allowed by the CallFilter.
|
||||
fn ensure_executable<T: Config>(message: &VersionedXcm<CallOf<T>>) -> DispatchResult {
|
||||
use frame_support::traits::Contains;
|
||||
use xcm::prelude::{Transact, Xcm};
|
||||
|
||||
let mut message: Xcm<CallOf<T>> =
|
||||
message.clone().try_into().map_err(|_| Error::<T>::XCMDecodeFailed)?;
|
||||
|
||||
message.iter_mut().try_for_each(|inst| -> DispatchResult {
|
||||
let Transact { ref mut call, .. } = inst else { return Ok(()) };
|
||||
let call = call.ensure_decoded().map_err(|_| Error::<T>::XCMDecodeFailed)?;
|
||||
|
||||
if !<T as Config>::CallFilter::contains(call) {
|
||||
return Err(frame_system::Error::<T>::CallFiltered.into())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Can only be used for one call.
|
||||
pub struct Runtime<'a, E: Ext + 'a> {
|
||||
ext: &'a mut E,
|
||||
@@ -558,6 +601,32 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> {
|
||||
self.ext.gas_meter_mut().adjust_gas(charged, token);
|
||||
}
|
||||
|
||||
/// Charge, Run and adjust gas, for executing the given dispatchable.
|
||||
fn call_dispatchable<
|
||||
ErrorReturnCode: Get<ReturnCode>,
|
||||
F: FnOnce(&mut Self) -> DispatchResultWithPostInfo,
|
||||
>(
|
||||
&mut self,
|
||||
dispatch_info: DispatchInfo,
|
||||
run: F,
|
||||
) -> Result<ReturnCode, TrapReason> {
|
||||
use frame_support::dispatch::extract_actual_weight;
|
||||
let charged = self.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?;
|
||||
let result = run(self);
|
||||
let actual_weight = extract_actual_weight(&result, &dispatch_info);
|
||||
self.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight));
|
||||
match result {
|
||||
Ok(_) => Ok(ReturnCode::Success),
|
||||
Err(e) => {
|
||||
if self.ext.append_debug_buffer("") {
|
||||
self.ext.append_debug_buffer("call failed with: ");
|
||||
self.ext.append_debug_buffer(e.into());
|
||||
};
|
||||
Ok(ErrorReturnCode::get())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Read designated chunk from the sandbox memory.
|
||||
///
|
||||
/// Returns `Err` if one of the following conditions occurs:
|
||||
@@ -633,8 +702,10 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> {
|
||||
let ptr = ptr as usize;
|
||||
let mut bound_checked =
|
||||
memory.get(ptr..ptr + len as usize).ok_or_else(|| Error::<E::T>::OutOfBounds)?;
|
||||
|
||||
let decoded = D::decode_all_with_depth_limit(MAX_DECODE_NESTING, &mut bound_checked)
|
||||
.map_err(|_| DispatchError::from(Error::<E::T>::DecodingFailed))?;
|
||||
|
||||
Ok(decoded)
|
||||
}
|
||||
|
||||
@@ -1023,6 +1094,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> {
|
||||
// for every function.
|
||||
#[define_env(doc)]
|
||||
pub mod env {
|
||||
|
||||
/// Set the value at the given key in the contract storage.
|
||||
///
|
||||
/// Equivalent to the newer [`seal1`][`super::api_doc::Version1::set_storage`] version with the
|
||||
@@ -2584,7 +2656,7 @@ pub mod env {
|
||||
/// # Return Value
|
||||
///
|
||||
/// Returns `ReturnCode::Success` when the dispatchable was successfully executed and
|
||||
/// returned `Ok`. When the dispatchable was exeuted but returned an error
|
||||
/// returned `Ok`. When the dispatchable was executed but returned an error
|
||||
/// `ReturnCode::CallRuntimeFailed` is returned. The full error is not
|
||||
/// provided because it is not guaranteed to be stable.
|
||||
///
|
||||
@@ -2605,23 +2677,118 @@ pub mod env {
|
||||
call_ptr: u32,
|
||||
call_len: u32,
|
||||
) -> Result<ReturnCode, TrapReason> {
|
||||
use frame_support::dispatch::{extract_actual_weight, GetDispatchInfo};
|
||||
use frame_support::dispatch::GetDispatchInfo;
|
||||
ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?;
|
||||
let call: <E::T as Config>::RuntimeCall =
|
||||
ctx.read_sandbox_memory_as_unbounded(memory, call_ptr, call_len)?;
|
||||
let dispatch_info = call.get_dispatch_info();
|
||||
let charged = ctx.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?;
|
||||
let result = ctx.ext.call_runtime(call);
|
||||
let actual_weight = extract_actual_weight(&result, &dispatch_info);
|
||||
ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight));
|
||||
match result {
|
||||
Ok(_) => Ok(ReturnCode::Success),
|
||||
ctx.call_dispatchable::<CallRuntimeFailed, _>(call.get_dispatch_info(), |ctx| {
|
||||
ctx.ext.call_runtime(call)
|
||||
})
|
||||
}
|
||||
|
||||
/// Execute an XCM program locally, using the contract's address as the origin.
|
||||
/// This is equivalent to dispatching `pallet_xcm::execute` through call_runtime, except that
|
||||
/// the function is called directly instead of being dispatched.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `msg_ptr`: the pointer into the linear memory where the [`xcm::prelude::VersionedXcm`] is
|
||||
/// placed.
|
||||
/// - `msg_len`: the length of the message in bytes.
|
||||
/// - `output_ptr`: the pointer into the linear memory where the [`xcm::prelude::Outcome`]
|
||||
/// message id is placed.
|
||||
///
|
||||
/// # Return Value
|
||||
///
|
||||
/// Returns `ReturnCode::Success` when the XCM was successfully executed. When the XCM
|
||||
/// execution fails, `ReturnCode::XcmExecutionFailed` is returned.
|
||||
#[unstable]
|
||||
fn xcm_execute(
|
||||
ctx: _,
|
||||
memory: _,
|
||||
msg_ptr: u32,
|
||||
msg_len: u32,
|
||||
output_ptr: u32,
|
||||
) -> Result<ReturnCode, TrapReason> {
|
||||
use frame_support::dispatch::DispatchInfo;
|
||||
use xcm::VersionedXcm;
|
||||
use xcm_builder::{ExecuteController, ExecuteControllerWeightInfo};
|
||||
|
||||
ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?;
|
||||
let message: VersionedXcm<CallOf<E::T>> =
|
||||
ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?;
|
||||
|
||||
let execute_weight =
|
||||
<<E::T as Config>::Xcm as ExecuteController<_, _>>::WeightInfo::execute();
|
||||
let weight = ctx.ext.gas_meter().gas_left().max(execute_weight);
|
||||
let dispatch_info = DispatchInfo { weight, ..Default::default() };
|
||||
|
||||
ensure_executable::<E::T>(&message)?;
|
||||
ctx.call_dispatchable::<XcmExecutionFailed, _>(dispatch_info, |ctx| {
|
||||
let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into();
|
||||
let outcome = <<E::T as Config>::Xcm>::execute(
|
||||
origin,
|
||||
Box::new(message),
|
||||
weight.saturating_sub(execute_weight),
|
||||
)?;
|
||||
|
||||
ctx.write_sandbox_memory(memory, output_ptr, &outcome.encode())?;
|
||||
let pre_dispatch_weight =
|
||||
<<E::T as Config>::Xcm as ExecuteController<_, _>>::WeightInfo::execute();
|
||||
Ok(Some(outcome.weight_used().saturating_add(pre_dispatch_weight)).into())
|
||||
})
|
||||
}
|
||||
|
||||
/// Send an XCM program from the contract to the specified destination.
|
||||
/// This is equivalent to dispatching `pallet_xcm::send` through `call_runtime`, except that
|
||||
/// the function is called directly instead of being dispatched.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `dest_ptr`: the pointer into the linear memory where the
|
||||
/// [`xcm::prelude::VersionedMultiLocation`] is placed.
|
||||
/// - `msg_ptr`: the pointer into the linear memory where the [`xcm::prelude::VersionedXcm`] is
|
||||
/// placed.
|
||||
/// - `msg_len`: the length of the message in bytes.
|
||||
/// - `output_ptr`: the pointer into the linear memory where the [`xcm::v3::XcmHash`] message id
|
||||
/// is placed.
|
||||
///
|
||||
/// # Return Value
|
||||
///
|
||||
/// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM
|
||||
/// execution fails, `ReturnCode::CallRuntimeFailed` is returned.
|
||||
#[unstable]
|
||||
fn xcm_send(
|
||||
ctx: _,
|
||||
memory: _,
|
||||
dest_ptr: u32,
|
||||
msg_ptr: u32,
|
||||
msg_len: u32,
|
||||
output_ptr: u32,
|
||||
) -> Result<ReturnCode, TrapReason> {
|
||||
use xcm::{VersionedMultiLocation, VersionedXcm};
|
||||
use xcm_builder::{SendController, SendControllerWeightInfo};
|
||||
|
||||
ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?;
|
||||
let dest: VersionedMultiLocation = ctx.read_sandbox_memory_as(memory, dest_ptr)?;
|
||||
|
||||
let message: VersionedXcm<()> =
|
||||
ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?;
|
||||
let weight = <<E::T as Config>::Xcm as SendController<_>>::WeightInfo::send();
|
||||
ctx.charge_gas(RuntimeCosts::CallRuntime(weight))?;
|
||||
let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into();
|
||||
|
||||
match <<E::T as Config>::Xcm>::send(origin, dest.into(), message.into()) {
|
||||
Ok(message_id) => {
|
||||
ctx.write_sandbox_memory(memory, output_ptr, &message_id.encode())?;
|
||||
Ok(ReturnCode::Success)
|
||||
},
|
||||
Err(e) => {
|
||||
if ctx.ext.append_debug_buffer("") {
|
||||
ctx.ext.append_debug_buffer("seal0::call_runtime failed with: ");
|
||||
ctx.ext.append_debug_buffer("seal0::xcm_send failed with: ");
|
||||
ctx.ext.append_debug_buffer(e.into());
|
||||
};
|
||||
Ok(ReturnCode::CallRuntimeFailed)
|
||||
Ok(ReturnCode::XcmSendFailed)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user