From 6cc112fe220c9c02e56f7d819efea2f492cb47e6 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Thu, 16 Jan 2025 21:10:33 +0100 Subject: [PATCH] Address PR comments --- substrate/frame/revive/src/evm.rs | 2 + .../revive/src/evm/api/debug_rpc_types.rs | 132 ++---------------- .../revive/src/{debug.rs => evm/tracing.rs} | 109 +++------------ substrate/frame/revive/src/exec.rs | 36 +++-- substrate/frame/revive/src/lib.rs | 31 +--- substrate/frame/revive/src/tests.rs | 54 +++---- .../frame/revive/src/tests/test_debug.rs | 97 ------------- substrate/frame/revive/src/tracing.rs | 60 ++++++++ 8 files changed, 135 insertions(+), 386 deletions(-) rename substrate/frame/revive/src/{debug.rs => evm/tracing.rs} (53%) delete mode 100644 substrate/frame/revive/src/tests/test_debug.rs create mode 100644 substrate/frame/revive/src/tracing.rs diff --git a/substrate/frame/revive/src/evm.rs b/substrate/frame/revive/src/evm.rs index 7487803c05e1..33660a36aa6e 100644 --- a/substrate/frame/revive/src/evm.rs +++ b/substrate/frame/revive/src/evm.rs @@ -19,6 +19,8 @@ mod api; pub use api::*; +mod tracing; +pub use tracing::*; mod gas_encoder; pub use gas_encoder::*; pub mod runtime; diff --git a/substrate/frame/revive/src/evm/api/debug_rpc_types.rs b/substrate/frame/revive/src/evm/api/debug_rpc_types.rs index 3350d10f4e22..6682e16a085f 100644 --- a/substrate/frame/revive/src/evm/api/debug_rpc_types.rs +++ b/substrate/frame/revive/src/evm/api/debug_rpc_types.rs @@ -15,10 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - evm::{extract_revert_message, runtime::gas_from_weight, Bytes}, - ExecReturnValue, Weight, -}; +use crate::evm::Bytes; use alloc::{fmt, string::String, vec::Vec}; use codec::{Decode, Encode}; use scale_info::TypeInfo; @@ -143,92 +140,11 @@ pub enum CallType { DelegateCall, } -/// The traces of a transaction. -#[derive(TypeInfo, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] -#[serde(untagged)] -pub enum Traces -where - Output: Default + PartialEq, -{ - /// The call traces captured by a [`CallTracer`] during the transaction. - CallTraces(Vec>), -} - -/// The output and revert reason of an Ethereum trace. -#[derive( - TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq, -)] -pub struct EthOutput { - /// The call output. - pub output: Bytes, - /// The revert reason, if the call reverted. - #[serde(rename = "revertReason")] - pub revert_reason: Option, -} - -impl From for EthOutput { - fn from(value: ExecReturnValue) -> Self { - Self { - revert_reason: if value.did_revert() { - extract_revert_message(&value.data) - } else { - None - }, - output: Bytes(value.data), - } - } -} - -/// The traces used in Ethereum debug RPC. -pub type EthTraces = Traces; - -impl Traces { - /// Return mapped traces with the given gas mapper. - pub fn map( - self, - gas_mapper: impl Fn(Gas) -> T + Copy, - output_mapper: impl Fn(Output) -> V + Copy, - ) -> Traces - where - V: Default + PartialEq, - { - match self { - Traces::CallTraces(traces) => Traces::CallTraces( - traces.into_iter().map(|trace| trace.map(gas_mapper, output_mapper)).collect(), - ), - } - } -} - -impl Traces { - /// Return true if the traces are empty. - pub fn is_empty(&self) -> bool { - match self { - Traces::CallTraces(traces) => traces.is_empty(), - } - } - /// Return the traces as Ethereum traces. - pub fn as_eth_traces(self) -> EthTraces - where - crate::BalanceOf: Into, - { - self.map(|weight| gas_from_weight::(weight), |output| output.into()) - } -} - -/// Return true if the value is the default value. -pub fn is_default(value: &T) -> bool { - *value == T::default() -} - /// A smart contract execution call trace. #[derive( TypeInfo, Default, Encode, Decode, Serialize, Deserialize, Clone, Debug, Eq, PartialEq, )] -pub struct CallTrace -where - Output: Default + PartialEq, -{ +pub struct CallTrace { /// Address of the sender. pub from: H160, /// Address of the receiver. @@ -242,19 +158,22 @@ where #[serde(rename = "type")] pub call_type: CallType, /// Amount of gas provided for the call. - pub gas: Gas, + pub gas: U256, /// Amount of gas used. #[serde(rename = "gasUsed")] - pub gas_used: Gas, + pub gas_used: U256, /// Return data. - #[serde(flatten, skip_serializing_if = "is_default")] - pub output: Output, + #[serde(flatten, skip_serializing_if = "Bytes::is_empty")] + pub output: Bytes, /// The error message if the call failed. #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, + /// The revert reason, if the call reverted. + #[serde(rename = "revertReason")] + pub revert_reason: Option, /// List of sub-calls. #[serde(skip_serializing_if = "Vec::is_empty")] - pub calls: Vec>, + pub calls: Vec, /// List of logs emitted during the call. #[serde(skip_serializing_if = "Vec::is_empty")] pub logs: Vec, @@ -279,35 +198,6 @@ pub struct CallLog { pub position: u32, } -impl CallTrace -where - Output: Default + PartialEq, -{ - /// Return a new call gas with a mapped gas value. - pub fn map( - self, - gas_mapper: impl Fn(Gas) -> T + Copy, - output_mapper: impl Fn(Output) -> V + Copy, - ) -> CallTrace - where - V: Default + PartialEq, - { - CallTrace { - from: self.from, - to: self.to, - input: self.input, - value: self.value, - call_type: self.call_type, - error: self.error, - gas: gas_mapper(self.gas), - gas_used: gas_mapper(self.gas_used), - output: output_mapper(self.output), - calls: self.calls.into_iter().map(|call| call.map(gas_mapper, output_mapper)).collect(), - logs: self.logs, - } - } -} - /// A transaction trace #[derive(Serialize, Deserialize, Clone, Debug)] pub struct TransactionTrace { @@ -315,5 +205,5 @@ pub struct TransactionTrace { #[serde(rename = "txHash")] pub tx_hash: H256, /// The traces of the transaction. - pub result: EthTraces, + pub result: Vec, } diff --git a/substrate/frame/revive/src/debug.rs b/substrate/frame/revive/src/evm/tracing.rs similarity index 53% rename from substrate/frame/revive/src/debug.rs rename to substrate/frame/revive/src/evm/tracing.rs index ac9636466617..c046a93876b4 100644 --- a/substrate/frame/revive/src/debug.rs +++ b/substrate/frame/revive/src/evm/tracing.rs @@ -14,56 +14,19 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -use crate::{evm::TracerConfig, Config, DispatchError, Weight}; -pub use crate::{ - evm::{CallLog, CallTrace, CallType, EthTraces, Traces}, - exec::{ExecResult, ExportedFunction}, +use crate::{ + evm::{extract_revert_message, CallLog, CallTrace, CallType}, primitives::ExecReturnValue, + DispatchError, Tracer, Weight, }; -use alloc::{boxed::Box, format, string::ToString, vec::Vec}; +use alloc::{format, string::ToString, vec::Vec}; use sp_core::{H160, H256, U256}; -/// Umbrella trait for all interfaces that serves for debugging. -pub trait Debugger: CallInterceptor {} -impl Debugger for V where V: CallInterceptor {} - -/// Defines methods to capture contract calls -pub trait Tracing { - /// Called before a contract call is executed - fn enter_child_span( - &mut self, - from: H160, - to: H160, - is_delegate_call: bool, - is_read_only: bool, - value: U256, - input: &[u8], - gas: Weight, - ); - - /// Record a log event - fn log_event(&mut self, event: H160, topics: &[H256], data: &[u8]); - - /// Called after a contract call is executed - fn exit_child_span(&mut self, output: &ExecReturnValue, gas_left: Weight); - - /// Called when a contract call terminates with an error - fn exit_child_span_with_error(&mut self, error: DispatchError, gas_left: Weight); - - /// Collects and returns the traces recorded by the tracer, then clears them. - fn collect_traces(&mut self) -> Traces; -} - -/// Creates a new tracer from the given config. -pub fn make_tracer(config: TracerConfig) -> Box { - match config { - TracerConfig::CallTracer { with_logs } => Box::new(CallTracer::new(with_logs)), - } -} - +/// A Tracer that reports logs and nested call traces transactions #[derive(Default, Debug, Clone, PartialEq, Eq)] -pub struct CallTracer { +pub struct CallTracer U256> { + /// Map Weight to Gas equivalent + gas_mapper: GasMapper, /// Store all in-progress CallTrace instances traces: Vec, /// Stack of indices to the current active traces @@ -72,13 +35,19 @@ pub struct CallTracer { with_log: bool, } -impl CallTracer { - pub fn new(with_log: bool) -> Self { - Self { traces: Vec::new(), current_stack: Vec::new(), with_log } +impl U256> CallTracer { + /// Create a new [`CallTracer`] instance + pub fn new(with_log: bool, gas_mapper: GasMapper) -> Self { + Self { gas_mapper, traces: Vec::new(), current_stack: Vec::new(), with_log } + } + + /// Collect the traces and return them + pub fn collect_traces(&mut self) -> Vec { + core::mem::take(&mut self.traces) } } -impl Tracing for CallTracer { +impl U256> Tracer for CallTracer { fn enter_child_span( &mut self, from: H160, @@ -103,7 +72,7 @@ impl Tracing for CallTracer { value, call_type, input: input.to_vec(), - gas: gas_left, + gas: (self.gas_mapper)(gas_left), ..Default::default() }); @@ -129,10 +98,11 @@ impl Tracing for CallTracer { // Set the output of the current trace let current_index = self.current_stack.pop().unwrap(); let trace = &mut self.traces[current_index]; - trace.output = output.clone(); - trace.gas_used = gas_used; + trace.output = output.data.clone().into(); + trace.gas_used = (self.gas_mapper)(gas_used); if output.did_revert() { + trace.revert_reason = extract_revert_message(&output.data); trace.error = Some("execution reverted".to_string()); } @@ -146,7 +116,7 @@ impl Tracing for CallTracer { // Set the output of the current trace let current_index = self.current_stack.pop().unwrap(); let trace = &mut self.traces[current_index]; - trace.gas_used = gas_used; + trace.gas_used = (self.gas_mapper)(gas_used); trace.error = match error { DispatchError::Module(sp_runtime::ModuleError { message, .. }) => @@ -160,37 +130,4 @@ impl Tracing for CallTracer { self.traces[*parent_index].calls.push(child_trace); } } - - fn collect_traces(&mut self) -> Traces { - let traces = core::mem::take(&mut self.traces); - Traces::CallTraces(traces) - } -} - -/// Provides an interface for intercepting contract calls. -pub trait CallInterceptor { - /// Allows to intercept contract calls and decide whether they should be executed or not. - /// If the call is intercepted, the mocked result of the call is returned. - /// - /// # Arguments - /// - /// * `contract_address` - The address of the contract that is about to be executed. - /// * `entry_point` - Describes whether the call is the constructor or a regular call. - /// * `input_data` - The raw input data of the call. - /// - /// # Expected behavior - /// - /// This method should return: - /// * `Some(ExecResult)` - if the call should be intercepted and the mocked result of the call - /// is returned. - /// * `None` - otherwise, i.e. the call should be executed normally. - fn intercept_call( - _contract_address: &H160, - _entry_point: ExportedFunction, - _input_data: &[u8], - ) -> Option { - None - } } - -impl CallInterceptor for () {} diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 006ee93e3ec3..4c3ad508366c 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -17,12 +17,12 @@ use crate::{ address::{self, AddressMapper}, - debug::CallInterceptor, gas::GasMeter, limits, primitives::{ExecReturnValue, StorageDeposit}, runtime_decl_for_revive_api::{Decode, Encode, RuntimeDebugNoBound, TypeInfo}, storage::{self, meter::Diff, WriteOutcome}, + tracing::if_tracer, transient_storage::TransientStorage, BalanceOf, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, Error, Event, ImmutableData, ImmutableDataOf, Pallet as Contracts, @@ -1077,7 +1077,7 @@ where let contract_address = T::AddressMapper::to_address(account_id); let maybe_caller_address = caller.account_id().map(T::AddressMapper::to_address); - crate::if_tracer(|tracer| { + if_tracer(|tracer| { tracer.enter_child_span( maybe_caller_address.unwrap_or_default(), contract_address, @@ -1089,19 +1089,17 @@ where ); }); - let output = T::Debug::intercept_call(&contract_address, entry_point, &input_data) - .unwrap_or_else(|| executable.execute(self, entry_point, input_data)) - .map_err(|e| { - crate::if_tracer(|tracer| { - tracer.exit_child_span_with_error( - e.error, - top_frame_mut!(self).nested_gas.gas_consumed(), - ); - }); - ExecError { error: e.error, origin: ErrorOrigin::Callee } - })?; - - crate::if_tracer(|tracer| { + let output = executable.execute(self, entry_point, input_data).map_err(|e| { + if_tracer(|tracer| { + tracer.exit_child_span_with_error( + e.error, + top_frame_mut!(self).nested_gas.gas_consumed(), + ); + }); + ExecError { error: e.error, origin: ErrorOrigin::Callee } + })?; + + if_tracer(|tracer| { tracer.exit_child_span(&output, top_frame_mut!(self).nested_gas.gas_consumed()); }); @@ -1422,7 +1420,7 @@ where )? { self.run(executable, input_data) } else { - crate::if_tracer(|tracer| { + if_tracer(|tracer| { let address = T::AddressMapper::to_address(self.account_id()); tracer.enter_child_span( address, @@ -1442,7 +1440,7 @@ where value, ) { Ok(output) => { - crate::if_tracer(|t| { + if_tracer(|t| { t.exit_child_span( &output, Weight::zero(), // TODO define gas_used for transfer @@ -1451,7 +1449,7 @@ where Ok(()) }, Err(e) => { - crate::if_tracer(|t| { + if_tracer(|t| { t.exit_child_span_with_error( e.error.clone().into(), Weight::zero(), // TODO define gas_used for transfer @@ -1719,7 +1717,7 @@ where fn deposit_event(&mut self, topics: Vec, data: Vec) { let contract = T::AddressMapper::to_address(self.account_id()); - crate::if_tracer(|tracer| { + if_tracer(|tracer| { tracer.log_event(contract, &topics, &data); }); Contracts::::deposit_event(Event::ContractEmitted { contract, data, topics }); diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index fc65b29b261d..420016e1254d 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -35,15 +35,15 @@ mod wasm; mod tests; pub mod chain_extension; -pub mod debug; pub mod evm; pub mod test_utils; +pub mod tracing; pub mod weights; use crate::{ evm::{ runtime::{gas_from_fee, GAS_PRICE}, - GasEncoder, GenericTransaction, Traces, + GasEncoder, GenericTransaction, }, exec::{AccountIdOf, ExecError, Executable, Ext, Key, Stack as ExecStack}, gas::GasMeter, @@ -83,9 +83,9 @@ use sp_runtime::{ pub use crate::{ address::{create1, create2, AccountId32Mapper, AddressMapper}, - debug::Tracing, exec::{MomentOf, Origin}, pallet::*, + tracing::Tracer, }; pub use primitives::*; pub use weights::WeightInfo; @@ -118,7 +118,6 @@ const LOG_TARGET: &str = "runtime::revive"; #[frame_support::pallet] pub mod pallet { use super::*; - use crate::debug::Debugger; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use sp_core::U256; @@ -255,12 +254,6 @@ pub mod pallet { #[pallet::no_default_bounds] type InstantiateOrigin: EnsureOrigin; - /// Debugging utilities for contracts. - /// For production chains, it's recommended to use the `()` implementation of this - /// trait. - #[pallet::no_default_bounds] - type Debug: Debugger; - /// A type that exposes XCM APIs, allowing contracts to interact with other parachains, and /// execute XCM programs. #[pallet::no_default_bounds] @@ -367,7 +360,6 @@ pub mod pallet { type InstantiateOrigin = EnsureSigned; type WeightInfo = (); type WeightPrice = Self; - type Debug = (); type Xcm = (); type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; @@ -677,23 +669,6 @@ pub mod pallet { } } - environmental!(tracer: dyn Tracing + 'static); - - /// Run the given closure with the given tracer. - pub fn using_tracer R>(tracer: &mut (dyn Tracing + 'static), f: F) -> R { - tracer::using(tracer, f) - } - - /// Run the closure when the tracer is enabled. - pub fn if_tracer(f: F) { - tracer::with(f); - } - - /// Collect the traces from the tracer. - pub fn collect_traces() -> Option { - tracer::with(|tracer| tracer.collect_traces()) - } - #[pallet::call] impl Pallet where diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 9f65144961bc..313956ca13cf 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -16,12 +16,8 @@ // limitations under the License. mod pallet_dummy; -mod test_debug; -use self::{ - test_debug::TestDebug, - test_utils::{ensure_stored, expected_deposit}, -}; +use self::test_utils::{ensure_stored, expected_deposit}; use crate::{ self as pallet_revive, address::{create1, create2, AddressMapper}, @@ -29,7 +25,6 @@ use crate::{ ChainExtension, Environment, Ext, RegisteredChainExtension, Result as ExtensionResult, RetVal, ReturnFlags, }, - debug::make_tracer, evm::{runtime::GAS_PRICE, GenericTransaction}, exec::Key, limits, @@ -37,7 +32,7 @@ use crate::{ storage::DeletionQueueManager, test_utils::*, tests::test_utils::{get_contract, get_contract_checked}, - using_tracer, + tracing::using_tracer, wasm::Memory, weights::WeightInfo, AccountId32Mapper, BalanceOf, Code, CodeInfoOf, Config, ContractInfo, ContractInfoOf, @@ -525,7 +520,6 @@ impl Config for Test { type UploadOrigin = EnsureAccount; type InstantiateOrigin = EnsureAccount; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; - type Debug = TestDebug; type ChainId = ChainId; } @@ -4559,7 +4553,7 @@ fn unstable_interface_rejected() { #[test] fn tracing_works() { - use crate::evm::{TracerConfig, *}; + use crate::evm::*; use CallType::*; let (code, _code_hash) = compile_module("tracing").unwrap(); let (wasm_callee, _) = compile_module("tracing_callee").unwrap(); @@ -4573,9 +4567,9 @@ fn tracing_works() { builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); let tracer_options = vec![ - (TracerConfig::CallTracer { with_logs: false }, vec![]), + ( false , vec![]), ( - TracerConfig::CallTracer { with_logs: true }, + true , vec![ CallLog { address: addr, @@ -4593,18 +4587,16 @@ fn tracing_works() { ), ]; - for (tracer_config, logs) in tracer_options { - let mut tracer = make_tracer(tracer_config); - using_tracer(&mut *tracer, || { + for (with_logs, logs) in tracer_options { + let mut tracer = CallTracer::new(with_logs, |_| U256::zero()); + using_tracer(&mut tracer, || { builder::bare_call(addr).data((3u32, addr_callee).encode()).build() }); - let traces = tracer.collect_traces() - .map(|_| Weight::default(), |value| EthOutput::from(value)); assert_eq!( - traces, - Traces::CallTraces(vec![CallTrace { + tracer.collect_traces(), + vec![CallTrace { from: ALICE_ADDR, to: addr, input: (3u32, addr_callee).encode(), @@ -4615,14 +4607,12 @@ fn tracing_works() { from: addr, to: addr_callee, input: 2u32.encode(), - output: EthOutput { - output: hex_literal::hex!( - "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001a546869732066756e6374696f6e20616c77617973206661696c73000000000000" - ).to_vec().into(), - revert_reason: Some( - "execution reverted: This function always fails".to_string() - ), - }, + output: hex_literal::hex!( + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001a546869732066756e6374696f6e20616c77617973206661696c73000000000000" + ).to_vec().into(), + revert_reason: Some( + "execution reverted: This function always fails".to_string() + ), error: Some("execution reverted".to_string()), call_type: Call, ..Default::default() @@ -4638,10 +4628,7 @@ fn tracing_works() { from: addr, to: addr_callee, input: 1u32.encode(), - output: EthOutput { - output: Default::default(), - revert_reason: None, - }, + output: Default::default(), error: Some("ContractTrapped".to_string()), call_type: Call, ..Default::default() @@ -4657,10 +4644,7 @@ fn tracing_works() { from: addr, to: addr_callee, input: 0u32.encode(), - output: EthOutput { - output: 0u32.to_le_bytes().to_vec().into(), - revert_reason: None, - }, + output: 0u32.to_le_bytes().to_vec().into(), call_type: Call, ..Default::default() }, @@ -4679,7 +4663,7 @@ fn tracing_works() { }, ], ..Default::default() - },]) + },] ); } }); diff --git a/substrate/frame/revive/src/tests/test_debug.rs b/substrate/frame/revive/src/tests/test_debug.rs deleted file mode 100644 index 314011928f64..000000000000 --- a/substrate/frame/revive/src/tests/test_debug.rs +++ /dev/null @@ -1,97 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::*; - -use crate::{ - debug::{CallInterceptor, ExecResult, ExportedFunction}, - primitives::ExecReturnValue, - test_utils::*, -}; -use frame_support::traits::Currency; -use pretty_assertions::assert_eq; -use std::cell::RefCell; - -thread_local! { - static INTERCEPTED_ADDRESS: RefCell> = RefCell::new(None); -} - -pub struct TestDebug; - -impl CallInterceptor for TestDebug { - fn intercept_call( - contract_address: &sp_core::H160, - _entry_point: ExportedFunction, - _input_data: &[u8], - ) -> Option { - INTERCEPTED_ADDRESS.with(|i| { - if i.borrow().as_ref() == Some(contract_address) { - Some(Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![] })) - } else { - None - } - }) - } -} - -#[test] -fn call_interception_works() { - let (wasm, _) = compile_module("dummy").unwrap(); - - ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); - - let account_id = Contracts::bare_instantiate( - RuntimeOrigin::signed(ALICE), - 0, - GAS_LIMIT, - deposit_limit::().into(), - Code::Upload(wasm), - vec![], - // some salt to ensure that the address of this contract is unique among all tests - Some([0x41; 32]), - ) - .result - .unwrap() - .addr; - - // no interception yet - assert_ok!(Contracts::call( - RuntimeOrigin::signed(ALICE), - account_id, - 0, - GAS_LIMIT, - deposit_limit::(), - vec![], - )); - - // intercept calls to this contract - INTERCEPTED_ADDRESS.with(|i| *i.borrow_mut() = Some(account_id)); - - assert_err_ignore_postinfo!( - Contracts::call( - RuntimeOrigin::signed(ALICE), - account_id, - 0, - GAS_LIMIT, - deposit_limit::(), - vec![], - ), - >::ContractReverted, - ); - }); -} diff --git a/substrate/frame/revive/src/tracing.rs b/substrate/frame/revive/src/tracing.rs new file mode 100644 index 000000000000..3e0af6990444 --- /dev/null +++ b/substrate/frame/revive/src/tracing.rs @@ -0,0 +1,60 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use crate::{ + exec::{ExecResult, ExportedFunction}, + primitives::ExecReturnValue, +}; +use crate::{DispatchError, Weight}; +use environmental::environmental; +use sp_core::{H160, H256, U256}; + +environmental!(tracer: dyn Tracer + 'static); + +/// Run the given closure with the given tracer. +pub fn using_tracer R>(tracer: &mut (dyn Tracer + 'static), f: F) -> R { + tracer::using_once(tracer, f) +} + +/// Run the closure when the tracer is enabled. +pub fn if_tracer(f: F) { + tracer::with(f); +} + +/// Defines methods to capture contract calls +pub trait Tracer { + /// Called before a contract call is executed + fn enter_child_span( + &mut self, + from: H160, + to: H160, + is_delegate_call: bool, + is_read_only: bool, + value: U256, + input: &[u8], + gas: Weight, + ); + + /// Record a log event + fn log_event(&mut self, event: H160, topics: &[H256], data: &[u8]); + + /// Called after a contract call is executed + fn exit_child_span(&mut self, output: &ExecReturnValue, gas_left: Weight); + + /// Called when a contract call terminates with an error + fn exit_child_span_with_error(&mut self, error: DispatchError, gas_left: Weight); +}