diff --git a/Makefile b/Makefile index 906942f5b..acb1a3374 100644 --- a/Makefile +++ b/Makefile @@ -150,7 +150,7 @@ test-eth: githooks test-evm .PHONY: test-evm test-evm: githooks - SKIP_WASM_BUILD= ${cargo_test} -p module-evm --features tracing + SKIP_WASM_BUILD= ${cargo_test} -p module-evm -p module-evm-bridge --features tracing SKIP_WASM_BUILD= ${cargo_test} --release -p evm-jsontests --features evm-tests .PHONY: test-runtimes diff --git a/modules/evm-bridge/Cargo.toml b/modules/evm-bridge/Cargo.toml index b5221e1c2..86631d884 100644 --- a/modules/evm-bridge/Cargo.toml +++ b/modules/evm-bridge/Cargo.toml @@ -59,3 +59,4 @@ try-runtime = [ "frame-system/try-runtime", "module-evm/try-runtime", ] +tracing = ["module-evm/tracing"] diff --git a/modules/evm-bridge/src/tests.rs b/modules/evm-bridge/src/tests.rs index 43c66648f..46caf13fe 100644 --- a/modules/evm-bridge/src/tests.rs +++ b/modules/evm-bridge/src/tests.rs @@ -353,3 +353,81 @@ fn liquidation_err_fails_as_expected() { ); }); } + +#[cfg(feature = "tracing")] +#[test] +fn tracing_should_work() { + use module_evm::runner::tracing; + use primitives::evm::tracing::TracerConfig; + + ExtBuilder::default() + .balances(vec![(alice(), 1_000_000_000_000), (bob(), 1_000_000_000_000)]) + .build() + .execute_with(|| { + deploy_contracts(); + let mut tracer = tracing::Tracer::new(TracerConfig::CallTracer); + tracing::using(&mut tracer, || { + assert_err!( + EVMBridge::::transfer( + InvokeContext { + contract: erc20_address(), + sender: bob_evm_addr(), + origin: bob_evm_addr(), + }, + alice_evm_addr(), + 10 + ), + Error::::ExecutionRevert + ); + }); + let expected = r#"[ + { + "type": "CALL", + "from": "0x1000000000000000000000000000000000000002", + "to": "0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643", + "input": "0xa9059cbb0000000000000000000000001000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a", + "value": "0x0", + "gas": 200000, + "gasUsed": 200000, + "output": null, + "error": null, + "revertReason": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002645524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63650000000000000000000000000000000000000000000000000000", + "depth": 0, + "calls": [] + } + ]"#; + let expected = serde_json::from_str::>(expected).unwrap(); + assert_eq!(tracer.finalize(), tracing::TraceOutcome::Calls(expected)); + + tracing::using(&mut tracer, || { + assert_ok!(EVMBridge::::transfer( + InvokeContext { + contract: erc20_address(), + sender: alice_evm_addr(), + origin: alice_evm_addr(), + }, + bob_evm_addr(), + 100 + )); + }); + + let expected = r#"[ + { + "type": "CALL", + "from": "0x1000000000000000000000000000000000000001", + "to": "0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643", + "input": "0xa9059cbb00000000000000000000000010000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000064", + "value": "0x0", + "gas": 200000, + "gasUsed": 51929, + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "error": null, + "revertReason": null, + "depth": 0, + "calls": [] + } + ]"#; + let expected = serde_json::from_str::>(expected).unwrap(); + assert_eq!(tracer.finalize(), tracing::TraceOutcome::Calls(expected)); + }); +} diff --git a/modules/evm/rpc/runtime-api/src/lib.rs b/modules/evm/rpc/runtime-api/src/lib.rs index 0c22f9e80..b5a156863 100644 --- a/modules/evm/rpc/runtime-api/src/lib.rs +++ b/modules/evm/rpc/runtime-api/src/lib.rs @@ -83,27 +83,10 @@ sp_api::decl_runtime_apis! { #[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; + pub trait EVMTraceApi { + fn trace_extrinsic( + extrinsic: Block::Extrinsic, + tracer_config: primitives::evm::tracing::TracerConfig, + ) -> Result; } } diff --git a/modules/evm/src/runner/state.rs b/modules/evm/src/runner/state.rs index 06eac3605..5c2a17df0 100644 --- a/modules/evm/src/runner/state.rs +++ b/modules/evm/src/runner/state.rs @@ -738,7 +738,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu } if let Err(e) = self.record_external_operation(crate::ExternalOperation::AccountBasicRead) { - return (e.into(), Vec::new()); + return emit_exit!(e.into(), Vec::new()); } let context = Context { @@ -873,15 +873,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu self.state.metadata_mut().access_address(caller); self.state.metadata_mut().access_address(address); - event!(Create { - caller, - address, - scheme, - value, - init_code: &init_code, - target_gas - }); - if let Some(depth) = self.state.metadata().depth { if depth >= self.config.call_stack_limit { return Capture::Exit((ExitError::CallTooDeep.into(), None, Vec::new())); @@ -1011,15 +1002,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> StackExecu // Set target address *self.state.metadata_mut().target_mut() = Some(code_address); - event!(Call { - code_address, - transfer: &transfer, - input: &input, - target_gas, - is_static, - context: &context, - }); - let after_gas = if take_l64 && self.config.call_l64_after_gas { if self.config.estimate { let initial_after_gas = self.state.metadata().gasometer.gas(); @@ -1386,7 +1368,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler ) -> Capture<(ExitReason, Option, Vec), Self::CreateInterrupt> { if let Err(e) = self.maybe_record_init_code_cost(&init_code) { let reason: ExitReason = e.into(); - emit_exit!(reason.clone()); return Capture::Exit((reason, None, Vec::new())); } @@ -1402,6 +1383,14 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler init_code: Vec, target_gas: Option, ) -> Capture<(ExitReason, Option, Vec), Self::CreateInterrupt> { + event!(Create { + caller, + address: self.create_address(scheme).unwrap_or_default(), + value, + init_code: &init_code, + target_gas + }); + if let Err(e) = self.maybe_record_init_code_cost(&init_code) { let reason: ExitReason = e.into(); emit_exit!(reason.clone()); @@ -1449,6 +1438,15 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler is_static: bool, context: Context, ) -> Capture<(ExitReason, Vec), Self::CallInterrupt> { + event!(Call { + code_address, + transfer: &transfer, + input: &input, + target_gas, + is_static, + context: &context, + }); + let capture = self.call_inner( code_address, transfer, @@ -1558,7 +1556,7 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr } event!(PrecompileSubcall { - code_address: code_address.clone(), + code_address, transfer: &transfer, input: &input, target_gas: gas_limit, @@ -1576,7 +1574,7 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr is_static, context.clone(), ) { - Capture::Exit((s, v)) => (s, v), + Capture::Exit((s, v)) => emit_exit!(s, v), Capture::Trap(rt) => { // Ideally this would pass the interrupt back to the executor so it could be // handled like any other call, however the type signature of this function does diff --git a/modules/evm/src/runner/tracing.rs b/modules/evm/src/runner/tracing.rs index 08f134a48..c67fbcc07 100644 --- a/modules/evm/src/runner/tracing.rs +++ b/modules/evm/src/runner/tracing.rs @@ -17,13 +17,13 @@ // along with this program. If not, see . use module_evm_utility::{ - evm::{Context, CreateScheme, ExitError, ExitFatal, ExitReason, ExitSucceed, Opcode, Transfer}, + evm::{Context, 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, VMTrace}; +pub use primitives::evm::tracing::{CallTrace, CallType, OpcodeConfig, Step, TraceOutcome, TracerConfig}; #[derive(Debug, Copy, Clone)] pub enum Event<'a> { @@ -38,7 +38,6 @@ pub enum Event<'a> { Create { caller: H160, address: H160, - scheme: CreateScheme, value: U256, init_code: &'a [u8], target_gas: Option, @@ -88,43 +87,80 @@ pub enum Event<'a> { } pub struct Tracer { - events: Vec, + config: TracerConfig, + calls: Vec, stack: Vec, + depth: u32, steps: Vec, - opcode: Option, - snapshot: Option, + step_counter: u32, + gas: u64, + current_opcode: Option, } impl Tracer { - pub fn new() -> Self { + pub fn new(config: TracerConfig) -> Self { Self { - events: Vec::new(), + config, + calls: Vec::new(), stack: Vec::new(), + depth: 0, steps: Vec::new(), - opcode: None, - snapshot: None, + step_counter: 0, + gas: 0, + current_opcode: None, } } - pub fn finalize(&mut self) -> Vec { - self.events.drain(..).rev().collect() + #[inline] + fn trace_memory(&self) -> bool { + matches!( + self.config, + TracerConfig::OpcodeTracer(OpcodeConfig { + enable_memory: true, + .. + }) + ) + } + + #[inline] + fn trace_stack(&self) -> bool { + matches!( + self.config, + TracerConfig::OpcodeTracer(OpcodeConfig { + disable_stack: false, + .. + }) + ) } - pub fn steps(&self) -> Vec { - self.steps.clone() + #[inline] + fn trace_call(&self) -> bool { + matches!(self.config, TracerConfig::CallTracer) + } + + // increment step counter and check if we should record this step + #[inline] + fn count_step(&mut self) -> bool { + self.step_counter += 1; + if let TracerConfig::OpcodeTracer(OpcodeConfig { page, page_size, .. }) = self.config { + if self.step_counter > page * page_size && self.step_counter <= (page + 1) * page_size { + return true; + } + } + false } - 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, + pub fn finalize(&mut self) -> TraceOutcome { + match self.config { + TracerConfig::CallTracer => { + assert!(self.stack.is_empty(), "Call stack is not empty"); + TraceOutcome::Calls(self.calls.drain(..).collect()) + } + TracerConfig::OpcodeTracer(_) => TraceOutcome::Steps(self.steps.drain(..).collect()), } } + #[inline] fn evm_runtime_event(&mut self, event: evm_runtime::tracing::Event) { match event { evm_runtime::tracing::Event::Step { @@ -134,74 +170,99 @@ impl Tracer { stack, memory, } => { - self.opcode = Some(opcode); - self.steps.push(Step { - op: opcode.stringify().as_bytes().to_vec(), - pc: position.clone().unwrap_or_default() as u64, - depth: self.stack.last().map(|x| x.depth).unwrap_or_default(), - gas: if let Some(snapshot) = self.snapshot { - snapshot.gas() - } else { - 0 - }, - stack: stack.data().clone(), - memory: if memory.is_empty() { - None - } else { - Some(memory.data().clone()) - }, - }) + self.current_opcode = Some(opcode); + if self.count_step() { + self.steps.push(Step { + op: opcode, + pc: position.as_ref().map_or(0, |pc| *pc as u32), + depth: self.depth, + gas: 0, + stack: if self.trace_stack() { + stack + .data() + .iter() + .map(|x| { + let slice = x.as_fixed_bytes(); + // trim leading zeros + let start = slice.iter().position(|x| *x != 0).unwrap_or(31); + slice[start..].to_vec() + }) + .collect() + } else { + Vec::new() + }, + memory: if memory.is_empty() || !self.trace_memory() { + None + } else { + let chunks = memory.data().chunks(32); + let size = chunks.len(); + let mut slices: Vec> = Vec::with_capacity(size); + for (idx, chunk) in chunks.enumerate() { + if idx + 1 == size { + // last chunk must not be trimmed because it can be less than 32 bytes + slices.push(chunk.to_vec()); + } else { + // trim leading zeros + if let Some(start) = chunk.iter().position(|x| *x != 0) { + slices.push(chunk[start..].to_vec()); + } else { + slices.push(Vec::from([0u8])); + } + } + } + Some(slices) + }, + }) + } + } + evm_runtime::tracing::Event::StepResult { .. } => { + if let Some(step) = self.steps.last_mut() { + step.gas = self.gas; + } } _ => {} }; } + #[inline] 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, + | evm_gasometer::tracing::Event::RecordStipend { snapshot, .. } => { + self.gas = snapshot.map_or(0, |s| s.gas()); + } }; } -} -pub trait EventListener { - fn event(&mut self, event: Event); -} - -impl EventListener for Tracer { - fn event(&mut self, event: Event) { + #[inline] + fn call_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, + call_type: self.current_opcode.map_or(CallType::CALL, |x| x.into()), 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(), + value: transfer.as_ref().map_or(U256::zero(), |x| x.value), + gas: target_gas.unwrap_or(self.gas), gas_used: 0, output: None, error: None, @@ -238,7 +299,6 @@ impl EventListener for Tracer { value, init_code, target_gas, - .. } => { self.stack.push(CallTrace { call_type: CallType::CREATE, @@ -246,7 +306,7 @@ impl EventListener for Tracer { to: address, input: init_code.to_vec(), value, - gas: target_gas.unwrap_or_default(), + gas: target_gas.unwrap_or(self.gas), gas_used: 0, output: None, error: None, @@ -290,40 +350,56 @@ impl EventListener for Tracer { 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, - }); + if let Some(trace) = self.stack.last_mut() { + trace.calls.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(ExitSucceed::Returned) => { + if !return_value.is_empty() { + 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::Revert(_) => { + if !return_value.is_empty() { + 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()); - } + trace.gas_used = trace.gas.saturating_sub(self.gas); - if let Some(index) = self.events.iter().position(|x| x.depth > trace.depth) { - trace.calls = self.events.drain(index..).collect(); + if let Some(index) = self.calls.iter().position(|x| x.depth > trace.depth) { + let mut subcalls = self.calls.drain(index..).collect::>(); + if matches!(reason, ExitReason::Succeed(ExitSucceed::Suicided)) { + let mut suicide_call = trace.calls.pop().expect("suicide call should be injected"); + suicide_call.depth = trace.depth + 1; + subcalls.push(suicide_call); + } + trace.calls = subcalls; } - self.events.push(trace); + self.calls.push(trace); + } else { + panic!("exit event without call event") } } Event::Enter { depth } => { @@ -335,6 +411,30 @@ impl EventListener for Tracer { } } +pub trait EventListener { + fn event(&mut self, event: Event); +} + +impl EventListener for Tracer { + fn event(&mut self, event: Event) { + if self.trace_call() { + self.call_event(event); + } else { + match event { + Event::Exit { reason, .. } => { + if !matches!(reason, ExitReason::Succeed(ExitSucceed::Suicided)) { + self.depth = self.depth.saturating_sub(1); + } + } + Event::Enter { depth } => { + self.depth = depth; + } + _ => {} + } + } + } +} + pub struct EvmRuntimeTracer; impl evm_runtime::tracing::EventListener for EvmRuntimeTracer { @@ -373,157 +473,6 @@ 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 { diff --git a/modules/evm/src/tests.rs b/modules/evm/src/tests.rs index 3d88d7b16..d163ee36e 100644 --- a/modules/evm/src/tests.rs +++ b/modules/evm/src/tests.rs @@ -3009,24 +3009,63 @@ fn tracer_works() { "0x608060405234801561001057600080fd5b5060405161001d9061007e565b604051809103906000f080158015610039573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061008b565b610147806105be83390190565b6105248061009a6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80630be6fe5d146100515780631358f5251461006d57806386b714e214610089578063da1385d5146100a7575b600080fd5b61006b60048036038101906100669190610298565b6100c3565b005b610087600480360381019061008291906102d8565b6100d9565b005b610091610189565b60405161009e9190610384565b60405180910390f35b6100c160048036038101906100bc91906102d8565b6101ad565b005b6100cc826101ad565b6100d5816100d9565b5050565b60005b818110156101855760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631d834a1b8260006040518363ffffffff1660e01b81526004016101409291906103e9565b600060405180830381600087803b15801561015a57600080fd5b505af115801561016e573d6000803e3d6000fd5b50505050808061017d90610441565b9150506100dc565b5050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005b818110156102595760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631d834a1b8260016040518363ffffffff1660e01b81526004016102149291906104c5565b600060405180830381600087803b15801561022e57600080fd5b505af1158015610242573d6000803e3d6000fd5b50505050808061025190610441565b9150506101b0565b5050565b600080fd5b6000819050919050565b61027581610262565b811461028057600080fd5b50565b6000813590506102928161026c565b92915050565b600080604083850312156102af576102ae61025d565b5b60006102bd85828601610283565b92505060206102ce85828601610283565b9150509250929050565b6000602082840312156102ee576102ed61025d565b5b60006102fc84828501610283565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061034a61034561034084610305565b610325565b610305565b9050919050565b600061035c8261032f565b9050919050565b600061036e82610351565b9050919050565b61037e81610363565b82525050565b60006020820190506103996000830184610375565b92915050565b6103a881610262565b82525050565b6000819050919050565b60006103d36103ce6103c9846103ae565b610325565b610262565b9050919050565b6103e3816103b8565b82525050565b60006040820190506103fe600083018561039f565b61040b60208301846103da565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044c82610262565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561047f5761047e610412565b5b600182019050919050565b6000819050919050565b60006104af6104aa6104a58461048a565b610325565b610262565b9050919050565b6104bf81610494565b82525050565b60006040820190506104da600083018561039f565b6104e760208301846104b6565b939250505056fea2646970667358221220c53549ea0c54d760bc0fd8aa7f8eeebf806e4474546e87e9783e4ad3f55dfa6564736f6c63430008090033608060405234801561001057600080fd5b50610127806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80631d834a1b14602d575b600080fd5b60436004803603810190603f919060b8565b6045565b005b600081146067578060008084815260200190815260200160002081905550607e565b600080838152602001908152602001600020600090555b5050565b600080fd5b6000819050919050565b6098816087565b811460a257600080fd5b50565b60008135905060b2816091565b92915050565b6000806040838503121560cc5760cb6082565b5b600060d88582860160a5565b925050602060e78582860160a5565b915050925092905056fea2646970667358221220941edb58b322ea8088f4f9091a8a48c92e59c2f39db303d8e126a0c3dd434dde64736f6c63430008090033" ).unwrap(); + use primitives::evm::tracing::{OpcodeConfig, TraceOutcome, TracerConfig}; + new_test_ext().execute_with(|| { - // create contract - let result = ::Runner::create( - alice(), - contract, - 0, - 500000, - 100000, - vec![], - ::config(), - ) - .unwrap(); + let mut tracer = crate::runner::tracing::Tracer::new(TracerConfig::CallTracer); + let result = crate::runner::tracing::using(&mut tracer, || { + // create contract + ::Runner::create( + alice(), + contract, + 0, + 500000, + 100000, + vec![], + ::config(), + ).unwrap() + }); + let expected = r#" + [ + { + "type": "CREATE", + "from": "0x1000000000000000000000000000000000000001", + "to": "0x5f8bd49cd9f0cb2bd5bb9d4320dfe9b61023249d", + "input": "0x608060405234801561001057600080fd5b5060405161001d9061007e565b604051809103906000f080158015610039573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061008b565b610147806105be83390190565b6105248061009a6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80630be6fe5d146100515780631358f5251461006d57806386b714e214610089578063da1385d5146100a7575b600080fd5b61006b60048036038101906100669190610298565b6100c3565b005b610087600480360381019061008291906102d8565b6100d9565b005b610091610189565b60405161009e9190610384565b60405180910390f35b6100c160048036038101906100bc91906102d8565b6101ad565b005b6100cc826101ad565b6100d5816100d9565b5050565b60005b818110156101855760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631d834a1b8260006040518363ffffffff1660e01b81526004016101409291906103e9565b600060405180830381600087803b15801561015a57600080fd5b505af115801561016e573d6000803e3d6000fd5b50505050808061017d90610441565b9150506100dc565b5050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60005b818110156102595760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631d834a1b8260016040518363ffffffff1660e01b81526004016102149291906104c5565b600060405180830381600087803b15801561022e57600080fd5b505af1158015610242573d6000803e3d6000fd5b50505050808061025190610441565b9150506101b0565b5050565b600080fd5b6000819050919050565b61027581610262565b811461028057600080fd5b50565b6000813590506102928161026c565b92915050565b600080604083850312156102af576102ae61025d565b5b60006102bd85828601610283565b92505060206102ce85828601610283565b9150509250929050565b6000602082840312156102ee576102ed61025d565b5b60006102fc84828501610283565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061034a61034561034084610305565b610325565b610305565b9050919050565b600061035c8261032f565b9050919050565b600061036e82610351565b9050919050565b61037e81610363565b82525050565b60006020820190506103996000830184610375565b92915050565b6103a881610262565b82525050565b6000819050919050565b60006103d36103ce6103c9846103ae565b610325565b610262565b9050919050565b6103e3816103b8565b82525050565b60006040820190506103fe600083018561039f565b61040b60208301846103da565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044c82610262565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82141561047f5761047e610412565b5b600182019050919050565b6000819050919050565b60006104af6104aa6104a58461048a565b610325565b610262565b9050919050565b6104bf81610494565b82525050565b60006040820190506104da600083018561039f565b6104e760208301846104b6565b939250505056fea2646970667358221220c53549ea0c54d760bc0fd8aa7f8eeebf806e4474546e87e9783e4ad3f55dfa6564736f6c63430008090033608060405234801561001057600080fd5b50610127806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80631d834a1b14602d575b600080fd5b60436004803603810190603f919060b8565b6045565b005b600081146067578060008084815260200190815260200160002081905550607e565b600080838152602001908152602001600020600090555b5050565b600080fd5b6000819050919050565b6098816087565b811460a257600080fd5b50565b60008135905060b2816091565b92915050565b6000806040838503121560cc5760cb6082565b5b600060d88582860160a5565b925050602060e78582860160a5565b915050925092905056fea2646970667358221220941edb58b322ea8088f4f9091a8a48c92e59c2f39db303d8e126a0c3dd434dde64736f6c63430008090033", + "value": "0x0", + "gas": 500000, + "gasUsed": 457161, + "output": null, + "error": null, + "revertReason": null, + "depth": 0, + "calls": [ + { + "type": "CREATE", + "from": "0x5f8bd49cd9f0cb2bd5bb9d4320dfe9b61023249d", + "to": "0x7b8f8ca099f6e33cf1817cf67d0556429cfc54e4", + "input": "0x608060405234801561001057600080fd5b50610127806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80631d834a1b14602d575b600080fd5b60436004803603810190603f919060b8565b6045565b005b600081146067578060008084815260200190815260200160002081905550607e565b600080838152602001908152602001600020600090555b5050565b600080fd5b6000819050919050565b6098816087565b811460a257600080fd5b50565b60008135905060b2816091565b92915050565b6000806040838503121560cc5760cb6082565b5b600060d88582860160a5565b925050602060e78582860160a5565b915050925092905056fea2646970667358221220941edb58b322ea8088f4f9091a8a48c92e59c2f39db303d8e126a0c3dd434dde64736f6c63430008090033", + "value": "0x0", + "gas": 419604, + "gasUsed": 91133, + "output": null, + "error": null, + "revertReason": null, + "depth": 1, + "calls": [] + } + ] + } + ] + "#; + let expected = serde_json::from_str::>(expected).unwrap(); + assert_eq!(tracer.finalize(), TraceOutcome::Calls(expected)); let contract_address = result.value; let alice_account_id = ::AddressMapping::get_account_id(&alice()); - let mut tracer = crate::runner::tracing::Tracer::new(); + let mut tracer = crate::runner::tracing::Tracer::new(TracerConfig::CallTracer); crate::runner::tracing::using(&mut tracer, || { assert_ok!(EVM::call( RuntimeOrigin::signed(alice_account_id.clone()), @@ -3077,35 +3116,60 @@ fn tracer_works() { ] "#; let expected = serde_json::from_str::>(expected).unwrap(); - assert_eq!(tracer.finalize(), expected); - let steps = tracer.steps(); - assert_eq!(steps.len(), 553); - let step = r#" - { - "op": [80, 85, 83, 72, 49], - "pc": 0, - "depth": 0, - "gas": 978796, - "stack": [], - "memory": null - } - "#; - let step = serde_json::from_str::(step).unwrap(); - assert_eq!(steps.first().unwrap(), &step); - assert_eq!(String::from_utf8_lossy(&step.op), "PUSH1"); - - let step = r#" - { - "op": [83, 84, 79, 80], - "pc": 194, - "depth": 0, - "gas": 949994, - "stack": ["0x00000000000000000000000000000000000000000000000000000000da1385d5"], - "memory": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 131, 74, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] + assert_eq!(tracer.finalize(), TraceOutcome::Calls(expected)); + + let mut tracer = crate::runner::tracing::Tracer::new(TracerConfig::OpcodeTracer(OpcodeConfig { + page: 0, + page_size: 1_000, + disable_stack: false, + enable_memory: true, + })); + crate::runner::tracing::using(&mut tracer, || { + assert_ok!(EVM::call( + RuntimeOrigin::signed(alice_account_id.clone()), + contract_address, + hex! {" + da1385d5 + 0000000000000000000000000000000000000000000000000000000000000001 + "} + .to_vec(), + 0, + 1000000, + 0, + vec![], + )); + }); + + match tracer.finalize() { + TraceOutcome::Steps(steps) => { + assert_eq!(steps.len(), 553); + let step = r#" + { + "op": 96, + "pc": 0, + "depth": 0, + "gas": 978796, + "stack": [], + "memory": null + } + "#; + let step = serde_json::from_str::(step).unwrap(); + assert_eq!(steps.first().unwrap(), &step); + + let step = r#" + { + "op": 0, + "pc": 194, + "depth": 0, + "gas": 949993, + "stack": [[218, 19, 133, 213]], + "memory": [[0], [0], [128], [0], [29, 131, 74, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0], [0, 0, 0, 1]] + } + "#; + let step = serde_json::from_str::(step).unwrap(); + assert_eq!(steps.last().unwrap(), &step); } - "#; - let step = serde_json::from_str::(step).unwrap(); - assert_eq!(steps.last().unwrap(), &step); - assert_eq!(String::from_utf8_lossy(&step.op), "STOP"); + _ => panic!("unexpected trace outcome"), + } }) } diff --git a/primitives/src/evm.rs b/primitives/src/evm.rs index 98a94730b..d097cb4bd 100644 --- a/primitives/src/evm.rs +++ b/primitives/src/evm.rs @@ -336,9 +336,10 @@ pub use convert::*; #[cfg(feature = "tracing")] pub mod tracing { + use module_evm_utility::evm::Opcode; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; - use sp_core::{H160, H256, U256}; + use sp_core::{H160, U256}; use sp_runtime::RuntimeDebug; use sp_std::vec::Vec; @@ -356,6 +357,19 @@ pub mod tracing { SUICIDE, } + impl From for CallType { + fn from(op: Opcode) -> Self { + match op { + Opcode::CALLCODE => CallType::CALLCODE, + Opcode::DELEGATECALL => CallType::DELEGATECALL, + Opcode::STATICCALL => CallType::STATICCALL, + Opcode::CREATE | Opcode::CREATE2 => CallType::CREATE, + Opcode::SUICIDE => CallType::SUICIDE, + _ => CallType::CALL, + } + } + } + impl sp_std::fmt::Display for CallType { fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { match self { @@ -370,20 +384,24 @@ pub mod tracing { } #[cfg(feature = "std")] - fn vec_to_hex(data: &Vec, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&sp_core::bytes::to_hex(data, false)) - } + mod maybe_hex { + use serde::{Deserialize, Deserializer, Serializer}; + pub fn serialize(data: &Option>, serializer: S) -> Result { + if let Some(data) = data { + sp_core::bytes::serialize(data.as_slice(), serializer) + } else { + serializer.serialize_none() + } + } - #[cfg(feature = "std")] - fn hex_to_vec<'de, D>(deserializer: D) -> Result, D::Error> - where - D: serde::Deserializer<'de>, - { - use serde::de::Error; - String::deserialize(deserializer).and_then(|string| sp_core::bytes::from_hex(&string).map_err(Error::custom)) + pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result>, D::Error> { + use serde::de::Error; + match Option::deserialize(deserializer) { + Ok(Some(data)) => sp_core::bytes::from_hex(data).map_err(Error::custom).map(Some), + Ok(None) => Ok(None), + Err(e) => Err(e), + } + } } #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] @@ -394,10 +412,7 @@ pub mod tracing { pub call_type: CallType, pub from: H160, pub to: H160, - #[cfg_attr( - feature = "std", - serde(serialize_with = "vec_to_hex", deserialize_with = "hex_to_vec") - )] + #[cfg_attr(feature = "std", serde(with = "sp_core::bytes"))] pub input: Vec, pub value: U256, // gas limit @@ -405,10 +420,13 @@ pub mod tracing { pub gas: u64, #[codec(compact)] pub gas_used: u64, + #[cfg_attr(feature = "std", serde(with = "maybe_hex"))] // value returned from EVM, if any pub output: Option>, + #[cfg_attr(feature = "std", serde(with = "maybe_hex"))] // evm error, if any pub error: Option>, + #[cfg_attr(feature = "std", serde(with = "maybe_hex"))] // revert reason, if any pub revert_reason: Option>, // depth of the call @@ -422,24 +440,44 @@ pub mod tracing { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] pub struct Step { - pub op: Vec, + pub op: Opcode, #[codec(compact)] - pub pc: u64, + pub pc: u32, #[codec(compact)] pub depth: u32, #[codec(compact)] pub gas: u64, - pub stack: Vec, - pub memory: Option>, + // 32 bytes stack items without leading zeros + pub stack: Vec>, + // Chunks of memory 32 bytes each without leading zeros except the last one which is untouched + // Recreate the memory by joining the chunks. Each chunk (except the last one) should be 32 bytes + pub memory: Option>>, } #[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 VMTrace { - #[codec(compact)] - pub gas: u64, - pub return_value: H256, - pub struct_logs: Vec, + pub enum TraceOutcome { + Calls(Vec), + Steps(Vec), + } + + #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + pub enum TracerConfig { + CallTracer, + OpcodeTracer(OpcodeConfig), + } + + #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + pub struct OpcodeConfig { + // Tracing opcodes is very expensive, so we need to limit the number of opcodes to trace. + // Each trace call will have a maximum of `page_size` opcodes. If the number of opcodes + // is equal to `page_size` then another trace call will be needed to get the next page of opcodes. + pub page: u32, + // Number of opcodes to trace in a single page. + pub page_size: u32, + pub disable_stack: bool, + pub enable_memory: bool, } } diff --git a/runtime/acala/src/lib.rs b/runtime/acala/src/lib.rs index 30affa31a..c5ce67859 100644 --- a/runtime/acala/src/lib.rs +++ b/runtime/acala/src/lib.rs @@ -2230,86 +2230,16 @@ 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::Tracer::new(); + impl module_evm_rpc_runtime_api::EVMTraceApi for Runtime { + fn trace_extrinsic( + extrinsic: ::Extrinsic, + tracer_config: primitives::evm::tracing::TracerConfig, + ) -> Result { + let mut tracer = module_evm::runner::tracing::Tracer::new(tracer_config); module_evm::runner::tracing::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) - } + Executive::apply_extrinsic(extrinsic) }).map(|_| tracer.finalize()) } - - fn trace_vm( - from: H160, - to: H160, - data: Vec, - value: Balance, - gas_limit: u64, - storage_limit: u32, - access_list: Option>, - ) -> Result { - use sp_core::H256; - use sp_runtime::traits::UniqueSaturatedInto; - let mut tracer = module_evm::runner::tracing::Tracer::new(); - module_evm::runner::tracing::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(|res| (H256::from(res.value), UniqueSaturatedInto::::unique_saturated_into(res.used_gas))) - } 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(|res| (H256::from_slice(&res.value), UniqueSaturatedInto::::unique_saturated_into(res.used_gas))) - } - }).map(|(return_value, gas) | module_evm::runner::tracing::VMTrace { - gas, - return_value, - struct_logs: tracer.steps(), - }) - } } impl cumulus_primitives_core::CollectCollationInfo for Runtime { diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index 99774cb9c..b36513aa5 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -2232,86 +2232,16 @@ 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::Tracer::new(); + impl module_evm_rpc_runtime_api::EVMTraceApi for Runtime { + fn trace_extrinsic( + extrinsic: ::Extrinsic, + tracer_config: primitives::evm::tracing::TracerConfig, + ) -> Result { + let mut tracer = module_evm::runner::tracing::Tracer::new(tracer_config); module_evm::runner::tracing::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) - } + Executive::apply_extrinsic(extrinsic) }).map(|_| tracer.finalize()) } - - fn trace_vm( - from: H160, - to: H160, - data: Vec, - value: Balance, - gas_limit: u64, - storage_limit: u32, - access_list: Option>, - ) -> Result { - use sp_core::H256; - use sp_runtime::traits::UniqueSaturatedInto; - let mut tracer = module_evm::runner::tracing::Tracer::new(); - module_evm::runner::tracing::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(|res| (H256::from(res.value), UniqueSaturatedInto::::unique_saturated_into(res.used_gas))) - } 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(|res| (H256::from_slice(&res.value), UniqueSaturatedInto::::unique_saturated_into(res.used_gas))) - } - }).map(|(return_value, gas) | module_evm::runner::tracing::VMTrace { - gas, - return_value, - struct_logs: tracer.steps(), - }) - } } impl cumulus_primitives_core::CollectCollationInfo for Runtime { diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 018df9312..adf81885f 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -2431,86 +2431,16 @@ 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::Tracer::new(); + impl module_evm_rpc_runtime_api::EVMTraceApi for Runtime { + fn trace_extrinsic( + extrinsic: ::Extrinsic, + tracer_config: primitives::evm::tracing::TracerConfig, + ) -> Result { + let mut tracer = module_evm::runner::tracing::Tracer::new(tracer_config); module_evm::runner::tracing::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) - } + Executive::apply_extrinsic(extrinsic) }).map(|_| tracer.finalize()) } - - fn trace_vm( - from: H160, - to: H160, - data: Vec, - value: Balance, - gas_limit: u64, - storage_limit: u32, - access_list: Option>, - ) -> Result { - use sp_core::H256; - use sp_runtime::traits::UniqueSaturatedInto; - let mut tracer = module_evm::runner::tracing::Tracer::new(); - module_evm::runner::tracing::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(|res| (H256::from(res.value), UniqueSaturatedInto::::unique_saturated_into(res.used_gas))) - } 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(|res| (H256::from_slice(&res.value), UniqueSaturatedInto::::unique_saturated_into(res.used_gas))) - } - }).map(|(return_value, gas) | module_evm::runner::tracing::VMTrace { - gas, - return_value, - struct_logs: tracer.steps(), - }) - } } impl cumulus_primitives_core::CollectCollationInfo for Runtime {