-
Notifications
You must be signed in to change notification settings - Fork 2.2k
feat: complete vm and statediff tracers #3529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
48 commits
Select commit
Hold shift + click to select a range
772d9c1
missing stack logic
892340f
track new stack in trace step, use it in vm trace conversion
9eb1935
comment
86c4cfa
add todo
2ff63de
fix
b60a612
introduce graphwalker logic, move somethings to asscioated types on t…
db0b459
helper function to get all the address from a DF walk
19f3545
code layout
672acec
commnets + todos
816c707
use ref of vec rather than vec of regs
c443edb
forgot to push the current calltracenode to the count
01dcd43
isgts
6017856
trait bound gn
7cc6c66
fill in code
1416c66
merge main
9eb8012
remove get_code function
8aa012c
from -> new, comments removed
2a2fb85
trait wasnt doing anything
4fb2b3a
comments
3f5db80
comments + some tests
3035c34
assert stuff in test, layout
971f80d
use trait bounds instead of dyn dispatch, comments, naming
b0e4f4f
iterative walk of call graph
88d0c32
bad import
1ba15dd
expose addresses on walker
2b8f5a5
move make_trace & make_instructions to the walker
21092ce
start on iterative vm trace walker
1f14b56
naming, starting tests for vm trace walker
f90920c
lazy eval calltracenode walker
b0f1aea
clean up iterator
f5298c8
comment out vm trace walker for now
33f92c7
iterative build vm trace
acaddbd
start on BF walkers
d7fe088
comment out vmtrace walker
c896134
unused thing
5838f2e
Update crates/revm/revm-inspectors/src/tracing/builder/walker.rs
nhtyy 699c93d
iterative byte code filling
c00ed89
Merge branch 'n/vm-trace' of https://github.com/nhtyy/reth into n/vm-…
3b69972
comments
9f9d2c4
comments
7fac645
comment unused types
849577c
better expects
21d2872
extend, remove comments
6bb08dd
ordering of iterator in params
175bbec
use code_by_hash
08d8d40
Merge branch 'main' of https://github.com/paradigmxyz/reth into n/vm-…
77acc0a
Merge branch 'main' into n/vm-trace
mattsse 38c1483
nits
mattsse File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,16 @@ | ||
| use crate::tracing::{types::CallTraceNode, TracingInspectorConfig}; | ||
| use super::walker::CallTraceNodeWalkerBF; | ||
| use crate::tracing::{ | ||
| types::{CallTraceNode, CallTraceStep}, | ||
| TracingInspectorConfig, | ||
| }; | ||
| use reth_primitives::{Address, U64}; | ||
| use reth_rpc_types::{trace::parity::*, TransactionInfo}; | ||
| use revm::{ | ||
| db::DatabaseRef, | ||
| primitives::{AccountInfo, ExecutionResult, ResultAndState}, | ||
| interpreter::opcode, | ||
| primitives::{AccountInfo, ExecutionResult, ResultAndState, KECCAK_EMPTY}, | ||
| }; | ||
| use std::collections::HashSet; | ||
| use std::collections::{HashSet, VecDeque}; | ||
|
|
||
| /// A type for creating parity style traces | ||
| /// | ||
|
|
@@ -14,6 +19,7 @@ use std::collections::HashSet; | |
| pub struct ParityTraceBuilder { | ||
| /// Recorded trace nodes | ||
| nodes: Vec<CallTraceNode>, | ||
|
|
||
| /// How the traces were recorded | ||
| _config: TracingInspectorConfig, | ||
| } | ||
|
|
@@ -154,14 +160,31 @@ impl ParityTraceBuilder { | |
| DB: DatabaseRef, | ||
| { | ||
| let ResultAndState { result, state } = res; | ||
|
|
||
| let breadth_first_addresses = if trace_types.contains(&TraceType::VmTrace) { | ||
| CallTraceNodeWalkerBF::new(&self.nodes) | ||
| .map(|node| node.trace.address) | ||
| .collect::<Vec<_>>() | ||
| } else { | ||
| vec![] | ||
| }; | ||
|
|
||
| let mut trace_res = self.into_trace_results(result, trace_types); | ||
|
|
||
| // check the state diff case | ||
| if let Some(ref mut state_diff) = trace_res.state_diff { | ||
| populate_account_balance_nonce_diffs( | ||
| state_diff, | ||
| &db, | ||
| state.into_iter().map(|(addr, acc)| (addr, acc.info)), | ||
| )?; | ||
| } | ||
|
|
||
| // check the vm trace case | ||
| if let Some(ref mut vm_trace) = trace_res.vm_trace { | ||
| populate_vm_trace_bytecodes(&db, vm_trace, breadth_first_addresses)?; | ||
| } | ||
|
|
||
| Ok(trace_res) | ||
| } | ||
|
|
||
|
|
@@ -177,11 +200,8 @@ impl ParityTraceBuilder { | |
| let with_traces = trace_types.contains(&TraceType::Trace); | ||
| let with_diff = trace_types.contains(&TraceType::StateDiff); | ||
|
|
||
| let vm_trace = if trace_types.contains(&TraceType::VmTrace) { | ||
| Some(vm_trace(&self.nodes)) | ||
| } else { | ||
| None | ||
| }; | ||
| let vm_trace = | ||
| if trace_types.contains(&TraceType::VmTrace) { Some(self.vm_trace()) } else { None }; | ||
|
|
||
| let mut traces = Vec::with_capacity(if with_traces { self.nodes.len() } else { 0 }); | ||
| let mut diff = StateDiff::default(); | ||
|
|
@@ -218,13 +238,142 @@ impl ParityTraceBuilder { | |
| pub fn into_transaction_traces(self) -> Vec<TransactionTrace> { | ||
| self.into_transaction_traces_iter().collect() | ||
| } | ||
|
|
||
| /// Creates a VM trace by walking over `CallTraceNode`s | ||
| /// | ||
| /// does not have the code fields filled in | ||
| pub fn vm_trace(&self) -> VmTrace { | ||
| match self.nodes.get(0) { | ||
| Some(current) => self.make_vm_trace(current), | ||
| None => VmTrace { code: Default::default(), ops: Vec::new() }, | ||
| } | ||
| } | ||
|
|
||
| /// returns a VM trace without the code filled in | ||
| /// | ||
| /// iteratively creaters a VM trace by traversing an arena | ||
| fn make_vm_trace(&self, start: &CallTraceNode) -> VmTrace { | ||
| let mut child_idx_stack: Vec<usize> = Vec::with_capacity(self.nodes.len()); | ||
| let mut sub_stack: VecDeque<Option<VmTrace>> = VecDeque::with_capacity(self.nodes.len()); | ||
|
|
||
| let mut current = start; | ||
| let mut child_idx: usize = 0; | ||
|
|
||
| // finds the deepest nested calls of each call frame and fills them up bottom to top | ||
| let instructions = loop { | ||
| match current.children.get(child_idx) { | ||
| Some(child) => { | ||
| child_idx_stack.push(child_idx + 1); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah to read the next child when it gets popped from the stack, i could pop from the stack then increment, not sure which u prefer |
||
|
|
||
| child_idx = 0; | ||
| current = self.nodes.get(*child).expect("there should be a child"); | ||
| } | ||
| None => { | ||
| let mut instructions: Vec<VmInstruction> = | ||
| Vec::with_capacity(current.trace.steps.len()); | ||
|
|
||
| for step in ¤t.trace.steps { | ||
| let maybe_sub = match step.op.u8() { | ||
| opcode::CALL | | ||
| opcode::CALLCODE | | ||
| opcode::DELEGATECALL | | ||
| opcode::STATICCALL | | ||
| opcode::CREATE | | ||
| opcode::CREATE2 => { | ||
| sub_stack.pop_front().expect("there should be a sub trace") | ||
| } | ||
| _ => None, | ||
| }; | ||
|
|
||
| instructions.push(Self::make_instruction(step, maybe_sub)); | ||
| } | ||
|
|
||
| match current.parent { | ||
| Some(parent) => { | ||
| sub_stack.push_back(Some(VmTrace { | ||
| code: Default::default(), | ||
| ops: instructions, | ||
| })); | ||
|
|
||
| child_idx = child_idx_stack.pop().expect("there should be a child idx"); | ||
|
|
||
| current = self.nodes.get(parent).expect("there should be a parent"); | ||
| } | ||
| None => break instructions, | ||
| } | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| VmTrace { code: Default::default(), ops: instructions } | ||
| } | ||
|
|
||
| /// Creates a VM instruction from a [CallTraceStep] and a [VmTrace] for the subcall if there is | ||
| /// one | ||
| fn make_instruction(step: &CallTraceStep, maybe_sub: Option<VmTrace>) -> VmInstruction { | ||
| let maybe_storage = step.storage_change.map(|storage_change| StorageDelta { | ||
| key: storage_change.key, | ||
| val: storage_change.value, | ||
| }); | ||
|
|
||
| let maybe_memory = match step.memory.len() { | ||
| 0 => None, | ||
| _ => { | ||
| Some(MemoryDelta { off: step.memory_size, data: step.memory.data().clone().into() }) | ||
| } | ||
| }; | ||
|
|
||
| let maybe_execution = Some(VmExecutedOperation { | ||
| used: step.gas_cost, | ||
| push: step.new_stack.map(|new_stack| new_stack.into()), | ||
| mem: maybe_memory, | ||
| store: maybe_storage, | ||
| }); | ||
|
|
||
| VmInstruction { | ||
| pc: step.pc, | ||
| cost: 0, // TODO: use op gas cost | ||
| ex: maybe_execution, | ||
| sub: maybe_sub, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Construct the vmtrace for the entire callgraph | ||
| fn vm_trace(nodes: &[CallTraceNode]) -> VmTrace { | ||
| // TODO: populate vm trace | ||
| /// addresses are presorted via breadth first walk thru [CallTraceNode]s, this can be done by a | ||
| /// walker in [crate::tracing::builder::walker] | ||
| /// | ||
| /// iteratively fill the [VmTrace] code fields | ||
| pub(crate) fn populate_vm_trace_bytecodes<DB, I>( | ||
| db: &DB, | ||
| trace: &mut VmTrace, | ||
| breadth_first_addresses: I, | ||
| ) -> Result<(), DB::Error> | ||
| where | ||
| DB: DatabaseRef, | ||
| I: IntoIterator<Item = Address>, | ||
| { | ||
| let mut stack: VecDeque<&mut VmTrace> = VecDeque::new(); | ||
| stack.push_back(trace); | ||
|
|
||
| let mut addrs = breadth_first_addresses.into_iter(); | ||
|
|
||
| while let Some(curr_ref) = stack.pop_front() { | ||
| for op in curr_ref.ops.iter_mut() { | ||
| if let Some(sub) = op.sub.as_mut() { | ||
| stack.push_back(sub); | ||
| } | ||
| } | ||
|
|
||
| let addr = addrs.next().expect("there should be an address"); | ||
|
|
||
| VmTrace { code: nodes[0].trace.data.clone().into(), ops: vec![] } | ||
| let db_acc = db.basic(addr)?.unwrap_or_default(); | ||
|
|
||
| let code_hash = if db_acc.code_hash != KECCAK_EMPTY { db_acc.code_hash } else { continue }; | ||
|
|
||
| curr_ref.code = db.code_by_hash(code_hash)?.bytecode.into(); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| /// Loops over all state accounts in the accounts diff that contains all accounts that are included | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| use crate::tracing::types::CallTraceNode; | ||
| use std::collections::VecDeque; | ||
|
|
||
| /// Traverses Reths internal tracing structure breadth-first | ||
| /// | ||
| /// This is a lazy iterator | ||
| pub(crate) struct CallTraceNodeWalkerBF<'trace> { | ||
| /// the entire arena | ||
| nodes: &'trace Vec<CallTraceNode>, | ||
|
|
||
| /// holds indexes of nodes to visit as we traverse | ||
| queue: VecDeque<usize>, | ||
mattsse marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| impl<'trace> CallTraceNodeWalkerBF<'trace> { | ||
| pub(crate) fn new(nodes: &'trace Vec<CallTraceNode>) -> Self { | ||
| let mut queue = VecDeque::with_capacity(nodes.len()); | ||
| queue.push_back(0); | ||
|
|
||
| Self { nodes, queue } | ||
| } | ||
| } | ||
|
|
||
| impl<'trace> Iterator for CallTraceNodeWalkerBF<'trace> { | ||
| type Item = &'trace CallTraceNode; | ||
|
|
||
| fn next(&mut self) -> Option<Self::Item> { | ||
| match self.queue.pop_front() { | ||
| Some(idx) => { | ||
| let curr = self.nodes.get(idx).expect("there should be a node"); | ||
|
|
||
| self.queue.extend(curr.children.iter()); | ||
|
|
||
| Some(curr) | ||
| } | ||
| None => None, | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i still need to respect the config here, but the rest of the file doesnt seem to use the config yet + logging