From 3e2b463bbcf2b8183cc3a45262941a16aab0ad15 Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:59:55 +0800 Subject: [PATCH 01/12] init sentio tracer --- Cargo.toml | 4 +- src/tracing/builder/mod.rs | 3 + src/tracing/builder/sentio.rs | 316 ++++++++++++++++++++++++++++++++++ src/tracing/mod.rs | 1 + src/tracing/mux.rs | 1 + tests/it/main.rs | 1 + tests/it/sentio.rs | 212 +++++++++++++++++++++++ 7 files changed, 537 insertions(+), 1 deletion(-) create mode 100644 src/tracing/builder/sentio.rs create mode 100644 tests/it/sentio.rs diff --git a/Cargo.toml b/Cargo.toml index 9b3db9f0..c60b1f6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,8 @@ clippy.lint_groups_priority = "allow" # eth alloy-sol-types = "0.8" alloy-primitives = "0.8" -alloy-rpc-types = { version = "0.3", features = ["eth", "trace"] } +#alloy-rpc-types = { version = "0.3", features = ["eth", "trace"] } +alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "9a18801", features = ["eth", "trace"] } revm = { version = "14", default-features = false, features = ["std"] } anstyle = "1.0" @@ -46,6 +47,7 @@ serde_json = "1.0" # js-tracer boa_engine = { version = "0.19", optional = true } boa_gc = { version = "0.19", optional = true } +log = "0.4.22" [dev-dependencies] snapbox = "0.6" diff --git a/src/tracing/builder/mod.rs b/src/tracing/builder/mod.rs index e6e58d8c..8e96678e 100644 --- a/src/tracing/builder/mod.rs +++ b/src/tracing/builder/mod.rs @@ -6,5 +6,8 @@ pub mod geth; /// Parity style trace builders for `trace_` namespace pub mod parity; +// Sentio trace +pub mod sentio; + /// Walker types used for traversing various callgraphs mod walker; diff --git a/src/tracing/builder/sentio.rs b/src/tracing/builder/sentio.rs new file mode 100644 index 00000000..bc3212e5 --- /dev/null +++ b/src/tracing/builder/sentio.rs @@ -0,0 +1,316 @@ +//! Sentio trace builder + +use crate::tracing::{ + types::{CallTraceNode, CallTraceStepStackItem}, + TracingInspectorConfig, +}; +use alloy_primitives::{Address, U256}; +use revm::{db::DatabaseRef}; +use std::collections::{HashMap, HashSet}; +use std::fmt::Debug; +use alloy_rpc_types::trace::geth::sentio::{FunctionInfo, SentioReceipt, SentioTrace, SentioTracerConfig}; +use revm::interpreter::OpCode; +use log::warn; +use crate::tracing::types::{CallTraceStep, TraceMemberOrder}; +use crate::tracing::utils::maybe_revert_reason; + +#[derive(Clone, Debug)] +pub struct SentioTraceBuilder { + /// Recorded trace nodes. + nodes: Vec, + /// How the traces were recorded + _config: TracingInspectorConfig, + + // address => (pc => function) + function_map: HashMap>, + // address => (pc => bool) + call_map: HashMap>, + with_internal_calls: bool +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct InternalSentioTrace { + pub trace: SentioTrace, + pub exit_pc: Option, + pub function: Option, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct InternalFunctionInfo { + pub function_info: FunctionInfo, + pub address: Address +} + +impl SentioTraceBuilder { + pub fn new(nodes: Vec, config: SentioTracerConfig, _config: TracingInspectorConfig) -> Self { + let mut function_map: HashMap> = HashMap::new(); + for (address, function_infos) in config.functions.into_iter() { + let function_by_pc = function_infos.into_iter().map( + |function_info| (function_info.pc, InternalFunctionInfo{ function_info, address})).collect(); + function_map.insert(address, function_by_pc); + } + let mut call_map: HashMap> = HashMap::new(); + for (address, pcs) in config.calls.into_iter() { + let pc_set = pcs.into_iter().collect(); + call_map.insert(address, pc_set); + } + Self { nodes, _config, function_map, call_map, with_internal_calls: config.with_internal_calls } + } + + pub fn sentio_traces(&self, gas_used: u64, receipt: Option) -> SentioTrace { + SentioTrace { + receipt, + gas_used: U256::from(gas_used), + ..self.transform_call(&self.nodes[0], 0, 0).trace + } + } + + fn get_function_info(&self, address: &Address, pc: &usize) -> Option<&InternalFunctionInfo> { + if let Some(functions) = self.function_map.get(address) { + return functions.get(pc); + } + None + } + + fn is_call(&self, address: &Address, pc: &usize) -> bool { + if let Some(calls) = self.call_map.get(address) { + return calls.contains(pc); + } + false + } + + fn transform_call(&self, node: &CallTraceNode, inst_start_idx: usize, call_pc: usize) -> InternalSentioTrace { + let trace = &node.trace; + let root = InternalSentioTrace{ + trace: SentioTrace { + typ: trace.kind.to_string(), + pc: call_pc, + start_index: inst_start_idx, + gas: U256::from(trace.gas_limit), + gas_used: U256::from(trace.gas_used), + from: Some(trace.caller), + to: Some(trace.address), + input: Some(trace.data.clone()), + value: Some(trace.value), + output: Some(trace.output.clone()), + error: if trace.success { + None + } else if trace.is_revert() { + Some("execution reverted".to_string()) + } else { + Some(format!("{:?}", trace.status)) + }, + revert_reason: if trace.is_revert() { + maybe_revert_reason(trace.output.as_ref()) + } else { + None + }, + ..Default::default() + }, + exit_pc: None, + function: None, + }; + + let mut last_step: Option<&CallTraceStep> = None; + let mut last_pc: usize = call_pc; + let mut next_inst_idx = inst_start_idx; + + let mut frames: Vec = vec![root]; + + let mut entry_pc = HashSet::::new(); + if !trace.kind.is_any_create() && !trace.maybe_precompile.unwrap_or(false) && trace.data.len() >= 4 { + let sig_hash = trace.data.slice(0..4); + if let Some(functions) = self.function_map.get(&trace.address) { + for (pc, function) in functions { + if function.function_info.signature_hash == sig_hash { + entry_pc.insert(*pc); + } + } + } + } + let mut entry_found = false; + + for i in &node.ordering { + match i { + TraceMemberOrder::Call(child_idx) => { + let child_trace = self.transform_call(&self.nodes[node.children[*child_idx]], next_inst_idx, last_pc); + next_inst_idx = child_trace.trace.end_index.clone(); + frames.last_mut().unwrap().trace.traces.push(Box::from(child_trace.trace)); + } + TraceMemberOrder::Step(step_idx) => { + let step = &trace.steps[*step_idx]; + last_step = Some(step); + last_pc = step.pc; + next_inst_idx += 1; + + if !entry_found && entry_pc.contains(&last_pc) { + let Some(root) = frames.first_mut() else { + panic!("no root call"); + }; + root.trace.pc = last_pc; + root.trace.start_index = next_inst_idx - 1; + entry_found = true; + } + + if !self.with_internal_calls { + continue; + } + match step.op { + OpCode::JUMPDEST => { + // check internal function exit + let mut is_exit = false; + for (i, frame) in frames.iter().rev().enumerate() { + if frame.function.is_none() { + continue; + }; + if frame.exit_pc == Some(last_pc) { + let frames_to_pop = i + 1; + if frames_to_pop > 1 { + println!("tail call optimization size: {}", frames_to_pop); + } + for _ in 0..frames_to_pop { + let mut frame = frames.pop().unwrap(); + let InternalFunctionInfo{ function_info: function, address } = &frame.function.unwrap(); + let stack = step.stack.as_ref().unwrap(); + let output_enough = function.output_size <= stack.len(); + if !output_enough { + warn!("stack size not enough, stack: {}, output_size: {}, address: {}, function: {}, pc: {}", stack.len(), function.output_size, address, function.name, last_pc); + if step.is_error() { + warn!("stack size not enough has error, err: {}", step.as_error().unwrap()) + } + } + frame.trace = SentioTrace { + end_index: next_inst_idx - 1, + gas_used: frame.trace.gas - U256::from(step.gas_remaining), + output_stack: if output_enough { Some(stack[stack.len() - function.output_size..].to_vec()) } else { None }, + output_memory: if function.output_memory { Some(step.memory.clone().unwrap().into_bytes()) } else { None }, + ..frame.trace + }; + frames.last_mut().unwrap().trace.traces.push(Box::from(frame.trace)); + } + is_exit = true; + break; + } + } + if is_exit { + continue; + } + + // check internal function entry + if *step_idx == 0 { + continue; + } + let Some(InternalFunctionInfo { function_info: function, address}) = self.get_function_info(&step.contract, &step.pc) else { + continue; + }; + + // ensure callsite + let prev_step = &trace.steps[*step_idx - 1]; + if !prev_step.op.is_jump() { + continue; + } + if !self.is_call(&prev_step.contract, &prev_step.pc) { + continue; + }; + + // get exit pc from stack + let stack = step.stack.as_ref().unwrap(); + let input_enough = function.input_size <= stack.len(); + if !input_enough { + warn!("stack size not enough, stack: {}, input_size: {}, address: {}, function: {}, pc: {}", stack.len(), function.input_size, address, function.name, last_pc); + if step.is_error() { + warn!("stack size not enough has error, err: {}", step.as_error().unwrap()) + } + } + let Some(exit_pc) = stack.get(stack.len() - function.input_size - 1) else { + warn!("function entry stack not enough"); + continue; + }; + let frame = InternalSentioTrace { + trace: SentioTrace { + typ: OpCode::JUMP.to_string(), + pc: last_pc, + start_index: next_inst_idx - 2, + gas: U256::from(step.gas_remaining), + from: Some(step.contract), + to: Some(step.contract), + input_stack: if input_enough { Some(stack[stack.len() - function.input_size..].to_vec()) } else { None }, + name: Some(function.name.clone()), + input_memory: if function.input_memory { Some(step.memory.clone().unwrap().into_bytes()) } else { None }, + ..Default::default() + }, + exit_pc: Some(exit_pc.to::()), + function: Some(InternalFunctionInfo{ function_info: function.clone(), address: address.clone() }), + }; + frames.push(frame); + } + OpCode::REVERT => { + let stack = step.stack.as_ref().unwrap(); + let memory = step.memory.as_ref().unwrap(); + let [size, offset] = stack.last_chunk::<2>().unwrap(); + let output = memory.as_bytes().slice(offset.to::()..(offset + size).to::()); + let frame = SentioTrace { + typ: OpCode::REVERT.to_string(), + pc: last_pc, + start_index: next_inst_idx - 1, + end_index: next_inst_idx, + gas: U256::from(step.gas_remaining), + gas_used: U256::from(step.gas_cost), + error: frames.first().unwrap().trace.error.clone(), + output: Some(output), + ..Default::default() + }; + frames.last_mut().unwrap().trace.traces.push(Box::from(frame)); + } + _ => { } + } + } + TraceMemberOrder::Log(log_idx) => { + let Some(step) = last_step else { + panic!("log without step"); + }; + let (OpCode::LOG0 | OpCode::LOG1 | OpCode::LOG2 | OpCode::LOG3 | OpCode::LOG4) = step.op else { + panic!("log without log op"); + }; + let log = &node.logs[*log_idx]; + let frame = InternalSentioTrace { + trace: SentioTrace { + typ: step.op.to_string(), + pc: last_pc, + start_index: next_inst_idx - 1, + end_index: next_inst_idx, + gas: U256::from(step.gas_remaining), + gas_used: U256::from(step.gas_cost), + address: Some(node.trace.address), + code_address: Some(step.contract), + topics: Some(Vec::from(log.raw_log.topics())), + data: Some(log.raw_log.data.clone()), + ..Default::default() + }, + ..Default::default() + }; + frames.last_mut().unwrap().trace.traces.push(Box::from(frame.trace)); + } + _ => {} + } + } + while frames.len() > 1 { + let mut frame = frames.pop().unwrap(); + frame.trace = SentioTrace { + end_index: next_inst_idx, + gas_used: frame.trace.gas - U256::from(last_step.unwrap().gas_remaining), + output: Some(trace.output.clone()), + ..frame.trace + }; + frames.last_mut().unwrap().trace.traces.push(Box::from(frame.trace)); + } + if frames.len() != 1 { + warn!("frames size: {}", frames.len()); + } + let mut ret = frames.remove(0); + ret.trace.end_index = next_inst_idx; + ret + } +} diff --git a/src/tracing/mod.rs b/src/tracing/mod.rs index ae04e3b1..54c24e3b 100644 --- a/src/tracing/mod.rs +++ b/src/tracing/mod.rs @@ -27,6 +27,7 @@ mod builder; pub use builder::{ geth::{self, GethTraceBuilder}, parity::{self, ParityTraceBuilder}, + sentio::{self, SentioTraceBuilder} }; mod config; diff --git a/src/tracing/mux.rs b/src/tracing/mux.rs index 78897fbb..e92e4526 100644 --- a/src/tracing/mux.rs +++ b/src/tracing/mux.rs @@ -253,6 +253,7 @@ impl DelegatingInspector { Ok(DelegatingInspector::Mux(MuxInspector::try_from_config(config)?)) } + GethDebugBuiltInTracerType::SentioTracer => todo!(), }; inspector.map(|inspector| (tracer_type, inspector)) diff --git a/tests/it/main.rs b/tests/it/main.rs index f83a333b..e5e83264 100644 --- a/tests/it/main.rs +++ b/tests/it/main.rs @@ -4,3 +4,4 @@ mod geth; mod parity; mod transfer; mod writer; +mod sentio; diff --git a/tests/it/sentio.rs b/tests/it/sentio.rs new file mode 100644 index 00000000..105dde61 --- /dev/null +++ b/tests/it/sentio.rs @@ -0,0 +1,212 @@ +//! Geth tests + +use crate::utils::inspect; +use alloy_primitives::{hex, Address, Bytes, U256}; +use alloy_rpc_types::trace::geth::sentio::{FunctionInfo, SentioTracerConfig}; +use revm::{db::{CacheDB, EmptyDB}, primitives::{ + BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, HandlerCfg, + Output, SpecId, TransactTo, TxEnv, +}, DatabaseCommit}; +use revm::primitives::AccountInfo; +use revm_inspectors::tracing::{SentioTraceBuilder, TracingInspector, TracingInspectorConfig}; + +#[test] +fn test_sentiotracer_logs() { + /* + contract LogTracing { + event Log(address indexed addr, uint256 value); + + fallback() external payable { + emit Log(msg.sender, msg.value); + + try this.nestedEmitWithFailure() {} catch {} + try this.nestedEmitWithFailureAfterNestedEmit() {} catch {} + this.nestedEmitWithSuccess(); + } + + function nestedEmitWithFailure() external { + emit Log(msg.sender, 0); + require(false, "nestedEmitWithFailure"); + } + + function nestedEmitWithFailureAfterNestedEmit() external { + this.doubleNestedEmitWithSuccess(); + require(false, "nestedEmitWithFailureAfterNestedEmit"); + } + + function doubleNestedEmitWithSuccess() external { + emit Log(msg.sender, 0); + this.nestedEmitWithSuccess(); + } + + function nestedEmitWithSuccess() external { + emit Log(msg.sender, 0); + } + } + */ + let code = hex!("608060405234801561001057600080fd5b506103ac806100206000396000f3fe60806040526004361061003f5760003560e01c80630332ed131461014d5780636ae1ad40146101625780638384a00214610177578063de7eb4f31461018c575b60405134815233906000805160206103578339815191529060200160405180910390a2306001600160a01b0316636ae1ad406040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561009d57600080fd5b505af19250505080156100ae575060015b50306001600160a01b0316630332ed136040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156100ea57600080fd5b505af19250505080156100fb575060015b50306001600160a01b0316638384a0026040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561013757600080fd5b505af115801561014b573d6000803e3d6000fd5b005b34801561015957600080fd5b5061014b6101a1565b34801561016e57600080fd5b5061014b610253565b34801561018357600080fd5b5061014b6102b7565b34801561019857600080fd5b5061014b6102dd565b306001600160a01b031663de7eb4f36040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156101dc57600080fd5b505af11580156101f0573d6000803e3d6000fd5b505060405162461bcd60e51b8152602060048201526024808201527f6e6573746564456d6974576974684661696c75726541667465724e6573746564604482015263115b5a5d60e21b6064820152608401915061024a9050565b60405180910390fd5b6040516000815233906000805160206103578339815191529060200160405180910390a260405162461bcd60e51b81526020600482015260156024820152746e6573746564456d6974576974684661696c75726560581b604482015260640161024a565b6040516000815233906000805160206103578339815191529060200160405180910390a2565b6040516000815233906000805160206103578339815191529060200160405180910390a2306001600160a01b0316638384a0026040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561033c57600080fd5b505af1158015610350573d6000803e3d6000fd5b5050505056fef950957d2407bed19dc99b718b46b4ce6090c05589006dfb86fd22c34865b23ea2646970667358221220090a696b9fbd22c7d1cc2a0b6d4a48c32d3ba892480713689a3145b73cfeb02164736f6c63430008130033"); + let deployer = Address::ZERO; + + let mut db = CacheDB::new(EmptyDB::default()); + + let cfg = CfgEnvWithHandlerCfg::new(CfgEnv::default(), HandlerCfg::new(SpecId::LONDON)); + + let env = EnvWithHandlerCfg::new_with_cfg_env( + cfg.clone(), + BlockEnv::default(), + TxEnv { + caller: deployer, + gas_limit: 1000000, + transact_to: TransactTo::Create, + data: code.into(), + ..Default::default() + }, + ); + + let mut insp = TracingInspector::new(TracingInspectorConfig::default_geth()); + + // Create contract + let (res, _) = inspect(&mut db, env, &mut insp).unwrap(); + let addr = match res.result { + ExecutionResult::Success { output, .. } => match output { + Output::Create(_, addr) => addr.unwrap(), + _ => panic!("Create failed"), + }, + _ => panic!("Execution failed"), + }; + db.commit(res.state); + + let mut insp = + TracingInspector::new(TracingInspectorConfig::default_geth().set_record_logs(true).set_memory_snapshots(true).set_immediate_bytes(true)); + + let env = EnvWithHandlerCfg::new_with_cfg_env( + cfg, + BlockEnv::default(), + TxEnv { + caller: deployer, + gas_limit: 1000000, + transact_to: TransactTo::Call(addr), + data: Bytes::default(), // call fallback + ..Default::default() + }, + ); + + let (res, _) = inspect(&mut db, env, &mut insp).unwrap(); + assert!(res.result.is_success()); + + + let sentio_tracer_config = SentioTracerConfig { + with_internal_calls: true, + ..SentioTracerConfig::default() + }; + + let traces = insp.into_traces().into_nodes(); + let builder = SentioTraceBuilder::new(traces, sentio_tracer_config, TracingInspectorConfig::default_geth()); + let sentio_trace = builder.sentio_traces(res.result.gas_used(), None); + + assert_eq!(sentio_trace.traces.len(), 4); + assert_eq!(sentio_trace.traces[0].typ, "LOG2"); + assert_eq!(sentio_trace.traces[0].topics.clone().unwrap().len(), 2); + assert_eq!(sentio_trace.traces[1].error, Some("execution reverted".to_string())); + assert_eq!(sentio_trace.traces[1].revert_reason, Some("nestedEmitWithFailure".to_string())); + assert_eq!(sentio_trace.traces[1].traces[0].typ, "LOG2"); +} + +#[test] +fn test_sentiotracer_weth_transfer() { + let user1 = Address::from(hex!("0000000000000000000000000000000000000123")); + let user2 = Address::from(hex!("0000000000000000000000000000000000000456")); + let mut db = CacheDB::new(EmptyDB::default()); + db.insert_account_info(user1, AccountInfo { + balance: U256::from(10000000000_i64), + ..Default::default() + }); + let cfg = CfgEnvWithHandlerCfg::new(CfgEnv::default(), HandlerCfg::new(SpecId::LONDON)); + + // Create contract + let weth_code = hex!("60606040526040805190810160405280600d81526020017f57726170706564204574686572000000000000000000000000000000000000008152506000908051906020019061004f9291906100c8565b506040805190810160405280600481526020017f57455448000000000000000000000000000000000000000000000000000000008152506001908051906020019061009b9291906100c8565b506012600260006101000a81548160ff021916908360ff16021790555034156100c357600080fd5b61016d565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061010957805160ff1916838001178555610137565b82800160010185558215610137579182015b8281111561013657825182559160200191906001019061011b565b5b5090506101449190610148565b5090565b61016a91905b8082111561016657600081600090555060010161014e565b5090565b90565b610c348061017c6000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014757806318160ddd146101a157806323b872dd146101ca5780632e1a7d4d14610243578063313ce5671461026657806370a082311461029557806395d89b41146102e2578063a9059cbb14610370578063d0e30db0146103ca578063dd62ed3e146103d4575b6100b7610440565b005b34156100c457600080fd5b6100cc6104dd565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010c5780820151818401526020810190506100f1565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610187600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061057b565b604051808215151515815260200191505060405180910390f35b34156101ac57600080fd5b6101b461066d565b6040518082815260200191505060405180910390f35b34156101d557600080fd5b610229600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061068c565b604051808215151515815260200191505060405180910390f35b341561024e57600080fd5b61026460048080359060200190919050506109d9565b005b341561027157600080fd5b610279610b05565b604051808260ff1660ff16815260200191505060405180910390f35b34156102a057600080fd5b6102cc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b18565b6040518082815260200191505060405180910390f35b34156102ed57600080fd5b6102f5610b30565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561033557808201518184015260208101905061031a565b50505050905090810190601f1680156103625780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037b57600080fd5b6103b0600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610bce565b604051808215151515815260200191505060405180910390f35b6103d2610440565b005b34156103df57600080fd5b61042a600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610be3565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105735780601f1061054857610100808354040283529160200191610573565b820191906000526020600020905b81548152906001019060200180831161055657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156106dc57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141580156107b457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b156108cf5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561084457600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a2757600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501515610ab457600080fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bc65780601f10610b9b57610100808354040283529160200191610bc6565b820191906000526020600020905b815481529060010190602001808311610ba957829003601f168201915b505050505081565b6000610bdb33848461068c565b905092915050565b60046020528160005260406000206020528060005260406000206000915091505054815600a165627a7a72305820deb4c2ccab3c2fdca32ab3f46728389c2fe2c165d5fafa07661e4e004f6c344a0029"); + let env = EnvWithHandlerCfg::new_with_cfg_env( + cfg.clone(), + BlockEnv::default(), + TxEnv { + caller: user1, + gas_limit: 1000000, + transact_to: TransactTo::Create, + data: weth_code.into(), + ..Default::default() + }, + ); + let mut insp = TracingInspector::new(TracingInspectorConfig::default_geth().set_record_logs(true).set_memory_snapshots(true)); + let (res, _) = inspect(&mut db, env, &mut insp).unwrap(); + let contract_addr = match res.result { + ExecutionResult::Success { output, .. } => match output { + Output::Create(_, addr) => addr.unwrap(), + _ => panic!("Create failed"), + }, + _ => panic!("Execution failed"), + }; + db.commit(res.state); + + // build sentio tracer config + let functions_str = r#"[{"name":"approve","signatureHash":"0x095ea7b3","pc":327,"inputSize":2,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"approve","signatureHash":"0x095ea7b3","pc":338,"inputSize":2,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"approve","signatureHash":"0x095ea7b3","pc":391,"inputSize":2,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"totalSupply","signatureHash":"0x18160ddd","pc":417,"inputSize":0,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"totalSupply","signatureHash":"0x18160ddd","pc":428,"inputSize":0,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"totalSupply","signatureHash":"0x18160ddd","pc":436,"inputSize":0,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"transferFrom","signatureHash":"0x23b872dd","pc":458,"inputSize":3,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"transferFrom","signatureHash":"0x23b872dd","pc":469,"inputSize":3,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"transferFrom","signatureHash":"0x23b872dd","pc":553,"inputSize":3,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"withdraw","signatureHash":"0x2e1a7d4d","pc":579,"inputSize":1,"inputMemory":false,"outputSize":0,"outputMemory":false},{"name":"withdraw","signatureHash":"0x2e1a7d4d","pc":590,"inputSize":1,"inputMemory":false,"outputSize":0,"outputMemory":false},{"name":"withdraw","signatureHash":"0x2e1a7d4d","pc":612,"inputSize":1,"inputMemory":false,"outputSize":0,"outputMemory":false},{"name":"transfer","signatureHash":"0xa9059cbb","pc":880,"inputSize":2,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"transfer","signatureHash":"0xa9059cbb","pc":891,"inputSize":2,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"transfer","signatureHash":"0xa9059cbb","pc":944,"inputSize":2,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"deposit","signatureHash":"0xd0e30db0","pc":970,"inputSize":0,"inputMemory":false,"outputSize":0,"outputMemory":false},{"name":"deposit","signatureHash":"0xd0e30db0","pc":978,"inputSize":0,"inputMemory":false,"outputSize":0,"outputMemory":false},{"name":"deposit","signatureHash":"0xd0e30db0","pc":1088,"inputSize":0,"inputMemory":false,"outputSize":0,"outputMemory":false},{"name":"approve","signatureHash":"0x095ea7b3","pc":1403,"inputSize":2,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"totalSupply","signatureHash":"0x18160ddd","pc":1645,"inputSize":0,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"transferFrom","signatureHash":"0x23b872dd","pc":1676,"inputSize":3,"inputMemory":false,"outputSize":1,"outputMemory":false},{"name":"withdraw","signatureHash":"0x2e1a7d4d","pc":2521,"inputSize":1,"inputMemory":false,"outputSize":0,"outputMemory":false},{"name":"transfer","signatureHash":"0xa9059cbb","pc":3022,"inputSize":2,"inputMemory":false,"outputSize":1,"outputMemory":false}]"#; + let mut tracer_cfg: SentioTracerConfig = Default::default(); + let functions: Vec = serde_json::from_str(functions_str).unwrap(); + tracer_cfg.with_internal_calls = true; + tracer_cfg.functions.insert(contract_addr, functions); + tracer_cfg.calls.insert(contract_addr, vec![182, 3034]); + + // deposit some eth + let env = EnvWithHandlerCfg::new_with_cfg_env( + cfg.clone(), + BlockEnv::default(), + TxEnv { + caller: user1, + gas_limit: 1000000, + transact_to: TransactTo::Call(contract_addr), + value: U256::from(1000), + ..Default::default() + }, + ); + let mut insp = TracingInspector::new(TracingInspectorConfig::default_geth().set_record_logs(true).set_memory_snapshots(true)); + let (res, _) = inspect(&mut db, env, &mut insp).unwrap(); + assert!(res.result.is_success()); + db.commit(res.state); + + let traces = insp.into_traces().into_nodes(); + let builder = SentioTraceBuilder::new(traces, tracer_cfg.clone(), TracingInspectorConfig::default_geth()); + let sentio_trace = builder.sentio_traces(res.result.gas_used(), None); + assert_eq!(sentio_trace.traces[0].typ, "JUMP"); + assert_eq!(sentio_trace.traces[0].name, Some("deposit".to_string())); + assert_eq!(sentio_trace.traces[0].traces[0].typ, "LOG2"); + + // transfer weth + let env = EnvWithHandlerCfg::new_with_cfg_env( + cfg.clone(), + BlockEnv::default(), + TxEnv { + caller: user1, + gas_limit: 1000000, + transact_to: TransactTo::Call(contract_addr), + // transfer("0x0000000000000000000000000000000000000456", 100) + data: hex::decode("a9059cbb00000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000064").unwrap().into(), + ..Default::default() + }, + ); + let mut insp = TracingInspector::new(TracingInspectorConfig::default_geth().set_record_logs(true).set_memory_snapshots(true)); + let (res, _) = inspect(&mut db, env, &mut insp).unwrap(); + assert!(res.result.is_success()); + db.commit(res.state); + + let traces = insp.into_traces().into_nodes(); + let builder = SentioTraceBuilder::new(traces, tracer_cfg.clone(), TracingInspectorConfig::default_geth()); + let sentio_trace = builder.sentio_traces(res.result.gas_used(), None); + + assert_eq!(sentio_trace.pc, 880); + assert_eq!(sentio_trace.start_index, 60); + assert_eq!(sentio_trace.end_index, 273); + assert_eq!(sentio_trace.traces[0].typ, "JUMP"); + assert_eq!(sentio_trace.traces[0].name, Some("transferFrom".to_string())); + assert_eq!(sentio_trace.traces[0].input_stack.as_ref().unwrap().len(), 3); + assert_eq!(sentio_trace.traces[0].output_stack.as_ref().unwrap().len(), 1); + assert_eq!(sentio_trace.traces[0].traces[0].typ, "LOG3"); +} \ No newline at end of file From 3e140f037ac36d465b59f02b764011ea7c1e0c58 Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Thu, 12 Sep 2024 00:55:44 +0800 Subject: [PATCH 02/12] fix null values --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c60b1f6f..0d6603b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ clippy.lint_groups_priority = "allow" alloy-sol-types = "0.8" alloy-primitives = "0.8" #alloy-rpc-types = { version = "0.3", features = ["eth", "trace"] } -alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "9a18801", features = ["eth", "trace"] } +alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "1ab924f", features = ["eth", "trace"] } revm = { version = "14", default-features = false, features = ["std"] } anstyle = "1.0" From fe9b6947c478023a109c15fe23b6ee645c1cf1be Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Thu, 12 Sep 2024 09:58:49 +0800 Subject: [PATCH 03/12] fix debug param --- src/tracing/builder/sentio.rs | 25 ++++++++++++++----------- tests/it/sentio.rs | 1 + 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/tracing/builder/sentio.rs b/src/tracing/builder/sentio.rs index bc3212e5..47de1805 100644 --- a/src/tracing/builder/sentio.rs +++ b/src/tracing/builder/sentio.rs @@ -25,7 +25,8 @@ pub struct SentioTraceBuilder { function_map: HashMap>, // address => (pc => bool) call_map: HashMap>, - with_internal_calls: bool + + tracer_config: SentioTracerConfig, } #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -40,15 +41,16 @@ pub struct InternalSentioTrace { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct InternalFunctionInfo { pub function_info: FunctionInfo, - pub address: Address + pub address: Address, } impl SentioTraceBuilder { pub fn new(nodes: Vec, config: SentioTracerConfig, _config: TracingInspectorConfig) -> Self { + let tracer_config = config.clone(); let mut function_map: HashMap> = HashMap::new(); for (address, function_infos) in config.functions.into_iter() { let function_by_pc = function_infos.into_iter().map( - |function_info| (function_info.pc, InternalFunctionInfo{ function_info, address})).collect(); + |function_info| (function_info.pc, InternalFunctionInfo { function_info, address })).collect(); function_map.insert(address, function_by_pc); } let mut call_map: HashMap> = HashMap::new(); @@ -56,13 +58,14 @@ impl SentioTraceBuilder { let pc_set = pcs.into_iter().collect(); call_map.insert(address, pc_set); } - Self { nodes, _config, function_map, call_map, with_internal_calls: config.with_internal_calls } + Self { nodes, _config, function_map, call_map, tracer_config } } pub fn sentio_traces(&self, gas_used: u64, receipt: Option) -> SentioTrace { SentioTrace { receipt, gas_used: U256::from(gas_used), + tracer_config: if self.tracer_config.debug { Some(self.tracer_config.clone()) } else { None }, ..self.transform_call(&self.nodes[0], 0, 0).trace } } @@ -83,7 +86,7 @@ impl SentioTraceBuilder { fn transform_call(&self, node: &CallTraceNode, inst_start_idx: usize, call_pc: usize) -> InternalSentioTrace { let trace = &node.trace; - let root = InternalSentioTrace{ + let root = InternalSentioTrace { trace: SentioTrace { typ: trace.kind.to_string(), pc: call_pc, @@ -154,7 +157,7 @@ impl SentioTraceBuilder { entry_found = true; } - if !self.with_internal_calls { + if !self.tracer_config.with_internal_calls { continue; } match step.op { @@ -172,7 +175,7 @@ impl SentioTraceBuilder { } for _ in 0..frames_to_pop { let mut frame = frames.pop().unwrap(); - let InternalFunctionInfo{ function_info: function, address } = &frame.function.unwrap(); + let InternalFunctionInfo { function_info: function, address } = &frame.function.unwrap(); let stack = step.stack.as_ref().unwrap(); let output_enough = function.output_size <= stack.len(); if !output_enough { @@ -202,7 +205,7 @@ impl SentioTraceBuilder { if *step_idx == 0 { continue; } - let Some(InternalFunctionInfo { function_info: function, address}) = self.get_function_info(&step.contract, &step.pc) else { + let Some(InternalFunctionInfo { function_info: function, address }) = self.get_function_info(&step.contract, &step.pc) else { continue; }; @@ -237,12 +240,12 @@ impl SentioTraceBuilder { from: Some(step.contract), to: Some(step.contract), input_stack: if input_enough { Some(stack[stack.len() - function.input_size..].to_vec()) } else { None }, - name: Some(function.name.clone()), + name: if self.tracer_config.debug { Some(function.name.clone()) } else { None }, input_memory: if function.input_memory { Some(step.memory.clone().unwrap().into_bytes()) } else { None }, ..Default::default() }, exit_pc: Some(exit_pc.to::()), - function: Some(InternalFunctionInfo{ function_info: function.clone(), address: address.clone() }), + function: Some(InternalFunctionInfo { function_info: function.clone(), address: address.clone() }), }; frames.push(frame); } @@ -264,7 +267,7 @@ impl SentioTraceBuilder { }; frames.last_mut().unwrap().trace.traces.push(Box::from(frame)); } - _ => { } + _ => {} } } TraceMemberOrder::Log(log_idx) => { diff --git a/tests/it/sentio.rs b/tests/it/sentio.rs index 105dde61..aa8a342c 100644 --- a/tests/it/sentio.rs +++ b/tests/it/sentio.rs @@ -152,6 +152,7 @@ fn test_sentiotracer_weth_transfer() { let mut tracer_cfg: SentioTracerConfig = Default::default(); let functions: Vec = serde_json::from_str(functions_str).unwrap(); tracer_cfg.with_internal_calls = true; + tracer_cfg.debug = true; tracer_cfg.functions.insert(contract_addr, functions); tracer_cfg.calls.insert(contract_addr, vec![182, 3034]); From 2e59bc0a0c8097ba46c980d2f9bae9add1d1b801 Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:46:40 +0800 Subject: [PATCH 04/12] compat with sentio trace from erigon & geth --- Cargo.toml | 2 +- src/tracing/types.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0d6603b8..55badbf7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ clippy.lint_groups_priority = "allow" alloy-sol-types = "0.8" alloy-primitives = "0.8" #alloy-rpc-types = { version = "0.3", features = ["eth", "trace"] } -alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "1ab924f", features = ["eth", "trace"] } +alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "cbc82f0", features = ["eth", "trace"] } revm = { version = "14", default-features = false, features = ["std"] } anstyle = "1.0" diff --git a/src/tracing/types.rs b/src/tracing/types.rs index 4f3fea02..53c2381b 100644 --- a/src/tracing/types.rs +++ b/src/tracing/types.rs @@ -403,6 +403,7 @@ impl CallTraceNode { revert_reason: None, calls: Default::default(), logs: Default::default(), + start_index: (), // TODO maybe more elegant way to do this }; if self.trace.kind.is_static_call() { From 02eaf66e7de4a146359a1c78c2ff2bc0d0a943f9 Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:59:44 +0800 Subject: [PATCH 05/12] update dep --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 55badbf7..74e404f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ clippy.lint_groups_priority = "allow" alloy-sol-types = "0.8" alloy-primitives = "0.8" #alloy-rpc-types = { version = "0.3", features = ["eth", "trace"] } -alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "cbc82f0", features = ["eth", "trace"] } +alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "d8899d2", features = ["eth", "trace"] } revm = { version = "14", default-features = false, features = ["std"] } anstyle = "1.0" From 27c0a96739f4b2dfd48e39a6c362cb3cf3641812 Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:16:42 +0800 Subject: [PATCH 06/12] fix jump pc --- src/tracing/builder/sentio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracing/builder/sentio.rs b/src/tracing/builder/sentio.rs index 47de1805..9e29096d 100644 --- a/src/tracing/builder/sentio.rs +++ b/src/tracing/builder/sentio.rs @@ -234,7 +234,7 @@ impl SentioTraceBuilder { let frame = InternalSentioTrace { trace: SentioTrace { typ: OpCode::JUMP.to_string(), - pc: last_pc, + pc: prev_step.pc, start_index: next_inst_idx - 2, gas: U256::from(step.gas_remaining), from: Some(step.contract), From 60bda017b49d8a4006c0311f21de26982ea7c486 Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:22:33 +0800 Subject: [PATCH 07/12] add missing field --- Cargo.toml | 2 +- src/tracing/builder/sentio.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 74e404f5..0b421c30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ clippy.lint_groups_priority = "allow" alloy-sol-types = "0.8" alloy-primitives = "0.8" #alloy-rpc-types = { version = "0.3", features = ["eth", "trace"] } -alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "d8899d2", features = ["eth", "trace"] } +alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "41aca61", features = ["eth", "trace"] } revm = { version = "14", default-features = false, features = ["std"] } anstyle = "1.0" diff --git a/src/tracing/builder/sentio.rs b/src/tracing/builder/sentio.rs index 9e29096d..4528b3b3 100644 --- a/src/tracing/builder/sentio.rs +++ b/src/tracing/builder/sentio.rs @@ -235,6 +235,7 @@ impl SentioTraceBuilder { trace: SentioTrace { typ: OpCode::JUMP.to_string(), pc: prev_step.pc, + function_pc: Some(last_pc), start_index: next_inst_idx - 2, gas: U256::from(step.gas_remaining), from: Some(step.contract), From 87e2e39eb8424b80c021356d11e4cfb924286216 Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Sat, 14 Sep 2024 12:51:13 +0800 Subject: [PATCH 08/12] add sentio prestate tracer --- Cargo.toml | 2 +- src/tracing/builder/mod.rs | 2 + src/tracing/builder/sentio.rs | 11 +- src/tracing/builder/sentio_prestate.rs | 193 +++++++++++++++++++ src/tracing/mod.rs | 3 +- src/tracing/mux.rs | 1 + tests/it/main.rs | 1 + tests/it/sentio.rs | 12 +- tests/it/sentio_prestate.rs | 245 +++++++++++++++++++++++++ 9 files changed, 454 insertions(+), 16 deletions(-) create mode 100644 src/tracing/builder/sentio_prestate.rs create mode 100644 tests/it/sentio_prestate.rs diff --git a/Cargo.toml b/Cargo.toml index 0b421c30..5705f277 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ clippy.lint_groups_priority = "allow" alloy-sol-types = "0.8" alloy-primitives = "0.8" #alloy-rpc-types = { version = "0.3", features = ["eth", "trace"] } -alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "41aca61", features = ["eth", "trace"] } +alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "99d79ab", features = ["eth", "trace"] } revm = { version = "14", default-features = false, features = ["std"] } anstyle = "1.0" diff --git a/src/tracing/builder/mod.rs b/src/tracing/builder/mod.rs index 8e96678e..388ec1b3 100644 --- a/src/tracing/builder/mod.rs +++ b/src/tracing/builder/mod.rs @@ -8,6 +8,8 @@ pub mod parity; // Sentio trace pub mod sentio; +pub mod sentio_prestate; /// Walker types used for traversing various callgraphs mod walker; + diff --git a/src/tracing/builder/sentio.rs b/src/tracing/builder/sentio.rs index 4528b3b3..7e41cf42 100644 --- a/src/tracing/builder/sentio.rs +++ b/src/tracing/builder/sentio.rs @@ -1,11 +1,9 @@ //! Sentio trace builder use crate::tracing::{ - types::{CallTraceNode, CallTraceStepStackItem}, - TracingInspectorConfig, + types::{CallTraceNode}, }; use alloy_primitives::{Address, U256}; -use revm::{db::DatabaseRef}; use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use alloy_rpc_types::trace::geth::sentio::{FunctionInfo, SentioReceipt, SentioTrace, SentioTracerConfig}; @@ -18,8 +16,6 @@ use crate::tracing::utils::maybe_revert_reason; pub struct SentioTraceBuilder { /// Recorded trace nodes. nodes: Vec, - /// How the traces were recorded - _config: TracingInspectorConfig, // address => (pc => function) function_map: HashMap>, @@ -45,7 +41,7 @@ pub struct InternalFunctionInfo { } impl SentioTraceBuilder { - pub fn new(nodes: Vec, config: SentioTracerConfig, _config: TracingInspectorConfig) -> Self { + pub fn new(nodes: Vec, config: SentioTracerConfig) -> Self { let tracer_config = config.clone(); let mut function_map: HashMap> = HashMap::new(); for (address, function_infos) in config.functions.into_iter() { @@ -58,7 +54,7 @@ impl SentioTraceBuilder { let pc_set = pcs.into_iter().collect(); call_map.insert(address, pc_set); } - Self { nodes, _config, function_map, call_map, tracer_config } + Self { nodes, function_map, call_map, tracer_config } } pub fn sentio_traces(&self, gas_used: u64, receipt: Option) -> SentioTrace { @@ -297,7 +293,6 @@ impl SentioTraceBuilder { }; frames.last_mut().unwrap().trace.traces.push(Box::from(frame.trace)); } - _ => {} } } while frames.len() > 1 { diff --git a/src/tracing/builder/sentio_prestate.rs b/src/tracing/builder/sentio_prestate.rs new file mode 100644 index 00000000..19f46808 --- /dev/null +++ b/src/tracing/builder/sentio_prestate.rs @@ -0,0 +1,193 @@ +//! Sentio trace builder + +use crate::tracing::{ + types::{CallTraceNode}, +}; +use alloy_primitives::{keccak256, Address, B256, B512}; +use revm::{db::DatabaseRef}; +use std::collections::{btree_map, BTreeMap, HashMap}; +use std::default::Default; +use std::fmt::Debug; +use alloy_rpc_types::trace::geth::{AccountChangeKind}; +use alloy_rpc_types::trace::geth::sentio_prestate::{AccountState, SentioPrestateResult, SentioPrestateTracerConfig, State}; +use revm::interpreter::OpCode; +use revm::primitives::ResultAndState; +use crate::tracing::utils::{load_account_code}; + +#[derive(Clone, Debug)] +pub struct SentioPrestateTraceBuilder { + /// Recorded trace nodes. + nodes: Vec, + prestate_config: SentioPrestateTracerConfig +} + +struct AdditionalInfo { + pub code_address: HashMap, + pub code_address_by_slot: HashMap>, + pub mapping_keys: HashMap>, +} + +impl SentioPrestateTraceBuilder { + pub fn new(nodes: Vec, prestate_config: SentioPrestateTracerConfig) -> Self { + Self { nodes, prestate_config } + } + + pub fn sentio_prestate_traces( + &self, + ResultAndState { state, .. }: &ResultAndState, + db: DB, + ) -> Result { + let account_diffs = state.iter().map(|(addr, acc)| (*addr, acc)); + + let mut ret = if !self.prestate_config.diff_mode { + let mut pre = State::default(); + for (addr, changed_acc) in account_diffs { + let db_acc = db.basic_ref(addr)?.unwrap_or_default(); + let code = load_account_code(&db, &db_acc); + let mut acc_state = AccountState::from_account_info(db_acc.nonce, db_acc.balance, code); + for (key, slot) in changed_acc.storage.iter() { + acc_state.storage.insert((*key).into(), slot.original_value.into()); + } + pre.insert(addr, acc_state); + } + SentioPrestateResult { + pre, + post: None, + } + } else { + let mut pre = State::default(); + let mut post = State::default(); + let mut account_change_kinds = HashMap::with_capacity(account_diffs.len()); + for (addr, changed_acc) in account_diffs { + let db_acc = db.basic_ref(addr)?.unwrap_or_default(); + + let pre_code = load_account_code(&db, &db_acc); + + let mut pre_state = AccountState::from_account_info(db_acc.nonce, db_acc.balance, pre_code); + let mut post_state = AccountState::from_account_info( + changed_acc.info.nonce, + changed_acc.info.balance, + changed_acc.info.code.as_ref().map(|code| code.original_bytes()), + ); + + // handle storage changes + for (key, slot) in changed_acc.storage.iter().filter(|(_, slot)| slot.is_changed()) + { + pre_state.storage.insert((*key).into(), slot.original_value.into()); + post_state.storage.insert((*key).into(), slot.present_value.into()); + } + + pre.insert(addr, pre_state); + post.insert(addr, post_state); + + // determine the change type + let pre_change = if changed_acc.is_created() { + AccountChangeKind::Create + } else { + AccountChangeKind::Modify + }; + let post_change = if changed_acc.is_selfdestructed() { + AccountChangeKind::SelfDestruct + } else { + AccountChangeKind::Modify + }; + + account_change_kinds.insert(addr, (pre_change, post_change)); + } + + // ensure we're only keeping changed entries + pre.retain(|address, pre| { + if let btree_map::Entry::Occupied(entry) = post.entry(*address) { + if entry.get() == pre { + // remove unchanged account state from both sets + entry.remove(); + return false; + } + } + true + }); + for state in pre.values_mut().chain(post.values_mut()) { + state.storage.retain(|_, value| *value != B256::ZERO); + } + + self.diff_traces(&mut pre, &mut post, account_change_kinds); + SentioPrestateResult { + pre, + post: Some(post), + } + }; + for node in &self.nodes { + let caller = node.trace.address; + for step in &node.trace.steps { + let Some(stack) = &step.stack else { + continue; + }; + let code_address = step.contract; + match step.op { + OpCode::SLOAD | OpCode::SSTORE => { + if let Some(entry) = ret.pre.get_mut(&caller) { + let slot = B256::from(stack.last().unwrap().to_be_bytes()); + entry.code_address = Some(code_address); + entry.code_address_by_slot.insert(slot, code_address); + } + } + OpCode::KECCAK256 => { + if let Some(entry) = ret.pre.get_mut(&caller) { + let memory = &step.memory.clone().unwrap(); + let offset = stack.last().unwrap().to::(); + let raw_key = &memory.as_bytes()[offset..offset + 64]; + let hash_of_key = keccak256(raw_key); + entry.mapping_keys.insert(B512::from_slice(raw_key), hash_of_key); + + let base_slot = &raw_key[32..]; + entry.code_address_by_slot.insert(B256::from_slice(base_slot), code_address); + entry.code_address_by_slot.insert(hash_of_key, code_address); + } + } + _ => { } + } + } + } + if let Some(post) = &mut ret.post { + for (address, state) in &ret.pre { + let Some(post_state) = post.get_mut(address) else { + post.insert(*address, AccountState { + mapping_keys: state.mapping_keys.clone(), + ..AccountState::default() + }); + continue; + }; + post_state.mapping_keys = state.mapping_keys.clone(); + } + } + Ok(ret) + } + + /// Returns the difference between the pre and post state of the transaction depending on the + /// kind of changes of that account (pre,post) + fn diff_traces( + &self, + pre: &mut BTreeMap, + post: &mut BTreeMap, + change_type: HashMap, + ) { + post.retain(|addr, post_state| { + // Don't keep destroyed accounts in the post state + if change_type.get(addr).map(|ty| ty.1.is_selfdestruct()).unwrap_or(false) { + return false; + } + if let Some(pre_state) = pre.get(addr) { + // remove any unchanged account info + post_state.remove_matching_account_info(pre_state); + } + + true + }); + + // Don't keep created accounts in the pre state + pre.retain(|addr, _pre_state| { + // only keep accounts that are not created + change_type.get(addr).map(|ty| !ty.0.is_created()).unwrap_or(true) + }); + } +} diff --git a/src/tracing/mod.rs b/src/tracing/mod.rs index 54c24e3b..72b2ee9d 100644 --- a/src/tracing/mod.rs +++ b/src/tracing/mod.rs @@ -27,7 +27,8 @@ mod builder; pub use builder::{ geth::{self, GethTraceBuilder}, parity::{self, ParityTraceBuilder}, - sentio::{self, SentioTraceBuilder} + sentio::{self, SentioTraceBuilder}, + sentio_prestate::{self, SentioPrestateTraceBuilder} }; mod config; diff --git a/src/tracing/mux.rs b/src/tracing/mux.rs index e92e4526..86d04417 100644 --- a/src/tracing/mux.rs +++ b/src/tracing/mux.rs @@ -254,6 +254,7 @@ impl DelegatingInspector { Ok(DelegatingInspector::Mux(MuxInspector::try_from_config(config)?)) } GethDebugBuiltInTracerType::SentioTracer => todo!(), + GethDebugBuiltInTracerType::SentioPrestateTracer => todo!() }; inspector.map(|inspector| (tracer_type, inspector)) diff --git a/tests/it/main.rs b/tests/it/main.rs index e5e83264..c88897bc 100644 --- a/tests/it/main.rs +++ b/tests/it/main.rs @@ -5,3 +5,4 @@ mod parity; mod transfer; mod writer; mod sentio; +mod sentio_prestate; diff --git a/tests/it/sentio.rs b/tests/it/sentio.rs index aa8a342c..e50f6baf 100644 --- a/tests/it/sentio.rs +++ b/tests/it/sentio.rs @@ -1,4 +1,4 @@ -//! Geth tests +//! Sentio tests use crate::utils::inspect; use alloy_primitives::{hex, Address, Bytes, U256}; @@ -11,7 +11,7 @@ use revm::primitives::AccountInfo; use revm_inspectors::tracing::{SentioTraceBuilder, TracingInspector, TracingInspectorConfig}; #[test] -fn test_sentiotracer_logs() { +fn test_sentio_tracer_logs() { /* contract LogTracing { event Log(address indexed addr, uint256 value); @@ -101,7 +101,7 @@ fn test_sentiotracer_logs() { }; let traces = insp.into_traces().into_nodes(); - let builder = SentioTraceBuilder::new(traces, sentio_tracer_config, TracingInspectorConfig::default_geth()); + let builder = SentioTraceBuilder::new(traces, sentio_tracer_config); let sentio_trace = builder.sentio_traces(res.result.gas_used(), None); assert_eq!(sentio_trace.traces.len(), 4); @@ -113,7 +113,7 @@ fn test_sentiotracer_logs() { } #[test] -fn test_sentiotracer_weth_transfer() { +fn test_sentio_tracer_weth_transfer() { let user1 = Address::from(hex!("0000000000000000000000000000000000000123")); let user2 = Address::from(hex!("0000000000000000000000000000000000000456")); let mut db = CacheDB::new(EmptyDB::default()); @@ -174,7 +174,7 @@ fn test_sentiotracer_weth_transfer() { db.commit(res.state); let traces = insp.into_traces().into_nodes(); - let builder = SentioTraceBuilder::new(traces, tracer_cfg.clone(), TracingInspectorConfig::default_geth()); + let builder = SentioTraceBuilder::new(traces, tracer_cfg.clone()); let sentio_trace = builder.sentio_traces(res.result.gas_used(), None); assert_eq!(sentio_trace.traces[0].typ, "JUMP"); assert_eq!(sentio_trace.traces[0].name, Some("deposit".to_string())); @@ -199,7 +199,7 @@ fn test_sentiotracer_weth_transfer() { db.commit(res.state); let traces = insp.into_traces().into_nodes(); - let builder = SentioTraceBuilder::new(traces, tracer_cfg.clone(), TracingInspectorConfig::default_geth()); + let builder = SentioTraceBuilder::new(traces, tracer_cfg.clone()); let sentio_trace = builder.sentio_traces(res.result.gas_used(), None); assert_eq!(sentio_trace.pc, 880); diff --git a/tests/it/sentio_prestate.rs b/tests/it/sentio_prestate.rs new file mode 100644 index 00000000..63cd98db --- /dev/null +++ b/tests/it/sentio_prestate.rs @@ -0,0 +1,245 @@ +//! Sentio prestate tests + +use crate::utils::inspect; +use alloy_primitives::{hex, Address, Bytes, B256, B512, U256}; +use alloy_rpc_types::trace::geth::sentio_prestate::SentioPrestateTracerConfig; +use revm::{db::{CacheDB, EmptyDB}, primitives::{ + BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, HandlerCfg, + Output, SpecId, TransactTo, TxEnv, +}, DatabaseCommit}; +use revm::primitives::AccountInfo; +use revm_inspectors::tracing::{SentioPrestateTraceBuilder, TracingInspector, TracingInspectorConfig}; + +#[test] +fn test_sentio_prestate_tracer_logs() { + /* + contract LogTracing { + event Log(address indexed addr, uint256 value); + + fallback() external payable { + emit Log(msg.sender, msg.value); + + try this.nestedEmitWithFailure() {} catch {} + try this.nestedEmitWithFailureAfterNestedEmit() {} catch {} + this.nestedEmitWithSuccess(); + } + + function nestedEmitWithFailure() external { + emit Log(msg.sender, 0); + require(false, "nestedEmitWithFailure"); + } + + function nestedEmitWithFailureAfterNestedEmit() external { + this.doubleNestedEmitWithSuccess(); + require(false, "nestedEmitWithFailureAfterNestedEmit"); + } + + function doubleNestedEmitWithSuccess() external { + emit Log(msg.sender, 0); + this.nestedEmitWithSuccess(); + } + + function nestedEmitWithSuccess() external { + emit Log(msg.sender, 0); + } + } + */ + let code = hex!("608060405234801561001057600080fd5b506103ac806100206000396000f3fe60806040526004361061003f5760003560e01c80630332ed131461014d5780636ae1ad40146101625780638384a00214610177578063de7eb4f31461018c575b60405134815233906000805160206103578339815191529060200160405180910390a2306001600160a01b0316636ae1ad406040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561009d57600080fd5b505af19250505080156100ae575060015b50306001600160a01b0316630332ed136040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156100ea57600080fd5b505af19250505080156100fb575060015b50306001600160a01b0316638384a0026040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561013757600080fd5b505af115801561014b573d6000803e3d6000fd5b005b34801561015957600080fd5b5061014b6101a1565b34801561016e57600080fd5b5061014b610253565b34801561018357600080fd5b5061014b6102b7565b34801561019857600080fd5b5061014b6102dd565b306001600160a01b031663de7eb4f36040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156101dc57600080fd5b505af11580156101f0573d6000803e3d6000fd5b505060405162461bcd60e51b8152602060048201526024808201527f6e6573746564456d6974576974684661696c75726541667465724e6573746564604482015263115b5a5d60e21b6064820152608401915061024a9050565b60405180910390fd5b6040516000815233906000805160206103578339815191529060200160405180910390a260405162461bcd60e51b81526020600482015260156024820152746e6573746564456d6974576974684661696c75726560581b604482015260640161024a565b6040516000815233906000805160206103578339815191529060200160405180910390a2565b6040516000815233906000805160206103578339815191529060200160405180910390a2306001600160a01b0316638384a0026040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561033c57600080fd5b505af1158015610350573d6000803e3d6000fd5b5050505056fef950957d2407bed19dc99b718b46b4ce6090c05589006dfb86fd22c34865b23ea2646970667358221220090a696b9fbd22c7d1cc2a0b6d4a48c32d3ba892480713689a3145b73cfeb02164736f6c63430008130033"); + let deployer = Address::ZERO; + + let mut db = CacheDB::new(EmptyDB::default()); + + let cfg = CfgEnvWithHandlerCfg::new(CfgEnv::default(), HandlerCfg::new(SpecId::LONDON)); + + let env = EnvWithHandlerCfg::new_with_cfg_env( + cfg.clone(), + BlockEnv::default(), + TxEnv { + caller: deployer, + gas_limit: 1000000, + transact_to: TransactTo::Create, + data: code.into(), + ..Default::default() + }, + ); + + let prestate_tracer_config = SentioPrestateTracerConfig { + diff_mode: true, + }; + let inspector_config = TracingInspectorConfig::default_geth().set_record_logs(true).set_memory_snapshots(true); + let mut insp = TracingInspector::new(inspector_config.clone()); + + // Create contract + let (res, _) = inspect(&mut db, env, &mut insp).unwrap(); + let traces = insp.into_traces().into_nodes(); + let builder = SentioPrestateTraceBuilder::new(traces, prestate_tracer_config.clone()); + let trace = builder.sentio_prestate_traces(&res.clone(), &db).unwrap(); + // println!("{}", serde_json::to_string_pretty(&trace).unwrap()); + + assert!(trace.pre.contains_key(&Address::ZERO)); + assert_eq!(trace.post.unwrap().len(), 2); + + let addr = match res.result { + ExecutionResult::Success { output, .. } => match output { + Output::Create(_, addr) => addr.unwrap(), + _ => panic!("Create failed"), + }, + _ => panic!("Execution failed"), + }; + db.commit(res.state); + + + let mut insp = TracingInspector::new(inspector_config.clone()); + + let env = EnvWithHandlerCfg::new_with_cfg_env( + cfg, + BlockEnv::default(), + TxEnv { + caller: deployer, + gas_limit: 1000000, + transact_to: TransactTo::Call(addr), + data: Bytes::default(), // call fallback + ..Default::default() + }, + ); + + let (res, _) = inspect(&mut db, env, &mut insp).unwrap(); + assert!(res.result.is_success()); + + let traces = insp.into_traces().into_nodes(); + let builder = SentioPrestateTraceBuilder::new(traces, prestate_tracer_config.clone()); + let trace = builder.sentio_prestate_traces(&res.clone(), &db).unwrap(); + // println!("{}", serde_json::to_string_pretty(&trace).unwrap()); + assert!(trace.pre.contains_key(&Address::ZERO)); + assert_eq!(trace.post.unwrap().len(), 1); +} + +#[test] +fn test_sentio_prestate_tracer_weth_transfer() { + let user1 = Address::from(hex!("0000000000000000000000000000000000000123")); + let user2 = Address::from(hex!("0000000000000000000000000000000000000456")); + let mut db = CacheDB::new(EmptyDB::default()); + let init_balance = U256::from(10000000000_i64); + db.insert_account_info(user1, AccountInfo { + balance: init_balance, + ..Default::default() + }); + let cfg = CfgEnvWithHandlerCfg::new(CfgEnv::default(), HandlerCfg::new(SpecId::LONDON)); + + let prestate_tracer_config = SentioPrestateTracerConfig { + diff_mode: true, + }; + let inspector_config = TracingInspectorConfig::default_geth().set_record_logs(true).set_memory_snapshots(true); + + // Create contract + let weth_code = hex!("60606040526040805190810160405280600d81526020017f57726170706564204574686572000000000000000000000000000000000000008152506000908051906020019061004f9291906100c8565b506040805190810160405280600481526020017f57455448000000000000000000000000000000000000000000000000000000008152506001908051906020019061009b9291906100c8565b506012600260006101000a81548160ff021916908360ff16021790555034156100c357600080fd5b61016d565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061010957805160ff1916838001178555610137565b82800160010185558215610137579182015b8281111561013657825182559160200191906001019061011b565b5b5090506101449190610148565b5090565b61016a91905b8082111561016657600081600090555060010161014e565b5090565b90565b610c348061017c6000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014757806318160ddd146101a157806323b872dd146101ca5780632e1a7d4d14610243578063313ce5671461026657806370a082311461029557806395d89b41146102e2578063a9059cbb14610370578063d0e30db0146103ca578063dd62ed3e146103d4575b6100b7610440565b005b34156100c457600080fd5b6100cc6104dd565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010c5780820151818401526020810190506100f1565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610187600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061057b565b604051808215151515815260200191505060405180910390f35b34156101ac57600080fd5b6101b461066d565b6040518082815260200191505060405180910390f35b34156101d557600080fd5b610229600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061068c565b604051808215151515815260200191505060405180910390f35b341561024e57600080fd5b61026460048080359060200190919050506109d9565b005b341561027157600080fd5b610279610b05565b604051808260ff1660ff16815260200191505060405180910390f35b34156102a057600080fd5b6102cc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b18565b6040518082815260200191505060405180910390f35b34156102ed57600080fd5b6102f5610b30565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561033557808201518184015260208101905061031a565b50505050905090810190601f1680156103625780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037b57600080fd5b6103b0600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610bce565b604051808215151515815260200191505060405180910390f35b6103d2610440565b005b34156103df57600080fd5b61042a600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610be3565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105735780601f1061054857610100808354040283529160200191610573565b820191906000526020600020905b81548152906001019060200180831161055657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156106dc57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141580156107b457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b156108cf5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561084457600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a2757600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501515610ab457600080fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bc65780601f10610b9b57610100808354040283529160200191610bc6565b820191906000526020600020905b815481529060010190602001808311610ba957829003601f168201915b505050505081565b6000610bdb33848461068c565b905092915050565b60046020528160005260406000206020528060005260406000206000915091505054815600a165627a7a72305820deb4c2ccab3c2fdca32ab3f46728389c2fe2c165d5fafa07661e4e004f6c344a0029"); + let env = EnvWithHandlerCfg::new_with_cfg_env( + cfg.clone(), + BlockEnv::default(), + TxEnv { + caller: user1, + gas_limit: 1000000, + gas_price: U256::from(10), + transact_to: TransactTo::Create, + data: weth_code.into(), + ..Default::default() + }, + ); + let mut insp = TracingInspector::new(inspector_config.clone()); + let (res, _) = inspect(&mut db, env, &mut insp).unwrap(); + + let traces = insp.into_traces().into_nodes(); + let builder = SentioPrestateTraceBuilder::new(traces, prestate_tracer_config.clone()); + let trace = builder.sentio_prestate_traces(&res.clone(), &db).unwrap(); + + let contract_addr = match res.result { + ExecutionResult::Success { output, .. } => match output { + Output::Create(_, addr) => addr.unwrap(), + _ => panic!("Create failed"), + }, + _ => panic!("Execution failed"), + }; + db.commit(res.state); + + // println!("{}", serde_json::to_string_pretty(&trace).unwrap()); + let post = trace.post.unwrap(); + let user1_balance_pre = trace.pre.get(&user1).unwrap().balance.unwrap(); + let zero_balance_pre = trace.pre.get(&Address::ZERO).unwrap().balance.unwrap(); + let user1_balance_post = post.get(&user1).unwrap().balance.unwrap(); + let zero_balance_post = post.get(&Address::ZERO).unwrap().balance.unwrap(); + assert_eq!(user1_balance_pre + zero_balance_pre, user1_balance_post + zero_balance_post); + + let weth_acc = post.get(&contract_addr).unwrap(); + assert!(weth_acc.code.is_some()); + assert_eq!(weth_acc.storage.len(), 3); + + // deposit some eth + let deposit_eth = U256::from(1000); + let env = EnvWithHandlerCfg::new_with_cfg_env( + cfg.clone(), + BlockEnv::default(), + TxEnv { + caller: user1, + gas_limit: 1000000, + transact_to: TransactTo::Call(contract_addr), + value: deposit_eth, + ..Default::default() + }, + ); + let mut insp = TracingInspector::new(inspector_config); + let (res, _) = inspect(&mut db, env, &mut insp).unwrap(); + assert!(res.result.is_success()); + + let traces = insp.into_traces().into_nodes(); + let builder = SentioPrestateTraceBuilder::new(traces, prestate_tracer_config.clone()); + let trace = builder.sentio_prestate_traces(&res.clone(), &db).unwrap(); + // println!("{}", serde_json::to_string_pretty(&trace).unwrap()); + + let post = trace.post.unwrap(); + let user1_eth_pre = trace.pre.get(&user1).unwrap().balance.unwrap(); + let user1_eth_post = post.get(&user1).unwrap().balance.unwrap(); + let slot = B256::from_slice(&hex!("b0f566e5e54ad5f271b39da0d24b21fd2c693a146cc0643b02f3481c94580329")); + let user1_weth_post = post.get(&contract_addr).unwrap().storage.get(&slot).unwrap(); + assert_eq!(U256::from_be_slice(user1_weth_post.as_slice()), deposit_eth); + assert_eq!(user1_eth_pre, user1_eth_post + deposit_eth); + let contract_pre = trace.pre.get(&contract_addr).unwrap(); + assert_eq!(contract_pre.code_address_by_slot.len(), 2); + assert_eq!(contract_pre.code_address_by_slot.get(&slot).unwrap(), &contract_addr); + + let mapping_key = B512::from_slice(&hex!("00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000003")); + assert_eq!(contract_pre.mapping_keys.get(&mapping_key).unwrap(), &slot); + + db.commit(res.state); + + // transfer weth + let env = EnvWithHandlerCfg::new_with_cfg_env( + cfg.clone(), + BlockEnv::default(), + TxEnv { + caller: user1, + gas_limit: 1000000, + transact_to: TransactTo::Call(contract_addr), + // transfer("0x0000000000000000000000000000000000000456", 100) + data: hex::decode("a9059cbb00000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000064").unwrap().into(), + ..Default::default() + }, + ); + let mut insp = TracingInspector::new(inspector_config); + let (res, _) = inspect(&mut db, env, &mut insp).unwrap(); + assert!(res.result.is_success()); + + let traces = insp.into_traces().into_nodes(); + let builder = SentioPrestateTraceBuilder::new(traces, prestate_tracer_config.clone()); + let trace = builder.sentio_prestate_traces(&res.clone(), &db).unwrap(); + // println!("{}", serde_json::to_string_pretty(&trace).unwrap()); + db.commit(res.state); + + let post = trace.post.unwrap(); + let contract_pre = trace.pre.get(&contract_addr).unwrap(); + let contract_post = post.get(&contract_addr).unwrap(); + assert_eq!(contract_pre.code_address_by_slot.len(), 3); + assert_eq!(contract_pre.mapping_keys.len(), 2); + assert_eq!(contract_post.mapping_keys.len(), 2); + assert_eq!(contract_pre.storage.len(), 1); + assert_eq!(contract_post.storage.len(), 2); +} \ No newline at end of file From 14d00bb3a227352bd13962784603648c2c6c1a8f Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:36:59 +0700 Subject: [PATCH 09/12] try fix --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5705f277..fb56cfc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ clippy.lint_groups_priority = "allow" alloy-sol-types = "0.8" alloy-primitives = "0.8" #alloy-rpc-types = { version = "0.3", features = ["eth", "trace"] } -alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "99d79ab", features = ["eth", "trace"] } +alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "b5ea51a", features = ["eth", "trace"] } revm = { version = "14", default-features = false, features = ["std"] } anstyle = "1.0" From 698c42a70fe70b2a80fd1ed232210da44b772634 Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:52:59 +0700 Subject: [PATCH 10/12] sync --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fb56cfc2..9f466fbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ clippy.lint_groups_priority = "allow" alloy-sol-types = "0.8" alloy-primitives = "0.8" #alloy-rpc-types = { version = "0.3", features = ["eth", "trace"] } -alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "b5ea51a", features = ["eth", "trace"] } +alloy-rpc-types = { git = "https://github.com/sentioxyz/alloy", rev = "53a432b", features = ["eth", "trace"] } revm = { version = "14", default-features = false, features = ["std"] } anstyle = "1.0" From 950ab0a1cf346e6645accb56383dee5e3a474dd4 Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Thu, 19 Sep 2024 19:12:12 +0700 Subject: [PATCH 11/12] allow log without step --- src/tracing/builder/sentio.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tracing/builder/sentio.rs b/src/tracing/builder/sentio.rs index 7e41cf42..fb55dc36 100644 --- a/src/tracing/builder/sentio.rs +++ b/src/tracing/builder/sentio.rs @@ -268,13 +268,25 @@ impl SentioTraceBuilder { } } TraceMemberOrder::Log(log_idx) => { + let log = &node.logs[*log_idx]; let Some(step) = last_step else { - panic!("log without step"); + println!("log without step"); + let frame = InternalSentioTrace { + trace: SentioTrace { + typ: "LOG".to_string(), + address: Some(node.trace.address), + topics: Some(Vec::from(log.raw_log.topics())), + data: Some(log.raw_log.data.clone()), + ..Default::default() + }, + ..Default::default() + }; + frames.last_mut().unwrap().trace.traces.push(Box::from(frame.trace)); + continue; }; let (OpCode::LOG0 | OpCode::LOG1 | OpCode::LOG2 | OpCode::LOG3 | OpCode::LOG4) = step.op else { panic!("log without log op"); }; - let log = &node.logs[*log_idx]; let frame = InternalSentioTrace { trace: SentioTrace { typ: step.op.to_string(), From ddc77ebf755102d26d6ad57e6b8733d6a3dc8ae0 Mon Sep 17 00:00:00 2001 From: lwedge99 <116623885+lwedge99@users.noreply.github.com> Date: Sat, 21 Sep 2024 12:44:58 +0700 Subject: [PATCH 12/12] code address for call and jump --- src/tracing/builder/sentio.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tracing/builder/sentio.rs b/src/tracing/builder/sentio.rs index fb55dc36..a8a7c9fc 100644 --- a/src/tracing/builder/sentio.rs +++ b/src/tracing/builder/sentio.rs @@ -91,6 +91,7 @@ impl SentioTraceBuilder { gas_used: U256::from(trace.gas_used), from: Some(trace.caller), to: Some(trace.address), + code_address: Some(trace.address), input: Some(trace.data.clone()), value: Some(trace.value), output: Some(trace.output.clone()), @@ -236,6 +237,7 @@ impl SentioTraceBuilder { gas: U256::from(step.gas_remaining), from: Some(step.contract), to: Some(step.contract), + code_address: Some(step.contract), input_stack: if input_enough { Some(stack[stack.len() - function.input_size..].to_vec()) } else { None }, name: if self.tracer_config.debug { Some(function.name.clone()) } else { None }, input_memory: if function.input_memory { Some(step.memory.clone().unwrap().into_bytes()) } else { None },