From 6975969274253d7755504a43ae72683dcf8b962a Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Thu, 4 May 2023 09:54:35 +0530 Subject: [PATCH 01/15] feat: move XCM CE + pallet to astar-frame 2708b052 wip: move error to CE from pallet 8441e0f3 wip: add event for callback failure 841a6ae6 wip: remove `ink_env` dep from primitives 24b61a18 wip: change xcm ce extension id to 04 f2a20149 wip: minor refactoring and add to shibuya 4ee1d597 wip: lot of refactoring for ce types eb8af147 wip: rename types to primitives, add new query disptach 8ce0b068 wip: complete CE + basic tests 20612bd5 wip: add pallet+CE --- Cargo.toml | 3 + frame/pallet-xcm-transactor/Cargo.toml | 66 ++++ frame/pallet-xcm-transactor/README.md | 0 .../contracts-example/.gitignore | 9 + .../contracts-example/Cargo.toml | 34 ++ .../contracts-example/lib.rs | 104 ++++++ .../contracts-example/sdk.rs | 109 ++++++ .../primitives/Cargo.toml | 32 ++ .../primitives/src/lib.rs | 120 ++++++ .../src/chain_extension.rs | 242 ++++++++++++ frame/pallet-xcm-transactor/src/lib.rs | 350 ++++++++++++++++++ 11 files changed, 1069 insertions(+) create mode 100644 frame/pallet-xcm-transactor/Cargo.toml create mode 100644 frame/pallet-xcm-transactor/README.md create mode 100755 frame/pallet-xcm-transactor/contracts-example/.gitignore create mode 100755 frame/pallet-xcm-transactor/contracts-example/Cargo.toml create mode 100755 frame/pallet-xcm-transactor/contracts-example/lib.rs create mode 100644 frame/pallet-xcm-transactor/contracts-example/sdk.rs create mode 100644 frame/pallet-xcm-transactor/primitives/Cargo.toml create mode 100644 frame/pallet-xcm-transactor/primitives/src/lib.rs create mode 100644 frame/pallet-xcm-transactor/src/chain_extension.rs create mode 100644 frame/pallet-xcm-transactor/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 3eb8697f..ee84e41e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,9 @@ members = [ "frame/pallet-xvm", "frame/xc-asset-config", "frame/contracts-migration", + "frame/pallet-xcm-transactor", + "frame/pallet-xcm-transactor/primitives", + "frame/pallet-xcm-transactor/contracts-example", "primitives/xcm", "precompiles/assets-erc20", "precompiles/dapps-staking", diff --git a/frame/pallet-xcm-transactor/Cargo.toml b/frame/pallet-xcm-transactor/Cargo.toml new file mode 100644 index 00000000..169541b0 --- /dev/null +++ b/frame/pallet-xcm-transactor/Cargo.toml @@ -0,0 +1,66 @@ +[package] +name = "pallet-xcm-transactor" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +log = { workspace = true } +num_enum = { version = "0.6", default-features = false } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } + +# substrate core +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +# frame dependencies +frame-support = { workspace = true } +frame-system = { workspace = true } + +# pallets +pallet-contracts = { workspace = true } +pallet-xcm = { workspace = true } + +# xcm +xcm = { workspace = true } +xcm-executor = { workspace = true } + +# types +xcm-ce-primitives = { path = "./primitives", default-features = false} + +[features] +default = ["std"] +std = [ + "num_enum/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "frame-support/std", + "frame-system/std", + "xcm/std", + "xcm-executor/std", + "pallet-xcm/std", + "pallet-contracts/std", + "xcm-ce-primitives/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "pallet-contracts/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "pallet-xcm/try-runtime", + "pallet-contracts/try-runtime", +] diff --git a/frame/pallet-xcm-transactor/README.md b/frame/pallet-xcm-transactor/README.md new file mode 100644 index 00000000..e69de29b diff --git a/frame/pallet-xcm-transactor/contracts-example/.gitignore b/frame/pallet-xcm-transactor/contracts-example/.gitignore new file mode 100755 index 00000000..8de8f877 --- /dev/null +++ b/frame/pallet-xcm-transactor/contracts-example/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/frame/pallet-xcm-transactor/contracts-example/Cargo.toml b/frame/pallet-xcm-transactor/contracts-example/Cargo.toml new file mode 100755 index 00000000..25637804 --- /dev/null +++ b/frame/pallet-xcm-transactor/contracts-example/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "contracts-example" +version = "0.1.0" +authors = ["[your_name] <[your_email]>"] +edition = "2021" + +# [workspace] + +[dependencies] +ink = { version = "~4.2.0", default-features = false } + +num_enum = { version = "0.6", default-features = false } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false } +xcm-ce-primitives = { path = "../primitives", default-features = false } + +[lib] +path = "lib.rs" + +[profile.release] +overflow-checks = false # Disable integer overflow checks. + +[features] +default = ["std"] +std = [ + "ink/std", + "num_enum/std", + "scale/std", + "scale-info/std", + "xcm/std", + "xcm-ce-primitives/std", +] +ink-as-dependency = [] diff --git a/frame/pallet-xcm-transactor/contracts-example/lib.rs b/frame/pallet-xcm-transactor/contracts-example/lib.rs new file mode 100755 index 00000000..868b294e --- /dev/null +++ b/frame/pallet-xcm-transactor/contracts-example/lib.rs @@ -0,0 +1,104 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +mod sdk; +pub use sdk::{Error as XcmCEError, XcmExtension as _XcmExtension}; + +#[ink::contract] +mod contracts { + use super::*; + use ink::{env::DefaultEnvironment, storage::Mapping}; + use xcm::{latest::Weight, prelude::*}; + use xcm_ce_primitives::{QueryConfig, ValidateSendInput}; + + type XcmExtension = _XcmExtension; + + #[ink(storage)] + #[derive(Default)] + pub struct Contracts { + value: bool, + expecting_query: Mapping, + } + + impl Contracts { + #[ink(constructor, selector = 0xFFFFFFFF)] + pub fn default() -> Self { + Self { + ..Default::default() + } + } + + #[ink(message, selector = 0x11111111)] + pub fn execute(&mut self, xcm: VersionedXcm<()>) -> Result { + let weight = XcmExtension::prepare_execute(xcm)?; + ink::env::debug_println!("[1/2] Prepared XCM"); + + XcmExtension::execute()?; + ink::env::debug_println!("[2/2] Execute XCM"); + + Ok(weight) + } + + #[ink(message, selector = 0x22222222)] + pub fn send( + &mut self, + input: ValidateSendInput, + ) -> Result { + let fees = XcmExtension::validate_send(input)?; + ink::env::debug_println!("[1/2] Validate Send XCM"); + + XcmExtension::send()?; + ink::env::debug_println!("[2/2] Send XCM"); + + Ok(fees) + } + + #[ink(message, selector = 0x33333333)] + pub fn query( + &mut self, + config: QueryConfig, + dest: VersionedMultiLocation, + ) -> Result { + ink::env::debug_println!("[1/3] Registering Query..., {config:?}"); + let query_id = XcmExtension::new_query(config, dest)?; + ink::env::debug_println!("[2/3] Registered Query"); + + self.expecting_query.insert(query_id, &true); + ink::env::debug_println!("[3/3] Save Query"); + + Ok(query_id) + } + + #[ink(message, selector = 0x44444444)] + pub fn poll_response(&mut self, query_id: QueryId) -> Result { + ink::env::debug_println!("[1/1] Response Recieved for QueryId - {query_id}"); + XcmExtension::take_response(query_id) + } + + #[ink(message, selector = 0x55555555)] + pub fn handle_response( + &mut self, + query_id: QueryId, + _responder: MultiLocation, + _response: Response, + ) { + ink::env::debug_println!("[1/1] Response Recieved for QueryId - {query_id}"); + assert!(XcmExtension::pallet_account_id() == self.env().caller()); + match self.expecting_query.get(query_id) { + Some(expecting) if expecting == true => { + // NOTE: do not delete storage, because storage deposit + // refund will fail. + // self.expecting_query.remove(query_id); + self.value = !self.value; + } + _ => { + panic!("Not expecting response"); + } + } + } + + #[ink(message, selector = 0x66666666)] + pub fn get(&self) -> bool { + self.value + } + } +} diff --git a/frame/pallet-xcm-transactor/contracts-example/sdk.rs b/frame/pallet-xcm-transactor/contracts-example/sdk.rs new file mode 100644 index 00000000..15a37230 --- /dev/null +++ b/frame/pallet-xcm-transactor/contracts-example/sdk.rs @@ -0,0 +1,109 @@ +use core::marker::PhantomData; +use ink::env::{chain_extension::FromStatusCode, DefaultEnvironment, Environment}; +use scale::{Decode, Encode}; +use xcm::{latest::Weight, prelude::*}; +use xcm_ce_primitives::{ + create_error_enum, Command, QueryConfig, ValidateSendInput, XCM_EXTENSION_ID, +}; + +create_error_enum!(pub Error); + +impl FromStatusCode for Error { + fn from_status_code(status_code: u32) -> Result<(), Self> { + match status_code { + 0 => Ok(()), + code => Err(code.into()), + } + } +} + +/// XCM Chain Extension Interface +pub struct XcmExtension(PhantomData); + +impl XcmExtension { + const fn get_func_id(idx: u16) -> u32 { + ((ID as u32) << 16) + (idx as u32) + } + + pub fn prepare_execute(xcm: VersionedXcm<()>) -> Result { + let func_id: u32 = Self::get_func_id(Command::PrepareExecute.into()); + + // fn(VersionedXcm<()>) -> Result + ::ink::env::chain_extension::ChainExtensionMethod::build(func_id) + .input::>() + .output::() + .handle_error_code::() + .call(&(xcm)) + } + + pub fn execute() -> Result<(), Error> { + let func_id: u32 = Self::get_func_id(Command::Execute.into()); + + // fn() -> Result<(Weight), Error> + ::ink::env::chain_extension::ChainExtensionMethod::build(func_id) + .input::<()>() + .output::<(), false>() + .handle_error_code::() + .call(&()) + } + + pub fn validate_send(input: ValidateSendInput) -> Result { + let func_id: u32 = Self::get_func_id(Command::ValidateSend.into()); + + // fn(ValidateSendInput) -> Result + ::ink::env::chain_extension::ChainExtensionMethod::build(func_id) + .input::() + .output::() + .handle_error_code::() + .call(&(input)) + } + + pub fn send() -> Result<(), Error> { + let func_id: u32 = Self::get_func_id(Command::Send.into()); + + // fn() -> Result<(), Error> + ::ink::env::chain_extension::ChainExtensionMethod::build(func_id) + .input::<()>() + .output::<(), false>() + .handle_error_code::() + .call(&()) + } + + pub fn new_query( + config: QueryConfig, + dest: VersionedMultiLocation, + ) -> Result { + let func_id: u32 = Self::get_func_id(Command::NewQuery.into()); + + // fn(QueryConfig, VersionedMultiLocation) -> Result + ::ink::env::chain_extension::ChainExtensionMethod::build(func_id) + .input::<( + QueryConfig, + VersionedMultiLocation, + )>() + .output::() + .handle_error_code::() + .call(&(config, dest)) + } + + pub fn take_response(query_id: QueryId) -> Result { + let func_id: u32 = Self::get_func_id(Command::TakeResponse.into()); + + // fn(QueryId) -> Result + ::ink::env::chain_extension::ChainExtensionMethod::build(func_id) + .input::() + .output::() + .handle_error_code::() + .call(&(query_id)) + } + + pub fn pallet_account_id() -> E::AccountId { + let func_id = Self::get_func_id(Command::PalletAccountId.into()); + + ::ink::env::chain_extension::ChainExtensionMethod::build(func_id) + .input::<()>() + .output::() + .ignore_error_code() + .call(&()) + } +} diff --git a/frame/pallet-xcm-transactor/primitives/Cargo.toml b/frame/pallet-xcm-transactor/primitives/Cargo.toml new file mode 100644 index 00000000..63b44746 --- /dev/null +++ b/frame/pallet-xcm-transactor/primitives/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "xcm-ce-primitives" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +num_enum = { version = "0.6", default-features = false } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } + +#substarte +sp-core = { workspace = true } +sp-runtime = { workspace = true } + +# xcm +xcm = { workspace = true } + +[features] +default = ["std"] +std = [ + "num_enum/std", + "parity-scale-codec/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "xcm/std", +] diff --git a/frame/pallet-xcm-transactor/primitives/src/lib.rs b/frame/pallet-xcm-transactor/primitives/src/lib.rs new file mode 100644 index 00000000..c7f1ec13 --- /dev/null +++ b/frame/pallet-xcm-transactor/primitives/src/lib.rs @@ -0,0 +1,120 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use num_enum::{IntoPrimitive, TryFromPrimitive}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_core::{RuntimeDebug, H160}; +use xcm::{latest::Weight, prelude::*}; + +pub const XCM_EXTENSION_ID: u16 = 04; + +#[repr(u16)] +#[derive(TryFromPrimitive, IntoPrimitive)] +pub enum Command { + PrepareExecute = 0, + Execute = 1, + ValidateSend = 2, + Send = 3, + NewQuery = 4, + TakeResponse = 5, + PalletAccountId = 6, +} + +/// Type of XCM Response Query +#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo)] +// #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub enum QueryType { + // No callback, store the response for manual polling + NoCallback, + // Call Wasm contract's method on recieving response + // It expects the contract method to have following signature + // - (query_id: QueryId, responder: Multilocation, response: Response) + WASMContractCallback { + contract_id: AccountId, + selector: [u8; 4], + }, + // Call Evm contract's method on recieving response + // It expects the contract method to have following signature + // - (query_id: QueryId, responder: Multilocation, response: Response) + EVMContractCallback { + contract_id: H160, + selector: [u8; 4], + }, +} + +/// Query config +#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo)] +// #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub struct QueryConfig { + // query type + pub query_type: QueryType, + // blocknumber after which query will be expire + pub timeout: BlockNumber, +} + +#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode)] +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub struct ValidateSendInput { + pub dest: VersionedMultiLocation, + pub xcm: VersionedXcm<()>, +} + +pub struct PreparedExecution { + pub xcm: Xcm, + pub weight: Weight, +} + +pub struct ValidatedSend { + pub dest: MultiLocation, + pub xcm: Xcm<()>, +} + +#[macro_export] +macro_rules! create_error_enum { + ($vis:vis $type_name:ident) => { + #[repr(u32)] + #[derive( + ::core::cmp::PartialEq, + ::core::cmp::Eq, + ::core::marker::Copy, + ::core::clone::Clone, + // crate name mismatch, 'parity-scale-codec' is crate name but in ink! contract + // it is usually renamed to `scale` + Encode, + Decode, + ::core::fmt::Debug, + ::num_enum::IntoPrimitive, + ::num_enum::FromPrimitive, + )] + #[cfg_attr(feature = "std", derive(::scale_info::TypeInfo))] + $vis enum $type_name { + /// Success + Success = 0, + /// CE command not supported + InvalidCommand = 1, + /// The version of the Versioned value used is not able to be interpreted. + BadVersion = 2, + /// Origin not allow for registering queries + InvalidOrigin = 3, + /// Does not support the given query type + NotSupported = 4, + /// XCM execute preparation missing + PreparationMissing = 5, + /// Some of the XCM instructions failed to execute + ExecutionFailed = 6, + /// Failed to validate the XCM for sending + SendValidateFailed = 7, + /// Failed to send the XCM to destination + SendFailed = 8, + /// No response recieved for given query + NoResponse = 9, + /// Failed to weigh the XCM message + CannotWeigh = 10, + /// Unknown runtime error + #[num_enum(default)] + RuntimeError = 99, + } + }; +} + +create_error_enum!(pub XcmCeError); diff --git a/frame/pallet-xcm-transactor/src/chain_extension.rs b/frame/pallet-xcm-transactor/src/chain_extension.rs new file mode 100644 index 00000000..afddde96 --- /dev/null +++ b/frame/pallet-xcm-transactor/src/chain_extension.rs @@ -0,0 +1,242 @@ +use crate::{Config, Pallet, QueryConfig}; +use frame_support::{traits::EnsureOrigin, DefaultNoBound}; +use frame_system::RawOrigin; +// use log; +use pallet_contracts::chain_extension::{ + ChainExtension, Environment, Ext, InitState, Result as DispatchResult, RetVal, SysConfig, +}; +use pallet_xcm::{Pallet as XcmPallet, WeightInfo}; +use parity_scale_codec::Encode; +use sp_core::Get; +use sp_std::prelude::*; +use xcm::prelude::*; +pub use xcm_ce_primitives::{ + Command::{self, *}, + PreparedExecution, ValidateSendInput, ValidatedSend, + XcmCeError::{self, *}, + XCM_EXTENSION_ID, +}; +use xcm_executor::traits::WeightBounds; + +type RuntimeCallOf = ::RuntimeCall; + +macro_rules! unwrap { + ($val:expr, $err:expr) => { + match $val { + Ok(inner) => inner, + Err(_) => return Ok(RetVal::Converging($err.into())), + } + }; +} + +#[derive(DefaultNoBound)] +pub struct XCMExtension { + prepared_execute: Option>>, + validated_send: Option, +} + +impl ChainExtension for XCMExtension +where + ::AccountId: AsRef<[u8; 32]>, +{ + fn enabled() -> bool { + true + } + + fn call(&mut self, env: Environment) -> DispatchResult + where + E: Ext, + { + match unwrap!(env.func_id().try_into(), InvalidCommand) { + PrepareExecute => self.prepare_execute(env), + Execute => self.execute(env), + ValidateSend => self.validate_send(env), + Send => self.send(env), + NewQuery => self.new_query(env), + TakeResponse => self.take_response(env), + PalletAccountId => self.pallet_account_id(env), + } + } +} + +impl XCMExtension { + fn prepare_execute>( + &mut self, + env: Environment, + ) -> DispatchResult { + let mut env = env.buf_in_buf_out(); + // input parsing + let len = env.in_len(); + let input: VersionedXcm> = env.read_as_unbounded(len)?; + + let mut xcm = unwrap!(input.try_into(), BadVersion); + // calculate the weight + let weight = unwrap!(T::Weigher::weight(&mut xcm), CannotWeigh); + + // save the prepared xcm + self.prepared_execute = Some(PreparedExecution { xcm, weight }); + // write the output to buffer + weight.using_encoded(|w| env.write(w, true, None))?; + + Ok(RetVal::Converging(XcmCeError::Success.into())) + } + + fn execute>( + &mut self, + mut env: Environment, + ) -> DispatchResult { + let input = unwrap!( + self.prepared_execute.as_ref().take().ok_or(()), + PreparationMissing + ); + // charge for xcm weight + let charged = env.charge_weight(input.weight)?; + + // TODO: find better way to get origin + // https://github.com/paritytech/substrate/pull/13708 + let origin = RawOrigin::Signed(env.ext().address().clone()); + // ensure xcm execute origin + let origin_location = unwrap!( + T::ExecuteXcmOrigin::ensure_origin(origin.into()), + BadVersion + ); + + let hash = input.xcm.using_encoded(sp_io::hashing::blake2_256); + // execute XCM + // NOTE: not using pallet_xcm::execute here because it does not return XcmError + // which is needed to ensure xcm execution success + let outcome = T::XcmExecutor::execute_xcm_in_credit( + origin_location, + input.xcm.clone(), + hash, + input.weight, + input.weight, + ); + + // adjust with actual weights used + env.adjust_weight(charged, outcome.weight_used()); + // revert for anything but a complete execution + match outcome { + Outcome::Complete(_) => Ok(RetVal::Converging(Success.into())), + _ => Ok(RetVal::Converging(ExecutionFailed.into())), + } + } + + fn validate_send>( + &mut self, + env: Environment, + ) -> DispatchResult { + let mut env = env.buf_in_buf_out(); + let len = env.in_len(); + let input: ValidateSendInput = env.read_as_unbounded(len)?; + + let dest = unwrap!(input.dest.try_into(), BadVersion); + let xcm: Xcm<()> = unwrap!(input.xcm.try_into(), BadVersion); + // validate and get fees required to send + let (_, asset) = unwrap!( + validate_send::(dest, xcm.clone()), + SendValidateFailed + ); + + // save the validated input + self.validated_send = Some(ValidatedSend { dest, xcm }); + // write the fees to output + VersionedMultiAssets::from(asset).using_encoded(|a| env.write(a, true, None))?; + + Ok(RetVal::Converging(XcmCeError::Success.into())) + } + + fn send>( + &mut self, + mut env: Environment, + ) -> DispatchResult { + let input = unwrap!( + self.validated_send.as_ref().take().ok_or(()), + PreparationMissing + ); + + let base_weight = ::WeightInfo::send(); + env.charge_weight(base_weight)?; + + // TODO: find better way to get origin + // https://github.com/paritytech/substrate/pull/13708 + let origin = RawOrigin::Signed(env.ext().address().clone()); + + // send the xcm + unwrap!( + XcmPallet::::send( + origin.into(), + Box::new(input.dest.into()), + Box::new(xcm::VersionedXcm::V3(input.xcm.clone())), + ), + SendFailed + ); + + Ok(RetVal::Converging(XcmCeError::Success.into())) + } + + fn new_query>(&self, env: Environment) -> DispatchResult + where + ::AccountId: AsRef<[u8; 32]>, + { + let mut env = env.buf_in_buf_out(); + let len = env.in_len(); + let (query_config, dest): ( + QueryConfig, + VersionedMultiLocation, + ) = env.read_as_unbounded(len)?; + + let dest: MultiLocation = unwrap!(dest.try_into(), BadVersion); + + // TODO: find better way to get origin + // https://github.com/paritytech/substrate/pull/13708 + let origin = RawOrigin::Signed(env.ext().address().clone()); + // ensure origin is allowed to make queries + unwrap!( + T::RegisterQueryOrigin::ensure_origin(origin.into()), + InvalidOrigin + ); + + // register the query + let query_id: u64 = Pallet::::new_query( + query_config, + AccountId32 { + id: *env.ext().address().as_ref(), + network: T::Network::get(), + }, + dest, + )?; + + // write the query_id to buffer + query_id.using_encoded(|q| env.write(q, true, None))?; + + Ok(RetVal::Converging(XcmCeError::Success.into())) + } + + fn take_response>( + &self, + env: Environment, + ) -> DispatchResult { + let mut env = env.buf_in_buf_out(); + let query_id: u64 = env.read_as()?; + let response = unwrap!( + pallet_xcm::Pallet::::take_response(query_id) + .map(|ret| ret.0) + .ok_or(()), + XcmCeError::NoResponse + ); + VersionedResponse::from(response).using_encoded(|r| env.write(r, true, None))?; + + Ok(RetVal::Converging(XcmCeError::Success.into())) + } + + fn pallet_account_id>( + &self, + env: Environment, + ) -> DispatchResult { + let mut env = env.buf_in_buf_out(); + Pallet::::account_id().using_encoded(|r| env.write(r, true, None))?; + + Ok(RetVal::Converging(XcmCeError::Success.into())) + } +} diff --git a/frame/pallet-xcm-transactor/src/lib.rs b/frame/pallet-xcm-transactor/src/lib.rs new file mode 100644 index 00000000..c6bdf1a4 --- /dev/null +++ b/frame/pallet-xcm-transactor/src/lib.rs @@ -0,0 +1,350 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{pallet_prelude::*, PalletId}; +use frame_system::pallet_prelude::*; +pub use pallet::*; +use pallet_contracts::Pallet as PalletContracts; +use pallet_xcm::Pallet as PalletXcm; +use sp_core::H160; +use sp_runtime::traits::{AccountIdConversion, Zero}; +use sp_std::prelude::*; +use xcm::prelude::*; + +pub type MethodSelector = [u8; 4]; + +pub mod chain_extension; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_system::Config as SysConfig; + use pallet_xcm::ensure_response; + pub use xcm_ce_primitives::{QueryConfig, QueryType}; + + // Response info + #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] + pub struct ResponseInfo { + pub query_id: QueryId, + pub query_type: QueryType, + pub response: Response, + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_xcm::Config + pallet_contracts::Config { + /// The overarching event type. + type RuntimeEvent: IsType<::RuntimeEvent> + From>; + + /// The overarching call type. + type RuntimeCall: Parameter + + From> + + IsType<::RuntimeCall>; + + /// The overaching origin type + type RuntimeOrigin: Into::RuntimeOrigin>> + + IsType<::RuntimeOrigin>; + + /// Query Handler for creating quries and handling response + type CallbackHandler: OnCallback< + AccountId = Self::AccountId, + BlockNumber = Self::BlockNumber, + >; + + /// Required origin for sending registering new queries. If successful, it resolves to `MultiLocation` + /// which exists as an interior location within this chain's XCM context. + type RegisterQueryOrigin: EnsureOrigin< + ::RuntimeOrigin, + Success = MultiLocation, + >; + + /// Max weight for callback + #[pallet::constant] + type MaxCallbackWeight: Get; + + /// Relay network id + /// required only in CE for building interior AccountId32 junction + #[pallet::constant] + type Network: Get>; + } + + /// Mapping of ongoing queries and thier type + #[pallet::storage] + #[pallet::getter(fn callback_query)] + pub(super) type CallbackQueries = + StorageMap<_, Blake2_128Concat, QueryId, QueryType, OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // successfully handled callback + CallbackSuccess(QueryType), + CallbackFailed { + query_type: QueryType, + query_id: QueryId, + }, + // new query registered + QueryPrepared { + query_type: QueryType, + query_id: QueryId, + }, + } + + #[pallet::error] + pub enum Error { + /// The version of the Versioned value used is not able to be interpreted. + BadVersion, + /// Origin not allow for registering queries + InvalidOrigin, + /// Query not found in storage + UnexpectedQueryResponse, + /// Does not support the given query type + NotSupported, + /// Callback out of gas + /// TODO: use it + OutOfGas, + /// WASM Contract reverted + WASMContractReverted, + /// EVM Contract reverted + EVMContractReverted, + /// callback failed due to unkown reasons + /// TODO: split this error into known errors + CallbackFailed, + } + + #[pallet::call] + impl Pallet { + /// Dispatch for recieving callback from pallet_xcm's notify + /// and handle their routing + /// TODO: Weights, + /// (max callback weight) + 1 DB read + 1 event + some extra (from benchmarking) + #[pallet::call_index(0)] + #[pallet::weight(T::MaxCallbackWeight::get())] + pub fn on_callback_recieved( + origin: OriginFor, + query_id: QueryId, + response: Response, + ) -> DispatchResult { + // ensure the origin is a response + let responder = ensure_response(::RuntimeOrigin::from(origin))?; + // fetch the query + let query_type = + CallbackQueries::::get(query_id).ok_or(Error::::UnexpectedQueryResponse)?; + // handle the response routing + // TODO: in case of error, maybe save the response for manual + // polling as fallback. This will require taking into weight of storing + // response in the weights of `prepare_new_query` dispatch + if let Err(e) = T::CallbackHandler::on_callback( + responder, + ResponseInfo { + query_id, + query_type: query_type.clone(), + response, + }, + ) { + Self::deposit_event(Event::::CallbackFailed { + query_type, + query_id, + }); + return Err(e.into()); + } + + // remove query from storage + CallbackQueries::::remove(query_id); + + // deposit success event + Self::deposit_event(Event::::CallbackSuccess(query_type)); + Ok(()) + } + + /// Register a new query + /// THIS IS ONLY FOR WEIGHTS BENCHMARKING, since we cannot benchmark non-dispatch + /// TODO: Weights, + /// (1 DB read + 3 DB write + 1 event + some extra (need benchmarking)) + /// Weight for this does not take callback weights into account. That should be + /// done via XcmWeigher using WeightBounds, where all query instructions's + /// `QueryResponseInfo` `max_weight` is taken into account. + #[pallet::call_index(1)] + #[pallet::weight(Weight::from_parts(1_000_000, 1_000_000))] + pub fn prepare_new_query( + origin: OriginFor, + config: QueryConfig, + dest: Box, + ) -> DispatchResult { + let origin_location = T::RegisterQueryOrigin::ensure_origin(origin)?; + let interior: Junctions = origin_location + .try_into() + .map_err(|_| Error::::InvalidOrigin)?; + let query_type = config.query_type.clone(); + let dest = MultiLocation::try_from(*dest).map_err(|()| Error::::BadVersion)?; + + // register query + let query_id = Self::new_query(config, interior, dest)?; + Self::deposit_event(Event::::QueryPrepared { + query_type, + query_id, + }); + Ok(()) + } + } +} + +/// Handle the incoming xcm notify callback from ResponseHandler (pallet_xcm) +pub trait OnCallback { + // error type, that can be converted to dispatch error + type Error: Into; + // account id type + type AccountId; + // blocknumber type + type BlockNumber; + + // TODO: Query type itself should be generic like + // + // type QueryType: Member + Parameter + MaybeSerializeDeserialize + MaxEncodedLen + Convert + // type CallbackHandler: OnResponse + // + // #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen)] + // enum MyQueryType {} + // + // impl Convert for MyQueryType {} + + /// Check whether query type is supported or not + fn can_handle(query_type: &QueryType) -> bool; + + /// handle the xcm response + fn on_callback( + responder: impl Into, + response_info: ResponseInfo, + ) -> Result; +} + +impl OnCallback for Pallet { + type AccountId = T::AccountId; + type BlockNumber = T::BlockNumber; + type Error = Error; + + fn can_handle(query_type: &QueryType) -> bool { + match query_type { + QueryType::NoCallback => true, + QueryType::WASMContractCallback { .. } => true, + // TODO: add support for evm contracts + QueryType::EVMContractCallback { .. } => false, + } + } + + fn on_callback( + responder: impl Into, + response_info: ResponseInfo, + ) -> Result { + let ResponseInfo { + query_id, + query_type, + response, + } = response_info; + + match query_type { + QueryType::NoCallback => { + // TODO: Nothing to do, maybe error? + Ok(Weight::zero()) + } + QueryType::WASMContractCallback { + contract_id, + selector, + } => Self::call_wasm_contract_method( + contract_id, + selector, + query_id, + responder.into(), + response, + ), + QueryType::EVMContractCallback { + contract_id, + selector, + } => Self::call_evm_contract_method( + contract_id, + selector, + query_id, + responder.into(), + response, + ), + } + } +} + +impl Pallet { + /// The account ID of the pallet. + pub fn account_id() -> T::AccountId { + const ID: PalletId = PalletId(*b"py/xcmnt"); + AccountIdConversion::::into_account_truncating(&ID) + } + + /// Register new query originating from querier to dest + pub fn new_query( + QueryConfig { + query_type, + timeout, + }: QueryConfig, + querier: impl Into, + dest: impl Into, + ) -> Result> { + let querier = querier.into(); + + // check if with callback handler + if !(T::CallbackHandler::can_handle(&query_type)) { + return Err(Error::NotSupported); + } + + Ok(match query_type.clone() { + QueryType::NoCallback => PalletXcm::::new_query(dest, timeout, querier), + QueryType::WASMContractCallback { .. } | QueryType::EVMContractCallback { .. } => { + let call: ::RuntimeCall = Call::on_callback_recieved { + query_id: 0, + response: Response::Null, + } + .into(); + let id = PalletXcm::::new_notify_query(dest, call, timeout, querier); + CallbackQueries::::insert(id, query_type); + id + } + }) + } + + fn call_wasm_contract_method( + contract_id: T::AccountId, + selector: [u8; 4], + query_id: QueryId, + responder: MultiLocation, + response: Response, + ) -> Result> { + // TODO: Use responder to derieve a origin account id + let outcome = PalletContracts::::bare_call( + Self::account_id(), + contract_id, + Zero::zero(), + T::MaxCallbackWeight::get(), + None, + [selector.to_vec(), (query_id, responder, response).encode()].concat(), + // TODO: should not be true + true, + pallet_contracts::Determinism::Deterministic, + ); + + let retval = outcome.result.map_err(|_| Error::CallbackFailed)?; + if retval.did_revert() { + Err(Error::WASMContractReverted) + } else { + Ok(outcome.gas_consumed) + } + } + + fn call_evm_contract_method( + _contract_id: H160, + _selector: [u8; 4], + _query_id: QueryId, + _responder: MultiLocation, + _response: Response, + ) -> Result> { + Ok(Weight::zero()) + } +} From 9e2b13771426a78eda10ea76f2c7f7c90038e79c Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Thu, 4 May 2023 11:08:16 +0530 Subject: [PATCH 02/15] docs: add license header --- .../contracts-example/lib.rs | 18 +++++++++++++++++ .../contracts-example/sdk.rs | 20 +++++++++++++++++++ .../primitives/src/lib.rs | 18 +++++++++++++++++ .../src/chain_extension.rs | 20 +++++++++++++++++++ frame/pallet-xcm-transactor/src/lib.rs | 20 +++++++++++++++++++ 5 files changed, 96 insertions(+) diff --git a/frame/pallet-xcm-transactor/contracts-example/lib.rs b/frame/pallet-xcm-transactor/contracts-example/lib.rs index 868b294e..0d48ecc3 100755 --- a/frame/pallet-xcm-transactor/contracts-example/lib.rs +++ b/frame/pallet-xcm-transactor/contracts-example/lib.rs @@ -1,3 +1,21 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + #![cfg_attr(not(feature = "std"), no_std)] mod sdk; diff --git a/frame/pallet-xcm-transactor/contracts-example/sdk.rs b/frame/pallet-xcm-transactor/contracts-example/sdk.rs index 15a37230..dd4b6808 100644 --- a/frame/pallet-xcm-transactor/contracts-example/sdk.rs +++ b/frame/pallet-xcm-transactor/contracts-example/sdk.rs @@ -1,3 +1,23 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + use core::marker::PhantomData; use ink::env::{chain_extension::FromStatusCode, DefaultEnvironment, Environment}; use scale::{Decode, Encode}; diff --git a/frame/pallet-xcm-transactor/primitives/src/lib.rs b/frame/pallet-xcm-transactor/primitives/src/lib.rs index c7f1ec13..0a7dd040 100644 --- a/frame/pallet-xcm-transactor/primitives/src/lib.rs +++ b/frame/pallet-xcm-transactor/primitives/src/lib.rs @@ -1,3 +1,21 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + #![cfg_attr(not(feature = "std"), no_std)] use num_enum::{IntoPrimitive, TryFromPrimitive}; diff --git a/frame/pallet-xcm-transactor/src/chain_extension.rs b/frame/pallet-xcm-transactor/src/chain_extension.rs index afddde96..766ea972 100644 --- a/frame/pallet-xcm-transactor/src/chain_extension.rs +++ b/frame/pallet-xcm-transactor/src/chain_extension.rs @@ -1,3 +1,23 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + use crate::{Config, Pallet, QueryConfig}; use frame_support::{traits::EnsureOrigin, DefaultNoBound}; use frame_system::RawOrigin; diff --git a/frame/pallet-xcm-transactor/src/lib.rs b/frame/pallet-xcm-transactor/src/lib.rs index c6bdf1a4..ab8a5af9 100644 --- a/frame/pallet-xcm-transactor/src/lib.rs +++ b/frame/pallet-xcm-transactor/src/lib.rs @@ -1,3 +1,23 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +//! Pallet to handle XCM Callbacks. + #![cfg_attr(not(feature = "std"), no_std)] use frame_support::{pallet_prelude::*, PalletId}; From 261796388c4a7cca96ab4246c82dbf9a4cbe9671 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Thu, 4 May 2023 11:09:31 +0530 Subject: [PATCH 03/15] feat: check querier in take response --- Cargo.toml | 2 +- .../contracts-example/Cargo.toml | 4 +- .../primitives/src/lib.rs | 10 +++ .../src/chain_extension.rs | 71 ++++++++++++------- frame/pallet-xcm-transactor/src/lib.rs | 56 +++++++++++---- 5 files changed, 99 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ee84e41e..576e5d29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ members = [ "frame/contracts-migration", "frame/pallet-xcm-transactor", "frame/pallet-xcm-transactor/primitives", - "frame/pallet-xcm-transactor/contracts-example", + # "frame/pallet-xcm-transactor/contracts-example", "primitives/xcm", "precompiles/assets-erc20", "precompiles/dapps-staking", diff --git a/frame/pallet-xcm-transactor/contracts-example/Cargo.toml b/frame/pallet-xcm-transactor/contracts-example/Cargo.toml index 25637804..71a8773a 100755 --- a/frame/pallet-xcm-transactor/contracts-example/Cargo.toml +++ b/frame/pallet-xcm-transactor/contracts-example/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "contracts-example" version = "0.1.0" -authors = ["[your_name] <[your_email]>"] +authors = ["Ashutosh Varma "] edition = "2021" -# [workspace] +[workspace] [dependencies] ink = { version = "~4.2.0", default-features = false } diff --git a/frame/pallet-xcm-transactor/primitives/src/lib.rs b/frame/pallet-xcm-transactor/primitives/src/lib.rs index 0a7dd040..8ee058de 100644 --- a/frame/pallet-xcm-transactor/primitives/src/lib.rs +++ b/frame/pallet-xcm-transactor/primitives/src/lib.rs @@ -29,12 +29,20 @@ pub const XCM_EXTENSION_ID: u16 = 04; #[repr(u16)] #[derive(TryFromPrimitive, IntoPrimitive)] pub enum Command { + /// Returns the weight for given XCM and saves it (in CE, per-call scratch buffer) for + /// execution PrepareExecute = 0, + /// Execute the XCM that was prepared earlier Execute = 1, + /// Returns the fee required to send XCM and saves it for sending ValidateSend = 2, + /// Send the validated XCM Send = 3, + /// Register the new query NewQuery = 4, + /// Take the response for query if available TakeResponse = 5, + /// Get the pallet account id which will call the contract callback PalletAccountId = 6, } @@ -128,6 +136,8 @@ macro_rules! create_error_enum { NoResponse = 9, /// Failed to weigh the XCM message CannotWeigh = 10, + /// Querier mismatch + InvalidQuerier = 11, /// Unknown runtime error #[num_enum(default)] RuntimeError = 99, diff --git a/frame/pallet-xcm-transactor/src/chain_extension.rs b/frame/pallet-xcm-transactor/src/chain_extension.rs index 766ea972..3c10a08d 100644 --- a/frame/pallet-xcm-transactor/src/chain_extension.rs +++ b/frame/pallet-xcm-transactor/src/chain_extension.rs @@ -21,13 +21,11 @@ use crate::{Config, Pallet, QueryConfig}; use frame_support::{traits::EnsureOrigin, DefaultNoBound}; use frame_system::RawOrigin; -// use log; use pallet_contracts::chain_extension::{ ChainExtension, Environment, Ext, InitState, Result as DispatchResult, RetVal, SysConfig, }; use pallet_xcm::{Pallet as XcmPallet, WeightInfo}; use parity_scale_codec::Encode; -use sp_core::Get; use sp_std::prelude::*; use xcm::prelude::*; pub use xcm_ce_primitives::{ @@ -59,10 +57,6 @@ impl ChainExtension for XCMExtension where ::AccountId: AsRef<[u8; 32]>, { - fn enabled() -> bool { - true - } - fn call(&mut self, env: Environment) -> DispatchResult where E: Ext, @@ -80,6 +74,8 @@ where } impl XCMExtension { + /// Returns the weight for given XCM and saves it (in CE, per-call scratch buffer) for + /// execution fn prepare_execute>( &mut self, env: Environment, @@ -101,6 +97,7 @@ impl XCMExtension { Ok(RetVal::Converging(XcmCeError::Success.into())) } + /// Execute the XCM that was prepared earlier fn execute>( &mut self, mut env: Environment, @@ -118,7 +115,7 @@ impl XCMExtension { // ensure xcm execute origin let origin_location = unwrap!( T::ExecuteXcmOrigin::ensure_origin(origin.into()), - BadVersion + InvalidOrigin ); let hash = input.xcm.using_encoded(sp_io::hashing::blake2_256); @@ -142,6 +139,8 @@ impl XCMExtension { } } + /// Returns the fee required to send XCM and saves + /// it for sending fn validate_send>( &mut self, env: Environment, @@ -166,6 +165,7 @@ impl XCMExtension { Ok(RetVal::Converging(XcmCeError::Success.into())) } + /// Send the validated XCM fn send>( &mut self, mut env: Environment, @@ -195,6 +195,8 @@ impl XCMExtension { Ok(RetVal::Converging(XcmCeError::Success.into())) } + /// Register the new query + /// TODO: figure out weights fn new_query>(&self, env: Environment) -> DispatchResult where ::AccountId: AsRef<[u8; 32]>, @@ -208,48 +210,52 @@ impl XCMExtension { let dest: MultiLocation = unwrap!(dest.try_into(), BadVersion); - // TODO: find better way to get origin - // https://github.com/paritytech/substrate/pull/13708 - let origin = RawOrigin::Signed(env.ext().address().clone()); - // ensure origin is allowed to make queries - unwrap!( - T::RegisterQueryOrigin::ensure_origin(origin.into()), + // convert to interior junction + let interior: Junctions = unwrap!( + Self::querier_location(env.ext().address().clone()), InvalidOrigin ); // register the query - let query_id: u64 = Pallet::::new_query( - query_config, - AccountId32 { - id: *env.ext().address().as_ref(), - network: T::Network::get(), - }, - dest, - )?; + let query_id: u64 = Pallet::::new_query(query_config, interior, dest)?; // write the query_id to buffer query_id.using_encoded(|q| env.write(q, true, None))?; - Ok(RetVal::Converging(XcmCeError::Success.into())) + Ok(RetVal::Converging(Success.into())) } + /// Take the response for query if available + /// TODO: figure out weights fn take_response>( &self, env: Environment, ) -> DispatchResult { let mut env = env.buf_in_buf_out(); let query_id: u64 = env.read_as()?; + // convert to interior junction + let interior: Junctions = unwrap!( + Self::querier_location(env.ext().address().clone()), + InvalidOrigin + ); + let response = unwrap!( - pallet_xcm::Pallet::::take_response(query_id) - .map(|ret| ret.0) - .ok_or(()), - XcmCeError::NoResponse + unwrap!( + Pallet::::take_response(interior, query_id), + InvalidQuerier + ) + .map(|r| r.0) + .ok_or(()), + NoResponse ); + VersionedResponse::from(response).using_encoded(|r| env.write(r, true, None))?; Ok(RetVal::Converging(XcmCeError::Success.into())) } + /// Get the pallet account id which will call the contract callback + /// TODO: figure out weights fn pallet_account_id>( &self, env: Environment, @@ -260,3 +266,16 @@ impl XCMExtension { Ok(RetVal::Converging(XcmCeError::Success.into())) } } + +impl XCMExtension { + fn querier_location(account_id: T::AccountId) -> Result { + // TODO: find better way to get origin + // https://github.com/paritytech/substrate/pull/13708 + let origin = RawOrigin::Signed(account_id); + // ensure origin is allowed to make queries + let origin_location = + T::RegisterQueryOrigin::ensure_origin(origin.into()).map_err(|_| InvalidOrigin)?; + // convert to interior junction + origin_location.try_into().map_err(|_| InvalidOrigin) + } +} diff --git a/frame/pallet-xcm-transactor/src/lib.rs b/frame/pallet-xcm-transactor/src/lib.rs index ab8a5af9..23bcbaaf 100644 --- a/frame/pallet-xcm-transactor/src/lib.rs +++ b/frame/pallet-xcm-transactor/src/lib.rs @@ -30,8 +30,6 @@ use sp_runtime::traits::{AccountIdConversion, Zero}; use sp_std::prelude::*; use xcm::prelude::*; -pub type MethodSelector = [u8; 4]; - pub mod chain_extension; #[frame_support::pallet] @@ -41,7 +39,7 @@ pub mod pallet { use pallet_xcm::ensure_response; pub use xcm_ce_primitives::{QueryConfig, QueryType}; - // Response info + /// Response info #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct ResponseInfo { pub query_id: QueryId, @@ -49,6 +47,13 @@ pub mod pallet { pub response: Response, } + /// Query infor + #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] + pub struct QueryInfo { + pub query_type: QueryType, + pub querier: Junctions, + } + #[pallet::pallet] pub struct Pallet(_); @@ -82,18 +87,13 @@ pub mod pallet { /// Max weight for callback #[pallet::constant] type MaxCallbackWeight: Get; - - /// Relay network id - /// required only in CE for building interior AccountId32 junction - #[pallet::constant] - type Network: Get>; } /// Mapping of ongoing queries and thier type #[pallet::storage] #[pallet::getter(fn callback_query)] pub(super) type CallbackQueries = - StorageMap<_, Blake2_128Concat, QueryId, QueryType, OptionQuery>; + StorageMap<_, Blake2_128Concat, QueryId, QueryInfo, OptionQuery>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -121,6 +121,8 @@ pub mod pallet { UnexpectedQueryResponse, /// Does not support the given query type NotSupported, + /// Querier mismatch + InvalidQuerier, /// Callback out of gas /// TODO: use it OutOfGas, @@ -149,7 +151,7 @@ pub mod pallet { // ensure the origin is a response let responder = ensure_response(::RuntimeOrigin::from(origin))?; // fetch the query - let query_type = + let QueryInfo { query_type, .. } = CallbackQueries::::get(query_id).ok_or(Error::::UnexpectedQueryResponse)?; // handle the response routing // TODO: in case of error, maybe save the response for manual @@ -185,6 +187,8 @@ pub mod pallet { /// Weight for this does not take callback weights into account. That should be /// done via XcmWeigher using WeightBounds, where all query instructions's /// `QueryResponseInfo` `max_weight` is taken into account. + /// https://github.com/paritytech/polkadot/blob/b9d192c418da83c784e366c86b7db0b2ff0789d9/runtime/kusama/src/weights/xcm/mod.rs#L99-L106 + /// Kusama uses WeightInfoBounds but does not take `max_weight` into account #[pallet::call_index(1)] #[pallet::weight(Weight::from_parts(1_000_000, 1_000_000))] pub fn prepare_new_query( @@ -212,11 +216,11 @@ pub mod pallet { /// Handle the incoming xcm notify callback from ResponseHandler (pallet_xcm) pub trait OnCallback { - // error type, that can be converted to dispatch error + /// error type, that can be converted to dispatch error type Error: Into; - // account id type + /// account id type type AccountId; - // blocknumber type + /// blocknumber type type BlockNumber; // TODO: Query type itself should be generic like @@ -323,13 +327,35 @@ impl Pallet { response: Response::Null, } .into(); - let id = PalletXcm::::new_notify_query(dest, call, timeout, querier); - CallbackQueries::::insert(id, query_type); + let id = PalletXcm::::new_notify_query(dest, call, timeout, querier.clone()); + CallbackQueries::::insert( + id, + QueryInfo { + query_type, + querier, + }, + ); id } }) } + /// Take the response if available and querier matches + pub fn take_response( + querier: impl Into, + query_id: QueryId, + ) -> Result, Error> { + let query_info = + CallbackQueries::::get(query_id).ok_or(Error::::UnexpectedQueryResponse)?; + + if querier.into() == query_info.querier { + let response = pallet_xcm::Pallet::::take_response(query_id); + Ok(response) + } else { + Err(Error::::InvalidQuerier) + } + } + fn call_wasm_contract_method( contract_id: T::AccountId, selector: [u8; 4], From 8a5054ffb270528cae73973e60c0f4168e82b6ac Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Fri, 5 May 2023 15:10:55 +0530 Subject: [PATCH 04/15] feat: move xcm ce simulator tests to astar-frame --- Cargo.toml | 12 +- .../xcm-simulator/Cargo.toml | 87 + .../xcm-simulator/fixtures/README.md | 5 + .../xcm-simulator/fixtures/xcm_flip.json | 5196 +++++++++++++++++ .../xcm-simulator/fixtures/xcm_flip.wasm | Bin 0 -> 76223 bytes .../xcm-simulator/src/README.md | 22 + .../xcm-simulator/src/lib.rs | 421 ++ .../xcm-simulator/src/mocks/mod.rs | 317 + .../xcm-simulator/src/mocks/msg_queue.rs | 189 + .../xcm-simulator/src/mocks/parachain.rs | 529 ++ .../xcm-simulator/src/mocks/relay_chain.rs | 212 + 11 files changed, 6987 insertions(+), 3 deletions(-) create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/Cargo.toml create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/fixtures/README.md create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.json create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.wasm create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/src/README.md create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/src/mocks/mod.rs create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/src/mocks/msg_queue.rs create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/src/mocks/parachain.rs create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/src/mocks/relay_chain.rs diff --git a/Cargo.toml b/Cargo.toml index 576e5d29..907849b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,10 @@ members = [ "frame/pallet-xvm", "frame/xc-asset-config", "frame/contracts-migration", - "frame/pallet-xcm-transactor", - "frame/pallet-xcm-transactor/primitives", - # "frame/pallet-xcm-transactor/contracts-example", + "frame/pallet-xcm-transactor", + "frame/pallet-xcm-transactor/primitives", + "frame/pallet-xcm-transactor/xcm-simulator", + # "frame/pallet-xcm-transactor/contracts-example", "primitives/xcm", "precompiles/assets-erc20", "precompiles/dapps-staking", @@ -123,12 +124,17 @@ pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = pallet-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false } pallet-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false } pallet-assets = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false } +pallet-insecure-randomness-collective-flip = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.39", default-features = false } # Polkadot # (wasm) +cumulus-pallet-xcm = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.39", default-features = false } xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false } xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false } +xcm-simulator = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false } polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false } +polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false } +polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false } xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false } # (native) diff --git a/frame/pallet-xcm-transactor/xcm-simulator/Cargo.toml b/frame/pallet-xcm-transactor/xcm-simulator/Cargo.toml new file mode 100644 index 00000000..a2b9905f --- /dev/null +++ b/frame/pallet-xcm-transactor/xcm-simulator/Cargo.toml @@ -0,0 +1,87 @@ +[package] +name = "xcm-simulator-tests" +version = "0.1.0" +description = "XCM Simulator tests" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dev-dependencies] +log = { workspace = true } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } + +# Base functionality +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-assets = { workspace = true } +pallet-balances = { workspace = true } +pallet-contracts = { workspace = true } +pallet-contracts-primitives = { workspace = true } +pallet-insecure-randomness-collective-flip = { workspace = true } +pallet-timestamp = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } +sp-tracing = { workspace = true } + +# Custom Astar inclusions +pallet-dapps-staking = { workspace = true } +pallet-xc-asset-config = { workspace = true } +xcm-primitives = { path = "../../../primitives/xcm", default-features = false } + +# polkadot deps +polkadot-primitives = { workspace = true } + +# XCM +cumulus-pallet-xcm = { workspace = true } +pallet-xcm = { workspace = true } +polkadot-core-primitives = { workspace = true } +polkadot-parachain = { workspace = true } +polkadot-runtime-parachains = { workspace = true } +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } +xcm-simulator = { workspace = true } + +pallet-xcm-transactor = { path = "../", default-features = false } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "sp-std/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-tracing/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-timestamp/std", + "pallet-contracts/std", + "pallet-contracts-primitives/std", + "pallet-insecure-randomness-collective-flip/std", + "pallet-xcm/std", + "cumulus-pallet-xcm/std", + "pallet-assets/std", + "xcm-primitives/std", + "polkadot-primitives/std", + "pallet-dapps-staking/std", + "pallet-xcm-transactor/std", +] +runtime-benchmarks = [ + "frame-system/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-contracts/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", + "polkadot-runtime-parachains/runtime-benchmarks", + "polkadot-parachain/runtime-benchmarks", + "pallet-xcm-transactor/runtime-benchmarks", +] diff --git a/frame/pallet-xcm-transactor/xcm-simulator/fixtures/README.md b/frame/pallet-xcm-transactor/xcm-simulator/fixtures/README.md new file mode 100644 index 00000000..de7b88ef --- /dev/null +++ b/frame/pallet-xcm-transactor/xcm-simulator/fixtures/README.md @@ -0,0 +1,5 @@ +## fixtures +This directory contains contracts which are used for testing XCM CE. + +The json files are for informational purposes only and are not consumed by the tests. + diff --git a/frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.json b/frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.json new file mode 100644 index 00000000..b05f6a5e --- /dev/null +++ b/frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.json @@ -0,0 +1,5196 @@ +{ + "source": { + "hash": "0xb53f65b20a11c687464e0ecf95ed2abb4a3a41fd64ecfa8458b97cdfd5f8c01d", + "language": "ink! 4.1.0", + "compiler": "rustc 1.68.0-nightly", + "build_info": { + "build_mode": "Debug", + "cargo_contract_version": "2.0.0", + "rust_toolchain": "nightly-x86_64-unknown-linux-gnu", + "wasm_opt_settings": { + "keep_debug_symbols": false, + "optimization_passes": "Z" + } + } + }, + "contract": { + "name": "contracts-example", + "version": "0.1.0", + "authors": [ + "[your_name] <[your_email]>" + ] + }, + "spec": { + "constructors": [ + { + "args": [], + "default": false, + "docs": [], + "label": "default", + "payable": false, + "returnType": { + "displayName": [ + "ink_primitives", + "ConstructorResult" + ], + "type": 1 + }, + "selector": "0xffffffff" + } + ], + "docs": [], + "environment": { + "accountId": { + "displayName": [ + "AccountId" + ], + "type": 86 + }, + "balance": { + "displayName": [ + "Balance" + ], + "type": 26 + }, + "blockNumber": { + "displayName": [ + "BlockNumber" + ], + "type": 17 + }, + "chainExtension": { + "displayName": [ + "ChainExtension" + ], + "type": 95 + }, + "hash": { + "displayName": [ + "Hash" + ], + "type": 94 + }, + "maxEventTopics": 4, + "timestamp": { + "displayName": [ + "Timestamp" + ], + "type": 23 + } + }, + "events": [], + "lang_error": { + "displayName": [ + "ink", + "LangError" + ], + "type": 3 + }, + "messages": [ + { + "args": [ + { + "label": "xcm", + "type": { + "displayName": [ + "VersionedXcm" + ], + "type": 4 + } + } + ], + "default": false, + "docs": [], + "label": "execute", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 77 + }, + "selector": "0x11111111" + }, + { + "args": [ + { + "label": "input", + "type": { + "displayName": [ + "ValidateSendInput" + ], + "type": 80 + } + } + ], + "default": false, + "docs": [], + "label": "send", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 82 + }, + "selector": "0x22222222" + }, + { + "args": [ + { + "label": "config", + "type": { + "displayName": [ + "QueryConfig" + ], + "type": 85 + } + }, + { + "label": "dest", + "type": { + "displayName": [ + "VersionedMultiLocation" + ], + "type": 81 + } + } + ], + "default": false, + "docs": [], + "label": "query", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 89 + }, + "selector": "0x33333333" + }, + { + "args": [ + { + "label": "query_id", + "type": { + "displayName": [ + "QueryId" + ], + "type": 23 + } + } + ], + "default": false, + "docs": [], + "label": "poll_response", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 91 + }, + "selector": "0x44444444" + }, + { + "args": [ + { + "label": "query_id", + "type": { + "displayName": [ + "QueryId" + ], + "type": 23 + } + }, + { + "label": "_responder", + "type": { + "displayName": [ + "MultiLocation" + ], + "type": 51 + } + }, + { + "label": "_response", + "type": { + "displayName": [ + "Response" + ], + "type": 60 + } + } + ], + "default": false, + "docs": [], + "label": "handle_response", + "mutates": true, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 1 + }, + "selector": "0x55555555" + }, + { + "args": [], + "default": false, + "docs": [], + "label": "get", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 93 + }, + "selector": "0x66666666" + } + ] + }, + "storage": { + "root": { + "layout": { + "struct": { + "fields": [ + { + "layout": { + "leaf": { + "key": "0x00000000", + "ty": 0 + } + }, + "name": "value" + }, + { + "layout": { + "root": { + "layout": { + "leaf": { + "key": "0x44b7479a", + "ty": 0 + } + }, + "root_key": "0x44b7479a" + } + }, + "name": "expecting_query" + } + ], + "name": "Contracts" + } + }, + "root_key": "0x00000000" + } + }, + "types": [ + { + "id": 0, + "type": { + "def": { + "primitive": "bool" + } + } + }, + { + "id": 1, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 2 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 2 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 2, + "type": { + "def": { + "tuple": [] + } + } + }, + { + "id": 3, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 1, + "name": "CouldNotReadInput" + } + ] + } + }, + "path": [ + "ink_primitives", + "LangError" + ] + } + }, + { + "id": 4, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 5, + "typeName": "v2::Xcm" + } + ], + "index": 2, + "name": "V2" + }, + { + "fields": [ + { + "type": 44, + "typeName": "v3::Xcm" + } + ], + "index": 3, + "name": "V3" + } + ] + } + }, + "params": [ + { + "name": "RuntimeCall", + "type": null + } + ], + "path": [ + "xcm", + "VersionedXcm" + ] + } + }, + { + "id": 5, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 6, + "typeName": "Vec>" + } + ] + } + }, + "params": [ + { + "name": "RuntimeCall", + "type": null + } + ], + "path": [ + "xcm", + "v2", + "Xcm" + ] + } + }, + { + "id": 6, + "type": { + "def": { + "sequence": { + "type": 7 + } + } + } + }, + { + "id": 7, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 8, + "typeName": "MultiAssets" + } + ], + "index": 0, + "name": "WithdrawAsset" + }, + { + "fields": [ + { + "type": 8, + "typeName": "MultiAssets" + } + ], + "index": 1, + "name": "ReserveAssetDeposited" + }, + { + "fields": [ + { + "type": 8, + "typeName": "MultiAssets" + } + ], + "index": 2, + "name": "ReceiveTeleportedAsset" + }, + { + "fields": [ + { + "name": "query_id", + "type": 22, + "typeName": "QueryId" + }, + { + "name": "response", + "type": 34, + "typeName": "Response" + }, + { + "name": "max_weight", + "type": 22, + "typeName": "u64" + } + ], + "index": 3, + "name": "QueryResponse" + }, + { + "fields": [ + { + "name": "assets", + "type": 8, + "typeName": "MultiAssets" + }, + { + "name": "beneficiary", + "type": 12, + "typeName": "MultiLocation" + } + ], + "index": 4, + "name": "TransferAsset" + }, + { + "fields": [ + { + "name": "assets", + "type": 8, + "typeName": "MultiAssets" + }, + { + "name": "dest", + "type": 12, + "typeName": "MultiLocation" + }, + { + "name": "xcm", + "type": 5, + "typeName": "Xcm<()>" + } + ], + "index": 5, + "name": "TransferReserveAsset" + }, + { + "fields": [ + { + "name": "origin_type", + "type": 38, + "typeName": "OriginKind" + }, + { + "name": "require_weight_at_most", + "type": 22, + "typeName": "u64" + }, + { + "name": "call", + "type": 39, + "typeName": "DoubleEncoded" + } + ], + "index": 6, + "name": "Transact" + }, + { + "fields": [ + { + "name": "sender", + "type": 16, + "typeName": "u32" + }, + { + "name": "max_message_size", + "type": 16, + "typeName": "u32" + }, + { + "name": "max_capacity", + "type": 16, + "typeName": "u32" + } + ], + "index": 7, + "name": "HrmpNewChannelOpenRequest" + }, + { + "fields": [ + { + "name": "recipient", + "type": 16, + "typeName": "u32" + } + ], + "index": 8, + "name": "HrmpChannelAccepted" + }, + { + "fields": [ + { + "name": "initiator", + "type": 16, + "typeName": "u32" + }, + { + "name": "sender", + "type": 16, + "typeName": "u32" + }, + { + "name": "recipient", + "type": 16, + "typeName": "u32" + } + ], + "index": 9, + "name": "HrmpChannelClosing" + }, + { + "index": 10, + "name": "ClearOrigin" + }, + { + "fields": [ + { + "type": 14, + "typeName": "InteriorMultiLocation" + } + ], + "index": 11, + "name": "DescendOrigin" + }, + { + "fields": [ + { + "name": "query_id", + "type": 22, + "typeName": "QueryId" + }, + { + "name": "dest", + "type": 12, + "typeName": "MultiLocation" + }, + { + "name": "max_response_weight", + "type": 22, + "typeName": "u64" + } + ], + "index": 12, + "name": "ReportError" + }, + { + "fields": [ + { + "name": "assets", + "type": 40, + "typeName": "MultiAssetFilter" + }, + { + "name": "max_assets", + "type": 16, + "typeName": "u32" + }, + { + "name": "beneficiary", + "type": 12, + "typeName": "MultiLocation" + } + ], + "index": 13, + "name": "DepositAsset" + }, + { + "fields": [ + { + "name": "assets", + "type": 40, + "typeName": "MultiAssetFilter" + }, + { + "name": "max_assets", + "type": 16, + "typeName": "u32" + }, + { + "name": "dest", + "type": 12, + "typeName": "MultiLocation" + }, + { + "name": "xcm", + "type": 5, + "typeName": "Xcm<()>" + } + ], + "index": 14, + "name": "DepositReserveAsset" + }, + { + "fields": [ + { + "name": "give", + "type": 40, + "typeName": "MultiAssetFilter" + }, + { + "name": "receive", + "type": 8, + "typeName": "MultiAssets" + } + ], + "index": 15, + "name": "ExchangeAsset" + }, + { + "fields": [ + { + "name": "assets", + "type": 40, + "typeName": "MultiAssetFilter" + }, + { + "name": "reserve", + "type": 12, + "typeName": "MultiLocation" + }, + { + "name": "xcm", + "type": 5, + "typeName": "Xcm<()>" + } + ], + "index": 16, + "name": "InitiateReserveWithdraw" + }, + { + "fields": [ + { + "name": "assets", + "type": 40, + "typeName": "MultiAssetFilter" + }, + { + "name": "dest", + "type": 12, + "typeName": "MultiLocation" + }, + { + "name": "xcm", + "type": 5, + "typeName": "Xcm<()>" + } + ], + "index": 17, + "name": "InitiateTeleport" + }, + { + "fields": [ + { + "name": "query_id", + "type": 22, + "typeName": "QueryId" + }, + { + "name": "dest", + "type": 12, + "typeName": "MultiLocation" + }, + { + "name": "assets", + "type": 40, + "typeName": "MultiAssetFilter" + }, + { + "name": "max_response_weight", + "type": 22, + "typeName": "u64" + } + ], + "index": 18, + "name": "QueryHolding" + }, + { + "fields": [ + { + "name": "fees", + "type": 10, + "typeName": "MultiAsset" + }, + { + "name": "weight_limit", + "type": 43, + "typeName": "WeightLimit" + } + ], + "index": 19, + "name": "BuyExecution" + }, + { + "index": 20, + "name": "RefundSurplus" + }, + { + "fields": [ + { + "type": 5, + "typeName": "Xcm" + } + ], + "index": 21, + "name": "SetErrorHandler" + }, + { + "fields": [ + { + "type": 5, + "typeName": "Xcm" + } + ], + "index": 22, + "name": "SetAppendix" + }, + { + "index": 23, + "name": "ClearError" + }, + { + "fields": [ + { + "name": "assets", + "type": 8, + "typeName": "MultiAssets" + }, + { + "name": "ticket", + "type": 12, + "typeName": "MultiLocation" + } + ], + "index": 24, + "name": "ClaimAsset" + }, + { + "fields": [ + { + "type": 22, + "typeName": "u64" + } + ], + "index": 25, + "name": "Trap" + }, + { + "fields": [ + { + "name": "query_id", + "type": 22, + "typeName": "QueryId" + }, + { + "name": "max_response_weight", + "type": 22, + "typeName": "u64" + } + ], + "index": 26, + "name": "SubscribeVersion" + }, + { + "index": 27, + "name": "UnsubscribeVersion" + } + ] + } + }, + "params": [ + { + "name": "RuntimeCall", + "type": null + } + ], + "path": [ + "xcm", + "v2", + "Instruction" + ] + } + }, + { + "id": 8, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 9, + "typeName": "Vec" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "multiasset", + "MultiAssets" + ] + } + }, + { + "id": 9, + "type": { + "def": { + "sequence": { + "type": 10 + } + } + } + }, + { + "id": 10, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "id", + "type": 11, + "typeName": "AssetId" + }, + { + "name": "fun", + "type": 29, + "typeName": "Fungibility" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "multiasset", + "MultiAsset" + ] + } + }, + { + "id": 11, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 12, + "typeName": "MultiLocation" + } + ], + "index": 0, + "name": "Concrete" + }, + { + "fields": [ + { + "type": 20, + "typeName": "Vec" + } + ], + "index": 1, + "name": "Abstract" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "multiasset", + "AssetId" + ] + } + }, + { + "id": 12, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "parents", + "type": 13, + "typeName": "u8" + }, + { + "name": "interior", + "type": 14, + "typeName": "Junctions" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "multilocation", + "MultiLocation" + ] + } + }, + { + "id": 13, + "type": { + "def": { + "primitive": "u8" + } + } + }, + { + "id": 14, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Here" + }, + { + "fields": [ + { + "type": 15, + "typeName": "Junction" + } + ], + "index": 1, + "name": "X1" + }, + { + "fields": [ + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + } + ], + "index": 2, + "name": "X2" + }, + { + "fields": [ + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + } + ], + "index": 3, + "name": "X3" + }, + { + "fields": [ + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + } + ], + "index": 4, + "name": "X4" + }, + { + "fields": [ + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + } + ], + "index": 5, + "name": "X5" + }, + { + "fields": [ + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + } + ], + "index": 6, + "name": "X6" + }, + { + "fields": [ + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + } + ], + "index": 7, + "name": "X7" + }, + { + "fields": [ + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + }, + { + "type": 15, + "typeName": "Junction" + } + ], + "index": 8, + "name": "X8" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "multilocation", + "Junctions" + ] + } + }, + { + "id": 15, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 16, + "typeName": "u32" + } + ], + "index": 0, + "name": "Parachain" + }, + { + "fields": [ + { + "name": "network", + "type": 18, + "typeName": "NetworkId" + }, + { + "name": "id", + "type": 21, + "typeName": "[u8; 32]" + } + ], + "index": 1, + "name": "AccountId32" + }, + { + "fields": [ + { + "name": "network", + "type": 18, + "typeName": "NetworkId" + }, + { + "name": "index", + "type": 22, + "typeName": "u64" + } + ], + "index": 2, + "name": "AccountIndex64" + }, + { + "fields": [ + { + "name": "network", + "type": 18, + "typeName": "NetworkId" + }, + { + "name": "key", + "type": 24, + "typeName": "[u8; 20]" + } + ], + "index": 3, + "name": "AccountKey20" + }, + { + "fields": [ + { + "type": 13, + "typeName": "u8" + } + ], + "index": 4, + "name": "PalletInstance" + }, + { + "fields": [ + { + "type": 25, + "typeName": "u128" + } + ], + "index": 5, + "name": "GeneralIndex" + }, + { + "fields": [ + { + "type": 19, + "typeName": "WeakBoundedVec>" + } + ], + "index": 6, + "name": "GeneralKey" + }, + { + "index": 7, + "name": "OnlyChild" + }, + { + "fields": [ + { + "name": "id", + "type": 27, + "typeName": "BodyId" + }, + { + "name": "part", + "type": 28, + "typeName": "BodyPart" + } + ], + "index": 8, + "name": "Plurality" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "junction", + "Junction" + ] + } + }, + { + "id": 16, + "type": { + "def": { + "compact": { + "type": 17 + } + } + } + }, + { + "id": 17, + "type": { + "def": { + "primitive": "u32" + } + } + }, + { + "id": 18, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Any" + }, + { + "fields": [ + { + "type": 19, + "typeName": "WeakBoundedVec>" + } + ], + "index": 1, + "name": "Named" + }, + { + "index": 2, + "name": "Polkadot" + }, + { + "index": 3, + "name": "Kusama" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "NetworkId" + ] + } + }, + { + "id": 19, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 20, + "typeName": "Vec" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 13 + }, + { + "name": "S", + "type": null + } + ], + "path": [ + "bounded_collections", + "weak_bounded_vec", + "WeakBoundedVec" + ] + } + }, + { + "id": 20, + "type": { + "def": { + "sequence": { + "type": 13 + } + } + } + }, + { + "id": 21, + "type": { + "def": { + "array": { + "len": 32, + "type": 13 + } + } + } + }, + { + "id": 22, + "type": { + "def": { + "compact": { + "type": 23 + } + } + } + }, + { + "id": 23, + "type": { + "def": { + "primitive": "u64" + } + } + }, + { + "id": 24, + "type": { + "def": { + "array": { + "len": 20, + "type": 13 + } + } + } + }, + { + "id": 25, + "type": { + "def": { + "compact": { + "type": 26 + } + } + } + }, + { + "id": 26, + "type": { + "def": { + "primitive": "u128" + } + } + }, + { + "id": 27, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Unit" + }, + { + "fields": [ + { + "type": 19, + "typeName": "WeakBoundedVec>" + } + ], + "index": 1, + "name": "Named" + }, + { + "fields": [ + { + "type": 16, + "typeName": "u32" + } + ], + "index": 2, + "name": "Index" + }, + { + "index": 3, + "name": "Executive" + }, + { + "index": 4, + "name": "Technical" + }, + { + "index": 5, + "name": "Legislative" + }, + { + "index": 6, + "name": "Judicial" + }, + { + "index": 7, + "name": "Defense" + }, + { + "index": 8, + "name": "Administration" + }, + { + "index": 9, + "name": "Treasury" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "BodyId" + ] + } + }, + { + "id": 28, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Voice" + }, + { + "fields": [ + { + "name": "count", + "type": 16, + "typeName": "u32" + } + ], + "index": 1, + "name": "Members" + }, + { + "fields": [ + { + "name": "nom", + "type": 16, + "typeName": "u32" + }, + { + "name": "denom", + "type": 16, + "typeName": "u32" + } + ], + "index": 2, + "name": "Fraction" + }, + { + "fields": [ + { + "name": "nom", + "type": 16, + "typeName": "u32" + }, + { + "name": "denom", + "type": 16, + "typeName": "u32" + } + ], + "index": 3, + "name": "AtLeastProportion" + }, + { + "fields": [ + { + "name": "nom", + "type": 16, + "typeName": "u32" + }, + { + "name": "denom", + "type": 16, + "typeName": "u32" + } + ], + "index": 4, + "name": "MoreThanProportion" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "BodyPart" + ] + } + }, + { + "id": 29, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 25, + "typeName": "u128" + } + ], + "index": 0, + "name": "Fungible" + }, + { + "fields": [ + { + "type": 30, + "typeName": "AssetInstance" + } + ], + "index": 1, + "name": "NonFungible" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "multiasset", + "Fungibility" + ] + } + }, + { + "id": 30, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Undefined" + }, + { + "fields": [ + { + "type": 25, + "typeName": "u128" + } + ], + "index": 1, + "name": "Index" + }, + { + "fields": [ + { + "type": 31, + "typeName": "[u8; 4]" + } + ], + "index": 2, + "name": "Array4" + }, + { + "fields": [ + { + "type": 32, + "typeName": "[u8; 8]" + } + ], + "index": 3, + "name": "Array8" + }, + { + "fields": [ + { + "type": 33, + "typeName": "[u8; 16]" + } + ], + "index": 4, + "name": "Array16" + }, + { + "fields": [ + { + "type": 21, + "typeName": "[u8; 32]" + } + ], + "index": 5, + "name": "Array32" + }, + { + "fields": [ + { + "type": 20, + "typeName": "Vec" + } + ], + "index": 6, + "name": "Blob" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "multiasset", + "AssetInstance" + ] + } + }, + { + "id": 31, + "type": { + "def": { + "array": { + "len": 4, + "type": 13 + } + } + } + }, + { + "id": 32, + "type": { + "def": { + "array": { + "len": 8, + "type": 13 + } + } + } + }, + { + "id": 33, + "type": { + "def": { + "array": { + "len": 16, + "type": 13 + } + } + } + }, + { + "id": 34, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Null" + }, + { + "fields": [ + { + "type": 8, + "typeName": "MultiAssets" + } + ], + "index": 1, + "name": "Assets" + }, + { + "fields": [ + { + "type": 35, + "typeName": "Option<(u32, Error)>" + } + ], + "index": 2, + "name": "ExecutionResult" + }, + { + "fields": [ + { + "type": 17, + "typeName": "super::Version" + } + ], + "index": 3, + "name": "Version" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "Response" + ] + } + }, + { + "id": 35, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "None" + }, + { + "fields": [ + { + "type": 36 + } + ], + "index": 1, + "name": "Some" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 36 + } + ], + "path": [ + "Option" + ] + } + }, + { + "id": 36, + "type": { + "def": { + "tuple": [ + 17, + 37 + ] + } + } + }, + { + "id": 37, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Overflow" + }, + { + "index": 1, + "name": "Unimplemented" + }, + { + "index": 2, + "name": "UntrustedReserveLocation" + }, + { + "index": 3, + "name": "UntrustedTeleportLocation" + }, + { + "index": 4, + "name": "MultiLocationFull" + }, + { + "index": 5, + "name": "MultiLocationNotInvertible" + }, + { + "index": 6, + "name": "BadOrigin" + }, + { + "index": 7, + "name": "InvalidLocation" + }, + { + "index": 8, + "name": "AssetNotFound" + }, + { + "index": 9, + "name": "FailedToTransactAsset" + }, + { + "index": 10, + "name": "NotWithdrawable" + }, + { + "index": 11, + "name": "LocationCannotHold" + }, + { + "index": 12, + "name": "ExceedsMaxMessageSize" + }, + { + "index": 13, + "name": "DestinationUnsupported" + }, + { + "index": 14, + "name": "Transport" + }, + { + "index": 15, + "name": "Unroutable" + }, + { + "index": 16, + "name": "UnknownClaim" + }, + { + "index": 17, + "name": "FailedToDecode" + }, + { + "index": 18, + "name": "MaxWeightInvalid" + }, + { + "index": 19, + "name": "NotHoldingFees" + }, + { + "index": 20, + "name": "TooExpensive" + }, + { + "fields": [ + { + "type": 23, + "typeName": "u64" + } + ], + "index": 21, + "name": "Trap" + }, + { + "index": 22, + "name": "UnhandledXcmVersion" + }, + { + "fields": [ + { + "type": 23, + "typeName": "Weight" + } + ], + "index": 23, + "name": "WeightLimitReached" + }, + { + "index": 24, + "name": "Barrier" + }, + { + "index": 25, + "name": "WeightNotComputable" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "traits", + "Error" + ] + } + }, + { + "id": 38, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Native" + }, + { + "index": 1, + "name": "SovereignAccount" + }, + { + "index": 2, + "name": "Superuser" + }, + { + "index": 3, + "name": "Xcm" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "OriginKind" + ] + } + }, + { + "id": 39, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "encoded", + "type": 20, + "typeName": "Vec" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": null + } + ], + "path": [ + "xcm", + "double_encoded", + "DoubleEncoded" + ] + } + }, + { + "id": 40, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 8, + "typeName": "MultiAssets" + } + ], + "index": 0, + "name": "Definite" + }, + { + "fields": [ + { + "type": 41, + "typeName": "WildMultiAsset" + } + ], + "index": 1, + "name": "Wild" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "multiasset", + "MultiAssetFilter" + ] + } + }, + { + "id": 41, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "All" + }, + { + "fields": [ + { + "name": "id", + "type": 11, + "typeName": "AssetId" + }, + { + "name": "fun", + "type": 42, + "typeName": "WildFungibility" + } + ], + "index": 1, + "name": "AllOf" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "multiasset", + "WildMultiAsset" + ] + } + }, + { + "id": 42, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Fungible" + }, + { + "index": 1, + "name": "NonFungible" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "multiasset", + "WildFungibility" + ] + } + }, + { + "id": 43, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Unlimited" + }, + { + "fields": [ + { + "type": 22, + "typeName": "u64" + } + ], + "index": 1, + "name": "Limited" + } + ] + } + }, + "path": [ + "xcm", + "v2", + "WeightLimit" + ] + } + }, + { + "id": 44, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 45, + "typeName": "Vec>" + } + ] + } + }, + "params": [ + { + "name": "Call", + "type": null + } + ], + "path": [ + "xcm", + "v3", + "Xcm" + ] + } + }, + { + "id": 45, + "type": { + "def": { + "sequence": { + "type": 46 + } + } + } + }, + { + "id": 46, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 47, + "typeName": "MultiAssets" + } + ], + "index": 0, + "name": "WithdrawAsset" + }, + { + "fields": [ + { + "type": 47, + "typeName": "MultiAssets" + } + ], + "index": 1, + "name": "ReserveAssetDeposited" + }, + { + "fields": [ + { + "type": 47, + "typeName": "MultiAssets" + } + ], + "index": 2, + "name": "ReceiveTeleportedAsset" + }, + { + "fields": [ + { + "name": "query_id", + "type": 22, + "typeName": "QueryId" + }, + { + "name": "response", + "type": 60, + "typeName": "Response" + }, + { + "name": "max_weight", + "type": 64, + "typeName": "Weight" + }, + { + "name": "querier", + "type": 71, + "typeName": "Option" + } + ], + "index": 3, + "name": "QueryResponse" + }, + { + "fields": [ + { + "name": "assets", + "type": 47, + "typeName": "MultiAssets" + }, + { + "name": "beneficiary", + "type": 51, + "typeName": "MultiLocation" + } + ], + "index": 4, + "name": "TransferAsset" + }, + { + "fields": [ + { + "name": "assets", + "type": 47, + "typeName": "MultiAssets" + }, + { + "name": "dest", + "type": 51, + "typeName": "MultiLocation" + }, + { + "name": "xcm", + "type": 44, + "typeName": "Xcm<()>" + } + ], + "index": 5, + "name": "TransferReserveAsset" + }, + { + "fields": [ + { + "name": "origin_kind", + "type": 38, + "typeName": "OriginKind" + }, + { + "name": "require_weight_at_most", + "type": 64, + "typeName": "Weight" + }, + { + "name": "call", + "type": 39, + "typeName": "DoubleEncoded" + } + ], + "index": 6, + "name": "Transact" + }, + { + "fields": [ + { + "name": "sender", + "type": 16, + "typeName": "u32" + }, + { + "name": "max_message_size", + "type": 16, + "typeName": "u32" + }, + { + "name": "max_capacity", + "type": 16, + "typeName": "u32" + } + ], + "index": 7, + "name": "HrmpNewChannelOpenRequest" + }, + { + "fields": [ + { + "name": "recipient", + "type": 16, + "typeName": "u32" + } + ], + "index": 8, + "name": "HrmpChannelAccepted" + }, + { + "fields": [ + { + "name": "initiator", + "type": 16, + "typeName": "u32" + }, + { + "name": "sender", + "type": 16, + "typeName": "u32" + }, + { + "name": "recipient", + "type": 16, + "typeName": "u32" + } + ], + "index": 9, + "name": "HrmpChannelClosing" + }, + { + "index": 10, + "name": "ClearOrigin" + }, + { + "fields": [ + { + "type": 52, + "typeName": "InteriorMultiLocation" + } + ], + "index": 11, + "name": "DescendOrigin" + }, + { + "fields": [ + { + "type": 72, + "typeName": "QueryResponseInfo" + } + ], + "index": 12, + "name": "ReportError" + }, + { + "fields": [ + { + "name": "assets", + "type": 73, + "typeName": "MultiAssetFilter" + }, + { + "name": "beneficiary", + "type": 51, + "typeName": "MultiLocation" + } + ], + "index": 13, + "name": "DepositAsset" + }, + { + "fields": [ + { + "name": "assets", + "type": 73, + "typeName": "MultiAssetFilter" + }, + { + "name": "dest", + "type": 51, + "typeName": "MultiLocation" + }, + { + "name": "xcm", + "type": 44, + "typeName": "Xcm<()>" + } + ], + "index": 14, + "name": "DepositReserveAsset" + }, + { + "fields": [ + { + "name": "give", + "type": 73, + "typeName": "MultiAssetFilter" + }, + { + "name": "want", + "type": 47, + "typeName": "MultiAssets" + }, + { + "name": "maximal", + "type": 0, + "typeName": "bool" + } + ], + "index": 15, + "name": "ExchangeAsset" + }, + { + "fields": [ + { + "name": "assets", + "type": 73, + "typeName": "MultiAssetFilter" + }, + { + "name": "reserve", + "type": 51, + "typeName": "MultiLocation" + }, + { + "name": "xcm", + "type": 44, + "typeName": "Xcm<()>" + } + ], + "index": 16, + "name": "InitiateReserveWithdraw" + }, + { + "fields": [ + { + "name": "assets", + "type": 73, + "typeName": "MultiAssetFilter" + }, + { + "name": "dest", + "type": 51, + "typeName": "MultiLocation" + }, + { + "name": "xcm", + "type": 44, + "typeName": "Xcm<()>" + } + ], + "index": 17, + "name": "InitiateTeleport" + }, + { + "fields": [ + { + "name": "response_info", + "type": 72, + "typeName": "QueryResponseInfo" + }, + { + "name": "assets", + "type": 73, + "typeName": "MultiAssetFilter" + } + ], + "index": 18, + "name": "ReportHolding" + }, + { + "fields": [ + { + "name": "fees", + "type": 49, + "typeName": "MultiAsset" + }, + { + "name": "weight_limit", + "type": 76, + "typeName": "WeightLimit" + } + ], + "index": 19, + "name": "BuyExecution" + }, + { + "index": 20, + "name": "RefundSurplus" + }, + { + "fields": [ + { + "type": 44, + "typeName": "Xcm" + } + ], + "index": 21, + "name": "SetErrorHandler" + }, + { + "fields": [ + { + "type": 44, + "typeName": "Xcm" + } + ], + "index": 22, + "name": "SetAppendix" + }, + { + "index": 23, + "name": "ClearError" + }, + { + "fields": [ + { + "name": "assets", + "type": 47, + "typeName": "MultiAssets" + }, + { + "name": "ticket", + "type": 51, + "typeName": "MultiLocation" + } + ], + "index": 24, + "name": "ClaimAsset" + }, + { + "fields": [ + { + "type": 22, + "typeName": "u64" + } + ], + "index": 25, + "name": "Trap" + }, + { + "fields": [ + { + "name": "query_id", + "type": 22, + "typeName": "QueryId" + }, + { + "name": "max_response_weight", + "type": 64, + "typeName": "Weight" + } + ], + "index": 26, + "name": "SubscribeVersion" + }, + { + "index": 27, + "name": "UnsubscribeVersion" + }, + { + "fields": [ + { + "type": 47, + "typeName": "MultiAssets" + } + ], + "index": 28, + "name": "BurnAsset" + }, + { + "fields": [ + { + "type": 47, + "typeName": "MultiAssets" + } + ], + "index": 29, + "name": "ExpectAsset" + }, + { + "fields": [ + { + "type": 71, + "typeName": "Option" + } + ], + "index": 30, + "name": "ExpectOrigin" + }, + { + "fields": [ + { + "type": 61, + "typeName": "Option<(u32, Error)>" + } + ], + "index": 31, + "name": "ExpectError" + }, + { + "fields": [ + { + "type": 69, + "typeName": "MaybeErrorCode" + } + ], + "index": 32, + "name": "ExpectTransactStatus" + }, + { + "fields": [ + { + "name": "module_name", + "type": 20, + "typeName": "Vec" + }, + { + "name": "response_info", + "type": 72, + "typeName": "QueryResponseInfo" + } + ], + "index": 33, + "name": "QueryPallet" + }, + { + "fields": [ + { + "name": "index", + "type": 16, + "typeName": "u32" + }, + { + "name": "name", + "type": 20, + "typeName": "Vec" + }, + { + "name": "module_name", + "type": 20, + "typeName": "Vec" + }, + { + "name": "crate_major", + "type": 16, + "typeName": "u32" + }, + { + "name": "min_crate_minor", + "type": 16, + "typeName": "u32" + } + ], + "index": 34, + "name": "ExpectPallet" + }, + { + "fields": [ + { + "type": 72, + "typeName": "QueryResponseInfo" + } + ], + "index": 35, + "name": "ReportTransactStatus" + }, + { + "index": 36, + "name": "ClearTransactStatus" + }, + { + "fields": [ + { + "type": 53, + "typeName": "Junction" + } + ], + "index": 37, + "name": "UniversalOrigin" + }, + { + "fields": [ + { + "name": "network", + "type": 55, + "typeName": "NetworkId" + }, + { + "name": "destination", + "type": 52, + "typeName": "InteriorMultiLocation" + }, + { + "name": "xcm", + "type": 44, + "typeName": "Xcm<()>" + } + ], + "index": 38, + "name": "ExportMessage" + }, + { + "fields": [ + { + "name": "asset", + "type": 49, + "typeName": "MultiAsset" + }, + { + "name": "unlocker", + "type": 51, + "typeName": "MultiLocation" + } + ], + "index": 39, + "name": "LockAsset" + }, + { + "fields": [ + { + "name": "asset", + "type": 49, + "typeName": "MultiAsset" + }, + { + "name": "target", + "type": 51, + "typeName": "MultiLocation" + } + ], + "index": 40, + "name": "UnlockAsset" + }, + { + "fields": [ + { + "name": "asset", + "type": 49, + "typeName": "MultiAsset" + }, + { + "name": "owner", + "type": 51, + "typeName": "MultiLocation" + } + ], + "index": 41, + "name": "NoteUnlockable" + }, + { + "fields": [ + { + "name": "asset", + "type": 49, + "typeName": "MultiAsset" + }, + { + "name": "locker", + "type": 51, + "typeName": "MultiLocation" + } + ], + "index": 42, + "name": "RequestUnlock" + }, + { + "fields": [ + { + "name": "jit_withdraw", + "type": 0, + "typeName": "bool" + } + ], + "index": 43, + "name": "SetFeesMode" + }, + { + "fields": [ + { + "type": 21, + "typeName": "[u8; 32]" + } + ], + "index": 44, + "name": "SetTopic" + }, + { + "index": 45, + "name": "ClearTopic" + }, + { + "fields": [ + { + "type": 51, + "typeName": "MultiLocation" + } + ], + "index": 46, + "name": "AliasOrigin" + }, + { + "fields": [ + { + "name": "weight_limit", + "type": 76, + "typeName": "WeightLimit" + }, + { + "name": "check_origin", + "type": 71, + "typeName": "Option" + } + ], + "index": 47, + "name": "UnpaidExecution" + } + ] + } + }, + "params": [ + { + "name": "Call", + "type": null + } + ], + "path": [ + "xcm", + "v3", + "Instruction" + ] + } + }, + { + "id": 47, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 48, + "typeName": "Vec" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "multiasset", + "MultiAssets" + ] + } + }, + { + "id": 48, + "type": { + "def": { + "sequence": { + "type": 49 + } + } + } + }, + { + "id": 49, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "id", + "type": 50, + "typeName": "AssetId" + }, + { + "name": "fun", + "type": 58, + "typeName": "Fungibility" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "multiasset", + "MultiAsset" + ] + } + }, + { + "id": 50, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 51, + "typeName": "MultiLocation" + } + ], + "index": 0, + "name": "Concrete" + }, + { + "fields": [ + { + "type": 21, + "typeName": "[u8; 32]" + } + ], + "index": 1, + "name": "Abstract" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "multiasset", + "AssetId" + ] + } + }, + { + "id": 51, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "parents", + "type": 13, + "typeName": "u8" + }, + { + "name": "interior", + "type": 52, + "typeName": "Junctions" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "multilocation", + "MultiLocation" + ] + } + }, + { + "id": 52, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Here" + }, + { + "fields": [ + { + "type": 53, + "typeName": "Junction" + } + ], + "index": 1, + "name": "X1" + }, + { + "fields": [ + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + } + ], + "index": 2, + "name": "X2" + }, + { + "fields": [ + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + } + ], + "index": 3, + "name": "X3" + }, + { + "fields": [ + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + } + ], + "index": 4, + "name": "X4" + }, + { + "fields": [ + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + } + ], + "index": 5, + "name": "X5" + }, + { + "fields": [ + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + } + ], + "index": 6, + "name": "X6" + }, + { + "fields": [ + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + } + ], + "index": 7, + "name": "X7" + }, + { + "fields": [ + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + }, + { + "type": 53, + "typeName": "Junction" + } + ], + "index": 8, + "name": "X8" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "junctions", + "Junctions" + ] + } + }, + { + "id": 53, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 16, + "typeName": "u32" + } + ], + "index": 0, + "name": "Parachain" + }, + { + "fields": [ + { + "name": "network", + "type": 54, + "typeName": "Option" + }, + { + "name": "id", + "type": 21, + "typeName": "[u8; 32]" + } + ], + "index": 1, + "name": "AccountId32" + }, + { + "fields": [ + { + "name": "network", + "type": 54, + "typeName": "Option" + }, + { + "name": "index", + "type": 22, + "typeName": "u64" + } + ], + "index": 2, + "name": "AccountIndex64" + }, + { + "fields": [ + { + "name": "network", + "type": 54, + "typeName": "Option" + }, + { + "name": "key", + "type": 24, + "typeName": "[u8; 20]" + } + ], + "index": 3, + "name": "AccountKey20" + }, + { + "fields": [ + { + "type": 13, + "typeName": "u8" + } + ], + "index": 4, + "name": "PalletInstance" + }, + { + "fields": [ + { + "type": 25, + "typeName": "u128" + } + ], + "index": 5, + "name": "GeneralIndex" + }, + { + "fields": [ + { + "name": "length", + "type": 13, + "typeName": "u8" + }, + { + "name": "data", + "type": 21, + "typeName": "[u8; 32]" + } + ], + "index": 6, + "name": "GeneralKey" + }, + { + "index": 7, + "name": "OnlyChild" + }, + { + "fields": [ + { + "name": "id", + "type": 56, + "typeName": "BodyId" + }, + { + "name": "part", + "type": 57, + "typeName": "BodyPart" + } + ], + "index": 8, + "name": "Plurality" + }, + { + "fields": [ + { + "type": 55, + "typeName": "NetworkId" + } + ], + "index": 9, + "name": "GlobalConsensus" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "junction", + "Junction" + ] + } + }, + { + "id": 54, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "None" + }, + { + "fields": [ + { + "type": 55 + } + ], + "index": 1, + "name": "Some" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 55 + } + ], + "path": [ + "Option" + ] + } + }, + { + "id": 55, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 21, + "typeName": "[u8; 32]" + } + ], + "index": 0, + "name": "ByGenesis" + }, + { + "fields": [ + { + "name": "block_number", + "type": 23, + "typeName": "u64" + }, + { + "name": "block_hash", + "type": 21, + "typeName": "[u8; 32]" + } + ], + "index": 1, + "name": "ByFork" + }, + { + "index": 2, + "name": "Polkadot" + }, + { + "index": 3, + "name": "Kusama" + }, + { + "index": 4, + "name": "Westend" + }, + { + "index": 5, + "name": "Rococo" + }, + { + "index": 6, + "name": "Wococo" + }, + { + "fields": [ + { + "name": "chain_id", + "type": 22, + "typeName": "u64" + } + ], + "index": 7, + "name": "Ethereum" + }, + { + "index": 8, + "name": "BitcoinCore" + }, + { + "index": 9, + "name": "BitcoinCash" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "junction", + "NetworkId" + ] + } + }, + { + "id": 56, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Unit" + }, + { + "fields": [ + { + "type": 31, + "typeName": "[u8; 4]" + } + ], + "index": 1, + "name": "Moniker" + }, + { + "fields": [ + { + "type": 16, + "typeName": "u32" + } + ], + "index": 2, + "name": "Index" + }, + { + "index": 3, + "name": "Executive" + }, + { + "index": 4, + "name": "Technical" + }, + { + "index": 5, + "name": "Legislative" + }, + { + "index": 6, + "name": "Judicial" + }, + { + "index": 7, + "name": "Defense" + }, + { + "index": 8, + "name": "Administration" + }, + { + "index": 9, + "name": "Treasury" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "junction", + "BodyId" + ] + } + }, + { + "id": 57, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Voice" + }, + { + "fields": [ + { + "name": "count", + "type": 16, + "typeName": "u32" + } + ], + "index": 1, + "name": "Members" + }, + { + "fields": [ + { + "name": "nom", + "type": 16, + "typeName": "u32" + }, + { + "name": "denom", + "type": 16, + "typeName": "u32" + } + ], + "index": 2, + "name": "Fraction" + }, + { + "fields": [ + { + "name": "nom", + "type": 16, + "typeName": "u32" + }, + { + "name": "denom", + "type": 16, + "typeName": "u32" + } + ], + "index": 3, + "name": "AtLeastProportion" + }, + { + "fields": [ + { + "name": "nom", + "type": 16, + "typeName": "u32" + }, + { + "name": "denom", + "type": 16, + "typeName": "u32" + } + ], + "index": 4, + "name": "MoreThanProportion" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "junction", + "BodyPart" + ] + } + }, + { + "id": 58, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 25, + "typeName": "u128" + } + ], + "index": 0, + "name": "Fungible" + }, + { + "fields": [ + { + "type": 59, + "typeName": "AssetInstance" + } + ], + "index": 1, + "name": "NonFungible" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "multiasset", + "Fungibility" + ] + } + }, + { + "id": 59, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Undefined" + }, + { + "fields": [ + { + "type": 25, + "typeName": "u128" + } + ], + "index": 1, + "name": "Index" + }, + { + "fields": [ + { + "type": 31, + "typeName": "[u8; 4]" + } + ], + "index": 2, + "name": "Array4" + }, + { + "fields": [ + { + "type": 32, + "typeName": "[u8; 8]" + } + ], + "index": 3, + "name": "Array8" + }, + { + "fields": [ + { + "type": 33, + "typeName": "[u8; 16]" + } + ], + "index": 4, + "name": "Array16" + }, + { + "fields": [ + { + "type": 21, + "typeName": "[u8; 32]" + } + ], + "index": 5, + "name": "Array32" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "multiasset", + "AssetInstance" + ] + } + }, + { + "id": 60, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Null" + }, + { + "fields": [ + { + "type": 47, + "typeName": "MultiAssets" + } + ], + "index": 1, + "name": "Assets" + }, + { + "fields": [ + { + "type": 61, + "typeName": "Option<(u32, Error)>" + } + ], + "index": 2, + "name": "ExecutionResult" + }, + { + "fields": [ + { + "type": 17, + "typeName": "super::Version" + } + ], + "index": 3, + "name": "Version" + }, + { + "fields": [ + { + "type": 65, + "typeName": "BoundedVec" + } + ], + "index": 4, + "name": "PalletsInfo" + }, + { + "fields": [ + { + "type": 69, + "typeName": "MaybeErrorCode" + } + ], + "index": 5, + "name": "DispatchResult" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "Response" + ] + } + }, + { + "id": 61, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "None" + }, + { + "fields": [ + { + "type": 62 + } + ], + "index": 1, + "name": "Some" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 62 + } + ], + "path": [ + "Option" + ] + } + }, + { + "id": 62, + "type": { + "def": { + "tuple": [ + 17, + 63 + ] + } + } + }, + { + "id": 63, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Overflow" + }, + { + "index": 1, + "name": "Unimplemented" + }, + { + "index": 2, + "name": "UntrustedReserveLocation" + }, + { + "index": 3, + "name": "UntrustedTeleportLocation" + }, + { + "index": 4, + "name": "LocationFull" + }, + { + "index": 5, + "name": "LocationNotInvertible" + }, + { + "index": 6, + "name": "BadOrigin" + }, + { + "index": 7, + "name": "InvalidLocation" + }, + { + "index": 8, + "name": "AssetNotFound" + }, + { + "index": 9, + "name": "FailedToTransactAsset" + }, + { + "index": 10, + "name": "NotWithdrawable" + }, + { + "index": 11, + "name": "LocationCannotHold" + }, + { + "index": 12, + "name": "ExceedsMaxMessageSize" + }, + { + "index": 13, + "name": "DestinationUnsupported" + }, + { + "index": 14, + "name": "Transport" + }, + { + "index": 15, + "name": "Unroutable" + }, + { + "index": 16, + "name": "UnknownClaim" + }, + { + "index": 17, + "name": "FailedToDecode" + }, + { + "index": 18, + "name": "MaxWeightInvalid" + }, + { + "index": 19, + "name": "NotHoldingFees" + }, + { + "index": 20, + "name": "TooExpensive" + }, + { + "fields": [ + { + "type": 23, + "typeName": "u64" + } + ], + "index": 21, + "name": "Trap" + }, + { + "index": 22, + "name": "ExpectationFalse" + }, + { + "index": 23, + "name": "PalletNotFound" + }, + { + "index": 24, + "name": "NameMismatch" + }, + { + "index": 25, + "name": "VersionIncompatible" + }, + { + "index": 26, + "name": "HoldingWouldOverflow" + }, + { + "index": 27, + "name": "ExportError" + }, + { + "index": 28, + "name": "ReanchorFailed" + }, + { + "index": 29, + "name": "NoDeal" + }, + { + "index": 30, + "name": "FeesNotMet" + }, + { + "index": 31, + "name": "LockError" + }, + { + "index": 32, + "name": "NoPermission" + }, + { + "index": 33, + "name": "Unanchored" + }, + { + "index": 34, + "name": "NotDepositable" + }, + { + "index": 35, + "name": "UnhandledXcmVersion" + }, + { + "fields": [ + { + "type": 64, + "typeName": "Weight" + } + ], + "index": 36, + "name": "WeightLimitReached" + }, + { + "index": 37, + "name": "Barrier" + }, + { + "index": 38, + "name": "WeightNotComputable" + }, + { + "index": 39, + "name": "ExceedsStackLimit" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "traits", + "Error" + ] + } + }, + { + "id": 64, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "ref_time", + "type": 22, + "typeName": "u64" + }, + { + "name": "proof_size", + "type": 22, + "typeName": "u64" + } + ] + } + }, + "path": [ + "sp_weights", + "weight_v2", + "Weight" + ] + } + }, + { + "id": 65, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 68, + "typeName": "Vec" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 66 + }, + { + "name": "S", + "type": null + } + ], + "path": [ + "bounded_collections", + "bounded_vec", + "BoundedVec" + ] + } + }, + { + "id": 66, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "index", + "type": 16, + "typeName": "u32" + }, + { + "name": "name", + "type": 67, + "typeName": "BoundedVec" + }, + { + "name": "module_name", + "type": 67, + "typeName": "BoundedVec" + }, + { + "name": "major", + "type": 16, + "typeName": "u32" + }, + { + "name": "minor", + "type": 16, + "typeName": "u32" + }, + { + "name": "patch", + "type": 16, + "typeName": "u32" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "PalletInfo" + ] + } + }, + { + "id": 67, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 20, + "typeName": "Vec" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 13 + }, + { + "name": "S", + "type": null + } + ], + "path": [ + "bounded_collections", + "bounded_vec", + "BoundedVec" + ] + } + }, + { + "id": 68, + "type": { + "def": { + "sequence": { + "type": 66 + } + } + } + }, + { + "id": 69, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Success" + }, + { + "fields": [ + { + "type": 70, + "typeName": "BoundedVec" + } + ], + "index": 1, + "name": "Error" + }, + { + "fields": [ + { + "type": 70, + "typeName": "BoundedVec" + } + ], + "index": 2, + "name": "TruncatedError" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "MaybeErrorCode" + ] + } + }, + { + "id": 70, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 20, + "typeName": "Vec" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 13 + }, + { + "name": "S", + "type": null + } + ], + "path": [ + "bounded_collections", + "bounded_vec", + "BoundedVec" + ] + } + }, + { + "id": 71, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "None" + }, + { + "fields": [ + { + "type": 51 + } + ], + "index": 1, + "name": "Some" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 51 + } + ], + "path": [ + "Option" + ] + } + }, + { + "id": 72, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "destination", + "type": 51, + "typeName": "MultiLocation" + }, + { + "name": "query_id", + "type": 22, + "typeName": "QueryId" + }, + { + "name": "max_weight", + "type": 64, + "typeName": "Weight" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "QueryResponseInfo" + ] + } + }, + { + "id": 73, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 47, + "typeName": "MultiAssets" + } + ], + "index": 0, + "name": "Definite" + }, + { + "fields": [ + { + "type": 74, + "typeName": "WildMultiAsset" + } + ], + "index": 1, + "name": "Wild" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "multiasset", + "MultiAssetFilter" + ] + } + }, + { + "id": 74, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "All" + }, + { + "fields": [ + { + "name": "id", + "type": 50, + "typeName": "AssetId" + }, + { + "name": "fun", + "type": 75, + "typeName": "WildFungibility" + } + ], + "index": 1, + "name": "AllOf" + }, + { + "fields": [ + { + "type": 16, + "typeName": "u32" + } + ], + "index": 2, + "name": "AllCounted" + }, + { + "fields": [ + { + "name": "id", + "type": 50, + "typeName": "AssetId" + }, + { + "name": "fun", + "type": 75, + "typeName": "WildFungibility" + }, + { + "name": "count", + "type": 16, + "typeName": "u32" + } + ], + "index": 3, + "name": "AllOfCounted" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "multiasset", + "WildMultiAsset" + ] + } + }, + { + "id": 75, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Fungible" + }, + { + "index": 1, + "name": "NonFungible" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "multiasset", + "WildFungibility" + ] + } + }, + { + "id": 76, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Unlimited" + }, + { + "fields": [ + { + "type": 64, + "typeName": "Weight" + } + ], + "index": 1, + "name": "Limited" + } + ] + } + }, + "path": [ + "xcm", + "v3", + "WeightLimit" + ] + } + }, + { + "id": 77, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 78 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 78 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 78, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 64 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 79 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 64 + }, + { + "name": "E", + "type": 79 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 79, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "Success" + }, + { + "index": 1, + "name": "NoResponse" + }, + { + "index": 2, + "name": "RuntimeError" + } + ] + } + }, + "path": [ + "contracts_example", + "sdk", + "Error" + ] + } + }, + { + "id": 80, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "dest", + "type": 81, + "typeName": "VersionedMultiLocation" + }, + { + "name": "xcm", + "type": 4, + "typeName": "VersionedXcm<()>" + } + ] + } + }, + "path": [ + "xcm_ce_primitives", + "ValidateSendInput" + ] + } + }, + { + "id": 81, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 12, + "typeName": "v2::MultiLocation" + } + ], + "index": 1, + "name": "V2" + }, + { + "fields": [ + { + "type": 51, + "typeName": "v3::MultiLocation" + } + ], + "index": 3, + "name": "V3" + } + ] + } + }, + "path": [ + "xcm", + "VersionedMultiLocation" + ] + } + }, + { + "id": 82, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 83 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 83 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 83, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 84 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 79 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 84 + }, + { + "name": "E", + "type": 79 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 84, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 8, + "typeName": "v2::MultiAssets" + } + ], + "index": 1, + "name": "V2" + }, + { + "fields": [ + { + "type": 47, + "typeName": "v3::MultiAssets" + } + ], + "index": 3, + "name": "V3" + } + ] + } + }, + "path": [ + "xcm", + "VersionedMultiAssets" + ] + } + }, + { + "id": 85, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "query_type", + "type": 87, + "typeName": "QueryType" + }, + { + "name": "timeout", + "type": 17, + "typeName": "BlockNumber" + } + ] + } + }, + "params": [ + { + "name": "AccountId", + "type": 86 + }, + { + "name": "BlockNumber", + "type": 17 + } + ], + "path": [ + "xcm_ce_primitives", + "QueryConfig" + ] + } + }, + { + "id": 86, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 21, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "AccountId" + ] + } + }, + { + "id": 87, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "NoCallback" + }, + { + "fields": [ + { + "name": "contract_id", + "type": 86, + "typeName": "AccountId" + }, + { + "name": "selector", + "type": 31, + "typeName": "[u8; 4]" + } + ], + "index": 1, + "name": "WASMContractCallback" + }, + { + "fields": [ + { + "name": "contract_id", + "type": 88, + "typeName": "H160" + }, + { + "name": "selector", + "type": 31, + "typeName": "[u8; 4]" + } + ], + "index": 2, + "name": "EVMContractCallback" + } + ] + } + }, + "params": [ + { + "name": "AccountId", + "type": 86 + } + ], + "path": [ + "xcm_ce_primitives", + "QueryType" + ] + } + }, + { + "id": 88, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 24, + "typeName": "[u8; 20]" + } + ] + } + }, + "path": [ + "primitive_types", + "H160" + ] + } + }, + { + "id": 89, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 90 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 90 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 90, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 23 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 79 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 23 + }, + { + "name": "E", + "type": 79 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 91, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 92 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 92 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 92, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 60 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 79 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 60 + }, + { + "name": "E", + "type": 79 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 93, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 0 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 0 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 94, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 21, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "Hash" + ] + } + }, + { + "id": 95, + "type": { + "def": { + "variant": {} + }, + "path": [ + "ink_env", + "types", + "NoChainExtension" + ] + } + } + ], + "version": "4" +} \ No newline at end of file diff --git a/frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.wasm b/frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.wasm new file mode 100644 index 0000000000000000000000000000000000000000..ad94b85c6ba80f650b674ea5f1a19666b1fd3786 GIT binary patch literal 76223 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 SJoEBy@dC#i@bfF%-2VZ30x0DG literal 0 HcmV?d00001 diff --git a/frame/pallet-xcm-transactor/xcm-simulator/src/README.md b/frame/pallet-xcm-transactor/xcm-simulator/src/README.md new file mode 100644 index 00000000..1e9fdc18 --- /dev/null +++ b/frame/pallet-xcm-transactor/xcm-simulator/src/README.md @@ -0,0 +1,22 @@ +# XCM Simulator Test Framework + +The `xcm-simulator` framework provides a nice & easy way to test complex XCM behavior in the form of a unit test. + +An entire system can be defined and/or mocked, which includes the relay chain and multiple parachains. +Sending of DMP, UMP and HRMP messages is supported. + +Tester controls when XCM message is dispatched and when it's received by the destination chain. + +# Structure + +A custom relay-chain runtime is defined in this crate. + +A custom parachain runtime is defined, loosely based on the `Shiden` runtime. +In current test setup, the same runtime is used to define two different parachains. +It's possible that in the future we decide to define a different runtime type (perhaps more resembling that of `Shibuya`). + +# Running Tests + +Running tests is same as with any other unit tests: + +`cargo test -p xcm-simulator-tests` \ No newline at end of file diff --git a/frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs b/frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs new file mode 100644 index 00000000..a1fbddd0 --- /dev/null +++ b/frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs @@ -0,0 +1,421 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +#[cfg(test)] +mod mocks; + +#[cfg(test)] +mod tests { + use crate::mocks::{parachain, *}; + + use frame_support::{assert_ok, traits::fungible::Inspect, weights::Weight}; + use pallet_contracts::Determinism; + use pallet_xcm_transactor::{ + chain_extension::{ValidateSendInput, XcmCeError as XcmCEError}, + QueryConfig, QueryType, + }; + use parity_scale_codec::{Decode, Encode}; + use sp_runtime::traits::Bounded; + use xcm::prelude::*; + use xcm_simulator::TestExt; + + type AccoundIdOf = ::AccountId; + type BlockNumberOf = ::BlockNumber; + + const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); + + const SELECTOR_CONSTRUCTOR: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFF]; + const SELECTOR_EXECUTE: [u8; 4] = [0x11, 0x11, 0x11, 0x11]; + const SELECTOR_SEND: [u8; 4] = [0x22, 0x22, 0x22, 0x22]; + const SELECTOR_QUERY: [u8; 4] = [0x33, 0x33, 0x33, 0x33]; + const SELECTOR_HANDLE_RESPONSE: [u8; 4] = [0x55, 0x55, 0x55, 0x55]; + const SELECTOR_GET: [u8; 4] = [0x66, 0x66, 0x66, 0x66]; + + #[test] + fn xcm_remote_contract_callback() { + MockNet::reset(); + + // deploy and initialize xcm flipper contract with `false` in ParaA + let mut contract_id = [0u8; 32].into(); + ParaA::execute_with(|| { + (contract_id, _) = deploy_contract::( + "xcm_flip", + ALICE.into(), + 0, + GAS_LIMIT, + None, + // selector + true + SELECTOR_CONSTRUCTOR.to_vec(), + ); + + // check for flip status + let outcome = ParachainContracts::bare_call( + ALICE.into(), + contract_id.clone(), + 0, + GAS_LIMIT, + None, + SELECTOR_GET.to_vec(), + true, + Determinism::Deterministic, + ); + let res = outcome.result.unwrap(); + // check for revert + assert!(!res.did_revert()); + // decode the return value + let flag = Result::::decode(&mut res.data.as_ref()).unwrap(); + assert_eq!(flag, Ok(false)); + }); + + // transfer funds to contract derieve account + ParaB::execute_with(|| { + use parachain::System; + + let account = sibling_para_account_account_id(1, contract_id.clone()); + assert_ok!(ParachainBalances::transfer( + parachain::RuntimeOrigin::signed(ALICE), + account, + INITIAL_BALANCE / 2, + )); + + System::reset_events(); + }); + + // check the execute + ParaA::execute_with(|| { + let transfer_amount = 100_000; + // transfer some native to contract + assert_ok!(ParachainBalances::transfer( + parachain::RuntimeOrigin::signed(ALICE), + contract_id.clone(), + transfer_amount, + )); + + let xcm: Xcm<()> = Xcm(vec![ + WithdrawAsset((Here, transfer_amount).into()), + BuyExecution { + fees: (Here, transfer_amount).into(), + weight_limit: Unlimited, + }, + DepositAsset { + assets: All.into(), + beneficiary: AccountId32 { + network: None, + id: ALICE.into(), + } + .into(), + }, + ]); + + // run execute in contract + let alice_balance_before = ParachainBalances::balance(&ALICE.into()); + let (res, _, _) = + call_contract_method::, ()>>( + ALICE.into(), + contract_id.clone(), + 0, + GAS_LIMIT, + None, + [SELECTOR_EXECUTE.to_vec(), VersionedXcm::V3(xcm).encode()].concat(), + true, + ); + + assert_eq!(res, Ok(Ok(Weight::from_parts(30, 0)))); + assert!( + // TODO: since bare_call doesn't charge, use call + ParachainBalances::balance(&ALICE.into()) == alice_balance_before + transfer_amount + ); + }); + + // + // Check send & query + // + ParaA::execute_with(|| { + use parachain::{Runtime, RuntimeCall}; + + let remark_call = RuntimeCall::System(frame_system::Call::remark_with_event { + remark: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 0], + }); + + let config = QueryConfig::, BlockNumberOf> { + query_type: QueryType::WASMContractCallback { + contract_id: contract_id.clone(), + selector: SELECTOR_HANDLE_RESPONSE, + }, + timeout: Bounded::max_value(), + }; + let dest: VersionedMultiLocation = (Parent, Parachain(2)).into(); + + let (res, _, _) = call_contract_method::< + parachain::Runtime, + Result, ()>, + >( + ALICE.into(), + contract_id.clone(), + 0, + GAS_LIMIT, + None, + [ + SELECTOR_QUERY.to_vec(), + (config.clone(), dest.clone()).encode(), + ] + .concat(), + true, + ); + assert_eq!(res, Ok(Ok(0))); + let query_id = res.unwrap().unwrap(); + + let xcm: Xcm<()> = Xcm(vec![ + WithdrawAsset((Here, INITIAL_BALANCE / 2).into()), + BuyExecution { + fees: (Here, INITIAL_BALANCE / 2).into(), + weight_limit: Unlimited, + }, + SetAppendix(Xcm(vec![ReportTransactStatus(QueryResponseInfo { + destination: (Parent, Parachain(1)).into(), + query_id, + max_weight: GAS_LIMIT, + })])), + Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts( + 100_000_000_000_000, + 1024 * 1024 * 1024, + ), + call: remark_call.encode().into(), + }, + ]); + + // send xcm + let (_res, _, _) = call_contract_method::< + parachain::Runtime, + Result, ()>, + >( + ALICE.into(), + contract_id.clone(), + 0, + GAS_LIMIT, + None, + [ + SELECTOR_SEND.to_vec(), + ValidateSendInput { + dest, + xcm: VersionedXcm::V3(xcm), + } + .encode(), + ] + .concat(), + true, + ); + + // dbg!(res); + }); + + // check if remark was executed in ParaB + ParaB::execute_with(|| { + use parachain::{RuntimeEvent, System}; + // check remark events + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ))); + + // clear the events + System::reset_events(); + }); + + // check for callback, if callback success then flip=true + ParaA::execute_with(|| { + // check for flip status + let (res, _, _) = call_contract_method::>( + ALICE.into(), + contract_id.clone(), + 0, + GAS_LIMIT, + None, + SELECTOR_GET.to_vec(), + true, + ); + assert_eq!(res, Ok(true)); + }); + + // ParaA::execute_with(|| { + // use parachain::XcmTransact; + // // dispatch call to flip contract + // // let call = parachain::RuntimeCall::Contracts(pallet_contracts::Call::call { + // // dest: contract_id.clone(), + // // value: 0, + // // gas_limit: Weight::from_parts(100_000_000_000, 1024 * 1024), + // // storage_deposit_limit: None, + // // data: SELECTOR_FLIP.to_vec(), + // // }); + // let call = parachain::RuntimeCall::System(frame_system::Call::remark_with_event { + // remark: vec![1, 2, 3, 4], + // }); + + // let query_id = XcmTransact::new_query( + // QueryConfig { + // query_type: QueryType::WASMContractCallback { + // contract_id: contract_id.clone(), + // selector: SELECTOR_XCM_FLIP, + // }, + // timeout: Bounded::max_value(), + // }, + // AccountId32 { + // id: ALICE.into(), + // network: Some(Kusama), + // } + // .into(), + // // Here, + // (Parent, Parachain(2)), + // ) + // .unwrap(); + + // let xcm: Xcm<()> = Xcm(vec![ + // WithdrawAsset((Here, INITIAL_BALANCE).into()), + // BuyExecution { + // fees: (Here, INITIAL_BALANCE).into(), + // weight_limit: Unlimited, + // }, + // SetAppendix(Xcm(vec![ReportTransactStatus(QueryResponseInfo { + // destination: (Parent, Parachain(1)).into(), + // query_id, + // max_weight: GAS_LIMIT, + // })])), + // Transact { + // origin_kind: OriginKind::SovereignAccount, + // require_weight_at_most: Weight::from_parts(100_000_000_000_000, 1024 * 1024 * 1024), + // call: call.encode().into(), + // }, + // ]); + + // // send the XCM to ParaA + // assert_ok!(ParachainPalletXcm::send( + // parachain::RuntimeOrigin::signed(ALICE), + // // parachain::RuntimeOrigin::root(), + // Box::new((Parent, Parachain(2)).into()), + // Box::new(VersionedXcm::V3(xcm)), + // )); + + // use parachain::System; + // System::reset_events(); + // // println!("{:?}", System::events()); + // }); + + // ParaB::execute_with(|| { + + // // let outcome = ParachainContracts::bare_call( + // // ALICE.into(), + // // contract_id.clone(), + // // 0, + // // GAS_LIMIT, + // // None, + // // SELECTOR_GET.to_vec(), + // // true, + // // Determinism::Deterministic, + // // ); + // // let res = outcome.result.unwrap(); + // // // check for revert + // // assert!(res.did_revert() == false); + // // // decode the return value, it should be false + // // let flag = Result::::decode(&mut res.data.as_ref()).unwrap(); + // // assert_eq!(flag, Ok(false)); + // }); + + // ParaA::execute_with(|| { + // // use parachain::System; + // // println!("{:?}", System::events()); + + // let outcome = ParachainContracts::bare_call( + // ALICE.into(), + // contract_id.clone(), + // 0, + // GAS_LIMIT, + // None, + // SELECTOR_GET.to_vec(), + // true, + // Determinism::Deterministic, + // ); + // let res = outcome.result.unwrap(); + // // check for revert + // assert!(res.did_revert() == false); + // // decode the return value, it should be false + // let flag = Result::::decode(&mut res.data.as_ref()).unwrap(); + + // // println!("{:?}", ParachainPalletXcm::query(0)); + // // println!( + // // "expecting response = {:?}", + // // ParachainPalletXcm::expecting_response( + // // &(Parent, Parachain(2)).into(), + // // 0, + // // Some(&Here.into()) + // // ) + // // ); + + // // std::thread::sleep(std::time::Duration::from_secs(1)); + // assert_eq!(flag, Ok(true)); + // }); + + // ParaA::execute_with(|| { + // // use parachain::System; + // // println!("{:?}", System::events()); + // let xcm: VersionedXcm<()> = VersionedXcm::V3(Xcm(vec![ + // WithdrawAsset((Here, 10).into()), + // BuyExecution { + // fees: (Here, 10).into(), + // weight_limit: Unlimited, + // }, + // SetAppendix(Xcm(vec![ReportError(QueryResponseInfo { + // destination: (Parent, Parachain(1)).into(), + // query_id: 1, + // max_weight: GAS_LIMIT, + // })])), + // ])); + // let outcome = ParachainContracts::bare_call( + // ALICE.into(), + // contract_id.clone(), + // 0, + // GAS_LIMIT, + // None, + // [SELECTOR_TEST.to_vec(), xcm.encode()].concat(), + // true, + // Determinism::Deterministic, + // ); + // println!("outcome={outcome:?}"); + // println!("{:?}", String::from_utf8(outcome.debug_message)); + // let res = outcome.result.unwrap(); + // // check for revert + // assert!(res.did_revert() == false); + // // decode the return value, it should be false + // let out = Result::, ()>::decode(&mut res.data.as_ref()).unwrap(); + // println!("out={out:?}"); + // // println!("{:?}", ParachainPalletXcm::query(0)); + // // println!( + // // "expecting response = {:?}", + // // ParachainPalletXcm::expecting_response( + // // &(Parent, Parachain(2)).into(), + // // 0, + // // Some(&Here.into()) + // // ) + // // ); + + // // std::thread::sleep(std::time::Duration::from_secs(1)); + // // assert_eq!(flag, Ok(true)); + // }); + } +} diff --git a/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/mod.rs b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/mod.rs new file mode 100644 index 00000000..df91aa68 --- /dev/null +++ b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/mod.rs @@ -0,0 +1,317 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +pub(crate) mod msg_queue; +pub(crate) mod parachain; +pub(crate) mod relay_chain; + +use frame_support::traits::{Currency, IsType, OnInitialize}; +use frame_support::weights::Weight; +use pallet_contracts::Determinism; +use pallet_contracts_primitives::{Code, ReturnFlags}; +use parity_scale_codec::Decode; +use sp_runtime::traits::{Bounded, Hash, StaticLookup}; +use sp_runtime::DispatchResult; +use xcm::latest::prelude::*; +use xcm_executor::traits::Convert; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; + +type ContractBalanceOf = <::Currency as Currency< + ::AccountId, +>>::Balance; + +pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0xFAu8; 32]); +pub const INITIAL_BALANCE: u128 = 1_000_000_000_000_000_000_000_000; +pub const DAPP_STAKER_REWARD_PER_BLOCK: parachain::Balance = 1_000; +pub const DAPP_STAKER_DEV_PER_BLOCK: parachain::Balance = 250; + +decl_test_parachain! { + pub struct ParaA { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(1), + } +} + +decl_test_parachain! { + pub struct ParaB { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(2), + } +} + +decl_test_relay_chain! { + pub struct Relay { + Runtime = relay_chain::Runtime, + XcmConfig = relay_chain::XcmConfig, + new_ext = relay_ext(), + } +} + +decl_test_network! { + pub struct MockNet { + relay_chain = Relay, + parachains = vec![ + (1, ParaA), + (2, ParaB), + ], + } +} + +// pub type RelayChainPalletXcm = pallet_xcm::Pallet; + +// pub type ParachainPalletXcm = pallet_xcm::Pallet; +// pub type ParachainAssets = pallet_assets::Pallet; +pub type ParachainBalances = pallet_balances::Pallet; +pub type ParachainContracts = pallet_contracts::Pallet; + +// pub fn parent_account_id() -> parachain::AccountId { +// let location = (Parent,); +// parachain::LocationToAccountId::convert(location.into()).unwrap() +// } + +/// Derive parachain sovereign account on relay chain, from parachain Id +pub fn child_para_account_id(para: u32) -> relay_chain::AccountId { + let location = (Parachain(para),); + relay_chain::LocationToAccountId::convert(location.into()).unwrap() +} + +/// Derive parachain sovereign account on a sibling parachain, from parachain Id +pub fn sibling_para_account_id(para: u32) -> parachain::AccountId { + let location = (Parent, X1(Parachain(para))); + parachain::LocationToAccountId::convert(location.into()).unwrap() +} + +/// Derive parachain's account's account on a sibling parachain +pub fn sibling_para_account_account_id( + para: u32, + who: sp_runtime::AccountId32, +) -> parachain::AccountId { + let location = ( + Parent, + Parachain(para), + AccountId32 { + // we have kusama as relay in mock + network: Some(Kusama), + id: who.into(), + }, + ); + parachain::LocationToAccountId::convert(location.into()).unwrap() +} + +/// Prepare parachain test externality +pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { + use parachain::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (sibling_para_account_account_id(1, ALICE), INITIAL_BALANCE), + (sibling_para_account_account_id(2, ALICE), INITIAL_BALANCE), + (sibling_para_account_id(1), INITIAL_BALANCE), + (sibling_para_account_id(2), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + + parachain::DappsStaking::on_initialize(1); + let (staker_rewards, dev_rewards) = issue_dapps_staking_rewards(); + parachain::DappsStaking::rewards(staker_rewards, dev_rewards); + }); + ext +} + +/// Prepare relay chain test externality +pub fn relay_ext() -> sp_io::TestExternalities { + use relay_chain::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (child_para_account_id(1), INITIAL_BALANCE), + (child_para_account_id(2), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +/// Issues and returns negative imbalances of (staker rewards, developer rewards) +fn issue_dapps_staking_rewards() -> (parachain::NegativeImbalance, parachain::NegativeImbalance) { + ( + parachain::Balances::issue(DAPP_STAKER_REWARD_PER_BLOCK), + parachain::Balances::issue(DAPP_STAKER_DEV_PER_BLOCK), + ) +} + +/// Register and configure the asset for use in XCM +/// It first create the asset in `pallet_assets` and then register the asset multilocation +/// mapping in `pallet_xc_asset_config`, and lastly set the asset per second for calculating +/// XCM execution cost (only applicable if `is_sufficent` is true) +pub fn _register_and_setup_xcm_asset( + origin: Runtime::RuntimeOrigin, + // AssetId for the new asset + asset_id: AssetId, + // Asset multilocation + asset_location: impl Into + Clone, + // Asset controller + asset_controller: ::Source, + // make asset payable, default true + is_sufficent: Option, + // minimum balance for account to exist (ED), default, 0 + min_balance: Option, + // Asset unit per second for calculating execution cost for XCM, default 1_000_000_000_000 + units_per_second: Option, +) -> DispatchResult +where + Runtime: pallet_xc_asset_config::Config + pallet_assets::Config, + AssetId: IsType<::AssetId> + + IsType<::AssetId> + + Clone, +{ + // Register the asset + pallet_assets::Pallet::::force_create( + origin.clone(), + ::AssetIdParameter::from(asset_id.clone().into()), + asset_controller, + is_sufficent.unwrap_or(true), + min_balance.unwrap_or(Bounded::min_value()), + )?; + + // Save the asset and multilocation mapping + pallet_xc_asset_config::Pallet::::register_asset_location( + origin.clone(), + Box::new(asset_location.clone().into().into_versioned()), + asset_id.into(), + )?; + + // set the units per second for XCM cost + pallet_xc_asset_config::Pallet::::set_asset_units_per_second( + origin, + Box::new(asset_location.into().into_versioned()), + units_per_second.unwrap_or(1_000_000_000_000), + ) +} + +/// Load a given wasm module from wasm binary contents along +/// with it's hash. +/// +/// The fixture files are located under the `fixtures/` directory. +pub fn load_module( + fixture_name: &str, +) -> std::io::Result<(Vec, ::Output)> +where + T: frame_system::Config, +{ + let fixture_path = ["fixtures/", fixture_name, ".wasm"].concat(); + let wasm_binary = std::fs::read(fixture_path)?; + let code_hash = T::Hashing::hash(&wasm_binary); + Ok((wasm_binary, code_hash)) +} + +/// Load and deploy the contract from wasm binary +/// and check for successful deploy +pub fn deploy_contract( + contract_name: &str, + origin: T::AccountId, + value: ContractBalanceOf, + gas_limit: Weight, + storage_deposit_limit: Option>, + data: Vec, +) -> (T::AccountId, ::Output) { + let (code, hash) = load_module::(contract_name).unwrap(); + let outcome = pallet_contracts::Pallet::::bare_instantiate( + origin, + value, + gas_limit, + storage_deposit_limit, + Code::Upload(code), + data, + // vec![], + vec![], + true, + ); + + // make sure it does not revert + let result = outcome.result.unwrap(); + assert!( + !result.result.did_revert(), + "deploy_contract: reverted - {:?}", + result + ); + (result.account_id, hash) +} + +pub fn call_contract_method( + origin: T::AccountId, + dest: T::AccountId, + value: ContractBalanceOf, + gas_limit: Weight, + storage_deposit_limit: Option>, + data: Vec, + debug: bool, +) -> (V, ReturnFlags, Weight) { + let outcome = pallet_contracts::Pallet::::bare_call( + origin, + dest, + value, + gas_limit, + storage_deposit_limit, + data, + debug, + Determinism::Deterministic, + ); + + if debug { + println!( + "Contract debug - {:?}", + String::from_utf8(outcome.debug_message.clone()) + ); + } + + let res = outcome.result.unwrap(); + // check for revert + assert!(!res.did_revert(), "Contract reverted!"); + + ( + V::decode(&mut res.data.as_ref()).unwrap(), + res.flags, + outcome.gas_consumed, + ) +} diff --git a/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/msg_queue.rs b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/msg_queue.rs new file mode 100644 index 00000000..437034eb --- /dev/null +++ b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/msg_queue.rs @@ -0,0 +1,189 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +use frame_support::weights::Weight; +use parity_scale_codec::{Decode, Encode}; +use sp_runtime::traits::Hash; +use sp_std::prelude::*; + +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; +use polkadot_parachain::primitives::{ + DmpMessageHandler, Id as ParaId, XcmpMessageFormat, XcmpMessageHandler, +}; +use xcm::{latest::prelude::*, VersionedXcm}; + +#[frame_support::pallet] +pub mod mock_msg_queue { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn parachain_id)] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn received_dmp)] + /// A queue of received DMP messages + pub(super) type ReceivedDmp = StorageValue<_, Vec>, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn received_xcmp)] + /// A queue of received XCMP messages + pub(super) type ReceivedXcmp = StorageValue<_, Vec>, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed OK. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // 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 Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + 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::::try_from(xcm) { + Ok(xcm) => { + let location = (Parent, Parachain(sender.into())); + >::append(xcm.clone()); + match T::XcmExecutor::execute_xcm( + location, + xcm.clone(), + message_hash, + max_weight, + ) { + Outcome::Error(e) => { + println!("Error in XCMP handling: {:?}, sender=Parachain({sender}), xcm={xcm:?}", e); + (Err(e.clone()), 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) => { + println!("Incomplete XCMP handling: {:?}, {sender}", e); + (Ok(w), Event::Fail(Some(hash), e)) + } + } + } + Err(()) => ( + Err(XcmError::UnhandledXcmVersion), + Event::BadVersion(Some(hash)), + ), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + 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::::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 DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let id = sp_io::hashing::blake2_256(&data[..]); + let maybe_versioned = VersionedXcm::::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); + >::append(x); + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + } + }, + } + } + limit + } + } +} diff --git a/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/parachain.rs b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/parachain.rs new file mode 100644 index 00000000..8e1db888 --- /dev/null +++ b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/parachain.rs @@ -0,0 +1,529 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + construct_runtime, + dispatch::DispatchClass, + match_types, parameter_types, + traits::{ + AsEnsureOriginWithArg, ConstU128, ConstU32, ConstU64, Currency, Everything, Imbalance, + Nothing, OnUnbalanced, + }, + weights::{ + constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_REF_TIME_PER_SECOND}, + Weight, + }, + PalletId, +}; +use frame_system::{ + limits::{BlockLength, BlockWeights}, + EnsureSigned, +}; +use pallet_contracts::chain_extension::RegisteredChainExtension; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use polkadot_core_primitives::BlakeTwo256; +use sp_core::{ConstBool, H256}; +use sp_runtime::{ + generic::Header, + traits::{AccountIdConversion, Convert, IdentityLookup}, + AccountId32, Perbill, RuntimeDebug, +}; +use sp_std::prelude::*; + +use super::msg_queue::*; +use xcm::latest::prelude::{AssetId as XcmAssetId, *}; +use xcm_builder::{ + Account32Hash, AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, ConvertedConcreteId, CurrencyAdapter, + EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungiblesAdapter, IsConcrete, + NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, WithComputedOrigin, +}; +use xcm_executor::{traits::JustTry, XcmExecutor}; + +use xcm_primitives::{ + AssetLocationIdConverter, FixedRateOfForeignAsset, ReserveAssetFilter, XcmFungibleFeeHandler, +}; + +pub type AccountId = AccountId32; +pub type Balance = u128; +pub type AssetId = u128; + +pub type NegativeImbalance = >::NegativeImbalance; + +pub type ShidenAssetLocationIdConverter = AssetLocationIdConverter; + +parameter_types! { + pub const BlockHashCount: u32 = 250; +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u32; + type Hash = H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::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]; +} + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetIdParameter = AssetId; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type AssetDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<10>; + type MetadataDepositPerByte = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type ApprovalDeposit = ConstU128<10>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type Extra = (); + type RemoveItemsLimit = ConstU32<100>; + type CallbackHandle = (); + type WeightInfo = pallet_assets::weights::SubstrateWeight; +} + +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<1>; + type WeightInfo = (); +} + +impl pallet_insecure_randomness_collective_flip::Config for Runtime {} + +/// Constant values used within the runtime. +pub const MICROSDN: Balance = 1_000_000_000_000; +pub const MILLISDN: Balance = 1_000 * MICROSDN; +/// We assume that ~10% of the block weight is consumed by `on_initalize` handlers. +/// This is used to limit the maximal weight of a single extrinsic. +const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); +/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used +/// by Operational extrinsics. +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); +/// We allow for 0.5 seconds of compute with a 6 second average block time. +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), + polkadot_primitives::MAX_POV_SIZE as u64, +); + +parameter_types! { + pub RuntimeBlockLength: BlockLength = + BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + // Operational transactions have some extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); + pub SS58Prefix: u8 = 5; +} + +// TODO: changing depost per item and per byte to `deposit` function will require storage migration it seems +parameter_types! { + pub const DepositPerItem: Balance = MILLISDN / 1_000_000; + pub const DepositPerByte: Balance = MILLISDN / 1_000_000; + // The lazy deletion runs inside on_initialize. + pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO * + RuntimeBlockWeights::get().max_block; + pub Schedule: pallet_contracts::Schedule = Default::default(); +} + +impl Convert for Runtime { + fn convert(w: Weight) -> Balance { + w.ref_time().into() + } +} + +impl pallet_contracts::Config for Runtime { + type Time = Timestamp; + type Randomness = Randomness; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + /// The safest default is to allow no calls at all. + /// + /// Runtimes should whitelist dispatchables that are allowed to be called from contracts + /// and make sure they are stable. Dispatchables exposed to contracts are not allowed to + /// change because that would break already deployed contracts. The `Call` structure itself + /// is not allowed to change the indices of existing pallets, too. + type CallFilter = Nothing; + type DepositPerItem = DepositPerItem; + type DepositPerByte = DepositPerByte; + type CallStack = [pallet_contracts::Frame; 5]; + /// We are not using the pallet_transaction_payment for simplicity + type WeightPrice = Self; + type WeightInfo = pallet_contracts::weights::SubstrateWeight; + type ChainExtension = pallet_xcm_transactor::chain_extension::XCMExtension; + type DeletionQueueDepth = ConstU32<128>; + type DeletionWeightLimit = DeletionWeightLimit; + type Schedule = Schedule; + type AddressGenerator = pallet_contracts::DefaultAddressGenerator; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; + type MaxStorageKeyLen = ConstU32<128>; + type UnsafeUnstableInterface = ConstBool; + type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; +} + +pub struct BurnFees; +impl OnUnbalanced for BurnFees { + /// Payout tips but burn all the fees + fn on_unbalanceds(mut fees_then_tips: impl Iterator) { + if let Some(mut fees_to_burn) = fees_then_tips.next() { + if let Some(tips) = fees_then_tips.next() { + fees_to_burn.subsume(tips) + } + drop(fees_to_burn); + } + } +} + +#[derive( + PartialEq, Eq, Copy, Clone, Encode, Decode, MaxEncodedLen, RuntimeDebug, scale_info::TypeInfo, +)] +pub enum SmartContract { + Wasm(u32), +} + +impl Default for SmartContract { + fn default() -> Self { + SmartContract::Wasm(0) + } +} + +parameter_types! { + pub const DappsStakingPalletId: PalletId = PalletId(*b"py/dpsst"); + pub const MaxUnlockingChunks: u32 = 5; + pub const UnbondingPeriod: u32 = 5; + pub const MaxEraStakeValues: u32 = 5; +} + +impl pallet_dapps_staking::Config for Runtime { + type Currency = Balances; + type BlockPerEra = ConstU32<5>; + type SmartContract = SmartContract; + type RegisterDeposit = ConstU128<1>; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_dapps_staking::weights::SubstrateWeight; + type MaxNumberOfStakersPerContract = ConstU32<8>; + type MinimumStakingAmount = ConstU128<1>; + type PalletId = DappsStakingPalletId; + type MinimumRemainingAmount = ConstU128<0>; + type MaxUnlockingChunks = ConstU32<4>; + type UnbondingPeriod = ConstU32<2>; + type MaxEraStakeValues = ConstU32<4>; + type UnregisteredDappRewardRetention = ConstU32<7>; +} + +parameter_types! { + pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry"); + pub TreasuryAccountId: AccountId = TreasuryPalletId::get().into_account_truncating(); +} + +impl pallet_xc_asset_config::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AssetId = AssetId; + type XcAssetChanged = (); + type ManagerOrigin = frame_system::EnsureRoot; + type WeightInfo = pallet_xc_asset_config::weights::SubstrateWeight; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} +parameter_types! { + pub const ReservedXcmpWeight: Weight = Weight::from_ref_time(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4)); + pub const ReservedDmpWeight: Weight = Weight::from_ref_time(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4)); +} + +parameter_types! { + pub RelayNetwork: Option = Some(NetworkId::Kusama); + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub UniversalLocation: InteriorMultiLocation = + X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(MsgQueue::parachain_id().into())); + pub const ShidenLocation: MultiLocation = Here.into_location(); + pub DummyCheckingAccount: AccountId = PolkadotXcm::check_account(); +} + +/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, + // Derives a private `Account32` by hashing `("multiloc", received multilocation)` + Account32Hash, +); + +/// Means for transacting the native currency on this chain. +pub type CurrencyTransactor = CurrencyAdapter< + // Use this currency: + Balances, + // Use this currency when it is a fungible asset matching the given location or name: + IsConcrete, + // Convert an XCM MultiLocation into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports of `Balances`. + (), +>; + +/// Means for transacting assets besides the native currency on this chain. +pub type FungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + Assets, + // Use this currency when it is a fungible asset matching the given location or name: + ConvertedConcreteId, + // Convert an XCM MultiLocation into a local account id: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We don't track any teleports of `Assets`. + NoChecking, + // We don't track any teleports of `Assets`. + DummyCheckingAccount, +>; + +/// Means for transacting assets on this chain. +pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a + // transaction from the Root origin. + ParentAsSuperuser, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + pallet_xcm::XcmPassthrough, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `Origin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, +); + +parameter_types! { + pub const UnitWeightCost: Weight = Weight::from_ref_time(10); + pub const MaxInstructions: u32 = 100; + pub NativePerSecond: (XcmAssetId, u128, u128) = (Concrete(ShidenLocation::get()), 1_000_000_000_000, 1024 * 1024); +} + +pub type XcmRouter = super::ParachainXcmRouter; + +match_types! { + pub type ParentOrParentsPlurality: impl Contains = { + MultiLocation { parents: 1, interior: Here } | + MultiLocation { parents: 1, interior: X1(Plurality { .. }) } + }; +} + +pub type XcmBarrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + // This will first calculate the derived origin, before checking it against the barrier implementation + WithComputedOrigin, UniversalLocation, ConstU32<8>>, + // Parent and its plurality get free execution + AllowUnpaidExecutionFrom, + // Expected responses are OK. + AllowKnownQueryResponses, + // Subscriptions for version tracking are OK. + AllowSubscriptionsFrom, +); + +// Used to handle XCM fee deposit into treasury account +pub type ShidenXcmFungibleFeeHandler = XcmFungibleFeeHandler< + AccountId, + ConvertedConcreteId, + Assets, + TreasuryAccountId, +>; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = ReserveAssetFilter; + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = XcmBarrier; + type Weigher = FixedWeightBounds; + type Trader = ( + FixedRateOfFungible, + FixedRateOfForeignAsset, + ); + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = ConstU32<64>; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; +} + +impl mock_msg_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<0>; + type WeightInfo = pallet_xcm::TestWeightInfo; +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +parameter_types! { + pub const CallbackGasLimit: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); +} + +impl pallet_xcm_transactor::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type CallbackHandler = XcmTransact; + type RegisterQueryOrigin = EnsureXcmOrigin; + type MaxCallbackWeight = CallbackGasLimit; +} + +impl RegisteredChainExtension + for pallet_xcm_transactor::chain_extension::XCMExtension +{ + const ID: u16 = pallet_xcm_transactor::chain_extension::XCM_EXTENSION_ID; +} + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + MsgQueue: mock_msg_queue::{Pallet, Storage, Event}, + PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin}, + Assets: pallet_assets::{Pallet, Call, Storage, Event}, + XcAssetConfig: pallet_xc_asset_config::{Pallet, Call, Storage, Event}, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin}, + DappsStaking: pallet_dapps_staking::{Pallet, Call, Event}, + Randomness: pallet_insecure_randomness_collective_flip::{Pallet, Storage}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Contracts: pallet_contracts::{Pallet, Call, Storage, Event}, + XcmTransact: pallet_xcm_transactor::{Pallet, Call, Storage, Event}, + } +); diff --git a/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/relay_chain.rs b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/relay_chain.rs new file mode 100644 index 00000000..00a208da --- /dev/null +++ b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/relay_chain.rs @@ -0,0 +1,212 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +use frame_support::{ + construct_runtime, parameter_types, + traits::{ConstU32, Everything, Nothing}, + weights::Weight, +}; +use sp_core::H256; +use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; + +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_runtime_parachains::{configuration, origin, shared, ump}; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, + ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, + CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, +}; +use xcm_executor::XcmExecutor; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::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]; +} + +impl shared::Config for Runtime {} + +impl configuration::Config for Runtime { + type WeightInfo = configuration::TestWeightInfo; +} + +parameter_types! { + pub const KsmLocation: MultiLocation = Here.into_location(); + pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub UniversalLocation: InteriorMultiLocation = Here; +} + +pub type SovereignAccountOf = ( + ChildParachainConvertsVia, + AccountId32Aliases, +); + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_ref_time(1_000); + pub KsmPerSecond: (AssetId, u128, u128) = (Concrete(KsmLocation::get()), 1, 1024 * 1024); + pub const MaxInstructions: u32 = 100; +} + +pub type XcmRouter = super::RelayChainXcmRouter; +pub type Barrier = AllowUnpaidExecutionFrom; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = (); + type AssetTrap = (); + type AssetClaims = (); + type SubscriptionService = (); + + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = ConstU32<64>; + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +pub type LocationToAccountId = (ChildParachainConvertsVia,); + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = XcmRouter; + // Anyone can execute XCM messages locally... + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<0>; + type WeightInfo = pallet_xcm::TestWeightInfo; +} + +parameter_types! { + pub const FirstMessageFactorPercent: u64 = 100; +} + +impl ump::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UmpSink = ump::XcmSink, Runtime>; + type FirstMessageFactorPercent = FirstMessageFactorPercent; + type ExecuteOverweightOrigin = frame_system::EnsureRoot; + type WeightInfo = ump::TestWeightInfo; +} + +impl origin::Config for Runtime {} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + ParasOrigin: origin::{Pallet, Origin}, + ParasUmp: ump::{Pallet, Call, Storage, Event}, + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin}, + } +); From 4d29c05d1d50d0288b937f83c138bb093b078ab6 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Tue, 9 May 2023 07:55:05 +0530 Subject: [PATCH 05/15] wip(refactor): move ink sdk into seperate crate --- .../basic-flip}/.gitignore | 0 .../basic-flip}/Cargo.toml | 16 ++++----- .../basic-flip}/lib.rs | 11 +++--- .../pallet-xcm-transactor/ink-sdk/.gitignore | 9 +++++ .../pallet-xcm-transactor/ink-sdk/Cargo.toml | 34 +++++++++++++++++++ frame/pallet-xcm-transactor/ink-sdk/README.md | 2 ++ .../sdk.rs => ink-sdk/lib.rs} | 5 +++ 7 files changed, 63 insertions(+), 14 deletions(-) rename frame/pallet-xcm-transactor/{contracts-example => contract-examples/basic-flip}/.gitignore (100%) rename frame/pallet-xcm-transactor/{contracts-example => contract-examples/basic-flip}/Cargo.toml (79%) rename frame/pallet-xcm-transactor/{contracts-example => contract-examples/basic-flip}/lib.rs (95%) create mode 100755 frame/pallet-xcm-transactor/ink-sdk/.gitignore create mode 100755 frame/pallet-xcm-transactor/ink-sdk/Cargo.toml create mode 100644 frame/pallet-xcm-transactor/ink-sdk/README.md rename frame/pallet-xcm-transactor/{contracts-example/sdk.rs => ink-sdk/lib.rs} (97%) mode change 100644 => 100755 diff --git a/frame/pallet-xcm-transactor/contracts-example/.gitignore b/frame/pallet-xcm-transactor/contract-examples/basic-flip/.gitignore similarity index 100% rename from frame/pallet-xcm-transactor/contracts-example/.gitignore rename to frame/pallet-xcm-transactor/contract-examples/basic-flip/.gitignore diff --git a/frame/pallet-xcm-transactor/contracts-example/Cargo.toml b/frame/pallet-xcm-transactor/contract-examples/basic-flip/Cargo.toml similarity index 79% rename from frame/pallet-xcm-transactor/contracts-example/Cargo.toml rename to frame/pallet-xcm-transactor/contract-examples/basic-flip/Cargo.toml index 71a8773a..08905407 100755 --- a/frame/pallet-xcm-transactor/contracts-example/Cargo.toml +++ b/frame/pallet-xcm-transactor/contract-examples/basic-flip/Cargo.toml @@ -1,11 +1,9 @@ [package] -name = "contracts-example" +name = "basic_flip" version = "0.1.0" authors = ["Ashutosh Varma "] edition = "2021" -[workspace] - [dependencies] ink = { version = "~4.2.0", default-features = false } @@ -13,14 +11,11 @@ num_enum = { version = "0.6", default-features = false } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true } xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false } -xcm-ce-primitives = { path = "../primitives", default-features = false } +xcm-ce-sdk = { path = "../../ink-sdk", default-features = false } [lib] path = "lib.rs" -[profile.release] -overflow-checks = false # Disable integer overflow checks. - [features] default = ["std"] std = [ @@ -29,6 +24,11 @@ std = [ "scale/std", "scale-info/std", "xcm/std", - "xcm-ce-primitives/std", + "xcm-ce-sdk/std", ] ink-as-dependency = [] + +[profile.release] +overflow-checks = false + +[workspace] diff --git a/frame/pallet-xcm-transactor/contracts-example/lib.rs b/frame/pallet-xcm-transactor/contract-examples/basic-flip/lib.rs similarity index 95% rename from frame/pallet-xcm-transactor/contracts-example/lib.rs rename to frame/pallet-xcm-transactor/contract-examples/basic-flip/lib.rs index 0d48ecc3..0730e64e 100755 --- a/frame/pallet-xcm-transactor/contracts-example/lib.rs +++ b/frame/pallet-xcm-transactor/contract-examples/basic-flip/lib.rs @@ -16,17 +16,16 @@ // You should have received a copy of the GNU General Public License // along with Astar. If not, see . -#![cfg_attr(not(feature = "std"), no_std)] - -mod sdk; -pub use sdk::{Error as XcmCEError, XcmExtension as _XcmExtension}; +#![cfg_attr(not(feature = "std"), no_std, no_main)] #[ink::contract] mod contracts { - use super::*; use ink::{env::DefaultEnvironment, storage::Mapping}; use xcm::{latest::Weight, prelude::*}; - use xcm_ce_primitives::{QueryConfig, ValidateSendInput}; + pub use xcm_ce_sdk::{ + types::{QueryConfig, ValidateSendInput}, + Error as XcmCEError, XcmExtension as _XcmExtension, + }; type XcmExtension = _XcmExtension; diff --git a/frame/pallet-xcm-transactor/ink-sdk/.gitignore b/frame/pallet-xcm-transactor/ink-sdk/.gitignore new file mode 100755 index 00000000..8de8f877 --- /dev/null +++ b/frame/pallet-xcm-transactor/ink-sdk/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/frame/pallet-xcm-transactor/ink-sdk/Cargo.toml b/frame/pallet-xcm-transactor/ink-sdk/Cargo.toml new file mode 100755 index 00000000..5db0b2bd --- /dev/null +++ b/frame/pallet-xcm-transactor/ink-sdk/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "xcm-ce-sdk" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +ink = { version = "~4.2.0", default-features = false } + +num_enum = { version = "0.6", default-features = false } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true } +xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.39", default-features = false } +xcm-ce-primitives = { path = "../primitives", default-features = false } + +[lib] +path = "lib.rs" + +# [profile.release] +# overflow-checks = false # Disable integer overflow checks. + +[features] +default = ["std"] +std = [ + "ink/std", + "num_enum/std", + "scale/std", + "scale-info/std", + "xcm/std", + "xcm-ce-primitives/std", +] +ink-as-dependency = [] diff --git a/frame/pallet-xcm-transactor/ink-sdk/README.md b/frame/pallet-xcm-transactor/ink-sdk/README.md new file mode 100644 index 00000000..a5ff1353 --- /dev/null +++ b/frame/pallet-xcm-transactor/ink-sdk/README.md @@ -0,0 +1,2 @@ +# xcm-ce-sdk +SDK for building chain extensions call for XCM CE and exports primitive types. diff --git a/frame/pallet-xcm-transactor/contracts-example/sdk.rs b/frame/pallet-xcm-transactor/ink-sdk/lib.rs old mode 100644 new mode 100755 similarity index 97% rename from frame/pallet-xcm-transactor/contracts-example/sdk.rs rename to frame/pallet-xcm-transactor/ink-sdk/lib.rs index dd4b6808..e4c9aab9 --- a/frame/pallet-xcm-transactor/contracts-example/sdk.rs +++ b/frame/pallet-xcm-transactor/ink-sdk/lib.rs @@ -26,6 +26,11 @@ use xcm_ce_primitives::{ create_error_enum, Command, QueryConfig, ValidateSendInput, XCM_EXTENSION_ID, }; +/// Re-export everything from xcm-ce-primitves +pub mod types { + pub use xcm_ce_primitives::*; +} + create_error_enum!(pub Error); impl FromStatusCode for Error { From cf44fd95f9a7db6ea56b8a87f48cfac47be07903 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Tue, 9 May 2023 07:55:50 +0530 Subject: [PATCH 06/15] wip: cleanup simulator tests and add fixtures --- .../xcm-simulator/src/lib.rs | 218 ++++-------------- 1 file changed, 41 insertions(+), 177 deletions(-) diff --git a/frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs b/frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs index a1fbddd0..7aeb4791 100644 --- a/frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs +++ b/frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs @@ -46,15 +46,18 @@ mod tests { const SELECTOR_HANDLE_RESPONSE: [u8; 4] = [0x55, 0x55, 0x55, 0x55]; const SELECTOR_GET: [u8; 4] = [0x66, 0x66, 0x66, 0x66]; - #[test] - fn xcm_remote_contract_callback() { - MockNet::reset(); + struct Fixture { + pub basic_flip_id: parachain::AccountId, + } + /// Deploy the xcm flipper contract in ParaA and fund it's derive account + /// in ParaB + fn xcm_flipper_fixture() -> Fixture { // deploy and initialize xcm flipper contract with `false` in ParaA - let mut contract_id = [0u8; 32].into(); + let mut basic_flip_id = [0u8; 32].into(); ParaA::execute_with(|| { - (contract_id, _) = deploy_contract::( - "xcm_flip", + (basic_flip_id, _) = deploy_contract::( + "basic_flip", ALICE.into(), 0, GAS_LIMIT, @@ -63,10 +66,10 @@ mod tests { SELECTOR_CONSTRUCTOR.to_vec(), ); - // check for flip status + // check for flip status, should be false let outcome = ParachainContracts::bare_call( ALICE.into(), - contract_id.clone(), + basic_flip_id.clone(), 0, GAS_LIMIT, None, @@ -82,11 +85,11 @@ mod tests { assert_eq!(flag, Ok(false)); }); - // transfer funds to contract derieve account + // transfer funds to contract derieve account in ParaB ParaB::execute_with(|| { use parachain::System; - let account = sibling_para_account_account_id(1, contract_id.clone()); + let account = sibling_para_account_account_id(1, basic_flip_id.clone()); assert_ok!(ParachainBalances::transfer( parachain::RuntimeOrigin::signed(ALICE), account, @@ -96,7 +99,21 @@ mod tests { System::reset_events(); }); - // check the execute + Fixture { basic_flip_id } + } + + /// Execute XCM from contract via CE + #[test] + fn test_ce_execute() { + MockNet::reset(); + + let Fixture { + basic_flip_id: contract_id, + } = xcm_flipper_fixture(); + + // + // check the execute + // ParaA::execute_with(|| { let transfer_amount = 100_000; // transfer some native to contract @@ -141,9 +158,19 @@ mod tests { ParachainBalances::balance(&ALICE.into()) == alice_balance_before + transfer_amount ); }); + } + + /// Send the XCM and handle response callback via CE + #[test] + fn test_ce_wasm_callback() { + MockNet::reset(); + + let Fixture { + basic_flip_id: contract_id, + } = xcm_flipper_fixture(); // - // Check send & query + // Check send & callback query // ParaA::execute_with(|| { use parachain::{Runtime, RuntimeCall}; @@ -161,6 +188,7 @@ mod tests { }; let dest: VersionedMultiLocation = (Parent, Parachain(2)).into(); + // register the callback query let (res, _, _) = call_contract_method::< parachain::Runtime, Result, ()>, @@ -189,7 +217,7 @@ mod tests { SetAppendix(Xcm(vec![ReportTransactStatus(QueryResponseInfo { destination: (Parent, Parachain(1)).into(), query_id, - max_weight: GAS_LIMIT, + max_weight: parachain::CallbackGasLimit::get(), })])), Transact { origin_kind: OriginKind::SovereignAccount, @@ -253,169 +281,5 @@ mod tests { ); assert_eq!(res, Ok(true)); }); - - // ParaA::execute_with(|| { - // use parachain::XcmTransact; - // // dispatch call to flip contract - // // let call = parachain::RuntimeCall::Contracts(pallet_contracts::Call::call { - // // dest: contract_id.clone(), - // // value: 0, - // // gas_limit: Weight::from_parts(100_000_000_000, 1024 * 1024), - // // storage_deposit_limit: None, - // // data: SELECTOR_FLIP.to_vec(), - // // }); - // let call = parachain::RuntimeCall::System(frame_system::Call::remark_with_event { - // remark: vec![1, 2, 3, 4], - // }); - - // let query_id = XcmTransact::new_query( - // QueryConfig { - // query_type: QueryType::WASMContractCallback { - // contract_id: contract_id.clone(), - // selector: SELECTOR_XCM_FLIP, - // }, - // timeout: Bounded::max_value(), - // }, - // AccountId32 { - // id: ALICE.into(), - // network: Some(Kusama), - // } - // .into(), - // // Here, - // (Parent, Parachain(2)), - // ) - // .unwrap(); - - // let xcm: Xcm<()> = Xcm(vec![ - // WithdrawAsset((Here, INITIAL_BALANCE).into()), - // BuyExecution { - // fees: (Here, INITIAL_BALANCE).into(), - // weight_limit: Unlimited, - // }, - // SetAppendix(Xcm(vec![ReportTransactStatus(QueryResponseInfo { - // destination: (Parent, Parachain(1)).into(), - // query_id, - // max_weight: GAS_LIMIT, - // })])), - // Transact { - // origin_kind: OriginKind::SovereignAccount, - // require_weight_at_most: Weight::from_parts(100_000_000_000_000, 1024 * 1024 * 1024), - // call: call.encode().into(), - // }, - // ]); - - // // send the XCM to ParaA - // assert_ok!(ParachainPalletXcm::send( - // parachain::RuntimeOrigin::signed(ALICE), - // // parachain::RuntimeOrigin::root(), - // Box::new((Parent, Parachain(2)).into()), - // Box::new(VersionedXcm::V3(xcm)), - // )); - - // use parachain::System; - // System::reset_events(); - // // println!("{:?}", System::events()); - // }); - - // ParaB::execute_with(|| { - - // // let outcome = ParachainContracts::bare_call( - // // ALICE.into(), - // // contract_id.clone(), - // // 0, - // // GAS_LIMIT, - // // None, - // // SELECTOR_GET.to_vec(), - // // true, - // // Determinism::Deterministic, - // // ); - // // let res = outcome.result.unwrap(); - // // // check for revert - // // assert!(res.did_revert() == false); - // // // decode the return value, it should be false - // // let flag = Result::::decode(&mut res.data.as_ref()).unwrap(); - // // assert_eq!(flag, Ok(false)); - // }); - - // ParaA::execute_with(|| { - // // use parachain::System; - // // println!("{:?}", System::events()); - - // let outcome = ParachainContracts::bare_call( - // ALICE.into(), - // contract_id.clone(), - // 0, - // GAS_LIMIT, - // None, - // SELECTOR_GET.to_vec(), - // true, - // Determinism::Deterministic, - // ); - // let res = outcome.result.unwrap(); - // // check for revert - // assert!(res.did_revert() == false); - // // decode the return value, it should be false - // let flag = Result::::decode(&mut res.data.as_ref()).unwrap(); - - // // println!("{:?}", ParachainPalletXcm::query(0)); - // // println!( - // // "expecting response = {:?}", - // // ParachainPalletXcm::expecting_response( - // // &(Parent, Parachain(2)).into(), - // // 0, - // // Some(&Here.into()) - // // ) - // // ); - - // // std::thread::sleep(std::time::Duration::from_secs(1)); - // assert_eq!(flag, Ok(true)); - // }); - - // ParaA::execute_with(|| { - // // use parachain::System; - // // println!("{:?}", System::events()); - // let xcm: VersionedXcm<()> = VersionedXcm::V3(Xcm(vec![ - // WithdrawAsset((Here, 10).into()), - // BuyExecution { - // fees: (Here, 10).into(), - // weight_limit: Unlimited, - // }, - // SetAppendix(Xcm(vec![ReportError(QueryResponseInfo { - // destination: (Parent, Parachain(1)).into(), - // query_id: 1, - // max_weight: GAS_LIMIT, - // })])), - // ])); - // let outcome = ParachainContracts::bare_call( - // ALICE.into(), - // contract_id.clone(), - // 0, - // GAS_LIMIT, - // None, - // [SELECTOR_TEST.to_vec(), xcm.encode()].concat(), - // true, - // Determinism::Deterministic, - // ); - // println!("outcome={outcome:?}"); - // println!("{:?}", String::from_utf8(outcome.debug_message)); - // let res = outcome.result.unwrap(); - // // check for revert - // assert!(res.did_revert() == false); - // // decode the return value, it should be false - // let out = Result::, ()>::decode(&mut res.data.as_ref()).unwrap(); - // println!("out={out:?}"); - // // println!("{:?}", ParachainPalletXcm::query(0)); - // // println!( - // // "expecting response = {:?}", - // // ParachainPalletXcm::expecting_response( - // // &(Parent, Parachain(2)).into(), - // // 0, - // // Some(&Here.into()) - // // ) - // // ); - - // // std::thread::sleep(std::time::Duration::from_secs(1)); - // // assert_eq!(flag, Ok(true)); - // }); } } From c275839c40af9bdeffbfa35707a64fdb3ef70edb Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Tue, 9 May 2023 07:59:31 +0530 Subject: [PATCH 07/15] wip: add cargo build script to compile contracts for fixtures --- Cargo.toml | 3 +- .../xcm-simulator/.gitignore | 1 + .../xcm-simulator/Cargo.toml | 9 + .../xcm-simulator/build.rs | 91 + .../xcm-simulator/fixtures/README.md | 5 - .../xcm-simulator/fixtures/xcm_flip.json | 5196 ----------------- .../xcm-simulator/fixtures/xcm_flip.wasm | Bin 76223 -> 0 bytes 7 files changed, 103 insertions(+), 5202 deletions(-) create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/.gitignore create mode 100644 frame/pallet-xcm-transactor/xcm-simulator/build.rs delete mode 100644 frame/pallet-xcm-transactor/xcm-simulator/fixtures/README.md delete mode 100644 frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.json delete mode 100644 frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.wasm diff --git a/Cargo.toml b/Cargo.toml index 907849b8..8be89924 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,8 @@ members = [ "frame/pallet-xcm-transactor", "frame/pallet-xcm-transactor/primitives", "frame/pallet-xcm-transactor/xcm-simulator", - # "frame/pallet-xcm-transactor/contracts-example", + "frame/pallet-xcm-transactor/ink-sdk", + # "frame/pallet-xcm-transactor/contract-examples/basic-flip", "primitives/xcm", "precompiles/assets-erc20", "precompiles/dapps-staking", diff --git a/frame/pallet-xcm-transactor/xcm-simulator/.gitignore b/frame/pallet-xcm-transactor/xcm-simulator/.gitignore new file mode 100644 index 00000000..116caa12 --- /dev/null +++ b/frame/pallet-xcm-transactor/xcm-simulator/.gitignore @@ -0,0 +1 @@ +fixtures diff --git a/frame/pallet-xcm-transactor/xcm-simulator/Cargo.toml b/frame/pallet-xcm-transactor/xcm-simulator/Cargo.toml index a2b9905f..71ca6123 100644 --- a/frame/pallet-xcm-transactor/xcm-simulator/Cargo.toml +++ b/frame/pallet-xcm-transactor/xcm-simulator/Cargo.toml @@ -48,6 +48,15 @@ xcm-simulator = { workspace = true } pallet-xcm-transactor = { path = "../", default-features = false } +[build-dependencies] +# Currently using patched version due to issue with `RUSTFLAGS` inside build script. +# cargo-build sets the `RUSTFLAGS` for adding linker flags which are not applied when +# invoking it inside a build script, thus contract compilation fails. +# Fix - use `CARGO_ENCODED_RUSTFLAGS` instead of `RUSTFLAGS` +# https://github.com/rust-lang/cargo/issues/10111 +# TODO: remove this once it is merged in upstream +contract-build = { git = "https://github.com/ashutoshvarma/cargo-contract", branch = "patch/fix-rustflags" } + [features] default = ["std"] std = [ diff --git a/frame/pallet-xcm-transactor/xcm-simulator/build.rs b/frame/pallet-xcm-transactor/xcm-simulator/build.rs new file mode 100644 index 00000000..5b8c935e --- /dev/null +++ b/frame/pallet-xcm-transactor/xcm-simulator/build.rs @@ -0,0 +1,91 @@ +// build.rs + +use std::{ + fs, + path::{Path, PathBuf}, +}; + +use contract_build::{ + BuildArtifacts, BuildMode, Features, ManifestPath, Network, OptimizationPasses, OutputType, + Target, UnstableFlags, Verbosity, +}; + +/// Execute the clousre with given directory as current dir +fn with_directory T>(f: F, dir: &Path) -> T { + let curr_dir = std::env::current_dir().unwrap(); + + std::env::set_current_dir(dir).unwrap(); + let res = f(); + std::env::set_current_dir(curr_dir).unwrap(); + + res +} + +/// Build the contracts and copy the artifacts to fixtures dir +fn build_contract(fixtures_dir: &Path, dir: &Path, name: &str) { + println!("[build.rs] Building Contract - {name}"); + + let build = with_directory( + || { + let manifest_path = ManifestPath::new("Cargo.toml").unwrap(); + + let args = contract_build::ExecuteArgs { + manifest_path, + verbosity: Verbosity::Verbose, + build_mode: BuildMode::Debug, + features: Features::default(), + network: Network::Online, + build_artifact: BuildArtifacts::All, + unstable_flags: UnstableFlags::default(), + optimization_passes: Some(OptimizationPasses::default()), + keep_debug_symbols: true, + lint: false, + output_type: OutputType::HumanReadable, + skip_wasm_validation: false, + target: Target::Wasm, + }; + + contract_build::execute(args).expect(&format!("Failed to build contract at - {dir:?}")) + }, + dir, + ); + + // copy wasm artifact + fs::copy( + build.dest_wasm.unwrap(), + fixtures_dir.join(format!("{name}.wasm")), + ) + .unwrap(); + + // copy metadata + fs::copy( + build.metadata_result.unwrap().dest_metadata, + fixtures_dir.join(format!("{name}.json")), + ) + .unwrap(); +} + +fn setup() -> (PathBuf, PathBuf) { + let fixture_env = std::env::var("CB_FIXTURES_DIR").unwrap_or("fixtures".to_string()); + let fixtures_dir = Path::new(&fixture_env); + + let contracts_env = + std::env::var("CB_CONTRACTS_DIR").unwrap_or("../contract-examples".to_string()); + let contracts_dir = Path::new(&contracts_env); + + // create fixtures dir if not exists + fs::create_dir_all(fixtures_dir).unwrap(); + + (fixtures_dir.to_path_buf(), contracts_dir.to_path_buf()) +} + +fn main() { + let (fixtures_dir, contracts_dir) = setup(); + build_contract( + &fixtures_dir, + &contracts_dir.join("basic-flip"), + "basic_flip", + ); + + println!("cargo:rerun-if-changed={}", contracts_dir.to_str().unwrap()); +} diff --git a/frame/pallet-xcm-transactor/xcm-simulator/fixtures/README.md b/frame/pallet-xcm-transactor/xcm-simulator/fixtures/README.md deleted file mode 100644 index de7b88ef..00000000 --- a/frame/pallet-xcm-transactor/xcm-simulator/fixtures/README.md +++ /dev/null @@ -1,5 +0,0 @@ -## fixtures -This directory contains contracts which are used for testing XCM CE. - -The json files are for informational purposes only and are not consumed by the tests. - diff --git a/frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.json b/frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.json deleted file mode 100644 index b05f6a5e..00000000 --- a/frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.json +++ /dev/null @@ -1,5196 +0,0 @@ -{ - "source": { - "hash": "0xb53f65b20a11c687464e0ecf95ed2abb4a3a41fd64ecfa8458b97cdfd5f8c01d", - "language": "ink! 4.1.0", - "compiler": "rustc 1.68.0-nightly", - "build_info": { - "build_mode": "Debug", - "cargo_contract_version": "2.0.0", - "rust_toolchain": "nightly-x86_64-unknown-linux-gnu", - "wasm_opt_settings": { - "keep_debug_symbols": false, - "optimization_passes": "Z" - } - } - }, - "contract": { - "name": "contracts-example", - "version": "0.1.0", - "authors": [ - "[your_name] <[your_email]>" - ] - }, - "spec": { - "constructors": [ - { - "args": [], - "default": false, - "docs": [], - "label": "default", - "payable": false, - "returnType": { - "displayName": [ - "ink_primitives", - "ConstructorResult" - ], - "type": 1 - }, - "selector": "0xffffffff" - } - ], - "docs": [], - "environment": { - "accountId": { - "displayName": [ - "AccountId" - ], - "type": 86 - }, - "balance": { - "displayName": [ - "Balance" - ], - "type": 26 - }, - "blockNumber": { - "displayName": [ - "BlockNumber" - ], - "type": 17 - }, - "chainExtension": { - "displayName": [ - "ChainExtension" - ], - "type": 95 - }, - "hash": { - "displayName": [ - "Hash" - ], - "type": 94 - }, - "maxEventTopics": 4, - "timestamp": { - "displayName": [ - "Timestamp" - ], - "type": 23 - } - }, - "events": [], - "lang_error": { - "displayName": [ - "ink", - "LangError" - ], - "type": 3 - }, - "messages": [ - { - "args": [ - { - "label": "xcm", - "type": { - "displayName": [ - "VersionedXcm" - ], - "type": 4 - } - } - ], - "default": false, - "docs": [], - "label": "execute", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "ink", - "MessageResult" - ], - "type": 77 - }, - "selector": "0x11111111" - }, - { - "args": [ - { - "label": "input", - "type": { - "displayName": [ - "ValidateSendInput" - ], - "type": 80 - } - } - ], - "default": false, - "docs": [], - "label": "send", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "ink", - "MessageResult" - ], - "type": 82 - }, - "selector": "0x22222222" - }, - { - "args": [ - { - "label": "config", - "type": { - "displayName": [ - "QueryConfig" - ], - "type": 85 - } - }, - { - "label": "dest", - "type": { - "displayName": [ - "VersionedMultiLocation" - ], - "type": 81 - } - } - ], - "default": false, - "docs": [], - "label": "query", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "ink", - "MessageResult" - ], - "type": 89 - }, - "selector": "0x33333333" - }, - { - "args": [ - { - "label": "query_id", - "type": { - "displayName": [ - "QueryId" - ], - "type": 23 - } - } - ], - "default": false, - "docs": [], - "label": "poll_response", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "ink", - "MessageResult" - ], - "type": 91 - }, - "selector": "0x44444444" - }, - { - "args": [ - { - "label": "query_id", - "type": { - "displayName": [ - "QueryId" - ], - "type": 23 - } - }, - { - "label": "_responder", - "type": { - "displayName": [ - "MultiLocation" - ], - "type": 51 - } - }, - { - "label": "_response", - "type": { - "displayName": [ - "Response" - ], - "type": 60 - } - } - ], - "default": false, - "docs": [], - "label": "handle_response", - "mutates": true, - "payable": false, - "returnType": { - "displayName": [ - "ink", - "MessageResult" - ], - "type": 1 - }, - "selector": "0x55555555" - }, - { - "args": [], - "default": false, - "docs": [], - "label": "get", - "mutates": false, - "payable": false, - "returnType": { - "displayName": [ - "ink", - "MessageResult" - ], - "type": 93 - }, - "selector": "0x66666666" - } - ] - }, - "storage": { - "root": { - "layout": { - "struct": { - "fields": [ - { - "layout": { - "leaf": { - "key": "0x00000000", - "ty": 0 - } - }, - "name": "value" - }, - { - "layout": { - "root": { - "layout": { - "leaf": { - "key": "0x44b7479a", - "ty": 0 - } - }, - "root_key": "0x44b7479a" - } - }, - "name": "expecting_query" - } - ], - "name": "Contracts" - } - }, - "root_key": "0x00000000" - } - }, - "types": [ - { - "id": 0, - "type": { - "def": { - "primitive": "bool" - } - } - }, - { - "id": 1, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 2 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 3 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 2 - }, - { - "name": "E", - "type": 3 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 2, - "type": { - "def": { - "tuple": [] - } - } - }, - { - "id": 3, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 1, - "name": "CouldNotReadInput" - } - ] - } - }, - "path": [ - "ink_primitives", - "LangError" - ] - } - }, - { - "id": 4, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 5, - "typeName": "v2::Xcm" - } - ], - "index": 2, - "name": "V2" - }, - { - "fields": [ - { - "type": 44, - "typeName": "v3::Xcm" - } - ], - "index": 3, - "name": "V3" - } - ] - } - }, - "params": [ - { - "name": "RuntimeCall", - "type": null - } - ], - "path": [ - "xcm", - "VersionedXcm" - ] - } - }, - { - "id": 5, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 6, - "typeName": "Vec>" - } - ] - } - }, - "params": [ - { - "name": "RuntimeCall", - "type": null - } - ], - "path": [ - "xcm", - "v2", - "Xcm" - ] - } - }, - { - "id": 6, - "type": { - "def": { - "sequence": { - "type": 7 - } - } - } - }, - { - "id": 7, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 8, - "typeName": "MultiAssets" - } - ], - "index": 0, - "name": "WithdrawAsset" - }, - { - "fields": [ - { - "type": 8, - "typeName": "MultiAssets" - } - ], - "index": 1, - "name": "ReserveAssetDeposited" - }, - { - "fields": [ - { - "type": 8, - "typeName": "MultiAssets" - } - ], - "index": 2, - "name": "ReceiveTeleportedAsset" - }, - { - "fields": [ - { - "name": "query_id", - "type": 22, - "typeName": "QueryId" - }, - { - "name": "response", - "type": 34, - "typeName": "Response" - }, - { - "name": "max_weight", - "type": 22, - "typeName": "u64" - } - ], - "index": 3, - "name": "QueryResponse" - }, - { - "fields": [ - { - "name": "assets", - "type": 8, - "typeName": "MultiAssets" - }, - { - "name": "beneficiary", - "type": 12, - "typeName": "MultiLocation" - } - ], - "index": 4, - "name": "TransferAsset" - }, - { - "fields": [ - { - "name": "assets", - "type": 8, - "typeName": "MultiAssets" - }, - { - "name": "dest", - "type": 12, - "typeName": "MultiLocation" - }, - { - "name": "xcm", - "type": 5, - "typeName": "Xcm<()>" - } - ], - "index": 5, - "name": "TransferReserveAsset" - }, - { - "fields": [ - { - "name": "origin_type", - "type": 38, - "typeName": "OriginKind" - }, - { - "name": "require_weight_at_most", - "type": 22, - "typeName": "u64" - }, - { - "name": "call", - "type": 39, - "typeName": "DoubleEncoded" - } - ], - "index": 6, - "name": "Transact" - }, - { - "fields": [ - { - "name": "sender", - "type": 16, - "typeName": "u32" - }, - { - "name": "max_message_size", - "type": 16, - "typeName": "u32" - }, - { - "name": "max_capacity", - "type": 16, - "typeName": "u32" - } - ], - "index": 7, - "name": "HrmpNewChannelOpenRequest" - }, - { - "fields": [ - { - "name": "recipient", - "type": 16, - "typeName": "u32" - } - ], - "index": 8, - "name": "HrmpChannelAccepted" - }, - { - "fields": [ - { - "name": "initiator", - "type": 16, - "typeName": "u32" - }, - { - "name": "sender", - "type": 16, - "typeName": "u32" - }, - { - "name": "recipient", - "type": 16, - "typeName": "u32" - } - ], - "index": 9, - "name": "HrmpChannelClosing" - }, - { - "index": 10, - "name": "ClearOrigin" - }, - { - "fields": [ - { - "type": 14, - "typeName": "InteriorMultiLocation" - } - ], - "index": 11, - "name": "DescendOrigin" - }, - { - "fields": [ - { - "name": "query_id", - "type": 22, - "typeName": "QueryId" - }, - { - "name": "dest", - "type": 12, - "typeName": "MultiLocation" - }, - { - "name": "max_response_weight", - "type": 22, - "typeName": "u64" - } - ], - "index": 12, - "name": "ReportError" - }, - { - "fields": [ - { - "name": "assets", - "type": 40, - "typeName": "MultiAssetFilter" - }, - { - "name": "max_assets", - "type": 16, - "typeName": "u32" - }, - { - "name": "beneficiary", - "type": 12, - "typeName": "MultiLocation" - } - ], - "index": 13, - "name": "DepositAsset" - }, - { - "fields": [ - { - "name": "assets", - "type": 40, - "typeName": "MultiAssetFilter" - }, - { - "name": "max_assets", - "type": 16, - "typeName": "u32" - }, - { - "name": "dest", - "type": 12, - "typeName": "MultiLocation" - }, - { - "name": "xcm", - "type": 5, - "typeName": "Xcm<()>" - } - ], - "index": 14, - "name": "DepositReserveAsset" - }, - { - "fields": [ - { - "name": "give", - "type": 40, - "typeName": "MultiAssetFilter" - }, - { - "name": "receive", - "type": 8, - "typeName": "MultiAssets" - } - ], - "index": 15, - "name": "ExchangeAsset" - }, - { - "fields": [ - { - "name": "assets", - "type": 40, - "typeName": "MultiAssetFilter" - }, - { - "name": "reserve", - "type": 12, - "typeName": "MultiLocation" - }, - { - "name": "xcm", - "type": 5, - "typeName": "Xcm<()>" - } - ], - "index": 16, - "name": "InitiateReserveWithdraw" - }, - { - "fields": [ - { - "name": "assets", - "type": 40, - "typeName": "MultiAssetFilter" - }, - { - "name": "dest", - "type": 12, - "typeName": "MultiLocation" - }, - { - "name": "xcm", - "type": 5, - "typeName": "Xcm<()>" - } - ], - "index": 17, - "name": "InitiateTeleport" - }, - { - "fields": [ - { - "name": "query_id", - "type": 22, - "typeName": "QueryId" - }, - { - "name": "dest", - "type": 12, - "typeName": "MultiLocation" - }, - { - "name": "assets", - "type": 40, - "typeName": "MultiAssetFilter" - }, - { - "name": "max_response_weight", - "type": 22, - "typeName": "u64" - } - ], - "index": 18, - "name": "QueryHolding" - }, - { - "fields": [ - { - "name": "fees", - "type": 10, - "typeName": "MultiAsset" - }, - { - "name": "weight_limit", - "type": 43, - "typeName": "WeightLimit" - } - ], - "index": 19, - "name": "BuyExecution" - }, - { - "index": 20, - "name": "RefundSurplus" - }, - { - "fields": [ - { - "type": 5, - "typeName": "Xcm" - } - ], - "index": 21, - "name": "SetErrorHandler" - }, - { - "fields": [ - { - "type": 5, - "typeName": "Xcm" - } - ], - "index": 22, - "name": "SetAppendix" - }, - { - "index": 23, - "name": "ClearError" - }, - { - "fields": [ - { - "name": "assets", - "type": 8, - "typeName": "MultiAssets" - }, - { - "name": "ticket", - "type": 12, - "typeName": "MultiLocation" - } - ], - "index": 24, - "name": "ClaimAsset" - }, - { - "fields": [ - { - "type": 22, - "typeName": "u64" - } - ], - "index": 25, - "name": "Trap" - }, - { - "fields": [ - { - "name": "query_id", - "type": 22, - "typeName": "QueryId" - }, - { - "name": "max_response_weight", - "type": 22, - "typeName": "u64" - } - ], - "index": 26, - "name": "SubscribeVersion" - }, - { - "index": 27, - "name": "UnsubscribeVersion" - } - ] - } - }, - "params": [ - { - "name": "RuntimeCall", - "type": null - } - ], - "path": [ - "xcm", - "v2", - "Instruction" - ] - } - }, - { - "id": 8, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 9, - "typeName": "Vec" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "multiasset", - "MultiAssets" - ] - } - }, - { - "id": 9, - "type": { - "def": { - "sequence": { - "type": 10 - } - } - } - }, - { - "id": 10, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "id", - "type": 11, - "typeName": "AssetId" - }, - { - "name": "fun", - "type": 29, - "typeName": "Fungibility" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "multiasset", - "MultiAsset" - ] - } - }, - { - "id": 11, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 12, - "typeName": "MultiLocation" - } - ], - "index": 0, - "name": "Concrete" - }, - { - "fields": [ - { - "type": 20, - "typeName": "Vec" - } - ], - "index": 1, - "name": "Abstract" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "multiasset", - "AssetId" - ] - } - }, - { - "id": 12, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "parents", - "type": 13, - "typeName": "u8" - }, - { - "name": "interior", - "type": 14, - "typeName": "Junctions" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "multilocation", - "MultiLocation" - ] - } - }, - { - "id": 13, - "type": { - "def": { - "primitive": "u8" - } - } - }, - { - "id": 14, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Here" - }, - { - "fields": [ - { - "type": 15, - "typeName": "Junction" - } - ], - "index": 1, - "name": "X1" - }, - { - "fields": [ - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - } - ], - "index": 2, - "name": "X2" - }, - { - "fields": [ - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - } - ], - "index": 3, - "name": "X3" - }, - { - "fields": [ - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - } - ], - "index": 4, - "name": "X4" - }, - { - "fields": [ - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - } - ], - "index": 5, - "name": "X5" - }, - { - "fields": [ - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - } - ], - "index": 6, - "name": "X6" - }, - { - "fields": [ - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - } - ], - "index": 7, - "name": "X7" - }, - { - "fields": [ - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - }, - { - "type": 15, - "typeName": "Junction" - } - ], - "index": 8, - "name": "X8" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "multilocation", - "Junctions" - ] - } - }, - { - "id": 15, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 16, - "typeName": "u32" - } - ], - "index": 0, - "name": "Parachain" - }, - { - "fields": [ - { - "name": "network", - "type": 18, - "typeName": "NetworkId" - }, - { - "name": "id", - "type": 21, - "typeName": "[u8; 32]" - } - ], - "index": 1, - "name": "AccountId32" - }, - { - "fields": [ - { - "name": "network", - "type": 18, - "typeName": "NetworkId" - }, - { - "name": "index", - "type": 22, - "typeName": "u64" - } - ], - "index": 2, - "name": "AccountIndex64" - }, - { - "fields": [ - { - "name": "network", - "type": 18, - "typeName": "NetworkId" - }, - { - "name": "key", - "type": 24, - "typeName": "[u8; 20]" - } - ], - "index": 3, - "name": "AccountKey20" - }, - { - "fields": [ - { - "type": 13, - "typeName": "u8" - } - ], - "index": 4, - "name": "PalletInstance" - }, - { - "fields": [ - { - "type": 25, - "typeName": "u128" - } - ], - "index": 5, - "name": "GeneralIndex" - }, - { - "fields": [ - { - "type": 19, - "typeName": "WeakBoundedVec>" - } - ], - "index": 6, - "name": "GeneralKey" - }, - { - "index": 7, - "name": "OnlyChild" - }, - { - "fields": [ - { - "name": "id", - "type": 27, - "typeName": "BodyId" - }, - { - "name": "part", - "type": 28, - "typeName": "BodyPart" - } - ], - "index": 8, - "name": "Plurality" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "junction", - "Junction" - ] - } - }, - { - "id": 16, - "type": { - "def": { - "compact": { - "type": 17 - } - } - } - }, - { - "id": 17, - "type": { - "def": { - "primitive": "u32" - } - } - }, - { - "id": 18, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Any" - }, - { - "fields": [ - { - "type": 19, - "typeName": "WeakBoundedVec>" - } - ], - "index": 1, - "name": "Named" - }, - { - "index": 2, - "name": "Polkadot" - }, - { - "index": 3, - "name": "Kusama" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "NetworkId" - ] - } - }, - { - "id": 19, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 20, - "typeName": "Vec" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 13 - }, - { - "name": "S", - "type": null - } - ], - "path": [ - "bounded_collections", - "weak_bounded_vec", - "WeakBoundedVec" - ] - } - }, - { - "id": 20, - "type": { - "def": { - "sequence": { - "type": 13 - } - } - } - }, - { - "id": 21, - "type": { - "def": { - "array": { - "len": 32, - "type": 13 - } - } - } - }, - { - "id": 22, - "type": { - "def": { - "compact": { - "type": 23 - } - } - } - }, - { - "id": 23, - "type": { - "def": { - "primitive": "u64" - } - } - }, - { - "id": 24, - "type": { - "def": { - "array": { - "len": 20, - "type": 13 - } - } - } - }, - { - "id": 25, - "type": { - "def": { - "compact": { - "type": 26 - } - } - } - }, - { - "id": 26, - "type": { - "def": { - "primitive": "u128" - } - } - }, - { - "id": 27, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Unit" - }, - { - "fields": [ - { - "type": 19, - "typeName": "WeakBoundedVec>" - } - ], - "index": 1, - "name": "Named" - }, - { - "fields": [ - { - "type": 16, - "typeName": "u32" - } - ], - "index": 2, - "name": "Index" - }, - { - "index": 3, - "name": "Executive" - }, - { - "index": 4, - "name": "Technical" - }, - { - "index": 5, - "name": "Legislative" - }, - { - "index": 6, - "name": "Judicial" - }, - { - "index": 7, - "name": "Defense" - }, - { - "index": 8, - "name": "Administration" - }, - { - "index": 9, - "name": "Treasury" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "BodyId" - ] - } - }, - { - "id": 28, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Voice" - }, - { - "fields": [ - { - "name": "count", - "type": 16, - "typeName": "u32" - } - ], - "index": 1, - "name": "Members" - }, - { - "fields": [ - { - "name": "nom", - "type": 16, - "typeName": "u32" - }, - { - "name": "denom", - "type": 16, - "typeName": "u32" - } - ], - "index": 2, - "name": "Fraction" - }, - { - "fields": [ - { - "name": "nom", - "type": 16, - "typeName": "u32" - }, - { - "name": "denom", - "type": 16, - "typeName": "u32" - } - ], - "index": 3, - "name": "AtLeastProportion" - }, - { - "fields": [ - { - "name": "nom", - "type": 16, - "typeName": "u32" - }, - { - "name": "denom", - "type": 16, - "typeName": "u32" - } - ], - "index": 4, - "name": "MoreThanProportion" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "BodyPart" - ] - } - }, - { - "id": 29, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 25, - "typeName": "u128" - } - ], - "index": 0, - "name": "Fungible" - }, - { - "fields": [ - { - "type": 30, - "typeName": "AssetInstance" - } - ], - "index": 1, - "name": "NonFungible" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "multiasset", - "Fungibility" - ] - } - }, - { - "id": 30, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Undefined" - }, - { - "fields": [ - { - "type": 25, - "typeName": "u128" - } - ], - "index": 1, - "name": "Index" - }, - { - "fields": [ - { - "type": 31, - "typeName": "[u8; 4]" - } - ], - "index": 2, - "name": "Array4" - }, - { - "fields": [ - { - "type": 32, - "typeName": "[u8; 8]" - } - ], - "index": 3, - "name": "Array8" - }, - { - "fields": [ - { - "type": 33, - "typeName": "[u8; 16]" - } - ], - "index": 4, - "name": "Array16" - }, - { - "fields": [ - { - "type": 21, - "typeName": "[u8; 32]" - } - ], - "index": 5, - "name": "Array32" - }, - { - "fields": [ - { - "type": 20, - "typeName": "Vec" - } - ], - "index": 6, - "name": "Blob" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "multiasset", - "AssetInstance" - ] - } - }, - { - "id": 31, - "type": { - "def": { - "array": { - "len": 4, - "type": 13 - } - } - } - }, - { - "id": 32, - "type": { - "def": { - "array": { - "len": 8, - "type": 13 - } - } - } - }, - { - "id": 33, - "type": { - "def": { - "array": { - "len": 16, - "type": 13 - } - } - } - }, - { - "id": 34, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Null" - }, - { - "fields": [ - { - "type": 8, - "typeName": "MultiAssets" - } - ], - "index": 1, - "name": "Assets" - }, - { - "fields": [ - { - "type": 35, - "typeName": "Option<(u32, Error)>" - } - ], - "index": 2, - "name": "ExecutionResult" - }, - { - "fields": [ - { - "type": 17, - "typeName": "super::Version" - } - ], - "index": 3, - "name": "Version" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "Response" - ] - } - }, - { - "id": 35, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "None" - }, - { - "fields": [ - { - "type": 36 - } - ], - "index": 1, - "name": "Some" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 36 - } - ], - "path": [ - "Option" - ] - } - }, - { - "id": 36, - "type": { - "def": { - "tuple": [ - 17, - 37 - ] - } - } - }, - { - "id": 37, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Overflow" - }, - { - "index": 1, - "name": "Unimplemented" - }, - { - "index": 2, - "name": "UntrustedReserveLocation" - }, - { - "index": 3, - "name": "UntrustedTeleportLocation" - }, - { - "index": 4, - "name": "MultiLocationFull" - }, - { - "index": 5, - "name": "MultiLocationNotInvertible" - }, - { - "index": 6, - "name": "BadOrigin" - }, - { - "index": 7, - "name": "InvalidLocation" - }, - { - "index": 8, - "name": "AssetNotFound" - }, - { - "index": 9, - "name": "FailedToTransactAsset" - }, - { - "index": 10, - "name": "NotWithdrawable" - }, - { - "index": 11, - "name": "LocationCannotHold" - }, - { - "index": 12, - "name": "ExceedsMaxMessageSize" - }, - { - "index": 13, - "name": "DestinationUnsupported" - }, - { - "index": 14, - "name": "Transport" - }, - { - "index": 15, - "name": "Unroutable" - }, - { - "index": 16, - "name": "UnknownClaim" - }, - { - "index": 17, - "name": "FailedToDecode" - }, - { - "index": 18, - "name": "MaxWeightInvalid" - }, - { - "index": 19, - "name": "NotHoldingFees" - }, - { - "index": 20, - "name": "TooExpensive" - }, - { - "fields": [ - { - "type": 23, - "typeName": "u64" - } - ], - "index": 21, - "name": "Trap" - }, - { - "index": 22, - "name": "UnhandledXcmVersion" - }, - { - "fields": [ - { - "type": 23, - "typeName": "Weight" - } - ], - "index": 23, - "name": "WeightLimitReached" - }, - { - "index": 24, - "name": "Barrier" - }, - { - "index": 25, - "name": "WeightNotComputable" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "traits", - "Error" - ] - } - }, - { - "id": 38, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Native" - }, - { - "index": 1, - "name": "SovereignAccount" - }, - { - "index": 2, - "name": "Superuser" - }, - { - "index": 3, - "name": "Xcm" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "OriginKind" - ] - } - }, - { - "id": 39, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "encoded", - "type": 20, - "typeName": "Vec" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": null - } - ], - "path": [ - "xcm", - "double_encoded", - "DoubleEncoded" - ] - } - }, - { - "id": 40, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 8, - "typeName": "MultiAssets" - } - ], - "index": 0, - "name": "Definite" - }, - { - "fields": [ - { - "type": 41, - "typeName": "WildMultiAsset" - } - ], - "index": 1, - "name": "Wild" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "multiasset", - "MultiAssetFilter" - ] - } - }, - { - "id": 41, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "All" - }, - { - "fields": [ - { - "name": "id", - "type": 11, - "typeName": "AssetId" - }, - { - "name": "fun", - "type": 42, - "typeName": "WildFungibility" - } - ], - "index": 1, - "name": "AllOf" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "multiasset", - "WildMultiAsset" - ] - } - }, - { - "id": 42, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Fungible" - }, - { - "index": 1, - "name": "NonFungible" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "multiasset", - "WildFungibility" - ] - } - }, - { - "id": 43, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Unlimited" - }, - { - "fields": [ - { - "type": 22, - "typeName": "u64" - } - ], - "index": 1, - "name": "Limited" - } - ] - } - }, - "path": [ - "xcm", - "v2", - "WeightLimit" - ] - } - }, - { - "id": 44, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 45, - "typeName": "Vec>" - } - ] - } - }, - "params": [ - { - "name": "Call", - "type": null - } - ], - "path": [ - "xcm", - "v3", - "Xcm" - ] - } - }, - { - "id": 45, - "type": { - "def": { - "sequence": { - "type": 46 - } - } - } - }, - { - "id": 46, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 47, - "typeName": "MultiAssets" - } - ], - "index": 0, - "name": "WithdrawAsset" - }, - { - "fields": [ - { - "type": 47, - "typeName": "MultiAssets" - } - ], - "index": 1, - "name": "ReserveAssetDeposited" - }, - { - "fields": [ - { - "type": 47, - "typeName": "MultiAssets" - } - ], - "index": 2, - "name": "ReceiveTeleportedAsset" - }, - { - "fields": [ - { - "name": "query_id", - "type": 22, - "typeName": "QueryId" - }, - { - "name": "response", - "type": 60, - "typeName": "Response" - }, - { - "name": "max_weight", - "type": 64, - "typeName": "Weight" - }, - { - "name": "querier", - "type": 71, - "typeName": "Option" - } - ], - "index": 3, - "name": "QueryResponse" - }, - { - "fields": [ - { - "name": "assets", - "type": 47, - "typeName": "MultiAssets" - }, - { - "name": "beneficiary", - "type": 51, - "typeName": "MultiLocation" - } - ], - "index": 4, - "name": "TransferAsset" - }, - { - "fields": [ - { - "name": "assets", - "type": 47, - "typeName": "MultiAssets" - }, - { - "name": "dest", - "type": 51, - "typeName": "MultiLocation" - }, - { - "name": "xcm", - "type": 44, - "typeName": "Xcm<()>" - } - ], - "index": 5, - "name": "TransferReserveAsset" - }, - { - "fields": [ - { - "name": "origin_kind", - "type": 38, - "typeName": "OriginKind" - }, - { - "name": "require_weight_at_most", - "type": 64, - "typeName": "Weight" - }, - { - "name": "call", - "type": 39, - "typeName": "DoubleEncoded" - } - ], - "index": 6, - "name": "Transact" - }, - { - "fields": [ - { - "name": "sender", - "type": 16, - "typeName": "u32" - }, - { - "name": "max_message_size", - "type": 16, - "typeName": "u32" - }, - { - "name": "max_capacity", - "type": 16, - "typeName": "u32" - } - ], - "index": 7, - "name": "HrmpNewChannelOpenRequest" - }, - { - "fields": [ - { - "name": "recipient", - "type": 16, - "typeName": "u32" - } - ], - "index": 8, - "name": "HrmpChannelAccepted" - }, - { - "fields": [ - { - "name": "initiator", - "type": 16, - "typeName": "u32" - }, - { - "name": "sender", - "type": 16, - "typeName": "u32" - }, - { - "name": "recipient", - "type": 16, - "typeName": "u32" - } - ], - "index": 9, - "name": "HrmpChannelClosing" - }, - { - "index": 10, - "name": "ClearOrigin" - }, - { - "fields": [ - { - "type": 52, - "typeName": "InteriorMultiLocation" - } - ], - "index": 11, - "name": "DescendOrigin" - }, - { - "fields": [ - { - "type": 72, - "typeName": "QueryResponseInfo" - } - ], - "index": 12, - "name": "ReportError" - }, - { - "fields": [ - { - "name": "assets", - "type": 73, - "typeName": "MultiAssetFilter" - }, - { - "name": "beneficiary", - "type": 51, - "typeName": "MultiLocation" - } - ], - "index": 13, - "name": "DepositAsset" - }, - { - "fields": [ - { - "name": "assets", - "type": 73, - "typeName": "MultiAssetFilter" - }, - { - "name": "dest", - "type": 51, - "typeName": "MultiLocation" - }, - { - "name": "xcm", - "type": 44, - "typeName": "Xcm<()>" - } - ], - "index": 14, - "name": "DepositReserveAsset" - }, - { - "fields": [ - { - "name": "give", - "type": 73, - "typeName": "MultiAssetFilter" - }, - { - "name": "want", - "type": 47, - "typeName": "MultiAssets" - }, - { - "name": "maximal", - "type": 0, - "typeName": "bool" - } - ], - "index": 15, - "name": "ExchangeAsset" - }, - { - "fields": [ - { - "name": "assets", - "type": 73, - "typeName": "MultiAssetFilter" - }, - { - "name": "reserve", - "type": 51, - "typeName": "MultiLocation" - }, - { - "name": "xcm", - "type": 44, - "typeName": "Xcm<()>" - } - ], - "index": 16, - "name": "InitiateReserveWithdraw" - }, - { - "fields": [ - { - "name": "assets", - "type": 73, - "typeName": "MultiAssetFilter" - }, - { - "name": "dest", - "type": 51, - "typeName": "MultiLocation" - }, - { - "name": "xcm", - "type": 44, - "typeName": "Xcm<()>" - } - ], - "index": 17, - "name": "InitiateTeleport" - }, - { - "fields": [ - { - "name": "response_info", - "type": 72, - "typeName": "QueryResponseInfo" - }, - { - "name": "assets", - "type": 73, - "typeName": "MultiAssetFilter" - } - ], - "index": 18, - "name": "ReportHolding" - }, - { - "fields": [ - { - "name": "fees", - "type": 49, - "typeName": "MultiAsset" - }, - { - "name": "weight_limit", - "type": 76, - "typeName": "WeightLimit" - } - ], - "index": 19, - "name": "BuyExecution" - }, - { - "index": 20, - "name": "RefundSurplus" - }, - { - "fields": [ - { - "type": 44, - "typeName": "Xcm" - } - ], - "index": 21, - "name": "SetErrorHandler" - }, - { - "fields": [ - { - "type": 44, - "typeName": "Xcm" - } - ], - "index": 22, - "name": "SetAppendix" - }, - { - "index": 23, - "name": "ClearError" - }, - { - "fields": [ - { - "name": "assets", - "type": 47, - "typeName": "MultiAssets" - }, - { - "name": "ticket", - "type": 51, - "typeName": "MultiLocation" - } - ], - "index": 24, - "name": "ClaimAsset" - }, - { - "fields": [ - { - "type": 22, - "typeName": "u64" - } - ], - "index": 25, - "name": "Trap" - }, - { - "fields": [ - { - "name": "query_id", - "type": 22, - "typeName": "QueryId" - }, - { - "name": "max_response_weight", - "type": 64, - "typeName": "Weight" - } - ], - "index": 26, - "name": "SubscribeVersion" - }, - { - "index": 27, - "name": "UnsubscribeVersion" - }, - { - "fields": [ - { - "type": 47, - "typeName": "MultiAssets" - } - ], - "index": 28, - "name": "BurnAsset" - }, - { - "fields": [ - { - "type": 47, - "typeName": "MultiAssets" - } - ], - "index": 29, - "name": "ExpectAsset" - }, - { - "fields": [ - { - "type": 71, - "typeName": "Option" - } - ], - "index": 30, - "name": "ExpectOrigin" - }, - { - "fields": [ - { - "type": 61, - "typeName": "Option<(u32, Error)>" - } - ], - "index": 31, - "name": "ExpectError" - }, - { - "fields": [ - { - "type": 69, - "typeName": "MaybeErrorCode" - } - ], - "index": 32, - "name": "ExpectTransactStatus" - }, - { - "fields": [ - { - "name": "module_name", - "type": 20, - "typeName": "Vec" - }, - { - "name": "response_info", - "type": 72, - "typeName": "QueryResponseInfo" - } - ], - "index": 33, - "name": "QueryPallet" - }, - { - "fields": [ - { - "name": "index", - "type": 16, - "typeName": "u32" - }, - { - "name": "name", - "type": 20, - "typeName": "Vec" - }, - { - "name": "module_name", - "type": 20, - "typeName": "Vec" - }, - { - "name": "crate_major", - "type": 16, - "typeName": "u32" - }, - { - "name": "min_crate_minor", - "type": 16, - "typeName": "u32" - } - ], - "index": 34, - "name": "ExpectPallet" - }, - { - "fields": [ - { - "type": 72, - "typeName": "QueryResponseInfo" - } - ], - "index": 35, - "name": "ReportTransactStatus" - }, - { - "index": 36, - "name": "ClearTransactStatus" - }, - { - "fields": [ - { - "type": 53, - "typeName": "Junction" - } - ], - "index": 37, - "name": "UniversalOrigin" - }, - { - "fields": [ - { - "name": "network", - "type": 55, - "typeName": "NetworkId" - }, - { - "name": "destination", - "type": 52, - "typeName": "InteriorMultiLocation" - }, - { - "name": "xcm", - "type": 44, - "typeName": "Xcm<()>" - } - ], - "index": 38, - "name": "ExportMessage" - }, - { - "fields": [ - { - "name": "asset", - "type": 49, - "typeName": "MultiAsset" - }, - { - "name": "unlocker", - "type": 51, - "typeName": "MultiLocation" - } - ], - "index": 39, - "name": "LockAsset" - }, - { - "fields": [ - { - "name": "asset", - "type": 49, - "typeName": "MultiAsset" - }, - { - "name": "target", - "type": 51, - "typeName": "MultiLocation" - } - ], - "index": 40, - "name": "UnlockAsset" - }, - { - "fields": [ - { - "name": "asset", - "type": 49, - "typeName": "MultiAsset" - }, - { - "name": "owner", - "type": 51, - "typeName": "MultiLocation" - } - ], - "index": 41, - "name": "NoteUnlockable" - }, - { - "fields": [ - { - "name": "asset", - "type": 49, - "typeName": "MultiAsset" - }, - { - "name": "locker", - "type": 51, - "typeName": "MultiLocation" - } - ], - "index": 42, - "name": "RequestUnlock" - }, - { - "fields": [ - { - "name": "jit_withdraw", - "type": 0, - "typeName": "bool" - } - ], - "index": 43, - "name": "SetFeesMode" - }, - { - "fields": [ - { - "type": 21, - "typeName": "[u8; 32]" - } - ], - "index": 44, - "name": "SetTopic" - }, - { - "index": 45, - "name": "ClearTopic" - }, - { - "fields": [ - { - "type": 51, - "typeName": "MultiLocation" - } - ], - "index": 46, - "name": "AliasOrigin" - }, - { - "fields": [ - { - "name": "weight_limit", - "type": 76, - "typeName": "WeightLimit" - }, - { - "name": "check_origin", - "type": 71, - "typeName": "Option" - } - ], - "index": 47, - "name": "UnpaidExecution" - } - ] - } - }, - "params": [ - { - "name": "Call", - "type": null - } - ], - "path": [ - "xcm", - "v3", - "Instruction" - ] - } - }, - { - "id": 47, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 48, - "typeName": "Vec" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "multiasset", - "MultiAssets" - ] - } - }, - { - "id": 48, - "type": { - "def": { - "sequence": { - "type": 49 - } - } - } - }, - { - "id": 49, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "id", - "type": 50, - "typeName": "AssetId" - }, - { - "name": "fun", - "type": 58, - "typeName": "Fungibility" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "multiasset", - "MultiAsset" - ] - } - }, - { - "id": 50, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 51, - "typeName": "MultiLocation" - } - ], - "index": 0, - "name": "Concrete" - }, - { - "fields": [ - { - "type": 21, - "typeName": "[u8; 32]" - } - ], - "index": 1, - "name": "Abstract" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "multiasset", - "AssetId" - ] - } - }, - { - "id": 51, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "parents", - "type": 13, - "typeName": "u8" - }, - { - "name": "interior", - "type": 52, - "typeName": "Junctions" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "multilocation", - "MultiLocation" - ] - } - }, - { - "id": 52, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Here" - }, - { - "fields": [ - { - "type": 53, - "typeName": "Junction" - } - ], - "index": 1, - "name": "X1" - }, - { - "fields": [ - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - } - ], - "index": 2, - "name": "X2" - }, - { - "fields": [ - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - } - ], - "index": 3, - "name": "X3" - }, - { - "fields": [ - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - } - ], - "index": 4, - "name": "X4" - }, - { - "fields": [ - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - } - ], - "index": 5, - "name": "X5" - }, - { - "fields": [ - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - } - ], - "index": 6, - "name": "X6" - }, - { - "fields": [ - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - } - ], - "index": 7, - "name": "X7" - }, - { - "fields": [ - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - }, - { - "type": 53, - "typeName": "Junction" - } - ], - "index": 8, - "name": "X8" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "junctions", - "Junctions" - ] - } - }, - { - "id": 53, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 16, - "typeName": "u32" - } - ], - "index": 0, - "name": "Parachain" - }, - { - "fields": [ - { - "name": "network", - "type": 54, - "typeName": "Option" - }, - { - "name": "id", - "type": 21, - "typeName": "[u8; 32]" - } - ], - "index": 1, - "name": "AccountId32" - }, - { - "fields": [ - { - "name": "network", - "type": 54, - "typeName": "Option" - }, - { - "name": "index", - "type": 22, - "typeName": "u64" - } - ], - "index": 2, - "name": "AccountIndex64" - }, - { - "fields": [ - { - "name": "network", - "type": 54, - "typeName": "Option" - }, - { - "name": "key", - "type": 24, - "typeName": "[u8; 20]" - } - ], - "index": 3, - "name": "AccountKey20" - }, - { - "fields": [ - { - "type": 13, - "typeName": "u8" - } - ], - "index": 4, - "name": "PalletInstance" - }, - { - "fields": [ - { - "type": 25, - "typeName": "u128" - } - ], - "index": 5, - "name": "GeneralIndex" - }, - { - "fields": [ - { - "name": "length", - "type": 13, - "typeName": "u8" - }, - { - "name": "data", - "type": 21, - "typeName": "[u8; 32]" - } - ], - "index": 6, - "name": "GeneralKey" - }, - { - "index": 7, - "name": "OnlyChild" - }, - { - "fields": [ - { - "name": "id", - "type": 56, - "typeName": "BodyId" - }, - { - "name": "part", - "type": 57, - "typeName": "BodyPart" - } - ], - "index": 8, - "name": "Plurality" - }, - { - "fields": [ - { - "type": 55, - "typeName": "NetworkId" - } - ], - "index": 9, - "name": "GlobalConsensus" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "junction", - "Junction" - ] - } - }, - { - "id": 54, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "None" - }, - { - "fields": [ - { - "type": 55 - } - ], - "index": 1, - "name": "Some" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 55 - } - ], - "path": [ - "Option" - ] - } - }, - { - "id": 55, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 21, - "typeName": "[u8; 32]" - } - ], - "index": 0, - "name": "ByGenesis" - }, - { - "fields": [ - { - "name": "block_number", - "type": 23, - "typeName": "u64" - }, - { - "name": "block_hash", - "type": 21, - "typeName": "[u8; 32]" - } - ], - "index": 1, - "name": "ByFork" - }, - { - "index": 2, - "name": "Polkadot" - }, - { - "index": 3, - "name": "Kusama" - }, - { - "index": 4, - "name": "Westend" - }, - { - "index": 5, - "name": "Rococo" - }, - { - "index": 6, - "name": "Wococo" - }, - { - "fields": [ - { - "name": "chain_id", - "type": 22, - "typeName": "u64" - } - ], - "index": 7, - "name": "Ethereum" - }, - { - "index": 8, - "name": "BitcoinCore" - }, - { - "index": 9, - "name": "BitcoinCash" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "junction", - "NetworkId" - ] - } - }, - { - "id": 56, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Unit" - }, - { - "fields": [ - { - "type": 31, - "typeName": "[u8; 4]" - } - ], - "index": 1, - "name": "Moniker" - }, - { - "fields": [ - { - "type": 16, - "typeName": "u32" - } - ], - "index": 2, - "name": "Index" - }, - { - "index": 3, - "name": "Executive" - }, - { - "index": 4, - "name": "Technical" - }, - { - "index": 5, - "name": "Legislative" - }, - { - "index": 6, - "name": "Judicial" - }, - { - "index": 7, - "name": "Defense" - }, - { - "index": 8, - "name": "Administration" - }, - { - "index": 9, - "name": "Treasury" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "junction", - "BodyId" - ] - } - }, - { - "id": 57, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Voice" - }, - { - "fields": [ - { - "name": "count", - "type": 16, - "typeName": "u32" - } - ], - "index": 1, - "name": "Members" - }, - { - "fields": [ - { - "name": "nom", - "type": 16, - "typeName": "u32" - }, - { - "name": "denom", - "type": 16, - "typeName": "u32" - } - ], - "index": 2, - "name": "Fraction" - }, - { - "fields": [ - { - "name": "nom", - "type": 16, - "typeName": "u32" - }, - { - "name": "denom", - "type": 16, - "typeName": "u32" - } - ], - "index": 3, - "name": "AtLeastProportion" - }, - { - "fields": [ - { - "name": "nom", - "type": 16, - "typeName": "u32" - }, - { - "name": "denom", - "type": 16, - "typeName": "u32" - } - ], - "index": 4, - "name": "MoreThanProportion" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "junction", - "BodyPart" - ] - } - }, - { - "id": 58, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 25, - "typeName": "u128" - } - ], - "index": 0, - "name": "Fungible" - }, - { - "fields": [ - { - "type": 59, - "typeName": "AssetInstance" - } - ], - "index": 1, - "name": "NonFungible" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "multiasset", - "Fungibility" - ] - } - }, - { - "id": 59, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Undefined" - }, - { - "fields": [ - { - "type": 25, - "typeName": "u128" - } - ], - "index": 1, - "name": "Index" - }, - { - "fields": [ - { - "type": 31, - "typeName": "[u8; 4]" - } - ], - "index": 2, - "name": "Array4" - }, - { - "fields": [ - { - "type": 32, - "typeName": "[u8; 8]" - } - ], - "index": 3, - "name": "Array8" - }, - { - "fields": [ - { - "type": 33, - "typeName": "[u8; 16]" - } - ], - "index": 4, - "name": "Array16" - }, - { - "fields": [ - { - "type": 21, - "typeName": "[u8; 32]" - } - ], - "index": 5, - "name": "Array32" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "multiasset", - "AssetInstance" - ] - } - }, - { - "id": 60, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Null" - }, - { - "fields": [ - { - "type": 47, - "typeName": "MultiAssets" - } - ], - "index": 1, - "name": "Assets" - }, - { - "fields": [ - { - "type": 61, - "typeName": "Option<(u32, Error)>" - } - ], - "index": 2, - "name": "ExecutionResult" - }, - { - "fields": [ - { - "type": 17, - "typeName": "super::Version" - } - ], - "index": 3, - "name": "Version" - }, - { - "fields": [ - { - "type": 65, - "typeName": "BoundedVec" - } - ], - "index": 4, - "name": "PalletsInfo" - }, - { - "fields": [ - { - "type": 69, - "typeName": "MaybeErrorCode" - } - ], - "index": 5, - "name": "DispatchResult" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "Response" - ] - } - }, - { - "id": 61, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "None" - }, - { - "fields": [ - { - "type": 62 - } - ], - "index": 1, - "name": "Some" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 62 - } - ], - "path": [ - "Option" - ] - } - }, - { - "id": 62, - "type": { - "def": { - "tuple": [ - 17, - 63 - ] - } - } - }, - { - "id": 63, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Overflow" - }, - { - "index": 1, - "name": "Unimplemented" - }, - { - "index": 2, - "name": "UntrustedReserveLocation" - }, - { - "index": 3, - "name": "UntrustedTeleportLocation" - }, - { - "index": 4, - "name": "LocationFull" - }, - { - "index": 5, - "name": "LocationNotInvertible" - }, - { - "index": 6, - "name": "BadOrigin" - }, - { - "index": 7, - "name": "InvalidLocation" - }, - { - "index": 8, - "name": "AssetNotFound" - }, - { - "index": 9, - "name": "FailedToTransactAsset" - }, - { - "index": 10, - "name": "NotWithdrawable" - }, - { - "index": 11, - "name": "LocationCannotHold" - }, - { - "index": 12, - "name": "ExceedsMaxMessageSize" - }, - { - "index": 13, - "name": "DestinationUnsupported" - }, - { - "index": 14, - "name": "Transport" - }, - { - "index": 15, - "name": "Unroutable" - }, - { - "index": 16, - "name": "UnknownClaim" - }, - { - "index": 17, - "name": "FailedToDecode" - }, - { - "index": 18, - "name": "MaxWeightInvalid" - }, - { - "index": 19, - "name": "NotHoldingFees" - }, - { - "index": 20, - "name": "TooExpensive" - }, - { - "fields": [ - { - "type": 23, - "typeName": "u64" - } - ], - "index": 21, - "name": "Trap" - }, - { - "index": 22, - "name": "ExpectationFalse" - }, - { - "index": 23, - "name": "PalletNotFound" - }, - { - "index": 24, - "name": "NameMismatch" - }, - { - "index": 25, - "name": "VersionIncompatible" - }, - { - "index": 26, - "name": "HoldingWouldOverflow" - }, - { - "index": 27, - "name": "ExportError" - }, - { - "index": 28, - "name": "ReanchorFailed" - }, - { - "index": 29, - "name": "NoDeal" - }, - { - "index": 30, - "name": "FeesNotMet" - }, - { - "index": 31, - "name": "LockError" - }, - { - "index": 32, - "name": "NoPermission" - }, - { - "index": 33, - "name": "Unanchored" - }, - { - "index": 34, - "name": "NotDepositable" - }, - { - "index": 35, - "name": "UnhandledXcmVersion" - }, - { - "fields": [ - { - "type": 64, - "typeName": "Weight" - } - ], - "index": 36, - "name": "WeightLimitReached" - }, - { - "index": 37, - "name": "Barrier" - }, - { - "index": 38, - "name": "WeightNotComputable" - }, - { - "index": 39, - "name": "ExceedsStackLimit" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "traits", - "Error" - ] - } - }, - { - "id": 64, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "ref_time", - "type": 22, - "typeName": "u64" - }, - { - "name": "proof_size", - "type": 22, - "typeName": "u64" - } - ] - } - }, - "path": [ - "sp_weights", - "weight_v2", - "Weight" - ] - } - }, - { - "id": 65, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 68, - "typeName": "Vec" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 66 - }, - { - "name": "S", - "type": null - } - ], - "path": [ - "bounded_collections", - "bounded_vec", - "BoundedVec" - ] - } - }, - { - "id": 66, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "index", - "type": 16, - "typeName": "u32" - }, - { - "name": "name", - "type": 67, - "typeName": "BoundedVec" - }, - { - "name": "module_name", - "type": 67, - "typeName": "BoundedVec" - }, - { - "name": "major", - "type": 16, - "typeName": "u32" - }, - { - "name": "minor", - "type": 16, - "typeName": "u32" - }, - { - "name": "patch", - "type": 16, - "typeName": "u32" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "PalletInfo" - ] - } - }, - { - "id": 67, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 20, - "typeName": "Vec" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 13 - }, - { - "name": "S", - "type": null - } - ], - "path": [ - "bounded_collections", - "bounded_vec", - "BoundedVec" - ] - } - }, - { - "id": 68, - "type": { - "def": { - "sequence": { - "type": 66 - } - } - } - }, - { - "id": 69, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Success" - }, - { - "fields": [ - { - "type": 70, - "typeName": "BoundedVec" - } - ], - "index": 1, - "name": "Error" - }, - { - "fields": [ - { - "type": 70, - "typeName": "BoundedVec" - } - ], - "index": 2, - "name": "TruncatedError" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "MaybeErrorCode" - ] - } - }, - { - "id": 70, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 20, - "typeName": "Vec" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 13 - }, - { - "name": "S", - "type": null - } - ], - "path": [ - "bounded_collections", - "bounded_vec", - "BoundedVec" - ] - } - }, - { - "id": 71, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "None" - }, - { - "fields": [ - { - "type": 51 - } - ], - "index": 1, - "name": "Some" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 51 - } - ], - "path": [ - "Option" - ] - } - }, - { - "id": 72, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "destination", - "type": 51, - "typeName": "MultiLocation" - }, - { - "name": "query_id", - "type": 22, - "typeName": "QueryId" - }, - { - "name": "max_weight", - "type": 64, - "typeName": "Weight" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "QueryResponseInfo" - ] - } - }, - { - "id": 73, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 47, - "typeName": "MultiAssets" - } - ], - "index": 0, - "name": "Definite" - }, - { - "fields": [ - { - "type": 74, - "typeName": "WildMultiAsset" - } - ], - "index": 1, - "name": "Wild" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "multiasset", - "MultiAssetFilter" - ] - } - }, - { - "id": 74, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "All" - }, - { - "fields": [ - { - "name": "id", - "type": 50, - "typeName": "AssetId" - }, - { - "name": "fun", - "type": 75, - "typeName": "WildFungibility" - } - ], - "index": 1, - "name": "AllOf" - }, - { - "fields": [ - { - "type": 16, - "typeName": "u32" - } - ], - "index": 2, - "name": "AllCounted" - }, - { - "fields": [ - { - "name": "id", - "type": 50, - "typeName": "AssetId" - }, - { - "name": "fun", - "type": 75, - "typeName": "WildFungibility" - }, - { - "name": "count", - "type": 16, - "typeName": "u32" - } - ], - "index": 3, - "name": "AllOfCounted" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "multiasset", - "WildMultiAsset" - ] - } - }, - { - "id": 75, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Fungible" - }, - { - "index": 1, - "name": "NonFungible" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "multiasset", - "WildFungibility" - ] - } - }, - { - "id": 76, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Unlimited" - }, - { - "fields": [ - { - "type": 64, - "typeName": "Weight" - } - ], - "index": 1, - "name": "Limited" - } - ] - } - }, - "path": [ - "xcm", - "v3", - "WeightLimit" - ] - } - }, - { - "id": 77, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 78 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 3 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 78 - }, - { - "name": "E", - "type": 3 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 78, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 64 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 79 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 64 - }, - { - "name": "E", - "type": 79 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 79, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "Success" - }, - { - "index": 1, - "name": "NoResponse" - }, - { - "index": 2, - "name": "RuntimeError" - } - ] - } - }, - "path": [ - "contracts_example", - "sdk", - "Error" - ] - } - }, - { - "id": 80, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "dest", - "type": 81, - "typeName": "VersionedMultiLocation" - }, - { - "name": "xcm", - "type": 4, - "typeName": "VersionedXcm<()>" - } - ] - } - }, - "path": [ - "xcm_ce_primitives", - "ValidateSendInput" - ] - } - }, - { - "id": 81, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 12, - "typeName": "v2::MultiLocation" - } - ], - "index": 1, - "name": "V2" - }, - { - "fields": [ - { - "type": 51, - "typeName": "v3::MultiLocation" - } - ], - "index": 3, - "name": "V3" - } - ] - } - }, - "path": [ - "xcm", - "VersionedMultiLocation" - ] - } - }, - { - "id": 82, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 83 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 3 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 83 - }, - { - "name": "E", - "type": 3 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 83, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 84 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 79 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 84 - }, - { - "name": "E", - "type": 79 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 84, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 8, - "typeName": "v2::MultiAssets" - } - ], - "index": 1, - "name": "V2" - }, - { - "fields": [ - { - "type": 47, - "typeName": "v3::MultiAssets" - } - ], - "index": 3, - "name": "V3" - } - ] - } - }, - "path": [ - "xcm", - "VersionedMultiAssets" - ] - } - }, - { - "id": 85, - "type": { - "def": { - "composite": { - "fields": [ - { - "name": "query_type", - "type": 87, - "typeName": "QueryType" - }, - { - "name": "timeout", - "type": 17, - "typeName": "BlockNumber" - } - ] - } - }, - "params": [ - { - "name": "AccountId", - "type": 86 - }, - { - "name": "BlockNumber", - "type": 17 - } - ], - "path": [ - "xcm_ce_primitives", - "QueryConfig" - ] - } - }, - { - "id": 86, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 21, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_primitives", - "types", - "AccountId" - ] - } - }, - { - "id": 87, - "type": { - "def": { - "variant": { - "variants": [ - { - "index": 0, - "name": "NoCallback" - }, - { - "fields": [ - { - "name": "contract_id", - "type": 86, - "typeName": "AccountId" - }, - { - "name": "selector", - "type": 31, - "typeName": "[u8; 4]" - } - ], - "index": 1, - "name": "WASMContractCallback" - }, - { - "fields": [ - { - "name": "contract_id", - "type": 88, - "typeName": "H160" - }, - { - "name": "selector", - "type": 31, - "typeName": "[u8; 4]" - } - ], - "index": 2, - "name": "EVMContractCallback" - } - ] - } - }, - "params": [ - { - "name": "AccountId", - "type": 86 - } - ], - "path": [ - "xcm_ce_primitives", - "QueryType" - ] - } - }, - { - "id": 88, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 24, - "typeName": "[u8; 20]" - } - ] - } - }, - "path": [ - "primitive_types", - "H160" - ] - } - }, - { - "id": 89, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 90 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 3 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 90 - }, - { - "name": "E", - "type": 3 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 90, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 23 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 79 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 23 - }, - { - "name": "E", - "type": 79 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 91, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 92 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 3 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 92 - }, - { - "name": "E", - "type": 3 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 92, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 60 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 79 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 60 - }, - { - "name": "E", - "type": 79 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 93, - "type": { - "def": { - "variant": { - "variants": [ - { - "fields": [ - { - "type": 0 - } - ], - "index": 0, - "name": "Ok" - }, - { - "fields": [ - { - "type": 3 - } - ], - "index": 1, - "name": "Err" - } - ] - } - }, - "params": [ - { - "name": "T", - "type": 0 - }, - { - "name": "E", - "type": 3 - } - ], - "path": [ - "Result" - ] - } - }, - { - "id": 94, - "type": { - "def": { - "composite": { - "fields": [ - { - "type": 21, - "typeName": "[u8; 32]" - } - ] - } - }, - "path": [ - "ink_primitives", - "types", - "Hash" - ] - } - }, - { - "id": 95, - "type": { - "def": { - "variant": {} - }, - "path": [ - "ink_env", - "types", - "NoChainExtension" - ] - } - } - ], - "version": "4" -} \ No newline at end of file diff --git a/frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.wasm b/frame/pallet-xcm-transactor/xcm-simulator/fixtures/xcm_flip.wasm deleted file mode 100644 index ad94b85c6ba80f650b674ea5f1a19666b1fd3786..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76223 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 SJoEBy@dC#i@bfF%-2VZ30x0DG From c21cc8af4c8f8d19cd2cdab218ba2eb715f1d236 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Tue, 9 May 2023 09:33:22 +0530 Subject: [PATCH 08/15] wip: add readme and fix some dcos --- frame/pallet-xcm-transactor/README.md | 75 +++++++++++++++++++ frame/pallet-xcm-transactor/src/lib.rs | 15 ++-- .../xcm-simulator/build.rs | 20 ++++- 3 files changed, 102 insertions(+), 8 deletions(-) diff --git a/frame/pallet-xcm-transactor/README.md b/frame/pallet-xcm-transactor/README.md index e69de29b..92e159d0 100644 --- a/frame/pallet-xcm-transactor/README.md +++ b/frame/pallet-xcm-transactor/README.md @@ -0,0 +1,75 @@ +# XCM CE + Companion Pallet +_XCM Chain extension with callback functionality_ + +> This is WIP draft implementation with a lot of pending TODOs and security considerations still to be addressed. + +## Chain Extension + +The CE has following commands and [SDK for ink! contracts](frame/pallet-xcm-transactor/ink-sdk). +```rust +pub enum Command { + /// Returns the weight for given XCM and saves it (in CE, per-call scratch buffer) for + /// execution + PrepareExecute = 0, + /// Execute the XCM that was prepared earlier + Execute = 1, + /// Returns the fee required to send XCM and saves it for sending + ValidateSend = 2, + /// Send the validated XCM + Send = 3, + /// Register the new query + NewQuery = 4, + /// Take the response for query if available, in case of no callback query + TakeResponse = 5, + /// Get the pallet account id which will be the caller of contract callback + PalletAccountId = 6, +} + +``` + +## Callback Design +The callback design make use of `pallet_xcm`'s `OnResponse` handler which has capability to notify a dispatch on a XCM response (if notify query is registered). + +For us that dispatch is companion pallet's `on_callback_received` which will route the xcm response (`Response` enum) back to contract via a `bare_call` (if wasm contract) + +![image](https://user-images.githubusercontent.com/17181457/236989729-acf5ac13-4abe-4340-bcdc-6ca22fb5d411.png) + + +## Structure +``` +├── pallet-xcm-transactor +│ ├── contract-examples # contract examples using XCM CE, some of which are used as fixtures in tests +│ ├── ink-sdk # ink helper methods to build CE calls and export types +│ ├── primitives # common types to share with pallet and CE +│ ├── src # companion pallet, for callback support +│ └── xcm-simulator # xcm simulator, for testing XCM CE +``` + + +## Local testing +All the test scenarios are done inside XCM Simulator - [here](frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs) + +### Run tests +- cd into xcm simulator directory + ``` + cd frame/pallet-xcm-transactor/xcm-simulator + ``` +- `cargo test` - it will take a while for first time since it needs to build the contracts too + +To print the XCM logs, use the below command +``` +RUST_LOG="xcm=trace" cargo test -- --nocapture --test-threads=1 +``` + +### To add new contract to fixtures +1. Create the contract inside [`contract-examples`](frame/pallet-xcm-transactor/contract-examples) directory +2. Add the contract in [`build.rs`](frame/pallet-xcm-transactor/xcm-simulator/build.rs) of simulator tests so that contract will be compiled and copied to fixtures dir before test runs. + ``` + build_contract( + &fixtures_dir, + &contracts_dir.join("YOUR_CONTRACT"), + "YOUR_CONTRACT", + ); + ``` + +See the existing tests to know how fixtures are used. diff --git a/frame/pallet-xcm-transactor/src/lib.rs b/frame/pallet-xcm-transactor/src/lib.rs index 23bcbaaf..43422ee7 100644 --- a/frame/pallet-xcm-transactor/src/lib.rs +++ b/frame/pallet-xcm-transactor/src/lib.rs @@ -77,7 +77,7 @@ pub mod pallet { BlockNumber = Self::BlockNumber, >; - /// Required origin for sending registering new queries. If successful, it resolves to `MultiLocation` + /// Required origin for registering new queries. If successful, it resolves to `MultiLocation` /// which exists as an interior location within this chain's XCM context. type RegisterQueryOrigin: EnsureOrigin< ::RuntimeOrigin, @@ -181,14 +181,15 @@ pub mod pallet { } /// Register a new query - /// THIS IS ONLY FOR WEIGHTS BENCHMARKING, since we cannot benchmark non-dispatch + /// THIS IS ONLY FOR WEIGHTS BENCHMARKING /// TODO: Weights, /// (1 DB read + 3 DB write + 1 event + some extra (need benchmarking)) - /// Weight for this does not take callback weights into account. That should be - /// done via XcmWeigher using WeightBounds, where all query instructions's - /// `QueryResponseInfo` `max_weight` is taken into account. - /// https://github.com/paritytech/polkadot/blob/b9d192c418da83c784e366c86b7db0b2ff0789d9/runtime/kusama/src/weights/xcm/mod.rs#L99-L106 - /// Kusama uses WeightInfoBounds but does not take `max_weight` into account + /// + /// Weight for this does not take callback weights into account. That should be + /// done during calculation of fees to send XCM via + /// `cumulus_pallet_xcmp_queue::Config::PriceForSiblingDelivery`, if using that for XCM Router, + /// where all query instructions's `QueryResponseInfo` `max_weight` is taken into account + /// while calculating fees to send xcm. #[pallet::call_index(1)] #[pallet::weight(Weight::from_parts(1_000_000, 1_000_000))] pub fn prepare_new_query( diff --git a/frame/pallet-xcm-transactor/xcm-simulator/build.rs b/frame/pallet-xcm-transactor/xcm-simulator/build.rs index 5b8c935e..94f699c3 100644 --- a/frame/pallet-xcm-transactor/xcm-simulator/build.rs +++ b/frame/pallet-xcm-transactor/xcm-simulator/build.rs @@ -1,4 +1,22 @@ -// build.rs +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +/// build.rs use std::{ fs, From 342a893d605d1ab88ed9ed13cf47b6a35d781ce2 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Tue, 9 May 2023 11:02:54 +0530 Subject: [PATCH 09/15] wip: add docstring for pallet --- frame/pallet-xcm-transactor/src/lib.rs | 53 +++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/frame/pallet-xcm-transactor/src/lib.rs b/frame/pallet-xcm-transactor/src/lib.rs index 43422ee7..0fa246c0 100644 --- a/frame/pallet-xcm-transactor/src/lib.rs +++ b/frame/pallet-xcm-transactor/src/lib.rs @@ -17,7 +17,58 @@ // along with Astar. If not, see . //! Pallet to handle XCM Callbacks. - +//! +//! - [`Config`] +//! - [`Call`] +//! - [`Pallet`] +//! - [`Event`] +//! +//! ## Overview +//! +//! The pallet provides functionality for xcm query management and handling callbacks: +//! +//! - Registering a new query +//! - Taking response of query (if available) +//! - Handling the pallet_xcm's OnResponse notify +//! +//! ### Terminology +//! +//! - **Callback:** When recieving the XCM response from pallet_xcm notify routing that +//! response to desired destination (like wasm contract) +//! - **Pallet XCM Notify:** The pallet_xcm OnResponse handler can call (notify) custom +//! disptach on recieving the XCM response given that query is registered before +//! hand. +//! - **Manual Polling:** Instead of callback, the response is saved for the user to manually +//! poll it. +//! +//! To use it in your runtime, you need to implement the pallet's [`Config`]. +//! +//! ### Implementation +//! +//! The pallet provides implementations for the following traits. +//! - [`OnCallback`](pallet_xcm_transactor::OnCallback): Functions for dealing when a +//! callback is recieved. +//! +//! ### Goals +//! The callback system is designed to make following possible: +//! +//! - Registeration of new query which can either be manual polling or callbacks +//! - Allow query owners to take the response in case of manual polling +//! - Handle the incoming pallet_xcm's notify and route it with help of `CallbackHandler` +//! +//! ## Interface +//! +//! ### Permissioned Functions +//! - `on_callback_recieved`: Accepts the XCM Response and invoke the `CallbackHandler`, can only +//! be called in a response to XCM. +//! +//! ### Public Functions +//! - `new_query`: Registers a new query and returns the query id +//! - `account_id`: Get the account id associated with this pallet that will be the origin +//! of the callback +//! - `take_response`: Take the response if available + +// Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] use frame_support::{pallet_prelude::*, PalletId}; From 1b34116ea2942a28312161a12884b17706d100a2 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Thu, 11 May 2023 09:29:13 +0530 Subject: [PATCH 10/15] wip: move most logic to pallet for bechmarking --- .../src/chain_extension.rs | 120 +++++++-------- frame/pallet-xcm-transactor/src/lib.rs | 142 +++++++++++++----- .../xcm-simulator/build.rs | 1 - 3 files changed, 157 insertions(+), 106 deletions(-) diff --git a/frame/pallet-xcm-transactor/src/chain_extension.rs b/frame/pallet-xcm-transactor/src/chain_extension.rs index 3c10a08d..4cc820f9 100644 --- a/frame/pallet-xcm-transactor/src/chain_extension.rs +++ b/frame/pallet-xcm-transactor/src/chain_extension.rs @@ -18,8 +18,8 @@ #![cfg_attr(not(feature = "std"), no_std)] -use crate::{Config, Pallet, QueryConfig}; -use frame_support::{traits::EnsureOrigin, DefaultNoBound}; +use crate::{Config, Error as PalletError, Pallet, QueryConfig}; +use frame_support::DefaultNoBound; use frame_system::RawOrigin; use pallet_contracts::chain_extension::{ ChainExtension, Environment, Ext, InitState, Result as DispatchResult, RetVal, SysConfig, @@ -34,11 +34,19 @@ pub use xcm_ce_primitives::{ XcmCeError::{self, *}, XCM_EXTENSION_ID, }; -use xcm_executor::traits::WeightBounds; type RuntimeCallOf = ::RuntimeCall; macro_rules! unwrap { + ($val:expr) => { + match $val { + Ok(inner) => inner, + Err(e) => { + let err: XcmCeError = e.into(); + return Ok(RetVal::Converging(err.into())); + } + } + }; ($val:expr, $err:expr) => { match $val { Ok(inner) => inner, @@ -47,6 +55,20 @@ macro_rules! unwrap { }; } +impl From> for XcmCeError { + fn from(value: PalletError) -> Self { + match value { + PalletError::BadVersion => BadVersion, + PalletError::InvalidOrigin => InvalidOrigin, + PalletError::NotSupported => NotSupported, + PalletError::SendValidateFailed => SendValidateFailed, + PalletError::CannotWeigh => CannotWeigh, + PalletError::InvalidQuerier => InvalidQuerier, + _ => RuntimeError, + } + } +} + #[derive(DefaultNoBound)] pub struct XCMExtension { prepared_execute: Option>>, @@ -85,9 +107,8 @@ impl XCMExtension { let len = env.in_len(); let input: VersionedXcm> = env.read_as_unbounded(len)?; - let mut xcm = unwrap!(input.try_into(), BadVersion); - // calculate the weight - let weight = unwrap!(T::Weigher::weight(&mut xcm), CannotWeigh); + let origin = RawOrigin::Signed(env.ext().address().clone()); + let (xcm, weight) = unwrap!(Pallet::::prepare_execute(origin.into(), Box::new(input))); // save the prepared xcm self.prepared_execute = Some(PreparedExecution { xcm, weight }); @@ -102,34 +123,26 @@ impl XCMExtension { &mut self, mut env: Environment, ) -> DispatchResult { - let input = unwrap!( + let PreparedExecution { xcm, weight } = unwrap!( self.prepared_execute.as_ref().take().ok_or(()), PreparationMissing ); // charge for xcm weight - let charged = env.charge_weight(input.weight)?; + let charged = env.charge_weight(*weight)?; // TODO: find better way to get origin // https://github.com/paritytech/substrate/pull/13708 let origin = RawOrigin::Signed(env.ext().address().clone()); - // ensure xcm execute origin - let origin_location = unwrap!( - T::ExecuteXcmOrigin::ensure_origin(origin.into()), + let outcome = unwrap!( + Pallet::::execute( + origin.into(), + Box::new(VersionedXcm::V3(xcm.clone())), + *weight, + ), + // TODO: mapp pallet error 1-1 with CE errors InvalidOrigin ); - let hash = input.xcm.using_encoded(sp_io::hashing::blake2_256); - // execute XCM - // NOTE: not using pallet_xcm::execute here because it does not return XcmError - // which is needed to ensure xcm execution success - let outcome = T::XcmExecutor::execute_xcm_in_credit( - origin_location, - input.xcm.clone(), - hash, - input.weight, - input.weight, - ); - // adjust with actual weights used env.adjust_weight(charged, outcome.weight_used()); // revert for anything but a complete execution @@ -147,21 +160,21 @@ impl XCMExtension { ) -> DispatchResult { let mut env = env.buf_in_buf_out(); let len = env.in_len(); - let input: ValidateSendInput = env.read_as_unbounded(len)?; + let ValidateSendInput { dest, xcm } = env.read_as_unbounded(len)?; + + let origin = RawOrigin::Signed(env.ext().address().clone()); - let dest = unwrap!(input.dest.try_into(), BadVersion); - let xcm: Xcm<()> = unwrap!(input.xcm.try_into(), BadVersion); // validate and get fees required to send - let (_, asset) = unwrap!( - validate_send::(dest, xcm.clone()), - SendValidateFailed - ); + let (xcm, dest, fees) = unwrap!(Pallet::::validate_send( + origin.into(), + Box::new(dest.clone()), + Box::new(xcm.clone()) + )); // save the validated input self.validated_send = Some(ValidatedSend { dest, xcm }); // write the fees to output - VersionedMultiAssets::from(asset).using_encoded(|a| env.write(a, true, None))?; - + VersionedMultiAssets::from(fees).using_encoded(|a| env.write(a, true, None))?; Ok(RetVal::Converging(XcmCeError::Success.into())) } @@ -208,20 +221,12 @@ impl XCMExtension { VersionedMultiLocation, ) = env.read_as_unbounded(len)?; - let dest: MultiLocation = unwrap!(dest.try_into(), BadVersion); - - // convert to interior junction - let interior: Junctions = unwrap!( - Self::querier_location(env.ext().address().clone()), - InvalidOrigin - ); - + let origin = RawOrigin::Signed(env.ext().address().clone()); // register the query - let query_id: u64 = Pallet::::new_query(query_config, interior, dest)?; + let query_id: u64 = Pallet::::new_query(origin.into(), query_config, Box::new(dest))?; // write the query_id to buffer query_id.using_encoded(|q| env.write(q, true, None))?; - Ok(RetVal::Converging(Success.into())) } @@ -233,24 +238,18 @@ impl XCMExtension { ) -> DispatchResult { let mut env = env.buf_in_buf_out(); let query_id: u64 = env.read_as()?; - // convert to interior junction - let interior: Junctions = unwrap!( - Self::querier_location(env.ext().address().clone()), - InvalidOrigin - ); + // TODO: find better way to get origin + // https://github.com/paritytech/substrate/pull/13708 + let origin = RawOrigin::Signed(env.ext().address().clone()); let response = unwrap!( - unwrap!( - Pallet::::take_response(interior, query_id), - InvalidQuerier - ) - .map(|r| r.0) - .ok_or(()), + unwrap!(Pallet::::take_response(origin.into(), query_id)) + .map(|r| r.0) + .ok_or(()), NoResponse ); VersionedResponse::from(response).using_encoded(|r| env.write(r, true, None))?; - Ok(RetVal::Converging(XcmCeError::Success.into())) } @@ -266,16 +265,3 @@ impl XCMExtension { Ok(RetVal::Converging(XcmCeError::Success.into())) } } - -impl XCMExtension { - fn querier_location(account_id: T::AccountId) -> Result { - // TODO: find better way to get origin - // https://github.com/paritytech/substrate/pull/13708 - let origin = RawOrigin::Signed(account_id); - // ensure origin is allowed to make queries - let origin_location = - T::RegisterQueryOrigin::ensure_origin(origin.into()).map_err(|_| InvalidOrigin)?; - // convert to interior junction - origin_location.try_into().map_err(|_| InvalidOrigin) - } -} diff --git a/frame/pallet-xcm-transactor/src/lib.rs b/frame/pallet-xcm-transactor/src/lib.rs index 0fa246c0..623bf19c 100644 --- a/frame/pallet-xcm-transactor/src/lib.rs +++ b/frame/pallet-xcm-transactor/src/lib.rs @@ -72,7 +72,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::{pallet_prelude::*, PalletId}; -use frame_system::pallet_prelude::*; +use frame_system::{pallet_prelude::*, Config as SysConfig}; pub use pallet::*; use pallet_contracts::Pallet as PalletContracts; use pallet_xcm::Pallet as PalletXcm; @@ -80,6 +80,7 @@ use sp_core::H160; use sp_runtime::traits::{AccountIdConversion, Zero}; use sp_std::prelude::*; use xcm::prelude::*; +use xcm_executor::traits::WeightBounds; pub mod chain_extension; @@ -149,17 +150,19 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - // successfully handled callback + /// successfully handled callback CallbackSuccess(QueryType), CallbackFailed { query_type: QueryType, query_id: QueryId, }, - // new query registered + /// new query registered QueryPrepared { query_type: QueryType, query_id: QueryId, }, + /// query response taken + ResponseTaken(QueryId), } #[pallet::error] @@ -174,6 +177,10 @@ pub mod pallet { NotSupported, /// Querier mismatch InvalidQuerier, + /// Failed to weigh XCM message + CannotWeigh, + /// Failed to validate xcm for sending + SendValidateFailed, /// Callback out of gas /// TODO: use it OutOfGas, @@ -230,39 +237,6 @@ pub mod pallet { Self::deposit_event(Event::::CallbackSuccess(query_type)); Ok(()) } - - /// Register a new query - /// THIS IS ONLY FOR WEIGHTS BENCHMARKING - /// TODO: Weights, - /// (1 DB read + 3 DB write + 1 event + some extra (need benchmarking)) - /// - /// Weight for this does not take callback weights into account. That should be - /// done during calculation of fees to send XCM via - /// `cumulus_pallet_xcmp_queue::Config::PriceForSiblingDelivery`, if using that for XCM Router, - /// where all query instructions's `QueryResponseInfo` `max_weight` is taken into account - /// while calculating fees to send xcm. - #[pallet::call_index(1)] - #[pallet::weight(Weight::from_parts(1_000_000, 1_000_000))] - pub fn prepare_new_query( - origin: OriginFor, - config: QueryConfig, - dest: Box, - ) -> DispatchResult { - let origin_location = T::RegisterQueryOrigin::ensure_origin(origin)?; - let interior: Junctions = origin_location - .try_into() - .map_err(|_| Error::::InvalidOrigin)?; - let query_type = config.query_type.clone(); - let dest = MultiLocation::try_from(*dest).map_err(|()| Error::::BadVersion)?; - - // register query - let query_id = Self::new_query(config, interior, dest)?; - Self::deposit_event(Event::::QueryPrepared { - query_type, - query_id, - }); - Ok(()) - } } } @@ -348,6 +322,7 @@ impl OnCallback for Pallet { } } +/// Public methods impl Pallet { /// The account ID of the pallet. pub fn account_id() -> T::AccountId { @@ -355,8 +330,99 @@ impl Pallet { AccountIdConversion::::into_account_truncating(&ID) } + /// Weigh the XCM to prepare for execution + pub fn prepare_execute( + origin: OriginFor, + xcm: Box::RuntimeCall>>, + ) -> Result<(Xcm<::RuntimeCall>, Weight), Error> { + T::ExecuteXcmOrigin::ensure_origin(origin).map_err(|_| Error::InvalidOrigin)?; + + let mut xcm = (*xcm).try_into().map_err(|_| Error::BadVersion)?; + let weight = T::Weigher::weight(&mut xcm).map_err(|_| Error::CannotWeigh)?; + Ok((xcm, weight)) + } + + pub fn execute( + origin: OriginFor, + xcm: Box::RuntimeCall>>, + weight_limit: Weight, + ) -> Result> { + let origin_location = + T::ExecuteXcmOrigin::ensure_origin(origin).map_err(|_| Error::InvalidOrigin)?; + let xcm: Xcm<::RuntimeCall> = + (*xcm).try_into().map_err(|_| Error::BadVersion)?; + + // execute XCM + // NOTE: not using pallet_xcm::execute here because it does not return XcmError + // which is needed to ensure xcm execution success + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + Ok(T::XcmExecutor::execute_xcm_in_credit( + origin_location, + xcm.clone(), + hash, + weight_limit, + weight_limit, + )) + } + + pub fn validate_send( + origin: OriginFor, + dest: Box, + xcm: Box>, + ) -> Result<(Xcm<()>, MultiLocation, VersionedMultiAssets), Error> { + T::SendXcmOrigin::ensure_origin(origin).map_err(|_| Error::InvalidOrigin)?; + let xcm: Xcm<()> = (*xcm).try_into().map_err(|_| Error::BadVersion)?; + let dest: MultiLocation = (*dest).try_into().map_err(|_| Error::BadVersion)?; + + let (_, fees) = validate_send::(dest.clone(), xcm.clone()) + .map_err(|_| Error::SendValidateFailed)?; + Ok((xcm, dest, VersionedMultiAssets::V3(fees))) + } + + /// Take the response if available and querier matches + pub fn take_response( + origin: OriginFor, + query_id: QueryId, + ) -> Result, Error> { + // ensure origin is allowed to make queries + let origin_location: Junctions = T::RegisterQueryOrigin::ensure_origin(origin) + .map_err(|_| Error::InvalidOrigin)? + .try_into() + .map_err(|_| Error::InvalidOrigin)?; + + let response = Self::do_take_response(origin_location, query_id)?; + Self::deposit_event(Event::ResponseTaken(query_id)); + Ok(response) + } + /// Register new query originating from querier to dest pub fn new_query( + origin: OriginFor, + config: QueryConfig, + dest: Box, + ) -> Result> { + let origin_location = + T::RegisterQueryOrigin::ensure_origin(origin).map_err(|_| Error::InvalidOrigin)?; + let interior: Junctions = origin_location + .try_into() + .map_err(|_| Error::::InvalidOrigin)?; + let query_type = config.query_type.clone(); + let dest = MultiLocation::try_from(*dest).map_err(|()| Error::::BadVersion)?; + + // register query + let query_id = Self::do_new_query(config, interior, dest)?; + Self::deposit_event(Event::::QueryPrepared { + query_type, + query_id, + }); + Ok(query_id) + } +} + +/// Internal methods +impl Pallet { + /// Register new query originating from querier to dest + fn do_new_query( QueryConfig { query_type, timeout, @@ -392,8 +458,7 @@ impl Pallet { }) } - /// Take the response if available and querier matches - pub fn take_response( + fn do_take_response( querier: impl Into, query_id: QueryId, ) -> Result, Error> { @@ -402,6 +467,7 @@ impl Pallet { if querier.into() == query_info.querier { let response = pallet_xcm::Pallet::::take_response(query_id); + Self::deposit_event(Event::ResponseTaken(query_id)); Ok(response) } else { Err(Error::::InvalidQuerier) diff --git a/frame/pallet-xcm-transactor/xcm-simulator/build.rs b/frame/pallet-xcm-transactor/xcm-simulator/build.rs index 94f699c3..020d58dd 100644 --- a/frame/pallet-xcm-transactor/xcm-simulator/build.rs +++ b/frame/pallet-xcm-transactor/xcm-simulator/build.rs @@ -17,7 +17,6 @@ // along with Astar. If not, see . /// build.rs - use std::{ fs, path::{Path, PathBuf}, From 0826653bc0e43945a70f2b886fb9db21fc964524 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Wed, 17 May 2023 16:08:55 +0530 Subject: [PATCH 11/15] wip: add benchmarks and weights --- .../pallet-xcm-transactor/src/benchmarking.rs | 267 ++++++++++++++ frame/pallet-xcm-transactor/src/mock.rs | 330 ++++++++++++++++++ frame/pallet-xcm-transactor/src/weights.rs | 192 ++++++++++ 3 files changed, 789 insertions(+) create mode 100644 frame/pallet-xcm-transactor/src/benchmarking.rs create mode 100644 frame/pallet-xcm-transactor/src/mock.rs create mode 100644 frame/pallet-xcm-transactor/src/weights.rs diff --git a/frame/pallet-xcm-transactor/src/benchmarking.rs b/frame/pallet-xcm-transactor/src/benchmarking.rs new file mode 100644 index 00000000..df21dd07 --- /dev/null +++ b/frame/pallet-xcm-transactor/src/benchmarking.rs @@ -0,0 +1,267 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use frame_benchmarking::v2::*; +use frame_support::traits::Contains; +use sp_std::prelude::*; +use xcm::latest::prelude::*; + +fn mock_xcm_response( + response_info: QueryResponseInfo, + querier: impl Into, + response: Response, + weight_limit: Weight, +) { + let QueryResponseInfo { + destination, + query_id, + max_weight, + } = response_info; + let querier = Some(querier.into()); + let response_xcm = Xcm(vec![QueryResponse { + querier, + query_id, + max_weight, + response, + }]); + let hash = response_xcm.using_encoded(sp_io::hashing::blake2_256); + T::XcmExecutor::execute_xcm_in_credit( + destination, + response_xcm, + hash, + weight_limit, + weight_limit, + ); +} + +/// Assert that the last event equals the provided one. +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +#[benchmarks(where ::AccountId: From<[u8; 32]>)] +mod benchmarks_module { + + use sp_runtime::traits::Bounded; + + use super::*; + + #[benchmark] + fn account_id() { + #[block] + { + let _ = Pallet::::account_id(); + } + } + + #[benchmark] + fn prepare_execute() -> Result<(), BenchmarkError> { + let execute_origin = + T::ExecuteXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let msg = Xcm(vec![ClearOrigin]); + let versioned_msg = VersionedXcm::from(msg); + + #[block] + { + let _ = Pallet::::prepare_execute(execute_origin, Box::new(versioned_msg)).unwrap(); + } + + Ok(()) + } + + #[benchmark] + fn execute() -> Result<(), BenchmarkError> { + let execute_origin = + T::ExecuteXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let origin_location = T::ExecuteXcmOrigin::try_origin(execute_origin.clone()) + .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; + let msg = Xcm(vec![ClearOrigin]); + if !T::XcmExecuteFilter::contains(&(origin_location, msg.clone())) { + return Err(BenchmarkError::Override(BenchmarkResult::from_weight( + Weight::MAX, + ))); + } + let versioned_msg = VersionedXcm::from(msg); + + #[block] + { + let _ = Pallet::::execute(execute_origin, Box::new(versioned_msg), Weight::zero()) + .unwrap(); + } + + Ok(()) + } + + #[benchmark] + fn validate_send() -> Result<(), BenchmarkError> { + let send_origin = + T::SendXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + if T::SendXcmOrigin::try_origin(send_origin.clone()).is_err() { + return Err(BenchmarkError::Override(BenchmarkResult::from_weight( + Weight::MAX, + ))); + } + let msg = Xcm(vec![ClearOrigin]); + let versioned_dest: VersionedMultiLocation = T::ReachableDest::get() + .ok_or(BenchmarkError::Override(BenchmarkResult::from_weight( + Weight::MAX, + )))? + .into(); + let versioned_msg = VersionedXcm::from(msg); + + #[block] + { + let _ = Pallet::::validate_send( + send_origin, + Box::new(versioned_dest), + Box::new(versioned_msg), + ) + // not a good idea to unwrap here but it's the only way to + // check if it worked + .unwrap(); + } + + Ok(()) + } + + #[benchmark] + fn take_response() -> Result<(), BenchmarkError> { + let query_origin = T::RegisterQueryOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; + if T::RegisterQueryOrigin::try_origin(query_origin.clone()).is_err() { + return Err(BenchmarkError::Override(BenchmarkResult::from_weight( + Weight::MAX, + ))); + } + + let responder = (Parent, Parachain(1000)); + let weight_limit = Weight::from_parts(100_000_000_000, 1024 * 1024); + //register query + let query_id = Pallet::::new_query( + query_origin.clone(), + QueryConfig { + query_type: QueryType::NoCallback, + timeout: Bounded::max_value(), + }, + Box::new(responder.into()), + ) + .map_err(|_| BenchmarkError::Stop("Failed to register new query"))?; + // mock response + mock_xcm_response::( + QueryResponseInfo { + destination: responder.into(), + query_id, + max_weight: Weight::zero(), + }, + Here, + Response::Null, + weight_limit, + ); + + #[block] + { + let _ = Pallet::::take_response(query_origin.clone(), query_id); + } + + // make sure response is taken + assert_eq!(pallet_xcm::Pallet::::query(query_id), None); + assert_last_event::(Event::::ResponseTaken(query_id).into()); + Ok(()) + } + + #[benchmark] + fn new_query() -> Result<(), BenchmarkError> { + let query_origin = T::RegisterQueryOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; + if T::RegisterQueryOrigin::try_origin(query_origin.clone()).is_err() { + return Err(BenchmarkError::Override(BenchmarkResult::from_weight( + Weight::MAX, + ))); + } + + let query_type = QueryType::WASMContractCallback { + contract_id: [0u8; 32].into(), + selector: [0u8; 4], + }; + let dest = (Parent, Parachain(1000)).into(); + + #[block] + { + let _ = Pallet::::new_query( + query_origin, + QueryConfig { + query_type: query_type.clone(), + timeout: Bounded::max_value(), + }, + Box::new(dest), + ); + } + + assert_last_event::( + Event::::QueryPrepared { + query_type, + // we are sure this will be the first query, so it fine to hardcode here + query_id: 0, + } + .into(), + ); + Ok(()) + } + + #[benchmark] + fn on_callback_recieved() { + let origin: ::RuntimeOrigin = ::RuntimeOrigin::from( + pallet_xcm::Origin::Response((Parent, Parachain(1000)).into()), + ) + .into(); + let query_id = 123; + let query_type = QueryType::NoCallback; + CallbackQueries::::insert( + query_id, + QueryInfo { + query_type: query_type.clone(), + // no use of querier, so any arbitary value will be fine + querier: Here, + }, + ); + + #[block] + { + let _ = Pallet::::on_callback_recieved(origin.into(), query_id, Response::Null); + } + + assert_eq!(CallbackQueries::::get(query_id), None); + assert_last_event::( + Event::::CallbackSuccess { + query_type, + query_id, + weight: Weight::zero(), + } + .into(), + ); + } + + impl_benchmark_test_suite! { + Pallet, + crate::mock::new_test_ext_with_balances(Vec::new()), + crate::mock::Test + } +} diff --git a/frame/pallet-xcm-transactor/src/mock.rs b/frame/pallet-xcm-transactor/src/mock.rs new file mode 100644 index 00000000..4551b334 --- /dev/null +++ b/frame/pallet-xcm-transactor/src/mock.rs @@ -0,0 +1,330 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +use frame_support::{ + construct_runtime, parameter_types, + traits::{Currency, Everything, Nothing}, + weights::Weight, +}; +use pallet_contracts::{chain_extension::RegisteredChainExtension, DefaultAddressGenerator, Frame}; +use pallet_xcm::TestWeightInfo; +use parity_scale_codec::Encode; +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_runtime_parachains::origin; +use sp_core::{ConstU32, ConstU64, H256}; +use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; +pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData}; +use xcm::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia, + ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, EnsureXcmOrigin, + FixedRateOfFungible, FixedWeightBounds, IsConcrete, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, +}; +use xcm_ce_primitives::XCM_EXTENSION_ID; +use xcm_executor::XcmExecutor; + +use crate::{self as pallet_xcm_transactor, chain_extension::XCMExtension}; + +pub type AccountId = AccountId32; +pub type Balance = u128; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; +type BalanceOf = <::Currency as Currency< + ::AccountId, +>>::Balance; + +construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Randomness: pallet_insecure_randomness_collective_flip::{Pallet, Storage}, + ParasOrigin: origin::{Pallet, Origin}, + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin, Config}, + Contracts: pallet_contracts::{Pallet, Call, Storage, Event}, + XcmTransact: pallet_xcm_transactor::{Pallet, Call, Storage, Event}, + } +); + +impl pallet_insecure_randomness_collective_flip::Config for Test {} +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<1>; + type WeightInfo = (); +} + +thread_local! { + pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); +} +// pub(crate) fn sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> { +// SENT_XCM.with(|q| (*q.borrow()).clone()) +// } +// pub(crate) fn take_sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> { +// SENT_XCM.with(|q| { +// let mut r = Vec::new(); +// std::mem::swap(&mut r, &mut *q.borrow_mut()); +// r +// }) +// } +/// Sender that never returns error, always sends +pub struct TestSendXcm; +impl SendXcm for TestSendXcm { + type Ticket = (MultiLocation, Xcm<()>); + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult<(MultiLocation, Xcm<()>)> { + let pair = (dest.take().unwrap(), msg.take().unwrap()); + Ok((pair, MultiAssets::new())) + } + fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result { + let hash = fake_message_hash(&pair.1); + SENT_XCM.with(|q| q.borrow_mut().push(pair)); + Ok(hash) + } +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Test { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Test { + 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]; +} + +parameter_types! { + pub const RelayLocation: MultiLocation = Here.into_location(); + pub const AnyNetwork: Option = None; + pub UniversalLocation: InteriorMultiLocation = Here; + pub UnitWeightCost: u64 = 1_000; +} + +pub type SovereignAccountOf = ( + ChildParachainConvertsVia, + AccountId32Aliases, +); + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(1_000, 1_000); + pub CurrencyPerSecondPerByte: (AssetId, u128, u128) = (Concrete(RelayLocation::get()), 1, 1); + pub TrustedAssets: (MultiAssetFilter, MultiLocation) = (All.into(), Here.into()); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; +} + +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + AllowKnownQueryResponses, + AllowSubscriptionsFrom, +); + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = TestSendXcm; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = Case; + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = XcmPallet; + type AssetTrap = XcmPallet; + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = XcmPallet; + type SubscriptionService = XcmPallet; + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parachain(1000).into()); +} + +impl pallet_xcm::Config for Test { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = TestSendXcm; + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type TrustedLockers = (); + type SovereignAccountOf = AccountId32Aliases<(), AccountId32>; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + type MaxLockers = frame_support::traits::ConstU32<8>; + type WeightInfo = TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; +} + +parameter_types! { + pub const CallbackGasLimit: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); + pub const DeletionWeightLimit: Weight = Weight::from_ref_time(500_000_000_000); + pub static UnstableInterface: bool = true; + pub Schedule: pallet_contracts::Schedule = Default::default(); + pub static DepositPerByte: BalanceOf = 1; + pub const DepositPerItem: BalanceOf = 1; +} + +impl RegisteredChainExtension + for XCMExtension +{ + const ID: u16 = XCM_EXTENSION_ID; +} + +impl pallet_contracts::Config for Test { + type Time = Timestamp; + type Randomness = Randomness; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type CallFilter = Nothing; + type CallStack = [Frame; 5]; + type WeightPrice = (); + type WeightInfo = (); + type ChainExtension = XCMExtension>; + type DeletionQueueDepth = ConstU32<1024>; + type DeletionWeightLimit = DeletionWeightLimit; + type Schedule = Schedule; + type DepositPerByte = DepositPerByte; + type DepositPerItem = DepositPerItem; + type AddressGenerator = DefaultAddressGenerator; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; + type MaxStorageKeyLen = ConstU32<128>; + type UnsafeUnstableInterface = UnstableInterface; + type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; +} + +impl pallet_xcm_transactor::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type CallbackHandler = XcmTransact; + type RegisterQueryOrigin = EnsureXcmOrigin; + type MaxCallbackWeight = CallbackGasLimit; + type WeightInfo = pallet_xcm_transactor::SubstrateWeight; +} + +impl origin::Config for Test {} + +pub(crate) fn new_test_ext_with_balances( + balances: Vec<(AccountId, Balance)>, +) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + >::assimilate_storage( + &pallet_xcm::GenesisConfig { + safe_xcm_version: Some(2), + }, + &mut t, + ) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +pub(crate) fn fake_message_hash(message: &Xcm) -> XcmHash { + message.using_encoded(sp_io::hashing::blake2_256) +} diff --git a/frame/pallet-xcm-transactor/src/weights.rs b/frame/pallet-xcm-transactor/src/weights.rs new file mode 100644 index 00000000..204ad3b4 --- /dev/null +++ b/frame/pallet-xcm-transactor/src/weights.rs @@ -0,0 +1,192 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +//! Autogenerated weights for pallet_xcm_transactor +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-05-17, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `AV`, CPU: `AMD Ryzen 7 5800H with Radeon Graphics` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("shibuya-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/astar-collator +// benchmark +// pallet +// --chain=shibuya-dev +// --pallet=pallet_xcm_transactor +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --steps=50 +// --repeat=20 +// --template=./scripts/templates/weight-template.hbs +// --output=weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_xcm_transactor. +pub trait WeightInfo { + fn account_id() -> Weight; + fn prepare_execute() -> Weight; + fn execute() -> Weight; + fn validate_send() -> Weight; + fn take_response() -> Weight; + fn new_query() -> Weight; + fn on_callback_recieved() -> Weight; +} + +/// Weights for pallet_xcm_transactor using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn account_id() -> Weight { + // Minimum execution time: 831 nanoseconds. + Weight::from_ref_time(921_000) + .saturating_add(Weight::from_proof_size(0)) + } + fn prepare_execute() -> Weight { + // Minimum execution time: 1_964 nanoseconds. + Weight::from_ref_time(2_454_000) + .saturating_add(Weight::from_proof_size(0)) + } + // Storage: Benchmark Override (r:0 w:0) + // Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + fn execute() -> Weight { + // Minimum execution time: 18_446_744_073_709_551 nanoseconds. + Weight::from_ref_time(18_446_744_073_709_551_000) + .saturating_add(Weight::from_proof_size(0)) + } + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + fn validate_send() -> Weight { + // Minimum execution time: 16_070 nanoseconds. + Weight::from_ref_time(16_421_000) + .saturating_add(Weight::from_proof_size(5274)) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + // Storage: XcmTransact CallbackQueries (r:1 w:0) + // Proof: XcmTransact CallbackQueries (max_values: None, max_size: Some(662), added: 3137, mode: MaxEncodedLen) + // Storage: PolkadotXcm Queries (r:1 w:1) + // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + fn take_response() -> Weight { + // Minimum execution time: 46_087 nanoseconds. + Weight::from_ref_time(47_109_000) + .saturating_add(Weight::from_proof_size(6755)) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + // Storage: PolkadotXcm QueryCounter (r:1 w:1) + // Proof Skipped: PolkadotXcm QueryCounter (max_values: Some(1), max_size: None, mode: Measured) + // Storage: XcmTransact CallbackQueries (r:0 w:1) + // Proof: XcmTransact CallbackQueries (max_values: None, max_size: Some(662), added: 3137, mode: MaxEncodedLen) + // Storage: PolkadotXcm Queries (r:0 w:1) + // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + fn new_query() -> Weight { + // Minimum execution time: 29_375 nanoseconds. + Weight::from_ref_time(31_400_000) + .saturating_add(Weight::from_proof_size(2367)) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + // Storage: XcmTransact CallbackQueries (r:1 w:1) + // Proof: XcmTransact CallbackQueries (max_values: None, max_size: Some(662), added: 3137, mode: MaxEncodedLen) + fn on_callback_recieved() -> Weight { + // Minimum execution time: 28_414 nanoseconds. + Weight::from_ref_time(29_886_000) + .saturating_add(Weight::from_proof_size(3137)) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn account_id() -> Weight { + // Minimum execution time: 831 nanoseconds. + Weight::from_ref_time(921_000) + .saturating_add(Weight::from_proof_size(0)) + } + fn prepare_execute() -> Weight { + // Minimum execution time: 1_964 nanoseconds. + Weight::from_ref_time(2_454_000) + .saturating_add(Weight::from_proof_size(0)) + } + // Storage: Benchmark Override (r:0 w:0) + // Proof Skipped: Benchmark Override (max_values: None, max_size: None, mode: Measured) + fn execute() -> Weight { + // Minimum execution time: 18_446_744_073_709_551 nanoseconds. + Weight::from_ref_time(18_446_744_073_709_551_000) + .saturating_add(Weight::from_proof_size(0)) + } + // Storage: PolkadotXcm SupportedVersion (r:1 w:0) + // Proof Skipped: PolkadotXcm SupportedVersion (max_values: None, max_size: None, mode: Measured) + // Storage: PolkadotXcm VersionDiscoveryQueue (r:1 w:1) + // Proof Skipped: PolkadotXcm VersionDiscoveryQueue (max_values: Some(1), max_size: None, mode: Measured) + // Storage: PolkadotXcm SafeXcmVersion (r:1 w:0) + // Proof Skipped: PolkadotXcm SafeXcmVersion (max_values: Some(1), max_size: None, mode: Measured) + fn validate_send() -> Weight { + // Minimum execution time: 16_070 nanoseconds. + Weight::from_ref_time(16_421_000) + .saturating_add(Weight::from_proof_size(5274)) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + // Storage: XcmTransact CallbackQueries (r:1 w:0) + // Proof: XcmTransact CallbackQueries (max_values: None, max_size: Some(662), added: 3137, mode: MaxEncodedLen) + // Storage: PolkadotXcm Queries (r:1 w:1) + // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + fn take_response() -> Weight { + // Minimum execution time: 46_087 nanoseconds. + Weight::from_ref_time(47_109_000) + .saturating_add(Weight::from_proof_size(6755)) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + // Storage: PolkadotXcm QueryCounter (r:1 w:1) + // Proof Skipped: PolkadotXcm QueryCounter (max_values: Some(1), max_size: None, mode: Measured) + // Storage: XcmTransact CallbackQueries (r:0 w:1) + // Proof: XcmTransact CallbackQueries (max_values: None, max_size: Some(662), added: 3137, mode: MaxEncodedLen) + // Storage: PolkadotXcm Queries (r:0 w:1) + // Proof Skipped: PolkadotXcm Queries (max_values: None, max_size: None, mode: Measured) + fn new_query() -> Weight { + // Minimum execution time: 29_375 nanoseconds. + Weight::from_ref_time(31_400_000) + .saturating_add(Weight::from_proof_size(2367)) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + // Storage: XcmTransact CallbackQueries (r:1 w:1) + // Proof: XcmTransact CallbackQueries (max_values: None, max_size: Some(662), added: 3137, mode: MaxEncodedLen) + fn on_callback_recieved() -> Weight { + // Minimum execution time: 28_414 nanoseconds. + Weight::from_ref_time(29_886_000) + .saturating_add(Weight::from_proof_size(3137)) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} From 46ec7015a8dd25a986afda4bf9584198288c3066 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Wed, 17 May 2023 16:09:58 +0530 Subject: [PATCH 12/15] wip: add weights in CE calls --- .../mod.rs} | 46 +++++++----- .../src/chain_extension/weights.rs | 73 +++++++++++++++++++ 2 files changed, 102 insertions(+), 17 deletions(-) rename frame/pallet-xcm-transactor/src/{chain_extension.rs => chain_extension/mod.rs} (88%) create mode 100644 frame/pallet-xcm-transactor/src/chain_extension/weights.rs diff --git a/frame/pallet-xcm-transactor/src/chain_extension.rs b/frame/pallet-xcm-transactor/src/chain_extension/mod.rs similarity index 88% rename from frame/pallet-xcm-transactor/src/chain_extension.rs rename to frame/pallet-xcm-transactor/src/chain_extension/mod.rs index 4cc820f9..37982d49 100644 --- a/frame/pallet-xcm-transactor/src/chain_extension.rs +++ b/frame/pallet-xcm-transactor/src/chain_extension/mod.rs @@ -18,13 +18,18 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub mod weights; +use core::marker::PhantomData; + +use weights::CEWeightInfo; + use crate::{Config, Error as PalletError, Pallet, QueryConfig}; use frame_support::DefaultNoBound; use frame_system::RawOrigin; use pallet_contracts::chain_extension::{ ChainExtension, Environment, Ext, InitState, Result as DispatchResult, RetVal, SysConfig, }; -use pallet_xcm::{Pallet as XcmPallet, WeightInfo}; +use pallet_xcm::{Pallet as XcmPallet, WeightInfo as PalletXcmWeightInfo}; use parity_scale_codec::Encode; use sp_std::prelude::*; use xcm::prelude::*; @@ -70,12 +75,13 @@ impl From> for XcmCeError { } #[derive(DefaultNoBound)] -pub struct XCMExtension { +pub struct XCMExtension { prepared_execute: Option>>, validated_send: Option, + _w: PhantomData, } -impl ChainExtension for XCMExtension +impl ChainExtension for XCMExtension where ::AccountId: AsRef<[u8; 32]>, { @@ -95,7 +101,7 @@ where } } -impl XCMExtension { +impl XCMExtension { /// Returns the weight for given XCM and saves it (in CE, per-call scratch buffer) for /// execution fn prepare_execute>( @@ -107,6 +113,9 @@ impl XCMExtension { let len = env.in_len(); let input: VersionedXcm> = env.read_as_unbounded(len)?; + // charge weight + env.charge_weight(W::prepare_execute(len))?; + let origin = RawOrigin::Signed(env.ext().address().clone()); let (xcm, weight) = unwrap!(Pallet::::prepare_execute(origin.into(), Box::new(input))); @@ -127,9 +136,8 @@ impl XCMExtension { self.prepared_execute.as_ref().take().ok_or(()), PreparationMissing ); - // charge for xcm weight - let charged = env.charge_weight(*weight)?; - + // charge weight + let charged = env.charge_weight(W::execute().saturating_add(*weight))?; // TODO: find better way to get origin // https://github.com/paritytech/substrate/pull/13708 let origin = RawOrigin::Signed(env.ext().address().clone()); @@ -142,9 +150,8 @@ impl XCMExtension { // TODO: mapp pallet error 1-1 with CE errors InvalidOrigin ); - // adjust with actual weights used - env.adjust_weight(charged, outcome.weight_used()); + env.adjust_weight(charged, outcome.weight_used().saturating_add(W::execute())); // revert for anything but a complete execution match outcome { Outcome::Complete(_) => Ok(RetVal::Converging(Success.into())), @@ -161,16 +168,16 @@ impl XCMExtension { let mut env = env.buf_in_buf_out(); let len = env.in_len(); let ValidateSendInput { dest, xcm } = env.read_as_unbounded(len)?; + // charge weight + env.charge_weight(W::validate_send(len))?; let origin = RawOrigin::Signed(env.ext().address().clone()); - // validate and get fees required to send let (xcm, dest, fees) = unwrap!(Pallet::::validate_send( origin.into(), Box::new(dest.clone()), Box::new(xcm.clone()) )); - // save the validated input self.validated_send = Some(ValidatedSend { dest, xcm }); // write the fees to output @@ -187,14 +194,13 @@ impl XCMExtension { self.validated_send.as_ref().take().ok_or(()), PreparationMissing ); - + // charge weight let base_weight = ::WeightInfo::send(); - env.charge_weight(base_weight)?; + env.charge_weight(base_weight.saturating_add(W::send()))?; // TODO: find better way to get origin // https://github.com/paritytech/substrate/pull/13708 let origin = RawOrigin::Signed(env.ext().address().clone()); - // send the xcm unwrap!( XcmPallet::::send( @@ -209,7 +215,6 @@ impl XCMExtension { } /// Register the new query - /// TODO: figure out weights fn new_query>(&self, env: Environment) -> DispatchResult where ::AccountId: AsRef<[u8; 32]>, @@ -220,11 +225,14 @@ impl XCMExtension { QueryConfig, VersionedMultiLocation, ) = env.read_as_unbounded(len)?; + // charge weight + // NOTE: we only charge the weight associated with query registration and processing of + // calllback only. This does not include the CALLBACK weights + env.charge_weight(W::new_query())?; let origin = RawOrigin::Signed(env.ext().address().clone()); // register the query let query_id: u64 = Pallet::::new_query(origin.into(), query_config, Box::new(dest))?; - // write the query_id to buffer query_id.using_encoded(|q| env.write(q, true, None))?; Ok(RetVal::Converging(Success.into())) @@ -238,7 +246,8 @@ impl XCMExtension { ) -> DispatchResult { let mut env = env.buf_in_buf_out(); let query_id: u64 = env.read_as()?; - + // charge weight + env.charge_weight(W::take_response())?; // TODO: find better way to get origin // https://github.com/paritytech/substrate/pull/13708 let origin = RawOrigin::Signed(env.ext().address().clone()); @@ -260,6 +269,9 @@ impl XCMExtension { env: Environment, ) -> DispatchResult { let mut env = env.buf_in_buf_out(); + // charge weight + env.charge_weight(W::account_id())?; + Pallet::::account_id().using_encoded(|r| env.write(r, true, None))?; Ok(RetVal::Converging(XcmCeError::Success.into())) diff --git a/frame/pallet-xcm-transactor/src/chain_extension/weights.rs b/frame/pallet-xcm-transactor/src/chain_extension/weights.rs new file mode 100644 index 00000000..f5cf1db0 --- /dev/null +++ b/frame/pallet-xcm-transactor/src/chain_extension/weights.rs @@ -0,0 +1,73 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use crate::{weights::{WeightInfo, SubstrateWeight}, Config}; +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_xcm_transactor chain extension. +pub trait CEWeightInfo { + fn account_id() -> Weight; + fn prepare_execute(len: u32) -> Weight; + fn execute() -> Weight; + fn validate_send(len: u32) -> Weight; + fn send() -> Weight; + fn take_response() -> Weight; + fn new_query() -> Weight; + fn read_as_unbounded(n: u32) -> Weight; +} + +/// Weights for pallet_xcm_transactor chain extension. +pub struct ChainExtensionWeight(PhantomData); +impl CEWeightInfo for ChainExtensionWeight { + fn account_id() -> Weight { + ::WeightInfo::account_id() + } + + fn prepare_execute(len: u32) -> Weight { + ::WeightInfo::prepare_execute().saturating_add(Self::read_as_unbounded(len)) + } + + fn execute() -> Weight { + ::WeightInfo::execute() + } + + fn validate_send(len: u32) -> Weight { + ::WeightInfo::validate_send().saturating_add(Self::read_as_unbounded(len)) + } + + fn send() -> Weight { + Weight::from_ref_time(100_000) + } + + fn take_response() -> Weight { + ::WeightInfo::take_response() + } + + fn new_query() -> Weight { + ::WeightInfo::new_query().saturating_add(::WeightInfo::on_callback_recieved()) + } + + fn read_as_unbounded(n: u32) -> Weight { + Weight::from_ref_time(1_000).saturating_mul(n.into()) + } +} From 889dad5ec45cc41be6fe335806258062566dc2f8 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Wed, 17 May 2023 16:10:17 +0530 Subject: [PATCH 13/15] wip: refactor pallet --- frame/pallet-xcm-transactor/src/lib.rs | 123 +++++++++++----------- frame/pallet-xcm-transactor/src/traits.rs | 79 ++++++++++++++ 2 files changed, 138 insertions(+), 64 deletions(-) create mode 100644 frame/pallet-xcm-transactor/src/traits.rs diff --git a/frame/pallet-xcm-transactor/src/lib.rs b/frame/pallet-xcm-transactor/src/lib.rs index 623bf19c..d8296c11 100644 --- a/frame/pallet-xcm-transactor/src/lib.rs +++ b/frame/pallet-xcm-transactor/src/lib.rs @@ -71,6 +71,19 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(test)] +mod mock; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +pub mod chain_extension; +pub mod traits; +pub mod weights; +pub use chain_extension::weights::*; +pub use traits::*; +pub use weights::*; + use frame_support::{pallet_prelude::*, PalletId}; use frame_system::{pallet_prelude::*, Config as SysConfig}; pub use pallet::*; @@ -82,8 +95,6 @@ use sp_std::prelude::*; use xcm::prelude::*; use xcm_executor::traits::WeightBounds; -pub mod chain_extension; - #[frame_support::pallet] pub mod pallet { use super::*; @@ -121,7 +132,8 @@ pub mod pallet { /// The overaching origin type type RuntimeOrigin: Into::RuntimeOrigin>> - + IsType<::RuntimeOrigin>; + + IsType<::RuntimeOrigin> + + From<::RuntimeOrigin>; /// Query Handler for creating quries and handling response type CallbackHandler: OnCallback< @@ -136,6 +148,9 @@ pub mod pallet { Success = MultiLocation, >; + /// Weights for pallet + type WeightInfo: WeightInfo; + /// Max weight for callback #[pallet::constant] type MaxCallbackWeight: Get; @@ -151,7 +166,11 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// successfully handled callback - CallbackSuccess(QueryType), + CallbackSuccess { + query_type: QueryType, + query_id: QueryId, + weight: Weight, + }, CallbackFailed { query_type: QueryType, query_id: QueryId, @@ -165,6 +184,7 @@ pub mod pallet { ResponseTaken(QueryId), } + #[derive(PartialEq)] #[pallet::error] pub enum Error { /// The version of the Versioned value used is not able to be interpreted. @@ -172,7 +192,7 @@ pub mod pallet { /// Origin not allow for registering queries InvalidOrigin, /// Query not found in storage - UnexpectedQueryResponse, + UnexpectedQuery, /// Does not support the given query type NotSupported, /// Querier mismatch @@ -197,8 +217,6 @@ pub mod pallet { impl Pallet { /// Dispatch for recieving callback from pallet_xcm's notify /// and handle their routing - /// TODO: Weights, - /// (max callback weight) + 1 DB read + 1 event + some extra (from benchmarking) #[pallet::call_index(0)] #[pallet::weight(T::MaxCallbackWeight::get())] pub fn on_callback_recieved( @@ -210,12 +228,13 @@ pub mod pallet { let responder = ensure_response(::RuntimeOrigin::from(origin))?; // fetch the query let QueryInfo { query_type, .. } = - CallbackQueries::::get(query_id).ok_or(Error::::UnexpectedQueryResponse)?; + CallbackQueries::::get(query_id).ok_or(Error::::UnexpectedQuery)?; + // handle the response routing // TODO: in case of error, maybe save the response for manual // polling as fallback. This will require taking into weight of storing // response in the weights of `prepare_new_query` dispatch - if let Err(e) = T::CallbackHandler::on_callback( + match T::CallbackHandler::on_callback( responder, ResponseInfo { query_id, @@ -223,52 +242,29 @@ pub mod pallet { response, }, ) { - Self::deposit_event(Event::::CallbackFailed { - query_type, - query_id, - }); - return Err(e.into()); + Ok(weight) => { + // deposit success event + Self::deposit_event(Event::::CallbackSuccess { + query_type, + query_id, + weight, + }); + // remove the query + CallbackQueries::::remove(query_id); + Ok(()) + } + Err(e) => { + Self::deposit_event(Event::::CallbackFailed { + query_type, + query_id, + }); + Err(e.into()) + } } - - // remove query from storage - CallbackQueries::::remove(query_id); - - // deposit success event - Self::deposit_event(Event::::CallbackSuccess(query_type)); - Ok(()) } } } -/// Handle the incoming xcm notify callback from ResponseHandler (pallet_xcm) -pub trait OnCallback { - /// error type, that can be converted to dispatch error - type Error: Into; - /// account id type - type AccountId; - /// blocknumber type - type BlockNumber; - - // TODO: Query type itself should be generic like - // - // type QueryType: Member + Parameter + MaybeSerializeDeserialize + MaxEncodedLen + Convert - // type CallbackHandler: OnResponse - // - // #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen)] - // enum MyQueryType {} - // - // impl Convert for MyQueryType {} - - /// Check whether query type is supported or not - fn can_handle(query_type: &QueryType) -> bool; - - /// handle the xcm response - fn on_callback( - responder: impl Into, - response_info: ResponseInfo, - ) -> Result; -} - impl OnCallback for Pallet { type AccountId = T::AccountId; type BlockNumber = T::BlockNumber; @@ -436,8 +432,7 @@ impl Pallet { if !(T::CallbackHandler::can_handle(&query_type)) { return Err(Error::NotSupported); } - - Ok(match query_type.clone() { + let id = match query_type.clone() { QueryType::NoCallback => PalletXcm::::new_query(dest, timeout, querier), QueryType::WASMContractCallback { .. } | QueryType::EVMContractCallback { .. } => { let call: ::RuntimeCall = Call::on_callback_recieved { @@ -445,25 +440,25 @@ impl Pallet { response: Response::Null, } .into(); - let id = PalletXcm::::new_notify_query(dest, call, timeout, querier.clone()); - CallbackQueries::::insert( - id, - QueryInfo { - query_type, - querier, - }, - ); - id + PalletXcm::::new_notify_query(dest, call, timeout, querier.clone()) } - }) + }; + + CallbackQueries::::insert( + id, + QueryInfo { + query_type, + querier, + }, + ); + Ok(id) } fn do_take_response( querier: impl Into, query_id: QueryId, ) -> Result, Error> { - let query_info = - CallbackQueries::::get(query_id).ok_or(Error::::UnexpectedQueryResponse)?; + let query_info = CallbackQueries::::get(query_id).ok_or(Error::::UnexpectedQuery)?; if querier.into() == query_info.querier { let response = pallet_xcm::Pallet::::take_response(query_id); diff --git a/frame/pallet-xcm-transactor/src/traits.rs b/frame/pallet-xcm-transactor/src/traits.rs new file mode 100644 index 00000000..d8bea5e5 --- /dev/null +++ b/frame/pallet-xcm-transactor/src/traits.rs @@ -0,0 +1,79 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar 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. + +// Astar 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 Astar. If not, see . + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +use core::marker::PhantomData; + +use frame_support::weights::Weight; +use sp_runtime::DispatchError; +use xcm::prelude::*; +use xcm_ce_primitives::QueryType; + +use crate::{Config, ResponseInfo}; + +/// Handle the incoming xcm notify callback from ResponseHandler (pallet_xcm) +pub trait OnCallback { + /// error type, that can be converted to dispatch error + type Error: Into; + /// account id type + type AccountId; + /// blocknumber type + type BlockNumber; + + // TODO: Query type itself should be generic like + // + // type QueryType: Member + Parameter + MaybeSerializeDeserialize + MaxEncodedLen + Convert + // type CallbackHandler: OnResponse + // + // #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen)] + // enum MyQueryType {} + // + // impl Convert for MyQueryType {} + + /// Check whether query type is supported or not + fn can_handle(query_type: &QueryType) -> bool; + + /// handle the xcm response + fn on_callback( + responder: impl Into, + response_info: ResponseInfo, + ) -> Result; +} + + +/// OnCallback implementation that does not supports any callback +/// Use this to disable callbacks +pub struct NoCallback(PhantomData); +impl OnCallback for NoCallback { + type Error = crate::Error; + type AccountId = T::AccountId; + type BlockNumber = T::BlockNumber; + + fn can_handle(_: &QueryType) -> bool { + false + } + + fn on_callback( + _: impl Into, + _: ResponseInfo, + ) -> Result { + Ok(Weight::zero()) + } +} From b172aa6079689b0cd259cd4ba055f0b90d468bc6 Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Wed, 17 May 2023 16:10:38 +0530 Subject: [PATCH 14/15] wip: update simulator tests --- frame/pallet-xcm-transactor/Cargo.toml | 16 ++- .../xcm-simulator/src/lib.rs | 122 +++++++++--------- .../xcm-simulator/src/mocks/parachain.rs | 17 ++- 3 files changed, 88 insertions(+), 67 deletions(-) diff --git a/frame/pallet-xcm-transactor/Cargo.toml b/frame/pallet-xcm-transactor/Cargo.toml index 169541b0..81e7d32c 100644 --- a/frame/pallet-xcm-transactor/Cargo.toml +++ b/frame/pallet-xcm-transactor/Cargo.toml @@ -21,6 +21,7 @@ sp-runtime = { workspace = true } sp-std = { workspace = true } # frame dependencies +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } @@ -33,10 +34,19 @@ xcm = { workspace = true } xcm-executor = { workspace = true } # types -xcm-ce-primitives = { path = "./primitives", default-features = false} +xcm-ce-primitives = { path = "./primitives", default-features = false } + +[dev-dependencies] +pallet-balances = { workspace = true } +pallet-insecure-randomness-collective-flip = { workspace = true } +pallet-timestamp = { workspace = true } +polkadot-core-primitives = { workspace = true } +polkadot-parachain = { workspace = true } +polkadot-runtime-parachains = { workspace = true } +xcm-builder = { workspace = true, features = ["std", "runtime-benchmarks"] } [features] -default = ["std"] +default = ["std", "runtime-benchmarks"] std = [ "num_enum/std", "parity-scale-codec/std", @@ -47,6 +57,7 @@ std = [ "sp-std/std", "frame-support/std", "frame-system/std", + "frame-benchmarking?/std", "xcm/std", "xcm-executor/std", "pallet-xcm/std", @@ -54,6 +65,7 @@ std = [ "xcm-ce-primitives/std", ] runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", diff --git a/frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs b/frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs index 7aeb4791..991f521c 100644 --- a/frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs +++ b/frame/pallet-xcm-transactor/xcm-simulator/src/lib.rs @@ -23,7 +23,7 @@ mod mocks; mod tests { use crate::mocks::{parachain, *}; - use frame_support::{assert_ok, traits::fungible::Inspect, weights::Weight}; + use frame_support::{assert_ok, weights::Weight}; use pallet_contracts::Determinism; use pallet_xcm_transactor::{ chain_extension::{ValidateSendInput, XcmCeError as XcmCEError}, @@ -40,7 +40,7 @@ mod tests { const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); const SELECTOR_CONSTRUCTOR: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFF]; - const SELECTOR_EXECUTE: [u8; 4] = [0x11, 0x11, 0x11, 0x11]; + // const SELECTOR_EXECUTE: [u8; 4] = [0x11, 0x11, 0x11, 0x11]; const SELECTOR_SEND: [u8; 4] = [0x22, 0x22, 0x22, 0x22]; const SELECTOR_QUERY: [u8; 4] = [0x33, 0x33, 0x33, 0x33]; const SELECTOR_HANDLE_RESPONSE: [u8; 4] = [0x55, 0x55, 0x55, 0x55]; @@ -102,65 +102,65 @@ mod tests { Fixture { basic_flip_id } } - /// Execute XCM from contract via CE - #[test] - fn test_ce_execute() { - MockNet::reset(); - - let Fixture { - basic_flip_id: contract_id, - } = xcm_flipper_fixture(); - - // - // check the execute - // - ParaA::execute_with(|| { - let transfer_amount = 100_000; - // transfer some native to contract - assert_ok!(ParachainBalances::transfer( - parachain::RuntimeOrigin::signed(ALICE), - contract_id.clone(), - transfer_amount, - )); - - let xcm: Xcm<()> = Xcm(vec![ - WithdrawAsset((Here, transfer_amount).into()), - BuyExecution { - fees: (Here, transfer_amount).into(), - weight_limit: Unlimited, - }, - DepositAsset { - assets: All.into(), - beneficiary: AccountId32 { - network: None, - id: ALICE.into(), - } - .into(), - }, - ]); - - // run execute in contract - let alice_balance_before = ParachainBalances::balance(&ALICE.into()); - let (res, _, _) = - call_contract_method::, ()>>( - ALICE.into(), - contract_id.clone(), - 0, - GAS_LIMIT, - None, - [SELECTOR_EXECUTE.to_vec(), VersionedXcm::V3(xcm).encode()].concat(), - true, - ); - - assert_eq!(res, Ok(Ok(Weight::from_parts(30, 0)))); - assert!( - // TODO: since bare_call doesn't charge, use call - ParachainBalances::balance(&ALICE.into()) == alice_balance_before + transfer_amount - ); - }); - } - - /// Send the XCM and handle response callback via CE + // /// Execute XCM from contract via CE + // #[test] + // fn test_ce_execute() { + // MockNet::reset(); + + // let Fixture { + // basic_flip_id: contract_id, + // } = xcm_flipper_fixture(); + + // // + // // check the execute + // // + // ParaA::execute_with(|| { + // let transfer_amount = 100_000; + // // transfer some native to contract + // assert_ok!(ParachainBalances::transfer( + // parachain::RuntimeOrigin::signed(ALICE), + // contract_id.clone(), + // transfer_amount, + // )); + + // let xcm: Xcm<()> = Xcm(vec![ + // WithdrawAsset((Here, transfer_amount).into()), + // BuyExecution { + // fees: (Here, transfer_amount).into(), + // weight_limit: Unlimited, + // }, + // DepositAsset { + // assets: All.into(), + // beneficiary: AccountId32 { + // network: None, + // id: ALICE.into(), + // } + // .into(), + // }, + // ]); + + // // run execute in contract + // let alice_balance_before = ParachainBalances::balance(&ALICE.into()); + // let (res, _, _) = + // call_contract_method::, ()>>( + // ALICE.into(), + // contract_id.clone(), + // 0, + // Weight::from_parts(u64::MAX, 1024 * 1024 * 10), + // None, + // [SELECTOR_EXECUTE.to_vec(), VersionedXcm::V3(xcm).encode()].concat(), + // true, + // ); + + // assert_eq!(res, Ok(Ok(Weight::from_parts(30, 0)))); + // assert!( + // // TODO: since bare_call doesn't charge, use call + // ParachainBalances::balance(&ALICE.into()) == alice_balance_before + transfer_amount + // ); + // }); + // } + + // /// Send the XCM and handle response callback via CE #[test] fn test_ce_wasm_callback() { MockNet::reset(); diff --git a/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/parachain.rs b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/parachain.rs index 8e1db888..057edb4f 100644 --- a/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/parachain.rs +++ b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/parachain.rs @@ -37,6 +37,7 @@ use frame_system::{ EnsureSigned, }; use pallet_contracts::chain_extension::RegisteredChainExtension; +use pallet_xcm_transactor::chain_extension::{XCMExtension, XCM_EXTENSION_ID}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use polkadot_core_primitives::BlakeTwo256; use sp_core::{ConstBool, H256}; @@ -224,7 +225,7 @@ impl pallet_contracts::Config for Runtime { /// We are not using the pallet_transaction_payment for simplicity type WeightPrice = Self; type WeightInfo = pallet_contracts::weights::SubstrateWeight; - type ChainExtension = pallet_xcm_transactor::chain_extension::XCMExtension; + type ChainExtension = XCMExtension>; type DeletionQueueDepth = ConstU32<128>; type DeletionWeightLimit = DeletionWeightLimit; type Schedule = Schedule; @@ -461,6 +462,11 @@ impl mock_msg_queue::Config for Runtime { pub type LocalOriginToLocation = SignedToAccountId32; +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parachain(1000).into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SendXcmOrigin = EnsureXcmOrigin; @@ -483,6 +489,8 @@ impl pallet_xcm::Config for Runtime { type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<0>; type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -499,12 +507,13 @@ impl pallet_xcm_transactor::Config for Runtime { type CallbackHandler = XcmTransact; type RegisterQueryOrigin = EnsureXcmOrigin; type MaxCallbackWeight = CallbackGasLimit; + type WeightInfo = pallet_xcm_transactor::SubstrateWeight; } -impl RegisteredChainExtension - for pallet_xcm_transactor::chain_extension::XCMExtension +impl RegisteredChainExtension + for XCMExtension { - const ID: u16 = pallet_xcm_transactor::chain_extension::XCM_EXTENSION_ID; + const ID: u16 = XCM_EXTENSION_ID; } construct_runtime!( From 75cc05671cbf7ec90807f2af216ac9d58d9925af Mon Sep 17 00:00:00 2001 From: Ashutosh Varma Date: Wed, 17 May 2023 20:22:43 +0530 Subject: [PATCH 15/15] wip: fix benchamrks features build --- frame/pallet-xcm-transactor/Cargo.toml | 5 +++-- frame/pallet-xcm-transactor/src/traits.rs | 1 - .../xcm-simulator/src/mocks/relay_chain.rs | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/frame/pallet-xcm-transactor/Cargo.toml b/frame/pallet-xcm-transactor/Cargo.toml index 81e7d32c..cb654866 100644 --- a/frame/pallet-xcm-transactor/Cargo.toml +++ b/frame/pallet-xcm-transactor/Cargo.toml @@ -43,10 +43,10 @@ pallet-timestamp = { workspace = true } polkadot-core-primitives = { workspace = true } polkadot-parachain = { workspace = true } polkadot-runtime-parachains = { workspace = true } -xcm-builder = { workspace = true, features = ["std", "runtime-benchmarks"] } +xcm-builder = { workspace = true, features = ["std"] } [features] -default = ["std", "runtime-benchmarks"] +default = ["std"] std = [ "num_enum/std", "parity-scale-codec/std", @@ -70,6 +70,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "pallet-contracts/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/frame/pallet-xcm-transactor/src/traits.rs b/frame/pallet-xcm-transactor/src/traits.rs index d8bea5e5..aed00985 100644 --- a/frame/pallet-xcm-transactor/src/traits.rs +++ b/frame/pallet-xcm-transactor/src/traits.rs @@ -57,7 +57,6 @@ pub trait OnCallback { ) -> Result; } - /// OnCallback implementation that does not supports any callback /// Use this to disable callbacks pub struct NoCallback(PhantomData); diff --git a/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/relay_chain.rs b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/relay_chain.rs index 00a208da..1e2cbd98 100644 --- a/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/relay_chain.rs +++ b/frame/pallet-xcm-transactor/xcm-simulator/src/mocks/relay_chain.rs @@ -155,6 +155,11 @@ pub type LocalOriginToLocation = SignedToAccountId32,); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parent.into()); +} + impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; @@ -178,6 +183,8 @@ impl pallet_xcm::Config for Runtime { type SovereignAccountOf = LocationToAccountId; type MaxLockers = ConstU32<0>; type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; } parameter_types! {