huff_neo_test_runner/
internal.rs1use alloy_primitives::{Address, U256};
2use foundry_evm::traces::{CallTraceStep, DecodedInternalCall, DecodedTraceStep, SparsedTraceArena};
3use revm::bytecode::opcode::{JUMP, JUMPDEST};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct HuffFunction {
8 pub name: String,
10 pub file: Option<String>,
12 pub entry_pc: usize,
14 pub takes: usize,
16 pub returns: usize,
18}
19
20struct OpenCall {
22 function: usize,
24 start: usize,
26 jump_pc: usize,
28}
29
30pub fn decode_huff_internal_calls(arena: &mut SparsedTraceArena, address: Address, functions: &[HuffFunction]) {
37 if functions.is_empty() {
38 return;
39 }
40
41 for node in arena.arena.nodes_mut().iter_mut().filter(|node| node.trace.address == address && !node.trace.kind.is_any_create()) {
42 let steps = &mut node.trace.steps;
43 let mut open: Vec<OpenCall> = Vec::new();
44
45 for i in 1..steps.len() {
46 if steps[i].op.get() != JUMPDEST || steps[i - 1].op.get() != JUMP {
47 continue;
48 }
49 let pc = steps[i].pc;
50
51 if let Some(depth) = open.iter().rposition(|call| call.jump_pc + 1 == pc) {
53 let call = &open[depth];
54 let function = &functions[call.function];
55 let decoded = DecodedInternalCall {
56 func_name: match &function.file {
57 Some(file) => format!("{file}::{}", function.name),
58 None => function.name.clone(),
59 },
60 args: Some(top_of_stack(&steps[call.start + 1], function.takes)),
61 return_data: (function.returns > 0).then(|| top_of_stack(&steps[i], function.returns)),
62 };
63 steps[call.start].decoded = Some(Box::new(DecodedTraceStep::InternalCall(decoded, i)));
64 open.truncate(depth);
65 continue;
66 }
67
68 if let Some(function) = functions.iter().position(|function| function.entry_pc == pc) {
69 open.push(OpenCall { function, start: i - 1, jump_pc: steps[i - 1].pc });
70 }
71 }
72 }
73}
74
75fn top_of_stack(step: &CallTraceStep, count: usize) -> Vec<String> {
77 let stack: &[U256] = step.stack.as_deref().unwrap_or_default();
78 stack[stack.len().saturating_sub(count)..].iter().map(|word| format!("{word:#x}")).collect()
79}