From 18ff2b996c8c790f0ea6eac3e34a2d89a92563fc Mon Sep 17 00:00:00 2001 From: Shaun Wang Date: Fri, 28 Jul 2023 15:23:57 +1200 Subject: [PATCH] `pallet-xvm` refactor (#980) * pallet-xvm refactor. * Add TODOs. * Update design. * Keep XVM call interface unified. * Renaming. * update & integration. * Update xvm precompiles mock & tests. * Replace 'UnknownError' with concrete errors. * Update CE & precompile. * Clean up. * Benchmarks and mock. * Updates for polkadot-v0.9.43. * Fix benchmarks. * Add benchmarking result and weight info. * Add license header to weight.rs. * Add pallet description docstring. * Record gas cost in XVM precompile. * Less weight is available with overheads cost. * Trace Ethereum transact result. * Handle record cost result. * Bump Shibuya semver and spec versoin. * Apply review suggestions. * Update with new benchmarking result. * Improve XVM call benchmarking. * Make local/shibuya/shiden/astar runtimes and client have the same semver. * Update with new benchmarking result. --- Cargo.lock | 28 ++- Cargo.toml | 2 +- bin/collator/Cargo.toml | 2 +- chain-extensions/types/xvm/Cargo.toml | 2 + chain-extensions/types/xvm/src/lib.rs | 40 ++-- chain-extensions/xvm/Cargo.toml | 6 +- chain-extensions/xvm/src/lib.rs | 68 +++--- pallets/ethereum-checked/src/lib.rs | 2 +- pallets/pallet-xvm/src/evm.rs | 97 -------- pallets/pallet-xvm/src/lib.rs | 229 ------------------- pallets/pallet-xvm/src/pallet/mod.rs | 160 -------------- pallets/pallet-xvm/src/wasm.rs | 94 -------- pallets/{pallet-xvm => xvm}/Cargo.toml | 30 +-- pallets/xvm/src/benchmarking.rs | 80 +++++++ pallets/xvm/src/lib.rs | 283 ++++++++++++++++++++++++ pallets/xvm/src/mock.rs | 215 ++++++++++++++++++ pallets/xvm/src/weights.rs | 90 ++++++++ precompiles/xvm/Cargo.toml | 10 +- precompiles/xvm/evm_sdk/flipper.sol | 4 +- precompiles/xvm/src/lib.rs | 68 +++--- precompiles/xvm/src/mock.rs | 52 ++++- precompiles/xvm/src/tests.rs | 10 +- primitives/src/lib.rs | 3 + primitives/src/xvm.rs | 112 ++++++++++ runtime/astar/Cargo.toml | 2 +- runtime/local/Cargo.toml | 4 +- runtime/local/src/chain_extensions.rs | 7 +- runtime/local/src/lib.rs | 14 +- runtime/local/src/precompiles.rs | 6 +- runtime/shibuya/Cargo.toml | 5 +- runtime/shibuya/src/chain_extensions.rs | 7 +- runtime/shibuya/src/lib.rs | 25 +-- runtime/shibuya/src/precompiles.rs | 6 +- runtime/shiden/Cargo.toml | 2 +- 34 files changed, 1013 insertions(+), 752 deletions(-) delete mode 100644 pallets/pallet-xvm/src/evm.rs delete mode 100644 pallets/pallet-xvm/src/lib.rs delete mode 100644 pallets/pallet-xvm/src/pallet/mod.rs delete mode 100644 pallets/pallet-xvm/src/wasm.rs rename pallets/{pallet-xvm => xvm}/Cargo.toml (64%) create mode 100644 pallets/xvm/src/benchmarking.rs create mode 100644 pallets/xvm/src/lib.rs create mode 100644 pallets/xvm/src/mock.rs create mode 100644 pallets/xvm/src/weights.rs create mode 100644 primitives/src/xvm.rs diff --git a/Cargo.lock b/Cargo.lock index 239307491e..004fee0ca2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -404,7 +404,7 @@ dependencies = [ [[package]] name = "astar-collator" -version = "5.15.0" +version = "5.16.0" dependencies = [ "astar-primitives", "astar-runtime", @@ -529,7 +529,7 @@ dependencies = [ [[package]] name = "astar-runtime" -version = "5.15.0" +version = "5.16.0" dependencies = [ "array-bytes 6.1.0", "astar-primitives", @@ -5475,7 +5475,7 @@ checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "local-runtime" -version = "5.15.0" +version = "5.16.0" dependencies = [ "array-bytes 6.1.0", "astar-primitives", @@ -6901,15 +6901,15 @@ dependencies = [ [[package]] name = "pallet-chain-extension-xvm" -version = "0.1.0" +version = "0.1.1" dependencies = [ + "astar-primitives", "frame-support", "frame-system", "log", "num-traits", "pallet-contracts", "pallet-contracts-primitives", - "pallet-xvm", "parity-scale-codec", "scale-info", "sp-core", @@ -7460,8 +7460,9 @@ dependencies = [ [[package]] name = "pallet-evm-precompile-xvm" -version = "0.1.0" +version = "0.1.1" dependencies = [ + "astar-primitives", "derive_more", "fp-evm", "frame-support", @@ -7472,7 +7473,6 @@ dependencies = [ "pallet-balances", "pallet-evm", "pallet-timestamp", - "pallet-xvm", "parity-scale-codec", "precompile-utils", "scale-info", @@ -8231,21 +8231,24 @@ dependencies = [ [[package]] name = "pallet-xvm" -version = "0.2.1" +version = "0.2.2" dependencies = [ "astar-primitives", + "fp-evm", "frame-benchmarking", "frame-support", "frame-system", - "impl-trait-for-tuples", "log", + "pallet-balances", "pallet-contracts", - "pallet-ethereum-checked", "pallet-evm", + "pallet-insecure-randomness-collective-flip", + "pallet-timestamp", "parity-scale-codec", "scale-info", "serde", "sp-core", + "sp-io", "sp-runtime", "sp-std", ] @@ -12296,7 +12299,7 @@ dependencies = [ [[package]] name = "shibuya-runtime" -version = "5.15.0" +version = "5.16.0" dependencies = [ "array-bytes 6.1.0", "astar-primitives", @@ -12404,7 +12407,7 @@ dependencies = [ [[package]] name = "shiden-runtime" -version = "5.15.0" +version = "5.16.0" dependencies = [ "array-bytes 6.1.0", "astar-primitives", @@ -15860,6 +15863,7 @@ dependencies = [ name = "xvm-chain-extension-types" version = "0.1.0" dependencies = [ + "astar-primitives", "parity-scale-codec", "scale-info", "sp-runtime", diff --git a/Cargo.toml b/Cargo.toml index abf1e25a9f..566fadbe41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -273,7 +273,7 @@ pallet-collator-selection = { path = "./pallets/collator-selection", default-fea pallet-custom-signatures = { path = "./pallets/custom-signatures", default-features = false } pallet-dapps-staking = { path = "./pallets/dapps-staking", default-features = false } pallet-xc-asset-config = { path = "./pallets/xc-asset-config", default-features = false } -pallet-xvm = { path = "./pallets/pallet-xvm", default-features = false } +pallet-xvm = { path = "./pallets/xvm", default-features = false } pallet-xcm = { path = "./pallets/pallet-xcm", default-features = false } pallet-ethereum-checked = { path = "./pallets/ethereum-checked", default-features = false } diff --git a/bin/collator/Cargo.toml b/bin/collator/Cargo.toml index 9584fc1368..d459cc8ba4 100644 --- a/bin/collator/Cargo.toml +++ b/bin/collator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "astar-collator" -version = "5.15.0" +version = "5.16.0" description = "Astar collator implementation in Rust." build = "build.rs" default-run = "astar-collator" diff --git a/chain-extensions/types/xvm/Cargo.toml b/chain-extensions/types/xvm/Cargo.toml index dd9129cc2b..e9888174c8 100644 --- a/chain-extensions/types/xvm/Cargo.toml +++ b/chain-extensions/types/xvm/Cargo.toml @@ -9,6 +9,7 @@ homepage.workspace = true repository.workspace = true [dependencies] +astar-primitives = { workspace = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } sp-runtime = { workspace = true } @@ -21,4 +22,5 @@ std = [ "scale-info/std", "sp-runtime/std", "sp-std/std", + "astar-primitives/std", ] diff --git a/chain-extensions/types/xvm/src/lib.rs b/chain-extensions/types/xvm/src/lib.rs index 76443104fd..d23ccd9102 100644 --- a/chain-extensions/types/xvm/src/lib.rs +++ b/chain-extensions/types/xvm/src/lib.rs @@ -18,31 +18,42 @@ #![cfg_attr(not(feature = "std"), no_std)] +use astar_primitives::xvm::CallError; use parity_scale_codec::{Decode, Encode}; -use sp_runtime::{DispatchError, ModuleError}; use sp_std::vec::Vec; #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] #[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Debug)] pub enum XvmExecutionResult { /// Success - Success = 0, - // TODO: expand this with concrete XVM errors - /// Error not (yet) covered by a dedidacted code - UnknownError = 255, + Ok, + /// Failure + Err(u32), } -impl TryFrom for XvmExecutionResult { - type Error = DispatchError; +impl From for XvmExecutionResult { + fn from(input: CallError) -> Self { + use CallError::*; - fn try_from(input: DispatchError) -> Result { - let _error_text = match input { - DispatchError::Module(ModuleError { message, .. }) => message, - _ => Some("No module error Info"), + // `0` is reserved for `Ok` + let error_code = match input { + InvalidVmId => 1, + SameVmCallNotAllowed => 2, + InvalidTarget => 3, + InputTooLarge => 4, + BadOrigin => 5, + ExecutionFailed(_) => 6, }; + Self::Err(error_code) + } +} - // TODO: expand this with concrete XVM errors (see dapps-staking types for example) - Ok(XvmExecutionResult::UnknownError) +impl From for u32 { + fn from(input: XvmExecutionResult) -> Self { + match input { + XvmExecutionResult::Ok => 0, + XvmExecutionResult::Err(code) => code, + } } } @@ -55,6 +66,3 @@ pub struct XvmCallArgs { /// Encoded call params pub input: Vec, } - -pub const FRONTIER_VM_ID: u8 = 0x0F; -pub const PARITY_WASM_VM_ID: u8 = 0x1F; diff --git a/chain-extensions/xvm/Cargo.toml b/chain-extensions/xvm/Cargo.toml index ae661fa79d..ef28b90dbd 100644 --- a/chain-extensions/xvm/Cargo.toml +++ b/chain-extensions/xvm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-chain-extension-xvm" -version = "0.1.0" +version = "0.1.1" license = "Apache-2.0" description = "Chain extension for XVM" authors.workspace = true @@ -22,7 +22,7 @@ sp-runtime = { workspace = true } sp-std = { workspace = true } # Astar -pallet-xvm = { workspace = true } +astar-primitives = { workspace = true } xvm-chain-extension-types = { workspace = true } [features] @@ -39,5 +39,5 @@ std = [ "sp-core/std", "sp-runtime/std", # Astar - "pallet-xvm/std", + "astar-primitives/std", ] diff --git a/chain-extensions/xvm/src/lib.rs b/chain-extensions/xvm/src/lib.rs index 91aa401cd6..9696fc6d7a 100644 --- a/chain-extensions/xvm/src/lib.rs +++ b/chain-extensions/xvm/src/lib.rs @@ -18,18 +18,18 @@ #![cfg_attr(not(feature = "std"), no_std)] +use astar_primitives::xvm::{CallError, Context, VmId, XvmCall}; use frame_support::dispatch::Encode; use pallet_contracts::{ chain_extension::{ChainExtension, Environment, Ext, InitState, RetVal}, Origin, }; -use pallet_xvm::XvmContext; use sp_runtime::DispatchError; use sp_std::marker::PhantomData; use xvm_chain_extension_types::{XvmCallArgs, XvmExecutionResult}; enum XvmFuncId { - XvmCall, + Call, // TODO: expand with other calls too } @@ -38,7 +38,7 @@ impl TryFrom for XvmFuncId { fn try_from(value: u16) -> Result { match value { - 1 => Ok(XvmFuncId::XvmCall), + 1 => Ok(XvmFuncId::Call), _ => Err(DispatchError::Other( "Unsupported func id in Xvm chain extension", )), @@ -47,17 +47,18 @@ impl TryFrom for XvmFuncId { } /// XVM chain extension. -pub struct XvmExtension(PhantomData); +pub struct XvmExtension(PhantomData<(T, XC)>); -impl Default for XvmExtension { +impl Default for XvmExtension { fn default() -> Self { XvmExtension(PhantomData) } } -impl ChainExtension for XvmExtension +impl ChainExtension for XvmExtension where - T: pallet_contracts::Config + pallet_xvm::Config, + T: pallet_contracts::Config, + XC: XvmCall, { fn call(&mut self, env: Environment) -> Result where @@ -67,13 +68,13 @@ where let mut env = env.buf_in_buf_out(); match func_id { - XvmFuncId::XvmCall => { + XvmFuncId::Call => { // We need to immediately charge for the worst case scenario. Gas equals Weight in pallet-contracts context. - let remaining_weight = env.ext().gas_meter().gas_left(); + let weight_limit = env.ext().gas_meter().gas_left(); + // TODO: track proof size in align fees ticket // We don't track used proof size, so we can't refund after. // So we will charge a 32KB dummy value as a temporary replacement. - let charged_weight = - env.charge_weight(remaining_weight.set_proof_size(32 * 1024))?; + let charged_weight = env.charge_weight(weight_limit.set_proof_size(32 * 1024))?; let caller = match env.ext().caller().clone() { Origin::Signed(address) => address, @@ -82,8 +83,9 @@ where target: "xvm-extension::xvm_call", "root origin not supported" ); - // TODO: expand XvmErrors with BadOrigin - return Ok(RetVal::Converging(XvmExecutionResult::UnknownError as u32)); + return Ok(RetVal::Converging( + XvmExecutionResult::from(CallError::BadOrigin).into(), + )); } }; @@ -91,38 +93,50 @@ where let _origin_address = env.ext().address().clone(); let _value = env.ext().value_transferred(); - let xvm_context = XvmContext { - id: vm_id, - max_weight: remaining_weight, - env: None, + let xvm_context = Context { + source_vm_id: VmId::Wasm, + weight_limit, }; - let call_result = - pallet_xvm::Pallet::::xvm_bare_call(xvm_context, caller, to, input); + let vm_id = { + match TryInto::::try_into(vm_id) { + Ok(id) => id, + Err(err) => { + // TODO: Propagate error + let result = Into::::into(err); + return Ok(RetVal::Converging(result.into())); + } + } + }; + let call_result = XC::call(xvm_context, vm_id, caller, to, input); - let actual_weight = pallet_xvm::consumed_weight(&call_result); + let actual_weight = match call_result { + Ok(ref info) => info.used_weight, + Err(ref err) => err.used_weight, + }; env.adjust_weight(charged_weight, actual_weight); match call_result { - Ok(success) => { + Ok(info) => { log::trace!( target: "xvm-extension::xvm_call", - "success: {:?}", success + "info: {:?}", info ); - let buffer: sp_std::vec::Vec<_> = success.output().encode(); + let buffer: sp_std::vec::Vec<_> = info.output.encode(); env.write(&buffer, false, None)?; - Ok(RetVal::Converging(XvmExecutionResult::Success as u32)) + Ok(RetVal::Converging(XvmExecutionResult::Ok.into())) } - Err(failure) => { + Err(err) => { log::trace!( target: "xvm-extension::xvm_call", - "failure: {:?}", failure + "err: {:?}", err ); // TODO Propagate error - Ok(RetVal::Converging(XvmExecutionResult::UnknownError as u32)) + let result = Into::::into(err.error); + Ok(RetVal::Converging(result.into())) } } } diff --git a/pallets/ethereum-checked/src/lib.rs b/pallets/ethereum-checked/src/lib.rs index 02a3c9aac1..506bcf5fdd 100644 --- a/pallets/ethereum-checked/src/lib.rs +++ b/pallets/ethereum-checked/src/lib.rs @@ -20,7 +20,7 @@ //! //! ## Overview //! -//! A `pallet-ethererum` like pallet that execute transactions from checked source, +//! A `pallet-ethereum like pallet that execute transactions from checked source, //! like XCM remote call, cross-VM call, etc. Only `Call` transactions are supported //! (no `Create`). //! diff --git a/pallets/pallet-xvm/src/evm.rs b/pallets/pallet-xvm/src/evm.rs deleted file mode 100644 index 383e43a862..0000000000 --- a/pallets/pallet-xvm/src/evm.rs +++ /dev/null @@ -1,97 +0,0 @@ -// 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 . - -//! EVM support for XVM pallet. - -use crate::*; -use frame_support::{traits::ConstU32, BoundedVec}; -use pallet_evm::GasWeightMapping; -use sp_core::U256; -use sp_runtime::traits::Get; - -use astar_primitives::ethereum_checked::{ - AccountMapping as AccountMappingT, CheckedEthereumTransact, CheckedEthereumTx, - MAX_ETHEREUM_TX_INPUT_SIZE, -}; - -/// EVM adapter for XVM calls. -/// -/// This adapter supports generic XVM calls and encode it into EVM native calls -/// using Solidity ABI codec (https://docs.soliditylang.org/en/v0.8.16/abi-spec.html). -pub struct EVM(sp_std::marker::PhantomData<(I, T, Transact)>); - -impl SyncVM for EVM -where - I: Get, - T: frame_system::Config + pallet_evm::Config + pallet_ethereum_checked::Config, - Transact: CheckedEthereumTransact, -{ - fn id() -> VmId { - I::get() - } - - fn xvm_call(context: XvmContext, from: T::AccountId, to: Vec, input: Vec) -> XvmResult { - log::trace!( - target: "xvm::EVM::xvm_call", - "Start EVM XVM: {:?}, {:?}, {:?}", - from, to, input, - ); - - let value = U256::zero(); - let gas_limit = T::GasWeightMapping::weight_to_gas(context.max_weight); - - let source = T::AccountMapping::into_h160(from); - let target = Decode::decode(&mut to.as_ref()).map_err(|_| XvmCallError { - error: XvmError::EncodingFailure, - consumed_weight: PLACEHOLDER_WEIGHT, - })?; - let bounded_input = BoundedVec::>::try_from(input) - .map_err(|_| XvmCallError { - error: XvmError::InputTooLarge, - consumed_weight: PLACEHOLDER_WEIGHT, - })?; - - let (post_dispatch_info, call_info) = Transact::xvm_transact( - source, - CheckedEthereumTx { - gas_limit: U256::from(gas_limit), - target, - value, - input: bounded_input, - maybe_access_list: None, - }, - ) - .map_err(|e| { - let consumed_weight = e.post_info.actual_weight.unwrap_or_default(); - XvmCallError { - error: XvmError::ExecutionError(Into::<&str>::into(e.error).into()), - consumed_weight, - } - })?; - - log::trace!( - target: "xvm::EVM::xvm_call", - "EVM XVM call result: exit_reason: {:?}, used_gas: {:?}", call_info.exit_reason, call_info.used_gas, - ); - - Ok(XvmCallOk { - output: call_info.value, - consumed_weight: post_dispatch_info.actual_weight.unwrap_or_default(), - }) - } -} diff --git a/pallets/pallet-xvm/src/lib.rs b/pallets/pallet-xvm/src/lib.rs deleted file mode 100644 index c38b8bcb64..0000000000 --- a/pallets/pallet-xvm/src/lib.rs +++ /dev/null @@ -1,229 +0,0 @@ -// 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 . - -//! # XVM pallet -//! -//! ## Overview -//! -//! ## Interface -//! -//! ### Dispatchable Function -//! -//! -//! ### Other -//! -//! - -#![cfg_attr(not(feature = "std"), no_std)] - -use frame_support::weights::Weight; -use parity_scale_codec::{Decode, Encode}; -use sp_runtime::{traits::Member, RuntimeDebug}; -use sp_std::prelude::*; - -pub mod pallet; -pub use pallet::pallet::*; - -/// EVM call adapter. -#[cfg(feature = "evm")] -pub mod evm; - -/// Wasm call adapter. -#[cfg(feature = "wasm")] -pub mod wasm; - -/// Unique VM identifier. -pub type VmId = u8; - -// TODO: remove later after solution is properly benchmarked -// Just a arbitrary weight constant to avoid having ZERO weight in some parts of execution -pub const PLACEHOLDER_WEIGHT: Weight = Weight::from_parts(1_000_000, 0); - -#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] -pub enum XvmError { - VmNotRecognized, - EncodingFailure, - ContextConversionFailed, - OutOfGas, - ExecutionError(Vec), - InputTooLarge, - // extend this list as part of improved error handling -} - -// TODO: Currently our precompile/chain-extension calls rely on direct `Call` usage of XVM pallet. -// This is perfectly fine when we're just calling a function in other VM and are interested whether the call was -// successful or not. -// -// Problem arises IF we want to get back arbitrary read value from the other VM - `DispatchResultWithPostInfo` isn't enough for this. -// We need to receive back a concrete value back from the other VM. - -/// Denotes a successful XVM call execution -#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] -pub struct XvmCallOk { - /// Output of XVM call. E.g. if call was a query, this will contain query response. - output: Vec, - /// Total consumed weight. This is in context of Substrate (1 unit of weight ~ 1 ps of execution time) - consumed_weight: Weight, -} - -impl XvmCallOk { - pub fn output(&self) -> &[u8] { - &self.output - } -} - -/// Denotes an successful XVM call execution -#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] -pub struct XvmCallError { - /// Result of XVM call - // TODO: use XvmError enum from pallet? Perhaps that's a better approach. Or at least provide mapping? - error: XvmError, - /// Total consumed weight. This is in context of Substrate (1 unit of weight ~ 1 ps of execution time) - consumed_weight: Weight, -} - -impl XvmCallError { - pub fn error(&self) -> &XvmError { - &self.error - } -} - -/// Result for executing X-VM calls -pub type XvmResult = Result; - -pub fn consumed_weight(result: &XvmResult) -> Weight { - match result { - Ok(res) => res.consumed_weight, - Err(err) => err.consumed_weight, - } -} - -/// XVM context consist of unique ID and optional execution arguments. -#[derive(Default, PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] -pub struct XvmContext { - /// Identifier (should be unique for each VM in tuple). - pub id: VmId, - /// Max allowed weight for the call - pub max_weight: Weight, - /// Encoded VM execution environment. - pub env: Option>, -} - -/// The engine that support synchronous smart contract execution. -/// For example, EVM. -pub trait SyncVM { - /// Unique VM identifier. - fn id() -> VmId; - - /// Make a call to VM contract and return result or error. - /// - /// - fn xvm_call(context: XvmContext, from: AccountId, to: Vec, input: Vec) -> XvmResult; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl SyncVM for Tuple { - fn id() -> VmId { - Default::default() - } - - fn xvm_call(context: XvmContext, from: AccountId, to: Vec, input: Vec) -> XvmResult { - for_tuples!( #( - if Tuple::id() == context.id { - log::trace!( - target: "xvm::SyncVm::xvm_call", - "VM found, run XVM call: {:?}, {:?}, {:?}, {:?}", - context, from, to, input, - ); - return Tuple::xvm_call(context, from, to, input) - } - )* ); - log::trace!( - target: "xvm::SyncVm::xvm_call", - "VM with ID {:?} not found", context.id - ); - Err(XvmCallError { - error: XvmError::VmNotRecognized, - consumed_weight: PLACEHOLDER_WEIGHT, - }) - } -} - -/// The engine that support asynchronous smart contract execution. -/// For example, XCVM. -pub trait AsyncVM { - /// Unique VM identifier. - fn id() -> VmId; - - /// Send a message. - fn xvm_send(context: XvmContext, from: AccountId, to: Vec, message: Vec) -> XvmResult; - - /// Query for incoming messages. - fn xvm_query(context: XvmContext, inbox: AccountId) -> XvmResult; -} - -#[impl_trait_for_tuples::impl_for_tuples(30)] -impl AsyncVM for Tuple { - fn id() -> VmId { - Default::default() - } - - fn xvm_send(context: XvmContext, from: AccountId, to: Vec, message: Vec) -> XvmResult { - for_tuples!( #( - if Tuple::id() == context.id { - log::trace!( - target: "xvm::AsyncVM::xvm_send", - "VM found, send message: {:?}, {:?}, {:?}, {:?}", - context, from, to, message, - ); - return Tuple::xvm_send(context, from, to, message) - } - )* ); - log::trace!( - target: "xvm::AsyncVM::xvm_send", - "VM with ID {:?} not found", context.id - ); - - Err(XvmCallError { - error: XvmError::VmNotRecognized, - consumed_weight: PLACEHOLDER_WEIGHT, - }) - } - - fn xvm_query(context: XvmContext, inbox: AccountId) -> XvmResult { - for_tuples!( #( - if Tuple::id() == context.id { - log::trace!( - target: "xvm::AsyncVM::xvm_query", - "VM found, query messages: {:?} {:?}", - context, inbox, - ); - return Tuple::xvm_query(context, inbox) - } - )* ); - log::trace!( - target: "xvm::AsyncVM::xvm_query", - "VM with ID {:?} not found", context.id - ); - - Err(XvmCallError { - error: XvmError::VmNotRecognized, - consumed_weight: PLACEHOLDER_WEIGHT, - }) - } -} diff --git a/pallets/pallet-xvm/src/pallet/mod.rs b/pallets/pallet-xvm/src/pallet/mod.rs deleted file mode 100644 index b34d4090a8..0000000000 --- a/pallets/pallet-xvm/src/pallet/mod.rs +++ /dev/null @@ -1,160 +0,0 @@ -// 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 . - -//! # XVM pallet -//! -//! ## Overview -//! -//! -//! ## Interface -//! -//! ### Dispatchable Function -//! -//! -//! ### Other -//! -//! - -#[frame_support::pallet] -#[allow(clippy::module_inception)] -pub mod pallet { - use crate::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(PhantomData); - - #[pallet::config] - pub trait Config: frame_system::Config { - /// Supported synchronous VM list, for example (EVM, WASM) - type SyncVM: SyncVM; - /// Supported asynchronous VM list. - type AsyncVM: AsyncVM; - /// General event type. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - } - - #[pallet::error] - pub enum Error {} - - #[pallet::event] - #[pallet::generate_deposit(pub(crate) fn deposit_event)] - pub enum Event { - XvmCall { result: Result, XvmError> }, - XvmSend { result: Result, XvmError> }, - XvmQuery { result: Result, XvmError> }, - } - - impl Pallet { - /// Internal interface for cross-pallet invocation. - /// Essentially does the same thing as `xvm_call`, but a bit differently: - /// - It does not verify origin - /// - It does not use `Dispatchable` API (cannot be called from tx) - /// - It does not deposit event upon completion - /// - It returns `XvmResult` letting the caller get return data directly - pub fn xvm_bare_call( - context: XvmContext, - from: T::AccountId, - to: Vec, - input: Vec, - ) -> XvmResult { - let result = T::SyncVM::xvm_call(context, from, to, input); - - log::trace!( - target: "xvm::pallet::xvm_bare_call", - "Execution result: {:?}", result - ); - - result - } - } - - #[pallet::call] - impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(context.max_weight)] - pub fn xvm_call( - origin: OriginFor, - context: XvmContext, - to: Vec, - input: Vec, - ) -> DispatchResultWithPostInfo { - let from = ensure_signed(origin)?; - - // Executing XVM call logic itself will consume some weight so that should be subtracted from the max allowed weight of XCM call - // TODO: fix - //context.max_weight = context.max_weight - PLACEHOLDER_WEIGHT; - - let result = T::SyncVM::xvm_call(context, from, to, input); - let consumed_weight = consumed_weight(&result); - - log::trace!( - target: "xvm::pallet::xvm_call", - "Execution result: {:?}, consumed_weight: {:?}", result, consumed_weight, - ); - - Self::deposit_event(Event::::XvmCall { - result: match result { - Ok(result) => Ok(result.output), - Err(result) => Err(result.error), - }, - }); - - Ok(Some(consumed_weight).into()) - } - - #[pallet::call_index(1)] - #[pallet::weight(context.max_weight)] - pub fn xvm_send( - origin: OriginFor, - context: XvmContext, - to: Vec, - message: Vec, - ) -> DispatchResultWithPostInfo { - let from = ensure_signed(origin)?; - let result = T::AsyncVM::xvm_send(context, from, to, message); - - Self::deposit_event(Event::::XvmSend { - result: match result { - Ok(result) => Ok(result.output), - Err(result) => Err(result.error), - }, - }); - - Ok(().into()) - } - - #[pallet::call_index(2)] - #[pallet::weight(context.max_weight)] - pub fn xvm_query(origin: OriginFor, context: XvmContext) -> DispatchResultWithPostInfo { - let inbox = ensure_signed(origin)?; - let result = T::AsyncVM::xvm_query(context, inbox); - - Self::deposit_event(Event::::XvmQuery { - result: match result { - Ok(result) => Ok(result.output), - Err(result) => Err(result.error), - }, - }); - - Ok(().into()) - } - } -} diff --git a/pallets/pallet-xvm/src/wasm.rs b/pallets/pallet-xvm/src/wasm.rs deleted file mode 100644 index e83bdf1d9b..0000000000 --- a/pallets/pallet-xvm/src/wasm.rs +++ /dev/null @@ -1,94 +0,0 @@ -// 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 . - -//! WASM (substrate contracts) support for XVM pallet. - -use crate::*; -use frame_support::traits::Currency; -use parity_scale_codec::HasCompact; -use scale_info::TypeInfo; -use sp_runtime::traits::Get; -use sp_runtime::traits::StaticLookup; -use sp_std::fmt::Debug; -pub struct WASM(sp_std::marker::PhantomData<(I, T)>); - -type BalanceOf = <::Currency as Currency< - ::AccountId, ->>::Balance; - -impl SyncVM for WASM -where - I: Get, - T: pallet_contracts::Config + frame_system::Config, - as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, -{ - fn id() -> VmId { - I::get() - } - - fn xvm_call(context: XvmContext, from: T::AccountId, to: Vec, input: Vec) -> XvmResult { - log::trace!( - target: "xvm::WASM::xvm_call", - "Start WASM XVM: {:?}, {:?}, {:?}", - from, to, input, - ); - let gas_limit = context.max_weight; - log::trace!( - target: "xvm::WASM::xvm_call", - "WASM xvm call gas (weight) limit: {:?}", gas_limit); - let dest = Decode::decode(&mut to.as_ref()).map_err(|_| XvmCallError { - error: XvmError::EncodingFailure, - consumed_weight: PLACEHOLDER_WEIGHT, - })?; - - let dest = T::Lookup::lookup(dest).map_err(|error| XvmCallError { - error: XvmError::ExecutionError(Into::<&str>::into(error).into()), - consumed_weight: PLACEHOLDER_WEIGHT, - })?; - let call_result = pallet_contracts::Pallet::::bare_call( - from, // no need to check origin, we consider it signed here - dest, - Default::default(), - gas_limit.into(), - None, - input, - pallet_contracts::DebugInfo::Skip, - pallet_contracts::CollectEvents::Skip, - pallet_contracts::Determinism::Enforced, - ); - - log::trace!( - target: "xvm::WASM::xvm_call", - "WASM XVM call result: {:?}", call_result - ); - - let consumed_weight = call_result.gas_consumed; - - match call_result.result { - Ok(success) => Ok(XvmCallOk { - output: success.data, - consumed_weight, - }), - - Err(error) => Err(XvmCallError { - error: XvmError::ExecutionError(Into::<&str>::into(error).into()), - consumed_weight, - }), - } - } -} diff --git a/pallets/pallet-xvm/Cargo.toml b/pallets/xvm/Cargo.toml similarity index 64% rename from pallets/pallet-xvm/Cargo.toml rename to pallets/xvm/Cargo.toml index 0d0fa29d1c..6f1022bf24 100644 --- a/pallets/pallet-xvm/Cargo.toml +++ b/pallets/xvm/Cargo.toml @@ -1,13 +1,12 @@ [package] name = "pallet-xvm" -version = "0.2.1" +version = "0.2.2" authors.workspace = true edition.workspace = true homepage.workspace = true repository.workspace = true [dependencies] -impl-trait-for-tuples = { workspace = true } log = { workspace = true } serde = { workspace = true, optional = true } @@ -24,43 +23,44 @@ sp-std = { workspace = true } frame-benchmarking = { workspace = true, optional = true } # EVM -pallet-evm = { workspace = true, optional = true } +pallet-evm = { workspace = true } # Substrate WASM VM support -pallet-contracts = { workspace = true, optional = true } +pallet-contracts = { workspace = true } # Astar astar-primitives = { workspace = true } -pallet-ethereum-checked = { workspace = true, optional = true } [dev-dependencies] +fp-evm = { workspace = true } +pallet-balances = { workspace = true, features = ["std"] } +pallet-insecure-randomness-collective-flip = { workspace = true, features = ["std"] } +pallet-timestamp = { workspace = true, features = ["std"] } +sp-io = { workspace = true } [features] default = ["std"] -evm = [ - "pallet-evm", - "pallet-ethereum-checked", -] -wasm = [ - "pallet-contracts", -] std = [ + "log/std", "parity-scale-codec/std", "frame-support/std", "frame-system/std", "pallet-contracts/std", "pallet-evm/std", + "pallet-insecure-randomness-collective-flip/std", "scale-info/std", "serde", "sp-core/std", "sp-runtime/std", "sp-std/std", "astar-primitives/std", - "pallet-ethereum-checked/std", ] runtime-benchmarks = [ "frame-benchmarking", - "pallet-ethereum-checked/runtime-benchmarks", ] -try-runtime = ["frame-support/try-runtime"] +try-runtime = [ + "frame-support/try-runtime", + "pallet-contracts/try-runtime", + "pallet-evm/try-runtime", +] diff --git a/pallets/xvm/src/benchmarking.rs b/pallets/xvm/src/benchmarking.rs new file mode 100644 index 0000000000..97edd7d0d5 --- /dev/null +++ b/pallets/xvm/src/benchmarking.rs @@ -0,0 +1,80 @@ +// 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 super::*; + +use frame_benchmarking::v2::*; +use frame_support::weights::Weight; +use parity_scale_codec::Encode; +use sp_core::H160; +use sp_runtime::MultiAddress; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn evm_call_overheads() { + let context = Context { + source_vm_id: VmId::Wasm, + weight_limit: Weight::from_parts(1_000_000, 1_000_000), + }; + let vm_id = VmId::Evm; + let source = whitelisted_caller(); + let target = H160::repeat_byte(1).encode(); + let input = vec![1, 2, 3]; + + #[block] + { + Pallet::::call_without_execution(context, vm_id, source, target, input).unwrap(); + } + } + + #[benchmark] + fn wasm_call_overheads() { + let context = Context { + source_vm_id: VmId::Evm, + weight_limit: Weight::from_parts(1_000_000, 1_000_000), + }; + let vm_id = VmId::Wasm; + let source = whitelisted_caller(); + let target = MultiAddress::::Id(whitelisted_caller()).encode(); + let input = vec![1, 2, 3]; + + #[block] + { + Pallet::::call_without_execution(context, vm_id, source, target, input).unwrap(); + } + } + + impl_benchmark_test_suite!( + Pallet, + crate::benchmarking::tests::new_test_ext(), + crate::mock::TestRuntime, + ); +} + +#[cfg(test)] +mod tests { + use crate::mock; + use sp_io::TestExternalities; + + pub fn new_test_ext() -> TestExternalities { + mock::ExtBuilder::default().build() + } +} diff --git a/pallets/xvm/src/lib.rs b/pallets/xvm/src/lib.rs new file mode 100644 index 0000000000..5da16856fc --- /dev/null +++ b/pallets/xvm/src/lib.rs @@ -0,0 +1,283 @@ +// 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 . + +//! # XVM pallet +//! +//! A module to provide +//! +//! ## Overview +//! +//! The XVM pallet provides a runtime interface to call different VMs. It currently +//! supports two VMs: EVM and WASM. With further development, more VMs can be added. +//! +//! Together with other functionalities like Chain Extension and precompiles, +//! the XVM pallet enables the runtime to support cross-VM calls. +//! +//! ## Interface +//! +//! ### Implementation +//! +//! - Implements `XvmCall` trait. +//! + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ensure, traits::ConstU32, BoundedVec}; +use pallet_contracts::{CollectEvents, DebugInfo, Determinism}; +use pallet_evm::GasWeightMapping; +use parity_scale_codec::Decode; +use sp_core::U256; +use sp_runtime::traits::StaticLookup; +use sp_std::{marker::PhantomData, prelude::*}; + +use astar_primitives::{ + ethereum_checked::{ + AccountMapping, CheckedEthereumTransact, CheckedEthereumTx, MAX_ETHEREUM_TX_INPUT_SIZE, + }, + xvm::{CallError, CallErrorWithWeight, CallInfo, CallResult, Context, VmId, XvmCall}, +}; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +pub mod weights; +pub use weights::WeightInfo; + +#[cfg(test)] +mod mock; + +pub use pallet::*; + +pub type WeightInfoOf = ::WeightInfo; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_contracts::Config { + /// Mapping from `Account` to `H160`. + type AccountMapping: AccountMapping; + + /// Mapping from Ethereum gas to Substrate weight. + type GasWeightMapping: GasWeightMapping; + + /// `CheckedEthereumTransact` implementation. + type EthereumTransact: CheckedEthereumTransact; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } +} + +impl XvmCall for Pallet { + fn call( + context: Context, + vm_id: VmId, + source: T::AccountId, + target: Vec, + input: Vec, + ) -> CallResult { + Pallet::::do_call(context, vm_id, source, target, input, false) + } +} + +impl Pallet { + fn do_call( + context: Context, + vm_id: VmId, + source: T::AccountId, + target: Vec, + input: Vec, + skip_execution: bool, + ) -> CallResult { + ensure!( + context.source_vm_id != vm_id, + CallErrorWithWeight { + error: CallError::SameVmCallNotAllowed, + used_weight: match vm_id { + VmId::Evm => WeightInfoOf::::evm_call_overheads(), + VmId::Wasm => WeightInfoOf::::wasm_call_overheads(), + }, + } + ); + + match vm_id { + VmId::Evm => Pallet::::evm_call(context, source, target, input, skip_execution), + VmId::Wasm => Pallet::::wasm_call(context, source, target, input, skip_execution), + } + } + + fn evm_call( + context: Context, + source: T::AccountId, + target: Vec, + input: Vec, + skip_execution: bool, + ) -> CallResult { + log::trace!( + target: "xvm::evm_call", + "Calling EVM: {:?} {:?}, {:?}, {:?}", + context, source, target, input, + ); + + let target_decoded = + Decode::decode(&mut target.as_ref()).map_err(|_| CallErrorWithWeight { + error: CallError::InvalidTarget, + used_weight: WeightInfoOf::::evm_call_overheads(), + })?; + let bounded_input = BoundedVec::>::try_from(input) + .map_err(|_| CallErrorWithWeight { + error: CallError::InputTooLarge, + used_weight: WeightInfoOf::::evm_call_overheads(), + })?; + + let value = U256::zero(); + // With overheads, less weight is available. + let weight_limit = context + .weight_limit + .saturating_sub(WeightInfoOf::::evm_call_overheads()); + let gas_limit = U256::from(T::GasWeightMapping::weight_to_gas(weight_limit)); + + let source = T::AccountMapping::into_h160(source); + let tx = CheckedEthereumTx { + gas_limit, + target: target_decoded, + value, + input: bounded_input, + maybe_access_list: None, + }; + + // Note the skip execution check should be exactly before `T::EthereumTransact::xvm_transact` + // to benchmark the correct overheads. + if skip_execution { + return Ok(CallInfo { + output: vec![], + used_weight: WeightInfoOf::::evm_call_overheads(), + }); + } + + let transact_result = T::EthereumTransact::xvm_transact(source, tx); + log::trace!( + target: "xvm::evm_call", + "EVM call result: {:?}", transact_result, + ); + + transact_result + .map(|(post_dispatch_info, call_info)| { + let used_weight = post_dispatch_info + .actual_weight + .unwrap_or_default() + .saturating_add(WeightInfoOf::::evm_call_overheads()); + CallInfo { + output: call_info.value, + used_weight, + } + }) + .map_err(|e| { + let used_weight = e + .post_info + .actual_weight + .unwrap_or_default() + .saturating_add(WeightInfoOf::::evm_call_overheads()); + CallErrorWithWeight { + error: CallError::ExecutionFailed(Into::<&str>::into(e.error).into()), + used_weight, + } + }) + } + + fn wasm_call( + context: Context, + source: T::AccountId, + target: Vec, + input: Vec, + skip_execution: bool, + ) -> CallResult { + log::trace!( + target: "xvm::wasm_call", + "Calling WASM: {:?} {:?}, {:?}, {:?}", + context, source, target, input, + ); + + let dest = { + let error = CallErrorWithWeight { + error: CallError::InvalidTarget, + used_weight: WeightInfoOf::::wasm_call_overheads(), + }; + let decoded = Decode::decode(&mut target.as_ref()).map_err(|_| error.clone())?; + T::Lookup::lookup(decoded).map_err(|_| error) + }?; + + // With overheads, less weight is available. + let weight_limit = context + .weight_limit + .saturating_sub(WeightInfoOf::::wasm_call_overheads()); + let value = Default::default(); + + // Note the skip execution check should be exactly before `pallet_contracts::bare_call` + // to benchmark the correct overheads. + if skip_execution { + return Ok(CallInfo { + output: vec![], + used_weight: WeightInfoOf::::wasm_call_overheads(), + }); + } + + let call_result = pallet_contracts::Pallet::::bare_call( + source, + dest, + value, + weight_limit, + None, + input, + DebugInfo::Skip, + CollectEvents::Skip, + Determinism::Enforced, + ); + log::trace!(target: "xvm::wasm_call", "WASM call result: {:?}", call_result); + + let used_weight = call_result + .gas_consumed + .saturating_add(WeightInfoOf::::wasm_call_overheads()); + match call_result.result { + Ok(success) => Ok(CallInfo { + output: success.data, + used_weight, + }), + Err(error) => Err(CallErrorWithWeight { + error: CallError::ExecutionFailed(Into::<&str>::into(error).into()), + used_weight, + }), + } + } + + #[cfg(feature = "runtime-benchmarks")] + pub fn call_without_execution( + context: Context, + vm_id: VmId, + source: T::AccountId, + target: Vec, + input: Vec, + ) -> CallResult { + Self::do_call(context, vm_id, source, target, input, true) + } +} diff --git a/pallets/xvm/src/mock.rs b/pallets/xvm/src/mock.rs new file mode 100644 index 0000000000..a02472c3b7 --- /dev/null +++ b/pallets/xvm/src/mock.rs @@ -0,0 +1,215 @@ +// 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)] + +use super::*; +use crate as pallet_xvm; + +use fp_evm::{CallInfo as EvmCallInfo, ExitReason, ExitSucceed, UsedGas}; +use frame_support::{ + construct_runtime, + dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}, + pallet_prelude::*, + parameter_types, + sp_io::TestExternalities, + traits::{ConstBool, ConstU128, ConstU64, Nothing}, +}; +use sp_core::{H160, H256}; +use sp_runtime::{ + testing::Header, + traits::{AccountIdLookup, BlakeTwo256}, + AccountId32, +}; + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, 0)); +} + +impl frame_system::Config for TestRuntime { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = BlockNumber; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = AccountIdLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for TestRuntime { + type MaxLocks = ConstU32<4>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU128<2>; + type AccountStore = System; + type WeightInfo = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; +} + +impl pallet_timestamp::Config for TestRuntime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<3>; + type WeightInfo = (); +} + +impl pallet_insecure_randomness_collective_flip::Config for TestRuntime {} + +parameter_types! { + pub const DepositPerItem: Balance = 1_000; + pub const DepositPerByte: Balance = 1_000; + pub const DefaultDepositLimit: Balance = 1_000; + pub Schedule: pallet_contracts::Schedule = Default::default(); +} + +impl pallet_contracts::Config for TestRuntime { + type Time = Timestamp; + type Randomness = RandomnessCollectiveFlip; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type CallFilter = Nothing; + type DepositPerItem = DepositPerItem; + type DepositPerByte = DepositPerByte; + type DefaultDepositLimit = DefaultDepositLimit; + type CallStack = [pallet_contracts::Frame; 5]; + type WeightPrice = (); + type WeightInfo = pallet_contracts::weights::SubstrateWeight; + type ChainExtension = (); + 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 HashedAccountMapping; +impl astar_primitives::ethereum_checked::AccountMapping for HashedAccountMapping { + fn into_h160(account_id: AccountId) -> H160 { + let data = (b"evm:", account_id); + return H160::from_slice(&data.using_encoded(sp_io::hashing::blake2_256)[0..20]); + } +} + +pub struct MockEthereumTransact; +impl CheckedEthereumTransact for MockEthereumTransact { + fn xvm_transact( + _source: H160, + _checked_tx: CheckedEthereumTx, + ) -> Result<(PostDispatchInfo, EvmCallInfo), DispatchErrorWithPostInfo> { + Ok(( + PostDispatchInfo { + actual_weight: Default::default(), + pays_fee: Default::default(), + }, + EvmCallInfo { + exit_reason: ExitReason::Succeed(ExitSucceed::Returned), + value: Default::default(), + used_gas: UsedGas { + standard: Default::default(), + effective: Default::default(), + }, + logs: Default::default(), + weight_info: None, + }, + )) + } +} + +pub struct MockGasWeightMapping; +impl GasWeightMapping for MockGasWeightMapping { + fn gas_to_weight(gas: u64, _without_base_weight: bool) -> Weight { + Weight::from_parts(gas, 0) + } + fn weight_to_gas(weight: Weight) -> u64 { + weight.ref_time() + } +} + +impl pallet_xvm::Config for TestRuntime { + type GasWeightMapping = MockGasWeightMapping; + type AccountMapping = HashedAccountMapping; + type EthereumTransact = MockEthereumTransact; + type WeightInfo = (); +} + +pub(crate) type AccountId = AccountId32; +pub(crate) type BlockNumber = u64; +pub(crate) type Balance = u128; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub struct TestRuntime + where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Timestamp: pallet_timestamp, + Balances: pallet_balances, + RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip, + Contracts: pallet_contracts, + Xvm: pallet_xvm, + } +); + +#[derive(Default)] +pub struct ExtBuilder; + +impl ExtBuilder { + #[allow(dead_code)] + pub fn build(self) -> TestExternalities { + let t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + let mut ext = TestExternalities::from(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext + } +} diff --git a/pallets/xvm/src/weights.rs b/pallets/xvm/src/weights.rs new file mode 100644 index 0000000000..0d458675c2 --- /dev/null +++ b/pallets/xvm/src/weights.rs @@ -0,0 +1,90 @@ +// 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_xvm +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-07-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `devserver-01`, CPU: `Intel(R) Xeon(R) E-2236 CPU @ 3.40GHz` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("shibuya-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/astar-collator +// benchmark +// pallet +// --chain=shibuya-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_xvm +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./benchmark-results/xvm_weights.rs +// --template=./scripts/templates/weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_xvm. +pub trait WeightInfo { + fn evm_call_overheads() -> Weight; + fn wasm_call_overheads() -> Weight; +} + +/// Weights for pallet_xvm using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn evm_call_overheads() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 771_000 picoseconds. + Weight::from_parts(818_000, 0) + } + fn wasm_call_overheads() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 304_000 picoseconds. + Weight::from_parts(337_000, 0) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn evm_call_overheads() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 771_000 picoseconds. + Weight::from_parts(818_000, 0) + } + fn wasm_call_overheads() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 304_000 picoseconds. + Weight::from_parts(337_000, 0) + } +} diff --git a/precompiles/xvm/Cargo.toml b/precompiles/xvm/Cargo.toml index cb431293f1..397a412373 100644 --- a/precompiles/xvm/Cargo.toml +++ b/precompiles/xvm/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pallet-evm-precompile-xvm" description = "Cross-VM call support for EVM." -version = "0.1.0" +version = "0.1.1" authors.workspace = true edition.workspace = true homepage.workspace = true @@ -10,7 +10,6 @@ repository.workspace = true [dependencies] log = { workspace = true } num_enum = { workspace = true } -pallet-xvm = { workspace = true } precompile-utils = { workspace = true } # Substrate @@ -26,6 +25,9 @@ sp-std = { workspace = true } fp-evm = { workspace = true } pallet-evm = { workspace = true } +# Astar +astar-primitives = { workspace = true } + [dev-dependencies] derive_more = { workspace = true } hex-literal = { workspace = true } @@ -34,7 +36,7 @@ serde = { workspace = true } precompile-utils = { workspace = true, features = ["testing"] } -pallet-balances = { workspace = true } +pallet-balances = { workspace = true, features = ["std"] } pallet-timestamp = { workspace = true } sp-runtime = { workspace = true } @@ -46,10 +48,10 @@ std = [ "frame-support/std", "frame-system/std", "pallet-evm/std", - "pallet-xvm/std", "precompile-utils/std", "sp-core/std", "sp-std/std", "sp-io/std", "sp-runtime/std", + "astar-primitives/std", ] diff --git a/precompiles/xvm/evm_sdk/flipper.sol b/precompiles/xvm/evm_sdk/flipper.sol index a15eaefd9e..fcbfdd3d40 100644 --- a/precompiles/xvm/evm_sdk/flipper.sol +++ b/precompiles/xvm/evm_sdk/flipper.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; interface XVM { function xvm_call( - bytes calldata context, + uint8 calldata vm_id, bytes calldata to, bytes calldata input, ) external; @@ -13,6 +13,6 @@ library Flipper { function flip(bytes to) { bytes input = "0xcafecafe"; - XVM_PRECOMPILE.xvm_call(0x1f00, to, input); + XVM_PRECOMPILE.xvm_call(0x1F, to, input); } } diff --git a/precompiles/xvm/src/lib.rs b/precompiles/xvm/src/lib.rs index 7f7fb55329..78772c5244 100644 --- a/precompiles/xvm/src/lib.rs +++ b/precompiles/xvm/src/lib.rs @@ -18,11 +18,10 @@ #![cfg_attr(not(feature = "std"), no_std)] +use astar_primitives::xvm::{Context, VmId, XvmCall}; use fp_evm::{PrecompileHandle, PrecompileOutput}; -use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}; -use pallet_evm::{AddressMapping, Precompile}; -use pallet_xvm::XvmContext; -use parity_scale_codec::Decode; +use frame_support::dispatch::Dispatchable; +use pallet_evm::{AddressMapping, GasWeightMapping, Precompile}; use sp_runtime::codec::Encode; use sp_std::marker::PhantomData; use sp_std::prelude::*; @@ -39,19 +38,18 @@ mod tests; #[precompile_utils::generate_function_selector] #[derive(Debug, PartialEq)] pub enum Action { - XvmCall = "xvm_call(bytes,bytes,bytes)", + XvmCall = "xvm_call(uint8,bytes,bytes)", } /// A precompile that expose XVM related functions. -pub struct XvmPrecompile(PhantomData); +pub struct XvmPrecompile(PhantomData<(T, XC)>); -impl Precompile for XvmPrecompile +impl Precompile for XvmPrecompile where - R: pallet_evm::Config + pallet_xvm::Config, + R: pallet_evm::Config, <::RuntimeCall as Dispatchable>::RuntimeOrigin: From>, - ::RuntimeCall: - From> + Dispatchable + GetDispatchInfo, + XC: XvmCall, { fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { log::trace!(target: "xvm-precompile", "In XVM precompile"); @@ -67,35 +65,44 @@ where } } -impl XvmPrecompile +impl XvmPrecompile where - R: pallet_evm::Config + pallet_xvm::Config, + R: pallet_evm::Config, <::RuntimeCall as Dispatchable>::RuntimeOrigin: From>, - ::RuntimeCall: - From> + Dispatchable + GetDispatchInfo, + XC: XvmCall, { fn xvm_call(handle: &mut impl PrecompileHandle) -> EvmResult { let mut input = handle.read_input()?; - input.expect_arguments(4)?; + input.expect_arguments(3)?; - // Read arguments and check it - // TODO: This approach probably needs to be revised - does contract call need to specify gas/weight? Usually it is implicit. - let context_raw = input.read::()?; - let context: XvmContext = Decode::decode(&mut context_raw.0.as_ref()) - .map_err(|_| revert("can not decode XVM context"))?; + let vm_id = { + let id = input.read::()?; + id.try_into().map_err(|_| revert("invalid vm id")) + }?; - // Fetch the remaining gas (weight) available for execution - // TODO: rework - //let remaining_gas = handle.remaining_gas(); - //let remaining_weight = R::GasWeightMapping::gas_to_weight(remaining_gas); - //context.max_weight = remaining_weight; + let remaining_gas = handle.remaining_gas(); + let weight_limit = R::GasWeightMapping::gas_to_weight(remaining_gas, true); + let xvm_context = Context { + source_vm_id: VmId::Evm, + weight_limit, + }; let call_to = input.read::()?.0; let call_input = input.read::()?.0; - let from = R::AddressMapping::into_account_id(handle.context().caller); - match &pallet_xvm::Pallet::::xvm_bare_call(context, from, call_to, call_input) { + + let call_result = XC::call(xvm_context, vm_id, from, call_to, call_input); + + let used_weight = match &call_result { + Ok(s) => s.used_weight, + Err(f) => f.used_weight, + }; + handle.record_cost(R::GasWeightMapping::weight_to_gas(used_weight))?; + handle + .record_external_cost(Some(used_weight.ref_time()), Some(used_weight.proof_size()))?; + + match call_result { Ok(success) => { log::trace!( target: "xvm-precompile::xvm_call", @@ -105,7 +112,7 @@ where Ok(succeed( EvmDataWriter::new() .write(true) - .write(Bytes(success.output().to_vec())) // TODO redundant clone + .write(Bytes(success.output)) .build(), )) } @@ -116,13 +123,10 @@ where "failure: {:?}", failure ); - let mut error_buffer = Vec::new(); - failure.error().encode_to(&mut error_buffer); - Ok(succeed( EvmDataWriter::new() .write(false) - .write(Bytes(error_buffer)) + .write(Bytes(failure.error.encode())) .build(), )) } diff --git a/precompiles/xvm/src/mock.rs b/precompiles/xvm/src/mock.rs index 793c8f80e5..bf209b7480 100644 --- a/precompiles/xvm/src/mock.rs +++ b/precompiles/xvm/src/mock.rs @@ -22,7 +22,7 @@ use super::*; use fp_evm::IsPrecompileResult; use frame_support::{ - construct_runtime, parameter_types, + construct_runtime, ensure, parameter_types, traits::{ConstU32, ConstU64, Everything}, weights::Weight, }; @@ -39,6 +39,8 @@ use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, }; +use astar_primitives::xvm::{CallError::*, CallErrorWithWeight, CallInfo, CallResult}; + pub type AccountId = TestAccount; pub type Balance = u128; pub type BlockNumber = u64; @@ -154,12 +156,14 @@ pub struct TestPrecompileSet(PhantomData); impl PrecompileSet for TestPrecompileSet where - R: pallet_evm::Config + pallet_xvm::Config, - XvmPrecompile: Precompile, + R: pallet_evm::Config, + XvmPrecompile: Precompile, { fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { match handle.code_address() { - a if a == PRECOMPILE_ADDRESS => Some(XvmPrecompile::::execute(handle)), + a if a == PRECOMPILE_ADDRESS => { + Some(XvmPrecompile::::execute(handle)) + } _ => None, } } @@ -232,10 +236,41 @@ impl pallet_evm::Config for Runtime { type GasLimitPovSizeRatio = ConstU64<4>; } -impl pallet_xvm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SyncVM = (); - type AsyncVM = (); +struct MockXvmWithArgsCheck; +impl XvmCall for MockXvmWithArgsCheck { + fn call( + _context: Context, + vm_id: VmId, + _source: AccountId, + target: Vec, + input: Vec, + ) -> CallResult { + ensure!( + vm_id != VmId::Evm, + CallErrorWithWeight { + error: SameVmCallNotAllowed, + used_weight: Weight::zero() + } + ); + ensure!( + target.len() == 20, + CallErrorWithWeight { + error: InvalidTarget, + used_weight: Weight::zero() + } + ); + ensure!( + input.len() <= 1024, + CallErrorWithWeight { + error: InputTooLarge, + used_weight: Weight::zero() + } + ); + Ok(CallInfo { + output: vec![], + used_weight: Weight::zero(), + }) + } } // Configure a mock runtime to test the pallet. @@ -249,7 +284,6 @@ construct_runtime!( Balances: pallet_balances, Evm: pallet_evm, Timestamp: pallet_timestamp, - Xvm: pallet_xvm, } ); diff --git a/precompiles/xvm/src/tests.rs b/precompiles/xvm/src/tests.rs index 2f3ba81831..844267fc40 100644 --- a/precompiles/xvm/src/tests.rs +++ b/precompiles/xvm/src/tests.rs @@ -19,6 +19,7 @@ use crate::mock::*; use crate::*; +use astar_primitives::xvm::CallError; use parity_scale_codec::Encode; use precompile_utils::testing::*; use precompile_utils::EvmDataWriter; @@ -49,25 +50,22 @@ fn wrong_argument_reverts() { .write(0u8) .write(Bytes(b"".to_vec())) .write(Bytes(b"".to_vec())) - .write(Bytes(b"".to_vec())) .build(), ) .expect_no_logs() - .execute_reverts(|output| output == b"can not decode XVM context"); + .execute_reverts(|output| output == b"invalid vm id"); }) } #[test] fn correct_arguments_works() { - let context: XvmContext = Default::default(); ExtBuilder::default().build().execute_with(|| { precompiles() .prepare_test( TestAccount::Alice, PRECOMPILE_ADDRESS, EvmDataWriter::new_with_selector(Action::XvmCall) - .write(Bytes(context.encode())) - .write(Bytes(b"".to_vec())) + .write(0x1Fu8) .write(Bytes(b"".to_vec())) .write(Bytes(b"".to_vec())) .build(), @@ -76,7 +74,7 @@ fn correct_arguments_works() { .execute_returns( EvmDataWriter::new() .write(false) // the XVM call should succeed but the internal should fail - .write(vec![0u8]) + .write(Bytes(CallError::InvalidTarget.encode())) .build(), ); }) diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 386a79f5ae..777ba49fd9 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -26,6 +26,9 @@ pub mod xcm; /// Checked Ethereum transaction primitives. pub mod ethereum_checked; +/// XVM primitives. +pub mod xvm; + use sp_runtime::traits::BlakeTwo256; use sp_runtime::{ generic, diff --git a/primitives/src/xvm.rs b/primitives/src/xvm.rs new file mode 100644 index 0000000000..e3ae06c526 --- /dev/null +++ b/primitives/src/xvm.rs @@ -0,0 +1,112 @@ +// 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 scale_info::TypeInfo; +use sp_runtime::RuntimeDebug; +use sp_std::{convert::TryFrom, prelude::*, result::Result}; + +/// Vm Id. +#[repr(u8)] +#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum VmId { + Evm = 0x0F, + Wasm = 0x1F, +} + +impl TryFrom for VmId { + type Error = CallError; + + fn try_from(value: u8) -> Result { + if value == VmId::Evm as u8 { + Ok(VmId::Evm) + } else if value == VmId::Wasm as u8 { + Ok(VmId::Wasm) + } else { + Err(CallError::InvalidVmId) + } + } +} + +/// XVM call info on success. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct CallInfo { + /// Output of the call. + pub output: Vec, + /// Actual used weight. + pub used_weight: Weight, +} + +/// XVM call error on failure. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum CallError { + /// Invalid VM id. + InvalidVmId, + /// Calling the contracts in the same VM is not allowed. + SameVmCallNotAllowed, + /// Target contract address is invalid. + InvalidTarget, + /// Input is too large. + InputTooLarge, + /// Bad origin. + BadOrigin, + /// The call failed on EVM or WASM execution. + ExecutionFailed(Vec), +} + +/// XVM call error with used weight info. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct CallErrorWithWeight { + /// Error info. + pub error: CallError, + /// Actual used weight. + pub used_weight: Weight, +} + +/// XVM call result. +pub type CallResult = Result; + +/// XVM context. +/// +/// Note this should be set by runtime, instead of passed by callers. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct Context { + /// The source VM of the call. + pub source_vm_id: VmId, + /// Max weight limit. + pub weight_limit: Weight, +} + +pub trait XvmCall { + /// Call a contract in XVM. + /// + /// Parameters: + /// - `context`: XVM context. + /// - `vm_id`: the VM Id of the target contract. + /// - `source`: Caller Id. + /// - `target`: Target contract address. + /// - `input`: call input data. + fn call( + context: Context, + vm_id: VmId, + source: AccountId, + target: Vec, + input: Vec, + ) -> CallResult; +} diff --git a/runtime/astar/Cargo.toml b/runtime/astar/Cargo.toml index 96c4b8ff1d..6a585846f4 100644 --- a/runtime/astar/Cargo.toml +++ b/runtime/astar/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "astar-runtime" -version = "5.15.0" +version = "5.16.0" build = "build.rs" authors.workspace = true edition.workspace = true diff --git a/runtime/local/Cargo.toml b/runtime/local/Cargo.toml index 398d2d076e..5a5d1de6de 100644 --- a/runtime/local/Cargo.toml +++ b/runtime/local/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "local-runtime" -version = "5.15.0" +version = "5.16.0" build = "build.rs" authors.workspace = true edition.workspace = true @@ -75,7 +75,7 @@ pallet-evm-precompile-dapps-staking = { workspace = true } pallet-evm-precompile-sr25519 = { workspace = true } pallet-evm-precompile-substrate-ecdsa = { workspace = true } pallet-evm-precompile-xvm = { workspace = true } -pallet-xvm = { workspace = true, features = ["evm", "wasm"] } +pallet-xvm = { workspace = true } # Moonbeam tracing moonbeam-evm-tracer = { workspace = true, optional = true } diff --git a/runtime/local/src/chain_extensions.rs b/runtime/local/src/chain_extensions.rs index e42df76c44..9bd7609c5f 100644 --- a/runtime/local/src/chain_extensions.rs +++ b/runtime/local/src/chain_extensions.rs @@ -16,10 +16,9 @@ // You should have received a copy of the GNU General Public License // along with Astar. If not, see . -//! -use super::Runtime; +use super::{Runtime, Xvm}; + /// Registered WASM contracts chain extensions. -/// pub use pallet_chain_extension_assets::AssetsExtension; use pallet_contracts::chain_extension::RegisteredChainExtension; @@ -32,7 +31,7 @@ impl RegisteredChainExtension for DappsStakingExtension { const ID: u16 = 00; } -impl RegisteredChainExtension for XvmExtension { +impl RegisteredChainExtension for XvmExtension { const ID: u16 = 01; } diff --git a/runtime/local/src/lib.rs b/runtime/local/src/lib.rs index dc6608ba81..765dc47b84 100644 --- a/runtime/local/src/lib.rs +++ b/runtime/local/src/lib.rs @@ -471,14 +471,11 @@ parameter_types! { pub WasmId: u8 = 0x1F; } -use pallet_xvm::{evm, wasm}; impl pallet_xvm::Config for Runtime { - type SyncVM = ( - evm::EVM, - wasm::WASM, - ); - type AsyncVM = (); - type RuntimeEvent = RuntimeEvent; + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type AccountMapping = HashedAccountMapping; + type EthereumTransact = EthereumChecked; + type WeightInfo = pallet_xvm::weights::SubstrateWeight; } parameter_types! { @@ -831,7 +828,7 @@ impl pallet_contracts::Config for Runtime { type WeightInfo = pallet_contracts::weights::SubstrateWeight; type ChainExtension = ( DappsStakingExtension, - XvmExtension, + XvmExtension, AssetsExtension>, ); type Schedule = Schedule; @@ -920,7 +917,6 @@ impl InstanceFilter for ProxyType { | RuntimeCall::Council(..) | RuntimeCall::TechnicalCommittee(..) | RuntimeCall::Treasury(..) - | RuntimeCall::Xvm(..) ) } // All Runtime calls from Pallet Balances allowed for proxy account diff --git a/runtime/local/src/precompiles.rs b/runtime/local/src/precompiles.rs index 6eddf6629e..9e0bf989d9 100644 --- a/runtime/local/src/precompiles.rs +++ b/runtime/local/src/precompiles.rs @@ -67,7 +67,7 @@ impl PrecompileSet for LocalNetworkPrecompiles where Erc20AssetsPrecompileSet: PrecompileSet, DappsStakingWrapper: Precompile, - XvmPrecompile: Precompile, + XvmPrecompile>: Precompile, Dispatch: Precompile, R: pallet_evm::Config + pallet_xvm::Config @@ -110,7 +110,9 @@ where // SubstrateEcdsa 0x5003 a if a == hash(20483) => Some(SubstrateEcdsaPrecompile::::execute(handle)), // Xvm 0x5005 - a if a == hash(20485) => Some(XvmPrecompile::::execute(handle)), + a if a == hash(20485) => { + Some(XvmPrecompile::>::execute(handle)) + } // If the address matches asset prefix, the we route through the asset precompile set a if &a.to_fixed_bytes()[0..4] == ASSET_PRECOMPILE_ADDRESS_PREFIX => { Erc20AssetsPrecompileSet::::new().execute(handle) diff --git a/runtime/shibuya/Cargo.toml b/runtime/shibuya/Cargo.toml index 556b1abd7d..ddb340dbec 100644 --- a/runtime/shibuya/Cargo.toml +++ b/runtime/shibuya/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shibuya-runtime" -version = "5.15.0" +version = "5.16.0" build = "build.rs" authors.workspace = true edition.workspace = true @@ -112,7 +112,7 @@ pallet-evm-precompile-xcm = { workspace = true } pallet-evm-precompile-xvm = { workspace = true } pallet-xc-asset-config = { workspace = true } pallet-xcm = { workspace = true } -pallet-xvm = { workspace = true, features = ["evm", "wasm"] } +pallet-xvm = { workspace = true } # Moonbeam tracing moonbeam-evm-tracer = { workspace = true, optional = true } @@ -253,6 +253,7 @@ runtime-benchmarks = [ "pallet-preimage/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", "pallet-ethereum-checked/runtime-benchmarks", + "pallet-xvm/runtime-benchmarks", "polkadot-runtime/runtime-benchmarks", "orml-xtokens/runtime-benchmarks", "astar-primitives/runtime-benchmarks", diff --git a/runtime/shibuya/src/chain_extensions.rs b/runtime/shibuya/src/chain_extensions.rs index e42df76c44..9bd7609c5f 100644 --- a/runtime/shibuya/src/chain_extensions.rs +++ b/runtime/shibuya/src/chain_extensions.rs @@ -16,10 +16,9 @@ // You should have received a copy of the GNU General Public License // along with Astar. If not, see . -//! -use super::Runtime; +use super::{Runtime, Xvm}; + /// Registered WASM contracts chain extensions. -/// pub use pallet_chain_extension_assets::AssetsExtension; use pallet_contracts::chain_extension::RegisteredChainExtension; @@ -32,7 +31,7 @@ impl RegisteredChainExtension for DappsStakingExtension { const ID: u16 = 00; } -impl RegisteredChainExtension for XvmExtension { +impl RegisteredChainExtension for XvmExtension { const ID: u16 = 01; } diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index 90303e918e..bc94244c62 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -69,7 +69,8 @@ use sp_runtime::{ use sp_std::prelude::*; pub use astar_primitives::{ - AccountId, Address, AssetId, Balance, BlockNumber, Hash, Header, Index, Signature, + ethereum_checked::CheckedEthereumTransact, AccountId, Address, AssetId, Balance, BlockNumber, + Hash, Header, Index, Signature, }; use astar_primitives::xcm::AssetLocationIdConverter; @@ -165,7 +166,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("shibuya"), impl_name: create_runtime_str!("shibuya"), authoring_version: 1, - spec_version: 105, + spec_version: 106, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, @@ -682,7 +683,7 @@ impl pallet_contracts::Config for Runtime { type WeightInfo = pallet_contracts::weights::SubstrateWeight; type ChainExtension = ( DappsStakingExtension, - XvmExtension, + XvmExtension, AssetsExtension>, ); type Schedule = Schedule; @@ -785,19 +786,11 @@ impl pallet_ethereum_checked::Config for Runtime { type WeightInfo = pallet_ethereum_checked::weights::SubstrateWeight; } -parameter_types! { - pub EvmId: u8 = 0x0F; - pub WasmId: u8 = 0x1F; -} - -use pallet_xvm::{evm, wasm}; impl pallet_xvm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SyncVM = ( - evm::EVM, - wasm::WASM, - ); - type AsyncVM = (); + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type AccountMapping = HashedAccountMapping; + type EthereumTransact = EthereumChecked; + type WeightInfo = pallet_xvm::weights::SubstrateWeight; } parameter_types! { @@ -1151,7 +1144,6 @@ impl InstanceFilter for ProxyType { | RuntimeCall::Council(..) | RuntimeCall::TechnicalCommittee(..) | RuntimeCall::Treasury(..) - | RuntimeCall::Xvm(..) ) } // All Runtime calls from Pallet Balances allowed for proxy account @@ -1437,6 +1429,7 @@ mod benches { [pallet_collator_selection, CollatorSelection] [pallet_xcm, PolkadotXcm] [pallet_ethereum_checked, EthereumChecked] + [pallet_xvm, Xvm] ); } diff --git a/runtime/shibuya/src/precompiles.rs b/runtime/shibuya/src/precompiles.rs index bd8d1714c7..d84839aa4b 100644 --- a/runtime/shibuya/src/precompiles.rs +++ b/runtime/shibuya/src/precompiles.rs @@ -73,7 +73,7 @@ where Erc20AssetsPrecompileSet: PrecompileSet, DappsStakingWrapper: Precompile, XcmPrecompile: Precompile, - XvmPrecompile: Precompile, + XvmPrecompile>: Precompile, Dispatch: Precompile, R: pallet_evm::Config + pallet_assets::Config @@ -120,7 +120,9 @@ where // Xcm 0x5004 a if a == hash(20484) => Some(XcmPrecompile::::execute(handle)), // Xvm 0x5005 - a if a == hash(20485) => Some(XvmPrecompile::::execute(handle)), + a if a == hash(20485) => { + Some(XvmPrecompile::>::execute(handle)) + } // If the address matches asset prefix, the we route through the asset precompile set a if &a.to_fixed_bytes()[0..4] == ASSET_PRECOMPILE_ADDRESS_PREFIX => { Erc20AssetsPrecompileSet::::new().execute(handle) diff --git a/runtime/shiden/Cargo.toml b/runtime/shiden/Cargo.toml index efcc9d3f0d..0268ef47e5 100644 --- a/runtime/shiden/Cargo.toml +++ b/runtime/shiden/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shiden-runtime" -version = "5.15.0" +version = "5.16.0" build = "build.rs" authors.workspace = true edition.workspace = true