diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 8b03bf650..893387c3d 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -18,8 +18,7 @@ concurrency: cancel-in-progress: true env: - TARPAULIN_VERSION: 0.27.2 - # LIBCLANG_PATH: "/usr/lib/llvm-11/lib" # default `llvm-config --prefix` + TARPAULIN_VERSION: 0.27.3 CARGO_INCREMENTAL: 0 jobs: test: diff --git a/Cargo.lock b/Cargo.lock index a328556ea..432bb8a51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,6 +57,7 @@ dependencies = [ "bstringify", "enumflags2", "frame-support", + "hex", "hex-literal", "log", "module-evm-utility", @@ -6567,6 +6568,7 @@ version = "2.24.0" dependencies = [ "acala-primitives", "env_logger", + "environmental", "frame-benchmarking", "frame-support", "frame-system", diff --git a/modules/auction-manager/src/mock.rs b/modules/auction-manager/src/mock.rs index 597b5cec1..5c0fc2d1b 100644 --- a/modules/auction-manager/src/mock.rs +++ b/modules/auction-manager/src/mock.rs @@ -36,7 +36,6 @@ use sp_runtime::{ traits::{AccountIdConversion, IdentityLookup, One as OneT}, BuildStorage, }; -use sp_std::cell::RefCell; pub type AccountId = u128; pub type BlockNumber = u64; @@ -119,19 +118,19 @@ impl module_cdp_treasury::Config for Runtime { type StableAsset = MockStableAsset; } -thread_local! { - static RELATIVE_PRICE: RefCell> = RefCell::new(Some(Price::one())); +parameter_types! { + static RelativePrice: Option = Some(Price::one()); } pub struct MockPriceSource; impl MockPriceSource { pub fn set_relative_price(price: Option) { - RELATIVE_PRICE.with(|v| *v.borrow_mut() = price); + RelativePrice::mutate(|v| *v = price); } } impl PriceProvider for MockPriceSource { fn get_relative_price(_base: CurrencyId, _quote: CurrencyId) -> Option { - RELATIVE_PRICE.with(|v| *v.borrow_mut()) + RelativePrice::get() } fn get_price(_currency_id: CurrencyId) -> Option { @@ -163,18 +162,18 @@ impl module_dex::Config for Runtime { type OnLiquidityPoolUpdated = (); } -thread_local! { - static IS_SHUTDOWN: RefCell = RefCell::new(false); +parameter_types! { + static IsShutdown: bool = false; } pub fn mock_shutdown() { - IS_SHUTDOWN.with(|v| *v.borrow_mut() = true) + IsShutdown::mutate(|v| *v = true) } pub struct MockEmergencyShutdown; impl EmergencyShutdown for MockEmergencyShutdown { fn is_shutdown() -> bool { - IS_SHUTDOWN.with(|v| *v.borrow_mut()) + IsShutdown::get() } } diff --git a/modules/cdp-engine/src/mock.rs b/modules/cdp-engine/src/mock.rs index cc8ed7906..9d1af9672 100644 --- a/modules/cdp-engine/src/mock.rs +++ b/modules/cdp-engine/src/mock.rs @@ -36,7 +36,7 @@ use sp_runtime::{ traits::{AccountIdConversion, IdentityLookup, One as OneT}, BuildStorage, }; -use sp_std::{cell::RefCell, str::FromStr}; +use sp_std::str::FromStr; pub type AccountId = AccountId32; pub type BlockNumber = u64; @@ -127,21 +127,21 @@ impl module_loans::Config for Runtime { type OnUpdateLoan = (); } -thread_local! { - static BTC_PRICE: RefCell> = RefCell::new(Some(Price::one())); - static DOT_PRICE: RefCell> = RefCell::new(Some(Price::one())); - static LP_AUSD_DOT_PRICE: RefCell> = RefCell::new(Some(Price::one())); - static LP_DOT_BTC_PRICE: RefCell> = RefCell::new(Some(Price::one())); +parameter_types! { + static BtcPrice: Option = Some(Price::one()); + static DotPrice: Option = Some(Price::one()); + static LpAusdDotPrice: Option = Some(Price::one()); + static LpDotBtcPrice: Option = Some(Price::one()); } pub struct MockPriceSource; impl MockPriceSource { pub fn set_price(currency_id: CurrencyId, price: Option) { match currency_id { - BTC => BTC_PRICE.with(|v| *v.borrow_mut() = price), - DOT => DOT_PRICE.with(|v| *v.borrow_mut() = price), - LP_AUSD_DOT => LP_AUSD_DOT_PRICE.with(|v| *v.borrow_mut() = price), - LP_DOT_BTC => LP_DOT_BTC_PRICE.with(|v| *v.borrow_mut() = price), + BTC => BtcPrice::mutate(|v| *v = price), + DOT => DotPrice::mutate(|v| *v = price), + LP_AUSD_DOT => LpAusdDotPrice::mutate(|v| *v = price), + LP_DOT_BTC => LpDotBtcPrice::mutate(|v| *v = price), _ => {} } } @@ -149,27 +149,24 @@ impl MockPriceSource { impl PriceProvider for MockPriceSource { fn get_price(currency_id: CurrencyId) -> Option { match currency_id { - BTC => BTC_PRICE.with(|v| *v.borrow()), - DOT => DOT_PRICE.with(|v| *v.borrow()), + BTC => BtcPrice::get(), + DOT => DotPrice::get(), AUSD => Some(Price::one()), - LP_AUSD_DOT => LP_AUSD_DOT_PRICE.with(|v| *v.borrow()), - LP_DOT_BTC => LP_DOT_BTC_PRICE.with(|v| *v.borrow()), + LP_AUSD_DOT => LpAusdDotPrice::get(), + LP_DOT_BTC => LpDotBtcPrice::get(), _ => None, } } } -thread_local! { - pub static AUCTION: RefCell> = RefCell::new(None); +parameter_types! { + pub static Auction: Option<(AccountId, CurrencyId, Balance, Balance)> = None; } pub struct MockAuctionManager; impl MockAuctionManager { pub fn auction() -> Option<(AccountId, CurrencyId, Balance, Balance)> { - AUCTION.with(|v| { - let cloned = v.borrow().clone(); - cloned - }) + Auction::get() } } impl AuctionManager for MockAuctionManager { @@ -183,12 +180,12 @@ impl AuctionManager for MockAuctionManager { amount: Self::Balance, target: Self::Balance, ) -> DispatchResult { - AUCTION.with(|v| *v.borrow_mut() = Some((refund_recipient.clone(), currency_id, amount, target))); + Auction::mutate(|v| *v = Some((refund_recipient.clone(), currency_id, amount, target))); Ok(()) } fn cancel_auction(_id: Self::AuctionId) -> DispatchResult { - AUCTION.with(|v| *v.borrow_mut() = None); + Auction::mutate(|v| *v = None); Ok(()) } @@ -308,12 +305,12 @@ impl module_evm_bridge::Config for Runtime { type EVM = EVM; } -thread_local! { - static IS_SHUTDOWN: RefCell = RefCell::new(false); +parameter_types! { + static IsShutdown: bool = false; } pub fn mock_shutdown() { - IS_SHUTDOWN.with(|v| *v.borrow_mut() = true) + IsShutdown::mutate(|v| *v = true) } pub fn liquidation_contract_addr() -> EvmAddress { @@ -323,38 +320,38 @@ pub fn liquidation_contract_addr() -> EvmAddress { pub struct MockEmergencyShutdown; impl EmergencyShutdown for MockEmergencyShutdown { fn is_shutdown() -> bool { - IS_SHUTDOWN.with(|v| *v.borrow_mut()) + IsShutdown::get() } } -thread_local! { - static LIQUIDATED: RefCell<(EvmAddress, EvmAddress, Balance, Balance)> = RefCell::new((EvmAddress::default(), EvmAddress::default(), 0, 0)); - static TRANSFERRED: RefCell<(EvmAddress, Balance)> = RefCell::new((EvmAddress::default(), 0)); - static REFUNDED: RefCell<(EvmAddress, Balance)> = RefCell::new((EvmAddress::default(), 0)); - static LIQUIDATION_RESULT: RefCell = RefCell::new(Err(Error::::LiquidationFailed.into())); - static REPAYMENT: RefCell> = RefCell::new(None); +parameter_types! { + static LIQUIDATED: (EvmAddress, EvmAddress, Balance, Balance) = (EvmAddress::default(), EvmAddress::default(), 0, 0); + static TRANSFERRED: (EvmAddress, Balance) = (EvmAddress::default(), 0); + static REFUNDED: (EvmAddress, Balance) = (EvmAddress::default(), 0); + static LiquidationResult: DispatchResult = Err(Error::::LiquidationFailed.into()); + static REPAYMENT: Option = None; } pub struct MockLiquidationEvmBridge; impl MockLiquidationEvmBridge { pub fn liquidated() -> (EvmAddress, EvmAddress, Balance, Balance) { - LIQUIDATED.with(|v| v.borrow().clone()) + LIQUIDATED::get() } pub fn transferred() -> (EvmAddress, Balance) { - TRANSFERRED.with(|v| v.borrow().clone()) + TRANSFERRED::get() } pub fn refunded() -> (EvmAddress, Balance) { - REFUNDED.with(|v| v.borrow().clone()) + REFUNDED::get() } pub fn reset() { - LIQUIDATION_RESULT.with(|v| *v.borrow_mut() = Err(Error::::LiquidationFailed.into())); - REPAYMENT.with(|v| *v.borrow_mut() = None); + LiquidationResult::mutate(|v| *v = Err(Error::::LiquidationFailed.into())); + REPAYMENT::mutate(|v| *v = None); } pub fn set_liquidation_result(r: DispatchResult) { - LIQUIDATION_RESULT.with(|v| *v.borrow_mut() = r); + LiquidationResult::mutate(|v| *v = r); } pub fn set_repayment(repayment: Balance) { - REPAYMENT.with(|v| *v.borrow_mut() = Some(repayment)); + REPAYMENT::mutate(|v| *v = Some(repayment)); } } impl LiquidationEvmBridge for MockLiquidationEvmBridge { @@ -365,23 +362,23 @@ impl LiquidationEvmBridge for MockLiquidationEvmBridge { amount: Balance, min_repayment: Balance, ) -> DispatchResult { - let result = LIQUIDATION_RESULT.with(|v| v.borrow().clone()); + let result = LiquidationResult::get(); if result.is_ok() { - let repayment = if let Some(r) = REPAYMENT.with(|v| v.borrow().clone()) { + let repayment = if let Some(r) = REPAYMENT::get() { r } else { min_repayment }; let _ = Currencies::deposit(GetStableCurrencyId::get(), &CDPEngineModule::account_id(), repayment); } - LIQUIDATED.with(|v| *v.borrow_mut() = (collateral, repay_dest, amount, min_repayment)); + LIQUIDATED::mutate(|v| *v = (collateral, repay_dest, amount, min_repayment)); result } fn on_collateral_transfer(_context: InvokeContext, collateral: EvmAddress, amount: Balance) { - TRANSFERRED.with(|v| *v.borrow_mut() = (collateral, amount)); + TRANSFERRED::mutate(|v| *v = (collateral, amount)); } fn on_repayment_refund(_context: InvokeContext, collateral: EvmAddress, repayment: Balance) { - REFUNDED.with(|v| *v.borrow_mut() = (collateral, repayment)); + REFUNDED::mutate(|v| *v = (collateral, repayment)); } } diff --git a/modules/cdp-treasury/src/mock.rs b/modules/cdp-treasury/src/mock.rs index a32c43f93..0ca479970 100644 --- a/modules/cdp-treasury/src/mock.rs +++ b/modules/cdp-treasury/src/mock.rs @@ -34,7 +34,6 @@ use nutsfinance_stable_asset::{ use orml_traits::parameter_type_with_key; use primitives::{DexShare, TokenSymbol, TradingPair}; use sp_runtime::{traits::IdentityLookup, BuildStorage}; -use sp_std::cell::RefCell; pub type AccountId = u128; pub type BlockNumber = u64; @@ -138,9 +137,9 @@ impl module_dex::Config for Runtime { type OnLiquidityPoolUpdated = (); } -thread_local! { - pub static TOTAL_COLLATERAL_AUCTION: RefCell = RefCell::new(0); - pub static TOTAL_COLLATERAL_IN_AUCTION: RefCell = RefCell::new(0); +parameter_types! { + pub static TotalCollateralAuction: u32 = 0; + pub static TotalCollateralInAuction: Balance = 0; } pub struct MockAuctionManager; @@ -155,8 +154,8 @@ impl AuctionManager for MockAuctionManager { amount: Self::Balance, _target: Self::Balance, ) -> DispatchResult { - TOTAL_COLLATERAL_AUCTION.with(|v| *v.borrow_mut() += 1); - TOTAL_COLLATERAL_IN_AUCTION.with(|v| *v.borrow_mut() += amount); + TotalCollateralAuction::mutate(|v| *v += 1); + TotalCollateralInAuction::mutate(|v| *v += amount); Ok(()) } @@ -185,10 +184,6 @@ parameter_types! { ]; } -thread_local! { - static IS_SHUTDOWN: RefCell = RefCell::new(false); -} - impl Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Currencies; diff --git a/modules/dex-oracle/src/mock.rs b/modules/dex-oracle/src/mock.rs index b4dbea012..6463d9766 100644 --- a/modules/dex-oracle/src/mock.rs +++ b/modules/dex-oracle/src/mock.rs @@ -30,7 +30,6 @@ use sp_runtime::{ traits::{IdentityLookup, Zero}, BuildStorage, DispatchError, }; -use sp_std::cell::RefCell; pub type AccountId = u128; @@ -64,16 +63,16 @@ impl pallet_timestamp::Config for Runtime { type WeightInfo = (); } -thread_local! { - static AUSD_DOT_POOL: RefCell<(Balance, Balance)> = RefCell::new((Zero::zero(), Zero::zero())); - static ACA_DOT_POOL: RefCell<(Balance, Balance)> = RefCell::new((Zero::zero(), Zero::zero())); +parameter_types! { + static AusdDotPool: (Balance, Balance) = (Zero::zero(), Zero::zero()); + static AcaDotPool: (Balance, Balance) = (Zero::zero(), Zero::zero()); } pub fn set_pool(trading_pair: &TradingPair, pool_0: Balance, pool_1: Balance) { if *trading_pair == AUSDDOTPair::get() { - AUSD_DOT_POOL.with(|v| *v.borrow_mut() = (pool_0, pool_1)); + AusdDotPool::mutate(|v| *v = (pool_0, pool_1)); } else if *trading_pair == ACADOTPair::get() { - ACA_DOT_POOL.with(|v| *v.borrow_mut() = (pool_0, pool_1)); + AcaDotPool::mutate(|v| *v = (pool_0, pool_1)); } } @@ -83,9 +82,9 @@ impl DEXManager for MockDEX { TradingPair::from_currency_ids(currency_id_0, currency_id_1) .map(|trading_pair| { if trading_pair == AUSDDOTPair::get() { - AUSD_DOT_POOL.with(|v| *v.borrow()) + AusdDotPool::get() } else if trading_pair == ACADOTPair::get() { - ACA_DOT_POOL.with(|v| *v.borrow()) + AcaDotPool::get() } else { (0, 0) } diff --git a/modules/dex/src/mock.rs b/modules/dex/src/mock.rs index 5baca06d1..2e686063e 100644 --- a/modules/dex/src/mock.rs +++ b/modules/dex/src/mock.rs @@ -30,7 +30,6 @@ use module_support::{mocks::MockErc20InfoMapping, SpecificJointsSwap}; use orml_traits::{parameter_type_with_key, MultiReservableCurrency}; use primitives::{Amount, TokenSymbol}; use sp_runtime::{traits::IdentityLookup, BuildStorage}; -use sp_std::cell::RefCell; pub type BlockNumber = u64; pub type AccountId = u128; @@ -105,8 +104,8 @@ parameter_types! { ]; } -thread_local! { - pub static AUSD_DOT_POOL_RECORD: RefCell<(Balance, Balance)> = RefCell::new((0, 0)); +parameter_types! { + pub static AusdDotPoolRecord: (Balance, Balance) = (0, 0); } pub struct MockOnLiquidityPoolUpdated; @@ -114,7 +113,7 @@ impl Happened<(TradingPair, Balance, Balance)> for MockOnLiquidityPoolUpdated { fn happened(info: &(TradingPair, Balance, Balance)) { let (trading_pair, new_pool_0, new_pool_1) = info; if *trading_pair == AUSDDOTPair::get() { - AUSD_DOT_POOL_RECORD.with(|v| *v.borrow_mut() = (*new_pool_0, *new_pool_1)); + AusdDotPoolRecord::mutate(|v| *v = (*new_pool_0, *new_pool_1)); } } } diff --git a/modules/evm-accounts/src/tests.rs b/modules/evm-accounts/src/tests.rs index 550723135..61cacd519 100644 --- a/modules/evm-accounts/src/tests.rs +++ b/modules/evm-accounts/src/tests.rs @@ -47,6 +47,14 @@ fn claim_account_work() { #[test] fn claim_account_should_not_work() { ExtBuilder::default().build().execute_with(|| { + assert_noop!( + EvmAccountsModule::claim_account( + RuntimeOrigin::signed(ALICE), + EvmAccountsModule::eth_address(&bob()), + [0u8; 65] + ), + Error::::BadSignature + ); assert_noop!( EvmAccountsModule::claim_account( RuntimeOrigin::signed(ALICE), diff --git a/modules/evm/Cargo.toml b/modules/evm/Cargo.toml index e2d2bb461..a94387468 100644 --- a/modules/evm/Cargo.toml +++ b/modules/evm/Cargo.toml @@ -23,6 +23,7 @@ serde_json = { workspace = true, features = ["alloc"], optional = true } hex = { workspace = true, features = ["alloc"], optional = true } num = { workspace = true, features = ["alloc"] } bn = { workspace = true } +environmental = { version = "1.1.4", default-features = false, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } @@ -60,7 +61,7 @@ pallet-utility = { workspace = true, features = ["std"] } default = ["std"] std = [ "serde/std", - + "environmental/std", "parity-scale-codec/std", "frame-support/std", "frame-system/std", @@ -100,7 +101,7 @@ try-runtime = [ "pallet-balances/try-runtime", "pallet-timestamp/try-runtime", ] -tracing = ["module-evm-utility/tracing"] +tracing = ["environmental", "primitives/tracing", "module-evm-utility/tracing"] wasm-bench = [ "wasm-bencher/wasm-bench", "hex", diff --git a/modules/evm/rpc/runtime-api/Cargo.toml b/modules/evm/rpc/runtime-api/Cargo.toml index 4b63c46c3..e0b0e786d 100644 --- a/modules/evm/rpc/runtime-api/Cargo.toml +++ b/modules/evm/rpc/runtime-api/Cargo.toml @@ -20,3 +20,4 @@ std = [ "sp-core/std", "primitives/std", ] +tracing = ["primitives/tracing"] diff --git a/modules/evm/rpc/runtime-api/src/lib.rs b/modules/evm/rpc/runtime-api/src/lib.rs index 6bfe5f914..5afefe6cc 100644 --- a/modules/evm/rpc/runtime-api/src/lib.rs +++ b/modules/evm/rpc/runtime-api/src/lib.rs @@ -80,3 +80,30 @@ sp_api::decl_runtime_apis! { ) -> Result; } } + +#[cfg(feature = "tracing")] +sp_api::decl_runtime_apis! { + pub trait EVMTraceApi where + Balance: Codec + MaybeDisplay + MaybeFromStr, + { + fn trace_call( + from: H160, + to: H160, + data: Vec, + value: Balance, + gas_limit: u64, + storage_limit: u32, + access_list: Option>, + ) -> Result, sp_runtime::DispatchError>; + + fn trace_vm( + from: H160, + to: H160, + data: Vec, + value: Balance, + gas_limit: u64, + storage_limit: u32, + access_list: Option>, + ) -> Result, sp_runtime::DispatchError>; + } +} diff --git a/modules/evm/src/runner/mod.rs b/modules/evm/src/runner/mod.rs index 65174c8d3..46400ff37 100644 --- a/modules/evm/src/runner/mod.rs +++ b/modules/evm/src/runner/mod.rs @@ -21,6 +21,9 @@ pub mod state; pub mod storage_meter; pub mod tagged_runtime; +#[cfg(feature = "tracing")] +pub mod tracing; + use crate::{BalanceOf, CallInfo, Config, CreateInfo}; use module_evm_utility::evm; pub use primitives::evm::{EvmAddress, Vicinity}; diff --git a/modules/evm/src/runner/state.rs b/modules/evm/src/runner/state.rs index 1caba4119..6b0e6d454 100644 --- a/modules/evm/src/runner/state.rs +++ b/modules/evm/src/runner/state.rs @@ -46,38 +46,17 @@ use sp_runtime::traits::Zero; use sp_std::{collections::btree_set::BTreeSet, rc::Rc, vec::Vec}; macro_rules! event { - ($x:expr) => {}; -} - -#[cfg(feature = "tracing")] -mod tracing { - pub struct Tracer; - impl module_evm_utility::evm::tracing::EventListener for Tracer { - fn event(&mut self, event: module_evm_utility::evm::tracing::Event) { - frame_support::log::debug!( - target: "evm", "evm tracing: {:?}", event - ); - } - } - impl module_evm_utility::evm_runtime::tracing::EventListener for Tracer { - fn event(&mut self, event: module_evm_utility::evm_runtime::tracing::Event) { - frame_support::log::debug!( - target: "evm", "evm_runtime tracing: {:?}", event - ); - } - } - impl module_evm_utility::evm_gasometer::tracing::EventListener for Tracer { - fn event(&mut self, event: module_evm_utility::evm_gasometer::tracing::Event) { - frame_support::log::debug!( - target: "evm", "evm_gasometer tracing: {:?}", event - ); + ($event:expr) => {{ + #[cfg(feature = "tracing")] + { + use crate::runner::tracing::{self, Event::*, EventListener}; + tracing::call_tracer_with(|tracer| { + EventListener::event(tracer, $event); + }); } - } + }}; } -#[cfg(feature = "tracing")] -use tracing::*; - macro_rules! emit_exit { ($reason:expr) => {{ let reason = $reason; @@ -212,6 +191,12 @@ impl<'config> StackSubstateMetadata<'config> { } pub fn spit_child(&self, gas_limit: u64, is_static: bool) -> Self { + event!(Enter { + depth: match self.depth { + None => 0, + Some(n) => n + 1, + } as u32 + }); Self { gasometer: Gasometer::new(gas_limit, self.gasometer.config()), storage_meter: StorageMeter::new(self.storage_meter.available_storage()), @@ -560,7 +545,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu value, init_code: &init_code, gas_limit, - address: self.create_address(CreateScheme::Legacy { caller }), + address: self.create_address(CreateScheme::Legacy { caller }).unwrap_or_default(), }); if let Some(limit) = self.config.max_initcode_size { @@ -603,27 +588,28 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu gas_limit: u64, access_list: Vec<(H160, Vec)>, // See EIP-2930 ) -> (ExitReason, Vec) { - if let Some(limit) = self.config.max_initcode_size { - if init_code.len() > limit { - self.state.metadata_mut().gasometer.fail(); - return emit_exit!(ExitError::CreateContractLimit.into(), Vec::new()); - } - } - - let code_hash = H256::from_slice(Keccak256::digest(&init_code).as_slice()); event!(TransactCreate2 { caller, value, init_code: &init_code, salt, gas_limit, - address: self.create_address(CreateScheme::Create2 { - caller, - code_hash, - salt, - }), + address: self + .create_address(CreateScheme::Create2 { + caller, + code_hash: H256::from_slice(Keccak256::digest(&init_code).as_slice()), + salt, + }) + .unwrap_or_default(), }); + if let Some(limit) = self.config.max_initcode_size { + if init_code.len() > limit { + self.state.metadata_mut().gasometer.fail(); + return emit_exit!(ExitError::CreateContractLimit.into(), Vec::new()); + } + } + if let Err(e) = self.record_create_transaction_cost(&init_code, &access_list) { return emit_exit!(e.into(), Vec::new()); } @@ -633,7 +619,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu caller, CreateScheme::Create2 { caller, - code_hash, + code_hash: H256::from_slice(Keccak256::digest(&init_code).as_slice()), salt, }, value, @@ -661,13 +647,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu gas_limit: u64, access_list: Vec<(H160, Vec)>, ) -> (ExitReason, Vec) { - if let Some(limit) = self.config.max_initcode_size { - if init_code.len() > limit { - self.state.metadata_mut().gasometer.fail(); - return emit_exit!(ExitError::CreateContractLimit.into(), Vec::new()); - } - } - event!(TransactCreate { caller, value, @@ -676,6 +655,13 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu address, }); + if let Some(limit) = self.config.max_initcode_size { + if init_code.len() > limit { + self.state.metadata_mut().gasometer.fail(); + return emit_exit!(ExitError::CreateContractLimit.into(), Vec::new()); + } + } + if let Err(e) = self.record_create_transaction_cost(&init_code, &access_list) { return emit_exit!(e.into(), Vec::new()); } @@ -714,10 +700,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu gas_limit: u64, access_list: Vec<(H160, Vec)>, ) -> (ExitReason, Vec) { - if let Err(e) = self.state.inc_nonce(caller) { - return (e.into(), Vec::new()); - } - event!(TransactCall { caller, address, @@ -726,6 +708,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu gas_limit, }); + if let Err(e) = self.state.inc_nonce(caller) { + return emit_exit!(e.into(), Vec::new()); + } + let transaction_cost = gasometer::call_transaction_cost(&data, &access_list); let gasometer = &mut self.state.metadata_mut().gasometer; match gasometer.record_transaction(transaction_cost) { diff --git a/modules/evm/src/runner/tracing.rs b/modules/evm/src/runner/tracing.rs new file mode 100644 index 000000000..5380e9693 --- /dev/null +++ b/modules/evm/src/runner/tracing.rs @@ -0,0 +1,565 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2024 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use module_evm_utility::{ + evm::{Context, CreateScheme, ExitError, ExitFatal, ExitReason, ExitSucceed, Opcode, Transfer}, + evm_gasometer, evm_runtime, +}; +use sp_core::{H160, H256, U256}; +use sp_std::prelude::*; + +pub use primitives::evm::tracing::{CallTrace, CallType, Step}; + +#[derive(Debug, Copy, Clone)] +pub enum Event<'a> { + Call { + code_address: H160, + transfer: &'a Option, + input: &'a [u8], + target_gas: Option, + is_static: bool, + context: &'a Context, + }, + Create { + caller: H160, + address: H160, + scheme: CreateScheme, + value: U256, + init_code: &'a [u8], + target_gas: Option, + }, + Suicide { + address: H160, + target: H160, + balance: U256, + }, + Exit { + reason: &'a ExitReason, + return_value: &'a [u8], + }, + TransactCall { + caller: H160, + address: H160, + value: U256, + data: &'a [u8], + gas_limit: u64, + }, + TransactCreate { + caller: H160, + value: U256, + init_code: &'a [u8], + gas_limit: u64, + address: H160, + }, + TransactCreate2 { + caller: H160, + value: U256, + init_code: &'a [u8], + salt: H256, + gas_limit: u64, + address: H160, + }, + PrecompileSubcall { + code_address: H160, + transfer: &'a Option, + input: &'a [u8], + target_gas: Option, + is_static: bool, + context: &'a Context, + }, + Enter { + depth: u32, + }, +} + +pub struct CallTracer { + events: Vec, + stack: Vec, + opcode: Option, + snapshot: Option, +} + +impl CallTracer { + pub fn new() -> Self { + Self { + events: Vec::new(), + stack: Vec::new(), + opcode: None, + snapshot: None, + } + } + + pub fn finalize(&mut self) -> Vec { + self.events.drain(..).rev().collect() + } + + fn call_type(&self) -> CallType { + match self.opcode { + Some(Opcode::CALLCODE) => CallType::CALLCODE, + Some(Opcode::DELEGATECALL) => CallType::DELEGATECALL, + Some(Opcode::STATICCALL) => CallType::STATICCALL, + Some(Opcode::CREATE) | Some(Opcode::CREATE2) => CallType::CREATE, + Some(Opcode::SUICIDE) => CallType::SUICIDE, + _ => CallType::CALL, + } + } + + fn evm_runtime_event(&mut self, event: evm_runtime::tracing::Event) { + match event { + evm_runtime::tracing::Event::Step { opcode, .. } => { + self.opcode = Some(opcode); + } + _ => {} + }; + } + + fn evm_gasometer_event(&mut self, event: evm_gasometer::tracing::Event) { + match event { + evm_gasometer::tracing::Event::RecordCost { snapshot, .. } + | evm_gasometer::tracing::Event::RecordDynamicCost { snapshot, .. } + | evm_gasometer::tracing::Event::RecordTransaction { snapshot, .. } + | evm_gasometer::tracing::Event::RecordRefund { snapshot, .. } + | evm_gasometer::tracing::Event::RecordStipend { snapshot, .. } => self.snapshot = snapshot, + }; + } +} + +pub struct OpcodeTracer { + pub steps: Vec, +} + +impl OpcodeTracer { + pub fn new() -> Self { + Self { steps: Vec::new() } + } +} + +impl evm_runtime::tracing::EventListener for OpcodeTracer { + fn event(&mut self, event: evm_runtime::tracing::Event) { + match event { + evm_runtime::tracing::Event::Step { + context: _, + opcode, + position, + stack, + memory, + } => self.steps.push(Step { + op: opcode.stringify().as_bytes().to_vec(), + pc: position.clone().unwrap_or_default() as u64, + stack: stack.data().clone(), + memory: memory.data().clone(), + }), + _ => {} + } + } +} + +pub trait EventListener { + fn event(&mut self, event: Event); +} + +impl EventListener for CallTracer { + fn event(&mut self, event: Event) { + match event { + Event::Call { + code_address, + transfer, + input, + target_gas, + is_static, + context, + } + | Event::PrecompileSubcall { + code_address, + transfer, + input, + target_gas, + is_static, + context, + } => { + let call_type = if is_static { + CallType::STATICCALL + } else { + self.call_type() + }; + self.stack.push(CallTrace { + call_type, + from: context.caller, + to: code_address, + input: input.to_vec(), + value: transfer.clone().map(|x| x.value).unwrap_or_default(), + gas: target_gas.unwrap_or_default(), + gas_used: 0, + output: None, + error: None, + revert_reason: None, + calls: Vec::new(), + depth: 0, + }); + } + Event::TransactCall { + caller, + address, + value, + data, + gas_limit, + } => { + self.stack.push(CallTrace { + call_type: CallType::CALL, + from: caller, + to: address, + input: data.to_vec(), + value, + gas: gas_limit, + gas_used: 0, + output: None, + error: None, + revert_reason: None, + calls: Vec::new(), + depth: 0, + }); + } + Event::Create { + caller, + address, + value, + init_code, + target_gas, + .. + } => { + self.stack.push(CallTrace { + call_type: CallType::CREATE, + from: caller, + to: address, + input: init_code.to_vec(), + value, + gas: target_gas.unwrap_or_default(), + gas_used: 0, + output: None, + error: None, + revert_reason: None, + calls: Vec::new(), + depth: 0, + }); + } + Event::TransactCreate { + caller, + value, + init_code, + gas_limit, + address, + } + | Event::TransactCreate2 { + caller, + value, + init_code, + gas_limit, + address, + .. + } => { + self.stack.push(CallTrace { + call_type: CallType::CREATE, + from: caller, + to: address, + input: init_code.to_vec(), + value, + gas: gas_limit, + gas_used: 0, + output: None, + error: None, + revert_reason: None, + calls: Vec::new(), + depth: 0, + }); + } + Event::Suicide { + address, + target, + balance, + } => { + self.stack.push(CallTrace { + call_type: CallType::SUICIDE, + from: address, + to: target, + input: vec![], + value: balance, + gas: 0, + gas_used: 0, + output: None, + error: None, + revert_reason: None, + calls: Vec::new(), + depth: 0, + }); + } + Event::Exit { reason, return_value } => { + if let Some(mut trace) = self.stack.pop() { + match reason { + ExitReason::Succeed(ExitSucceed::Returned) => trace.output = Some(return_value.to_vec()), + ExitReason::Succeed(_) => {} + ExitReason::Error(e) => trace.error = Some(e.stringify().as_bytes().to_vec()), + ExitReason::Revert(_) => trace.revert_reason = Some(return_value.to_vec()), + ExitReason::Fatal(e) => trace.error = Some(e.stringify().as_bytes().to_vec()), + } + + if let Some(snapshot) = self.snapshot { + trace.gas_used = trace.gas.saturating_sub(snapshot.gas()); + } + + if let Some(index) = self.events.iter().position(|x| x.depth > trace.depth) { + trace.calls = self.events.drain(index..).collect(); + } + + self.events.push(trace); + } + } + Event::Enter { depth } => { + if let Some(event) = self.stack.last_mut() { + event.depth = depth; + } + } + } + } +} + +pub struct EvmRuntimeTracer; + +impl evm_runtime::tracing::EventListener for EvmRuntimeTracer { + fn event(&mut self, event: evm_runtime::tracing::Event) { + call_tracer::with(|tracer| { + tracer.evm_runtime_event(event); + }); + } +} + +pub struct EvmGasometerTracer; + +impl evm_gasometer::tracing::EventListener for EvmGasometerTracer { + fn event(&mut self, event: evm_gasometer::tracing::Event) { + call_tracer::with(|tracer| { + tracer.evm_gasometer_event(event); + }); + } +} + +environmental::environmental!(call_tracer: CallTracer); + +pub fn call_tracer_using R>(new: &mut CallTracer, f: F) -> R { + call_tracer::using(new, || { + evm_gasometer::tracing::using(&mut EvmGasometerTracer, || { + evm_runtime::tracing::using(&mut EvmRuntimeTracer, f) + }) + }) +} + +pub(crate) fn call_tracer_with(f: F) { + call_tracer::with(f); +} + +pub fn opcode_tracer_using R>(new: &mut OpcodeTracer, f: F) -> R { + evm_runtime::tracing::using(new, f) +} + +trait Stringify { + fn stringify(&self) -> &str; +} + +impl Stringify for Opcode { + fn stringify(&self) -> &str { + match self { + &Opcode::STOP => "STOP", + &Opcode::ADD => "ADD", + &Opcode::MUL => "MUL", + &Opcode::SUB => "SUB", + &Opcode::DIV => "DIV", + &Opcode::SDIV => "SDIV", + &Opcode::MOD => "MOD", + &Opcode::SMOD => "SMOD", + &Opcode::ADDMOD => "ADDMOD", + &Opcode::MULMOD => "MULMOD", + &Opcode::EXP => "EXP", + &Opcode::SIGNEXTEND => "SIGNEXTEND", + &Opcode::LT => "LT", + &Opcode::GT => "GT", + &Opcode::SLT => "SLT", + &Opcode::SGT => "SGT", + &Opcode::EQ => "EQ", + &Opcode::ISZERO => "ISZERO", + &Opcode::AND => "AND", + &Opcode::OR => "OR", + &Opcode::XOR => "XOR", + &Opcode::NOT => "NOT", + &Opcode::BYTE => "BYTE", + &Opcode::SHL => "SHL", + &Opcode::SHR => "SHR", + &Opcode::SAR => "SAR", + &Opcode::SHA3 => "SHA3", + &Opcode::ADDRESS => "ADDRESS", + &Opcode::BALANCE => "BALANCE", + &Opcode::ORIGIN => "ORIGIN", + &Opcode::CALLER => "CALLER", + &Opcode::CALLVALUE => "CALLVALUE", + &Opcode::CALLDATALOAD => "CALLDATALOAD", + &Opcode::CALLDATASIZE => "CALLDATASIZE", + &Opcode::CALLDATACOPY => "CALLDATACOPY", + &Opcode::CODESIZE => "CODESIZE", + &Opcode::CODECOPY => "CODECOPY", + &Opcode::GASPRICE => "GASPRICE", + &Opcode::EXTCODESIZE => "EXTCODESIZE", + &Opcode::EXTCODECOPY => "EXTCODECOPY", + &Opcode::RETURNDATASIZE => "RETURNDATASIZE", + &Opcode::RETURNDATACOPY => "RETURNDATACOPY", + &Opcode::EXTCODEHASH => "EXTCODEHASH", + &Opcode::BLOCKHASH => "BLOCKHASH", + &Opcode::COINBASE => "COINBASE", + &Opcode::TIMESTAMP => "TIMESTAMP", + &Opcode::NUMBER => "NUMBER", + &Opcode::DIFFICULTY => "DIFFICULTY", + &Opcode::GASLIMIT => "GASLIMIT", + &Opcode::CHAINID => "CHAINID", + &Opcode::SELFBALANCE => "SELFBALANCE", + &Opcode::POP => "POP", + &Opcode::MLOAD => "MLOAD", + &Opcode::MSTORE => "MSTORE", + &Opcode::MSTORE8 => "MSTORE8", + &Opcode::SLOAD => "SLOAD", + &Opcode::SSTORE => "SSTORE", + &Opcode::JUMP => "JUMP", + &Opcode::JUMPI => "JUMPI", + &Opcode::PC => "PC", + &Opcode::MSIZE => "MSIZE", + &Opcode::GAS => "GAS", + &Opcode::JUMPDEST => "JUMPDEST", + &Opcode::PUSH1 => "PUSH1", + &Opcode::PUSH2 => "PUSH2", + &Opcode::PUSH3 => "PUSH3", + &Opcode::PUSH4 => "PUSH4", + &Opcode::PUSH5 => "PUSH5", + &Opcode::PUSH6 => "PUSH6", + &Opcode::PUSH7 => "PUSH7", + &Opcode::PUSH8 => "PUSH8", + &Opcode::PUSH9 => "PUSH9", + &Opcode::PUSH10 => "PUSH10", + &Opcode::PUSH11 => "PUSH11", + &Opcode::PUSH12 => "PUSH12", + &Opcode::PUSH13 => "PUSH13", + &Opcode::PUSH14 => "PUSH14", + &Opcode::PUSH15 => "PUSH15", + &Opcode::PUSH16 => "PUSH16", + &Opcode::PUSH17 => "PUSH17", + &Opcode::PUSH18 => "PUSH18", + &Opcode::PUSH19 => "PUSH19", + &Opcode::PUSH20 => "PUSH20", + &Opcode::PUSH21 => "PUSH21", + &Opcode::PUSH22 => "PUSH22", + &Opcode::PUSH23 => "PUSH23", + &Opcode::PUSH24 => "PUSH24", + &Opcode::PUSH25 => "PUSH25", + &Opcode::PUSH26 => "PUSH26", + &Opcode::PUSH27 => "PUSH27", + &Opcode::PUSH28 => "PUSH28", + &Opcode::PUSH29 => "PUSH29", + &Opcode::PUSH30 => "PUSH30", + &Opcode::PUSH31 => "PUSH31", + &Opcode::PUSH32 => "PUSH32", + &Opcode::DUP1 => "DUP1", + &Opcode::DUP2 => "DUP2", + &Opcode::DUP3 => "DUP3", + &Opcode::DUP4 => "DUP4", + &Opcode::DUP5 => "DUP5", + &Opcode::DUP6 => "DUP6", + &Opcode::DUP7 => "DUP7", + &Opcode::DUP8 => "DUP8", + &Opcode::DUP9 => "DUP9", + &Opcode::DUP10 => "DUP10", + &Opcode::DUP11 => "DUP11", + &Opcode::DUP12 => "DUP12", + &Opcode::DUP13 => "DUP13", + &Opcode::DUP14 => "DUP14", + &Opcode::DUP15 => "DUP15", + &Opcode::DUP16 => "DUP16", + &Opcode::SWAP1 => "SWAP1", + &Opcode::SWAP2 => "SWAP2", + &Opcode::SWAP3 => "SWAP3", + &Opcode::SWAP4 => "SWAP4", + &Opcode::SWAP5 => "SWAP5", + &Opcode::SWAP6 => "SWAP6", + &Opcode::SWAP7 => "SWAP7", + &Opcode::SWAP8 => "SWAP8", + &Opcode::SWAP9 => "SWAP9", + &Opcode::SWAP10 => "SWAP10", + &Opcode::SWAP11 => "SWAP11", + &Opcode::SWAP12 => "SWAP12", + &Opcode::SWAP13 => "SWAP13", + &Opcode::SWAP14 => "SWAP14", + &Opcode::SWAP15 => "SWAP15", + &Opcode::SWAP16 => "SWAP16", + &Opcode::LOG0 => "LOG0", + &Opcode::LOG1 => "LOG1", + &Opcode::LOG2 => "LOG2", + &Opcode::LOG3 => "LOG3", + &Opcode::LOG4 => "LOG4", + &Opcode::CREATE => "CREATE", + &Opcode::CALL => "CALL", + &Opcode::CALLCODE => "CALLCODE", + &Opcode::RETURN => "RETURN", + &Opcode::DELEGATECALL => "DELEGATECALL", + &Opcode::STATICCALL => "STATICCALL", + &Opcode::REVERT => "REVERT", + &Opcode::INVALID => "INVALID", + &Opcode::CREATE2 => "CREATE2", + &Opcode::EOFMAGIC => "EOFMAGIC", + &Opcode::SUICIDE => "SUICIDE", + _ => "UNKNOWN", + } + } +} + +impl Stringify for ExitError { + fn stringify(&self) -> &str { + match self { + ExitError::StackUnderflow => "StackUnderflow", + ExitError::StackOverflow => "StackOverflow", + ExitError::InvalidJump => "InvalidJump", + ExitError::InvalidRange => "InvalidRange", + ExitError::DesignatedInvalid => "DesignatedInvalid", + ExitError::CallTooDeep => "CallTooDeep", + ExitError::CreateCollision => "CreateCollision", + ExitError::CreateContractLimit => "CreateContractLimit", + ExitError::InvalidCode(_) => "InvalidCode", + ExitError::OutOfOffset => "OutOfOffset", + ExitError::OutOfGas => "OutOfGas", + ExitError::OutOfFund => "OutOfFund", + ExitError::PCUnderflow => "PCUnderflow", + ExitError::CreateEmpty => "CreateEmpty", + ExitError::Other(msg) => msg, + ExitError::MaxNonce => "MaxNonce", + } + } +} + +impl Stringify for ExitFatal { + fn stringify(&self) -> &str { + match self { + ExitFatal::NotSupported => "NotSupported", + ExitFatal::UnhandledInterrupt => "UnhandledInterrupt", + ExitFatal::CallErrorAsFatal(e) => e.stringify(), + ExitFatal::Other(msg) => msg, + } + } +} diff --git a/modules/homa-validator-list/src/mock.rs b/modules/homa-validator-list/src/mock.rs index 65792b063..802d506ba 100644 --- a/modules/homa-validator-list/src/mock.rs +++ b/modules/homa-validator-list/src/mock.rs @@ -30,7 +30,6 @@ use module_support::ExchangeRate; use orml_traits::parameter_type_with_key; use primitives::{Amount, Balance, CurrencyId, TokenSymbol}; use sp_runtime::{traits::IdentityLookup, BuildStorage}; -use sp_std::cell::RefCell; use std::collections::HashMap; pub type AccountId = u128; @@ -108,15 +107,15 @@ impl orml_currencies::Config for Runtime { type WeightInfo = (); } -thread_local! { - pub static SHARES: RefCell> = RefCell::new(HashMap::new()); - pub static ACCUMULATED_SLASH: RefCell = RefCell::new(0); +parameter_types! { + pub static Shares: HashMap<(AccountId, AccountId), Balance> = HashMap::new(); + pub static AccumulatedSlash: Balance = 0; } pub struct MockOnSlash; impl Happened for MockOnSlash { fn happened(amount: &Balance) { - ACCUMULATED_SLASH.with(|v| *v.borrow_mut() += amount); + AccumulatedSlash::mutate(|v| *v += amount); } } @@ -124,15 +123,15 @@ pub struct MockOnIncreaseGuarantee; impl Happened<(AccountId, AccountId, Balance)> for MockOnIncreaseGuarantee { fn happened(info: &(AccountId, AccountId, Balance)) { let (account_id, relaychain_id, amount) = info; - SHARES.with(|v| { - let mut old_map = v.borrow().clone(); + Shares::mutate(|v| { + let mut old_map = v.clone(); if let Some(share) = old_map.get_mut(&(*account_id, *relaychain_id)) { *share = share.saturating_add(*amount); } else { old_map.insert((*account_id, *relaychain_id), *amount); }; - *v.borrow_mut() = old_map; + *v = old_map; }); } } @@ -141,15 +140,15 @@ pub struct MockOnDecreaseGuarantee; impl Happened<(AccountId, AccountId, Balance)> for MockOnDecreaseGuarantee { fn happened(info: &(AccountId, AccountId, Balance)) { let (account_id, relaychain_id, amount) = info; - SHARES.with(|v| { - let mut old_map = v.borrow().clone(); + Shares::mutate(|v| { + let mut old_map = v.clone(); if let Some(share) = old_map.get_mut(&(*account_id, *relaychain_id)) { *share = share.saturating_sub(*amount); } else { old_map.insert((*account_id, *relaychain_id), Default::default()); }; - *v.borrow_mut() = old_map; + *v = old_map; }); } } diff --git a/modules/honzon/src/mock.rs b/modules/honzon/src/mock.rs index 51d509166..dc5c1595d 100644 --- a/modules/honzon/src/mock.rs +++ b/modules/honzon/src/mock.rs @@ -43,7 +43,7 @@ use sp_runtime::{ traits::{AccountIdConversion, IdentityLookup, One as OneT}, BuildStorage, FixedPointNumber, }; -use sp_std::{cell::RefCell, str::FromStr}; +use sp_std::str::FromStr; mod honzon { pub use super::super::*; @@ -170,18 +170,18 @@ impl AuctionManager for MockAuctionManager { } } -thread_local! { - static IS_SHUTDOWN: RefCell = RefCell::new(false); +parameter_types! { + static IsShutdown: bool = false; } pub fn mock_shutdown() { - IS_SHUTDOWN.with(|v| *v.borrow_mut() = true) + IsShutdown::mutate(|v| *v = true) } pub struct MockEmergencyShutdown; impl EmergencyShutdown for MockEmergencyShutdown { fn is_shutdown() -> bool { - IS_SHUTDOWN.with(|v| *v.borrow_mut()) + IsShutdown::get() } } diff --git a/modules/incentives/src/mock.rs b/modules/incentives/src/mock.rs index 252493508..2c9243cf2 100644 --- a/modules/incentives/src/mock.rs +++ b/modules/incentives/src/mock.rs @@ -30,7 +30,6 @@ pub use module_support::{Price, Ratio, SwapLimit}; use orml_traits::parameter_type_with_key; use primitives::{DexShare, TokenSymbol}; use sp_runtime::{traits::IdentityLookup, AccountId32, BuildStorage}; -use sp_std::cell::RefCell; pub type AccountId = AccountId32; @@ -88,18 +87,18 @@ impl orml_tokens::Config for Runtime { type DustRemovalWhitelist = Nothing; } -thread_local! { - static IS_SHUTDOWN: RefCell = RefCell::new(false); +parameter_types! { + static IsShutdown: bool = false; } pub fn mock_shutdown() { - IS_SHUTDOWN.with(|v| *v.borrow_mut() = true) + IsShutdown::mutate(|v| *v = true) } pub struct MockEmergencyShutdown; impl EmergencyShutdown for MockEmergencyShutdown { fn is_shutdown() -> bool { - IS_SHUTDOWN.with(|v| *v.borrow_mut()) + IsShutdown::get() } } diff --git a/modules/liquid-crowdloan/src/mock.rs b/modules/liquid-crowdloan/src/mock.rs index 827b073be..f984e0e08 100644 --- a/modules/liquid-crowdloan/src/mock.rs +++ b/modules/liquid-crowdloan/src/mock.rs @@ -33,7 +33,6 @@ use orml_traits::parameter_type_with_key; use primitives::{Amount, TokenSymbol}; use sp_core::H160; use sp_runtime::{traits::IdentityLookup, AccountId32, BuildStorage}; -use std::cell::RefCell; pub type AccountId = AccountId32; pub type BlockNumber = u64; @@ -118,9 +117,9 @@ impl module_currencies::Config for Runtime { type OnDust = (); } -thread_local! { - pub static TRANSFER_RECORD: RefCell> = RefCell::new(None); - pub static TRANSFER_OK: RefCell = RefCell::new(true); +parameter_types! { + pub static TransferRecord: Option<(AccountId, AccountId, Balance)> = None; + pub static TransferOk: bool = true; } pub struct MockXcmTransfer; @@ -130,8 +129,8 @@ impl CrowdloanVaultXcm for MockXcmTransfer { recipient: AccountId, amount: Balance, ) -> DispatchResult { - if TRANSFER_OK.with(|v| *v.borrow()) { - TRANSFER_RECORD.with(|v| *v.borrow_mut() = Some((vault, recipient, amount))); + if TransferOk::get() { + TransferRecord::mutate(|v| *v = Some((vault, recipient, amount))); Ok(()) } else { Err(DispatchError::Other("transfer failed")) @@ -193,8 +192,8 @@ impl ExtBuilder { } pub fn build(self) -> sp_io::TestExternalities { - TRANSFER_RECORD.with(|v| *v.borrow_mut() = None); - TRANSFER_OK.with(|v| *v.borrow_mut() = self.transfer_ok); + TransferRecord::mutate(|v| *v = None); + TransferOk::mutate(|v| *v = self.transfer_ok); let mut t = frame_system::GenesisConfig::::default() .build_storage() diff --git a/modules/loans/src/mock.rs b/modules/loans/src/mock.rs index 2e334ea00..4bcdc81ab 100644 --- a/modules/loans/src/mock.rs +++ b/modules/loans/src/mock.rs @@ -34,7 +34,6 @@ use sp_runtime::{ traits::{AccountIdConversion, IdentityLookup}, BuildStorage, }; -use sp_std::cell::RefCell; use std::collections::HashMap; pub type AccountId = u128; @@ -200,8 +199,8 @@ impl RiskManager for MockRiskManager { } } -thread_local! { - pub static DOT_SHARES: RefCell> = RefCell::new(HashMap::new()); +parameter_types! { + pub static DotShares: HashMap = HashMap::new(); } pub struct MockOnUpdateLoan; @@ -216,10 +215,10 @@ impl Happened<(AccountId, CurrencyId, Amount, Balance)> for MockOnUpdateLoan { }; if *currency_id == DOT { - DOT_SHARES.with(|v| { - let mut old_map = v.borrow().clone(); + DotShares::mutate(|v| { + let mut old_map = v.clone(); old_map.insert(*who, new_share_amount); - *v.borrow_mut() = old_map; + *v = old_map; }); } } diff --git a/modules/prices/src/mock.rs b/modules/prices/src/mock.rs index d54fb0ab9..0eed4a52f 100644 --- a/modules/prices/src/mock.rs +++ b/modules/prices/src/mock.rs @@ -31,7 +31,6 @@ use sp_runtime::{ traits::{IdentityLookup, One as OneT, Zero}, BuildStorage, DispatchError, FixedPointNumber, }; -use sp_std::cell::RefCell; pub type AccountId = u128; pub type BlockNumber = u64; @@ -61,18 +60,18 @@ impl frame_system::Config for Runtime { type AccountData = (); } -thread_local! { - static CHANGED: RefCell = RefCell::new(false); +parameter_types! { + static Changed: bool = false; } pub fn mock_oracle_update() { - CHANGED.with(|v| *v.borrow_mut() = true) + Changed::mutate(|v| *v = true) } pub struct MockDataProvider; impl DataProvider for MockDataProvider { fn get(currency_id: &CurrencyId) -> Option { - if CHANGED.with(|v| *v.borrow_mut()) { + if Changed::get() { match *currency_id { AUSD => None, TAI => Some(Price::saturating_from_integer(40000)), diff --git a/modules/transaction-payment/src/mock.rs b/modules/transaction-payment/src/mock.rs index 01af4cbd2..aecfcff61 100644 --- a/modules/transaction-payment/src/mock.rs +++ b/modules/transaction-payment/src/mock.rs @@ -41,7 +41,6 @@ use sp_runtime::{ traits::{AccountIdConversion, IdentityLookup, One}, BuildStorage, Perbill, }; -use sp_std::cell::RefCell; pub type AccountId = AccountId32; pub type BlockNumber = u64; @@ -145,10 +144,6 @@ impl module_currencies::Config for Runtime { type OnDust = (); } -thread_local! { - static IS_SHUTDOWN: RefCell = RefCell::new(false); -} - ord_parameter_types! { pub const Zero: AccountId = AccountId::new([0u8; 32]); } @@ -196,36 +191,36 @@ parameter_types! { pub DotFeeSwapPath: Vec = vec![DOT, AUSD, ACA]; } -thread_local! { - pub static TIP_UNBALANCED_AMOUNT: RefCell = RefCell::new(0); - pub static FEE_UNBALANCED_AMOUNT: RefCell = RefCell::new(0); +parameter_types! { + pub static TipUnbalancedAmount: u128 = 0; + pub static FeeUnbalancedAmount: u128 = 0; } pub struct DealWithFees; impl OnUnbalanced> for DealWithFees { fn on_unbalanceds(mut fees_then_tips: impl Iterator>) { if let Some(fees) = fees_then_tips.next() { - FEE_UNBALANCED_AMOUNT.with(|a| *a.borrow_mut() += fees.peek()); + FeeUnbalancedAmount::mutate(|a| *a += fees.peek()); if let Some(tips) = fees_then_tips.next() { - TIP_UNBALANCED_AMOUNT.with(|a| *a.borrow_mut() += tips.peek()); + TipUnbalancedAmount::mutate(|a| *a += tips.peek()); } } } } -thread_local! { - static RELATIVE_PRICE: RefCell> = RefCell::new(Some(Price::one())); +parameter_types! { + static RelativePrice: Option = Some(Price::one()); } pub struct MockPriceSource; impl MockPriceSource { pub fn set_relative_price(price: Option) { - RELATIVE_PRICE.with(|v| *v.borrow_mut() = price); + RelativePrice::mutate(|v| *v = price); } } impl PriceProvider for MockPriceSource { fn get_relative_price(_base: CurrencyId, _quote: CurrencyId) -> Option { - RELATIVE_PRICE.with(|v| *v.borrow_mut()) + RelativePrice::get() } fn get_price(_currency_id: CurrencyId) -> Option { @@ -256,7 +251,7 @@ impl WeightToFeeT for TransactionByteFee { type Balance = Balance; fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()).saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) + Self::Balance::saturated_from(weight.ref_time()).saturating_mul(TransactionByteFee::get()) } } @@ -287,8 +282,8 @@ impl Config for Runtime { type DefaultFeeTokens = DefaultFeeTokens; } -thread_local! { - static WEIGHT_TO_FEE: RefCell = RefCell::new(1); +parameter_types! { + static WeightToFeeStep: u128 = 1; } pub struct WeightToFee; @@ -299,7 +294,7 @@ impl WeightToFeePolynomial for WeightToFee { smallvec![frame_support::weights::WeightToFeeCoefficient { degree: 1, coeff_frac: Perbill::zero(), - coeff_integer: WEIGHT_TO_FEE.with(|v| *v.borrow()), + coeff_integer: WeightToFeeStep::get(), negative: false, }] } @@ -363,9 +358,9 @@ impl ExtBuilder { } fn set_constants(&self) { ExtrinsicBaseWeight::mutate(|v| *v = self.base_weight); - TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.byte_fee); - WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); - TIP_PER_WEIGHT_STEP.with(|v| *v.borrow_mut() = self.tip_per_weight_step); + TransactionByteFee::mutate(|v| *v = self.byte_fee); + WeightToFeeStep::mutate(|v| *v = self.weight_to_fee); + TipPerWeightStep::mutate(|v| *v = self.tip_per_weight_step); } pub fn build(self) -> sp_io::TestExternalities { self.set_constants(); diff --git a/modules/transaction-payment/src/tests.rs b/modules/transaction-payment/src/tests.rs index 93c27798b..2db98a6bd 100644 --- a/modules/transaction-payment/src/tests.rs +++ b/modules/transaction-payment/src/tests.rs @@ -27,9 +27,9 @@ use frame_support::{ dispatch::{DispatchClass, DispatchInfo, Pays}, }; use mock::{ - AccountId, BlockWeights, Currencies, DEXModule, ExtBuilder, FeePoolSize, MockPriceSource, Runtime, RuntimeCall, - RuntimeOrigin, System, TransactionPayment, ACA, ALICE, AUSD, BOB, CHARLIE, DAVE, DOT, FEE_UNBALANCED_AMOUNT, LDOT, - TIP_UNBALANCED_AMOUNT, + AccountId, BlockWeights, Currencies, DEXModule, ExtBuilder, FeePoolSize, FeeUnbalancedAmount, MockPriceSource, + Runtime, RuntimeCall, RuntimeOrigin, System, TipUnbalancedAmount, TransactionPayment, ACA, ALICE, AUSD, BOB, + CHARLIE, DAVE, DOT, LDOT, }; use module_support::{BuyWeightRate, DEXManager, Price, TransactionPayment as TransactionPaymentT}; use orml_traits::{MultiCurrency, MultiLockableCurrency}; @@ -310,8 +310,8 @@ fn pre_post_dispatch_and_refund_native_is_enough() { let refund = 200; // 1000 - 800 assert_eq!(Currencies::free_balance(ACA, &ALICE), 100000 - fee + refund); - assert_eq!(FEE_UNBALANCED_AMOUNT.with(|a| *a.borrow()), fee - refund); - assert_eq!(TIP_UNBALANCED_AMOUNT.with(|a| *a.borrow()), 0); + assert_eq!(FeeUnbalancedAmount::get(), fee - refund); + assert_eq!(TipUnbalancedAmount::get(), 0); System::assert_has_event(crate::mock::RuntimeEvent::TransactionPayment( crate::Event::TransactionFeePaid { @@ -323,7 +323,7 @@ fn pre_post_dispatch_and_refund_native_is_enough() { )); // reset and test refund with tip - FEE_UNBALANCED_AMOUNT.with(|a| *a.borrow_mut() = 0); + FeeUnbalancedAmount::mutate(|a| *a = 0); let tip: Balance = 5; let pre = ChargeTransactionPayment::::from(tip) @@ -340,8 +340,8 @@ fn pre_post_dispatch_and_refund_native_is_enough() { &Ok(()) )); assert_eq!(Currencies::free_balance(ACA, &CHARLIE), 100000 - fee - tip + refund); - assert_eq!(FEE_UNBALANCED_AMOUNT.with(|a| *a.borrow()), fee - refund); - assert_eq!(TIP_UNBALANCED_AMOUNT.with(|a| *a.borrow()), tip); + assert_eq!(FeeUnbalancedAmount::get(), fee - refund); + assert_eq!(TipUnbalancedAmount::get(), tip); System::assert_has_event(crate::mock::RuntimeEvent::TransactionPayment( crate::Event::TransactionFeePaid { @@ -438,11 +438,8 @@ fn pre_post_dispatch_and_refund_with_fee_currency_call(token: CurrencyId, surplu Currencies::free_balance(ACA, &ALICE), aca_init + refund + refund_surplus ); - assert_eq!( - FEE_UNBALANCED_AMOUNT.with(|a| *a.borrow()), - fee - refund + actual_surplus - ); - assert_eq!(TIP_UNBALANCED_AMOUNT.with(|a| *a.borrow()), 0); + assert_eq!(FeeUnbalancedAmount::get(), fee - refund + actual_surplus); + assert_eq!(TipUnbalancedAmount::get(), 0); System::assert_has_event(crate::mock::RuntimeEvent::TransactionPayment( crate::Event::TransactionFeePaid { @@ -454,7 +451,7 @@ fn pre_post_dispatch_and_refund_with_fee_currency_call(token: CurrencyId, surplu )); // reset and test refund with tip - FEE_UNBALANCED_AMOUNT.with(|a| *a.borrow_mut() = 0); + FeeUnbalancedAmount::mutate(|a| *a = 0); assert_ok!(Currencies::update_balance( RuntimeOrigin::root(), @@ -513,11 +510,8 @@ fn pre_post_dispatch_and_refund_with_fee_currency_call(token: CurrencyId, surplu Currencies::free_balance(ACA, &CHARLIE), aca_init + refund + refund_surplus ); - assert_eq!( - FEE_UNBALANCED_AMOUNT.with(|a| *a.borrow()), - fee - refund + surplus - refund_surplus - ); - assert_eq!(TIP_UNBALANCED_AMOUNT.with(|a| *a.borrow()), tip); + assert_eq!(FeeUnbalancedAmount::get(), fee - refund + surplus - refund_surplus); + assert_eq!(TipUnbalancedAmount::get(), tip); System::assert_has_event(crate::mock::RuntimeEvent::TransactionPayment( crate::Event::TransactionFeePaid { diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 708c464c5..27a95d15e 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -14,6 +14,7 @@ num_enum = { workspace = true } scale-info = { workspace = true } enumflags2 = { workspace = true, features = ["serde"] } paste = { workspace = true } +hex = { workspace = true, features = ["alloc"], optional = true } sp-core = { workspace = true } sp-runtime = { workspace = true } @@ -36,6 +37,7 @@ serde_json = { workspace = true, features = ["std"] } [features] default = ["std"] std = [ + "hex/std", "serde/std", "parity-scale-codec/std", "num_enum/std", @@ -55,3 +57,4 @@ std = [ ] evm-tests = [] try-runtime = [] +tracing = [] diff --git a/primitives/src/evm.rs b/primitives/src/evm.rs index 3df77f3e3..8ce5d2902 100644 --- a/primitives/src/evm.rs +++ b/primitives/src/evm.rs @@ -333,3 +333,78 @@ mod convert { } pub use convert::*; + +#[cfg(feature = "tracing")] +pub mod tracing { + use parity_scale_codec::{Decode, Encode}; + use scale_info::TypeInfo; + use sp_core::{H160, H256, U256}; + use sp_runtime::RuntimeDebug; + use sp_std::vec::Vec; + + #[cfg(feature = "std")] + use serde::{Deserialize, Serialize}; + + #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + pub enum CallType { + CALL, + CALLCODE, + STATICCALL, + DELEGATECALL, + CREATE, + SUICIDE, + } + + impl sp_std::fmt::Display for CallType { + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + match self { + CallType::CALL => write!(f, "CALL"), + CallType::CALLCODE => write!(f, "CALLCODE"), + CallType::STATICCALL => write!(f, "STATICCALL"), + CallType::DELEGATECALL => write!(f, "DELEGATECALL"), + CallType::CREATE => write!(f, "CREATE"), + CallType::SUICIDE => write!(f, "SUICIDE"), + } + } + } + + #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] + pub struct CallTrace { + #[cfg_attr(feature = "std", serde(rename = "type"))] + pub call_type: CallType, + pub from: H160, + pub to: H160, + pub input: Vec, + pub value: U256, + // gas limit + #[codec(compact)] + pub gas: u64, + #[codec(compact)] + pub gas_used: u64, + // value returned from EVM, if any + pub output: Option>, + // evm error, if any + pub error: Option>, + // revert reason, if any + pub revert_reason: Option>, + // depth of the call + #[codec(compact)] + pub depth: u32, + // List of sub-calls + pub calls: Vec, + } + + #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] + pub struct Step { + pub op: Vec, + #[codec(compact)] + pub pc: u64, + pub stack: Vec, + pub memory: Vec, + } +} diff --git a/runtime/acala/Cargo.toml b/runtime/acala/Cargo.toml index cdb5b5f53..1f605c882 100644 --- a/runtime/acala/Cargo.toml +++ b/runtime/acala/Cargo.toml @@ -407,3 +407,5 @@ no-metadata-docs = ["frame-support/no-metadata-docs"] # By default some types have documentation, `full-metadata-docs` allows to add documentation to # more types in the metadata. full-metadata-docs = ["frame-support/full-metadata-docs"] + +tracing = ["module-evm/tracing", "module-evm-rpc-runtime-api/tracing"] diff --git a/runtime/acala/src/lib.rs b/runtime/acala/src/lib.rs index 5a31b90d6..780365848 100644 --- a/runtime/acala/src/lib.rs +++ b/runtime/acala/src/lib.rs @@ -2224,6 +2224,83 @@ sp_api::impl_runtime_apis! { } } + #[cfg(feature = "tracing")] + impl module_evm_rpc_runtime_api::EVMTraceApi for Runtime { + fn trace_call( + from: H160, + to: H160, + data: Vec, + value: Balance, + gas_limit: u64, + storage_limit: u32, + access_list: Option>, + ) -> Result, sp_runtime::DispatchError> { + let mut tracer = module_evm::runner::tracing::CallTracer::new(); + module_evm::runner::tracing::call_tracer_using(&mut tracer, || { + if to == H160::zero() { + ::Runner::rpc_create( + from, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } else { + ::Runner::rpc_call( + from, + from, + to, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } + }).map(|_| tracer.finalize()) + } + + fn trace_vm( + from: H160, + to: H160, + data: Vec, + value: Balance, + gas_limit: u64, + storage_limit: u32, + access_list: Option>, + ) -> Result, sp_runtime::DispatchError> { + let mut tracer = module_evm::runner::tracing::OpcodeTracer::new(); + module_evm::runner::tracing::opcode_tracer_using(&mut tracer, || { + if to == H160::zero() { + ::Runner::rpc_create( + from, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } else { + ::Runner::rpc_call( + from, + from, + to, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } + }).map(|_| tracer.steps) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) diff --git a/runtime/karura/Cargo.toml b/runtime/karura/Cargo.toml index 3bb5eab50..c2bcdabd1 100644 --- a/runtime/karura/Cargo.toml +++ b/runtime/karura/Cargo.toml @@ -410,3 +410,5 @@ no-metadata-docs = ["frame-support/no-metadata-docs"] # By default some types have documentation, `full-metadata-docs` allows to add documentation to # more types in the metadata. full-metadata-docs = ["frame-support/full-metadata-docs"] + +tracing = ["module-evm/tracing", "module-evm-rpc-runtime-api/tracing"] diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index e8e77cd6e..057dab8a2 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -2226,6 +2226,83 @@ impl_runtime_apis! { } } + #[cfg(feature = "tracing")] + impl module_evm_rpc_runtime_api::EVMTraceApi for Runtime { + fn trace_call( + from: H160, + to: H160, + data: Vec, + value: Balance, + gas_limit: u64, + storage_limit: u32, + access_list: Option>, + ) -> Result, sp_runtime::DispatchError> { + let mut tracer = module_evm::runner::tracing::CallTracer::new(); + module_evm::runner::tracing::call_tracer_using(&mut tracer, || { + if to == H160::zero() { + ::Runner::rpc_create( + from, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } else { + ::Runner::rpc_call( + from, + from, + to, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } + }).map(|_| tracer.finalize()) + } + + fn trace_vm( + from: H160, + to: H160, + data: Vec, + value: Balance, + gas_limit: u64, + storage_limit: u32, + access_list: Option>, + ) -> Result, sp_runtime::DispatchError> { + let mut tracer = module_evm::runner::tracing::OpcodeTracer::new(); + module_evm::runner::tracing::opcode_tracer_using(&mut tracer, || { + if to == H160::zero() { + ::Runner::rpc_create( + from, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } else { + ::Runner::rpc_call( + from, + from, + to, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } + }).map(|_| tracer.steps) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) diff --git a/runtime/mandala/Cargo.toml b/runtime/mandala/Cargo.toml index d8c246f42..bf34f7787 100644 --- a/runtime/mandala/Cargo.toml +++ b/runtime/mandala/Cargo.toml @@ -441,3 +441,5 @@ no-metadata-docs = ["frame-support/no-metadata-docs"] # By default some types have documentation, `full-metadata-docs` allows to add documentation to # more types in the metadata. full-metadata-docs = ["frame-support/full-metadata-docs"] + +tracing = ["module-evm/tracing", "module-evm-rpc-runtime-api/tracing"] diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 25234aa33..00d61baf6 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -2430,6 +2430,83 @@ impl_runtime_apis! { } } + #[cfg(feature = "tracing")] + impl module_evm_rpc_runtime_api::EVMTraceApi for Runtime { + fn trace_call( + from: H160, + to: H160, + data: Vec, + value: Balance, + gas_limit: u64, + storage_limit: u32, + access_list: Option>, + ) -> Result, sp_runtime::DispatchError> { + let mut tracer = module_evm::runner::tracing::CallTracer::new(); + module_evm::runner::tracing::call_tracer_using(&mut tracer, || { + if to == H160::zero() { + ::Runner::rpc_create( + from, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } else { + ::Runner::rpc_call( + from, + from, + to, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } + }).map(|_| tracer.finalize()) + } + + fn trace_vm( + from: H160, + to: H160, + data: Vec, + value: Balance, + gas_limit: u64, + storage_limit: u32, + access_list: Option>, + ) -> Result, sp_runtime::DispatchError> { + let mut tracer = module_evm::runner::tracing::OpcodeTracer::new(); + module_evm::runner::tracing::opcode_tracer_using(&mut tracer, || { + if to == H160::zero() { + ::Runner::rpc_create( + from, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } else { + ::Runner::rpc_call( + from, + from, + to, + data, + value, + gas_limit, + storage_limit, + access_list.unwrap_or_default().into_iter().map(|v| (v.address, v.storage_keys)).collect(), + ::config(), + ).map(drop) + } + }).map(|_| tracer.steps) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header)