Skip to content

feat(l1): implement 4byteTracer#6704

Open
azteca1998 wants to merge 6 commits into
mainfrom
feat/four-byte-tracer
Open

feat(l1): implement 4byteTracer#6704
azteca1998 wants to merge 6 commits into
mainfrom
feat/four-byte-tracer

Conversation

@azteca1998
Copy link
Copy Markdown
Contributor

@azteca1998 azteca1998 commented May 21, 2026

Summary

  • Add 4byteTracer tracer type for debug_traceTransaction and block tracing endpoints
  • Records all function selectors (first 4 bytes of calldata) and their calldata sizes for every call in a transaction
  • Returns a map of "0xSELECTOR-SIZE" → count (e.g. {"0xa9059cbb-68": 1})
  • Implemented by post-processing the existing call tracer output (walks the call trace tree to extract selectors)

Closes part of #6572

Closes #6644

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 21, 2026

⚠️ Known Issues — intentionally skipped tests

Source: docs/known_issues.md

Known Issues

Tests intentionally excluded from CI. Source of truth for the Known
Issues
section the L1 workflow appends to each ef-tests job summary
and posts as a sticky PR comment.

EF Tests — Stateless coverage narrowed to EIP-8025 optional-proofs

make -C tooling/ef_tests/blockchain test calls test-stateless-zkevm
instead of test-stateless. The zkevm@v0.3.3 fixtures are filled against
bal@v5.6.1, out of sync with current bal spec; the broad target trips ~549
fixtures. Re-broaden once the zkevm bundle is regenerated.

Why and resolution path

PR #6527 broadened
test-stateless to extract the entire for_amsterdam/ tree from the
zkevm bundle and run all of it under --features stateless; combined with
this branch's bal-devnet-7 semantics that scope produces ~549
GasUsedMismatch / ReceiptsRootMismatch /
BlockAccessListHashMismatch failures.

test-stateless-zkevm filters cargo to the eip8025_optional_proofs
suite, which still validates the stateless harness without the bal-version
mismatch.

Re-broaden by switching test: back to test-stateless in
tooling/ef_tests/blockchain/Makefile once the zkevm bundle is regenerated
against the current bal spec.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 21, 2026

Lines of code report

Total lines added: 231
Total lines removed: 0
Total lines changed: 231

Detailed view
+-----------------------------------------+-------+------+
| File                                    | Lines | Diff |
+-----------------------------------------+-------+------+
| ethrex/crates/networking/rpc/tracing.rs | 546   | +231 |
+-----------------------------------------+-------+------+

@azteca1998 azteca1998 changed the title feat(rpc): implement 4byteTracer feat(l1): implement 4byteTracer May 21, 2026
@github-actions github-actions Bot added the L1 Ethereum client label May 21, 2026
azteca1998 added a commit that referenced this pull request May 26, 2026
Three correctness bugs versus geth's built-in 4byteTracer
(eth/tracers/native/4byte.go), each of which would cause clients matching
against geth output to mismatch every entry:

- The reported size was `len(calldata)` instead of `len(calldata) - 4`.
  Geth's key shape is `"0xSELECTOR-N"` where N is the argument-bytes
  length excluding the selector itself.
- The top-level transaction call was being counted. Geth skips depth 0
  and only records nested invocations.
- Every CallType was counted, including STATICCALL, CALLCODE, CREATE,
  CREATE2, and SELFDESTRUCT. Geth filters on the opcode and only counts
  CALL and DELEGATECALL — for create variants and selfdestruct the input
  isn't a function selector, and the geth tracer doesn't surface
  STATICCALL or CALLCODE either.

Also skip precompile-targeted invocations (addresses 0x01..=0x11 plus
P256VERIFY at 0x100) — matching geth's per-fork `isPrecompiled` check
fork-agnostically, since any precompile address is a precompile once its
fork activates.

Unit tests now lock in each rule: top-frame skip, short-input skip,
call-type filter, precompile skip, arg-size key, duplicate counting.
Integration tests gain block-level coverage and a missing-tx failure
case. Variant doc clarifies the size semantics and skipped categories.

Test setup boilerplate moves out of the misnamed `debug_trace_tests.rs`
into a shared `rpc::helpers` module so #6694, #6701, #6705, #6704 can
all rebase to drop their duplicate copies; the file is renamed to
`debug_four_byte_tracer_tests.rs` to fit its contents.
@azteca1998 azteca1998 marked this pull request as ready for review May 26, 2026 14:27
@azteca1998 azteca1998 requested a review from a team as a code owner May 26, 2026 14:27
@ethrex-project-sync ethrex-project-sync Bot moved this to In Review in ethrex_l1 May 26, 2026
@github-actions
Copy link
Copy Markdown

🤖 Kimi Code Review

The PR implements geth's 4byteTracer for the RPC debug namespace. The implementation is solid, well-documented, and thoroughly tested. I found one potential issue regarding subcall tracing parameters that should be verified.

Issues Found

1. Potential incorrect parameter for subcall tracing (tracing.rs:227)

The FourByteTracer passes false for the 4th parameter to trace_transaction_calls (line 227), with a comment indicating this means "need all subcalls". However, the tracer recursively processes frame.calls to collect 4-byte selectors from nested calls. If this parameter controls whether subcalls are populated in the trace, it should be true (like CallTracer on line 180) rather than false (like OpcodeTracer on line 210).

If false suppresses subcall tracing, the tracer will only see top-level calls and return empty results for contract interactions with internal calls.

Suggestion: Verify the semantics of this parameter. If it controls subcall inclusion, change to true:

.trace_transaction_calls(
    self.tx_hash,
    reexec,
    timeout,
    true,  // need all subcalls to collect nested selectors
    false,
)

The same applies to the block tracing variant at line 392.

Minor Observations

2. Precompile range check (tracing.rs:426-428)

The is_precompile_address function checks the range 1..=0x11 (1-17) for L1 precompiles. This includes BLS12 precompiles (0x0b-0x11) which are only active in certain forks. The comment acknowledges this is "fork-agnostic" and "slightly more aggressive than geth's per-fork check" — this is acceptable for a debug tracer but ensure this matches the intended behavior.

3. Error handling on empty traces (tracing.rs:233-235, 398-400)

The code returns RpcErr::Internal("Empty call trace") if trace.into_iter().next() returns None. This is defensive, but consider whether this is truly an error condition or if an empty map should be returned instead (consistent with the test expectation for


Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt

@github-actions
Copy link
Copy Markdown

🤖 Codex Code Review

Findings

  1. crates/networking/rpc/tracing.rs skips the top-level transaction frame entirely by iterating only over top_frame.calls. That makes 4byteTracer under-report ordinary contract calls with no internal calls. For a simple CALL transaction with calldata, ethrex would return {} where geth returns one selector entry. The test at crates/networking/rpc/tracing.rs codifies the same behavior, so this looks intentional but not geth-compatible.

  2. crates/networking/rpc/tracing.rs hardcodes a fork-agnostic precompile filter and explicitly documents the divergence. That will produce incorrect historical traces: before Cancun, 0x0a was not a precompile; before Prague, 0x0b..0x11 were not; before Osaka, 0x0100 was not. Calls to those addresses will be silently omitted from 4byteTracer on older blocks. It also duplicates the canonical precompile table instead of reusing ethrex_vm::precompiles_for_fork / is_precompile, so it is likely to drift again on future fork work.

Notes

  • I did not find a direct consensus or memory-safety issue here; the main problems are RPC correctness and geth compatibility.
  • I couldn’t run cargo test in this environment because Cargo attempted to refresh crates.io and network access is blocked.

Automated review by OpenAI Codex · gpt-5.4 · custom prompt

@github-actions
Copy link
Copy Markdown

🤖 Claude Code Review

Now I have enough context for a thorough review.


Code Review: feat(l1): implement 4byteTracer (PR 6704)

Overview

This PR implements the 4byteTracer for debug_traceTransaction and debug_traceBlockByNumber. It post-processes the existing callTracer output to extract 4-byte function selectors and argument sizes, returning a "0xSELECTOR-ARGSIZE" → count map. The approach (reuse call tracer, walk the tree) is pragmatic and avoids a new VM-level tracer.


Correctness

Geth compatibility — good overall.

The logic in collect_four_byte_recursive correctly mirrors geth's 4byte.go onEnter hook:

  • Top-level frame is skipped (depth 0).
  • Only CALL and DELEGATECALL are counted.
  • Reverted subcalls ARE counted (geth fires onEnter before execution, with no onExit rollback) — correct.
  • Key format "0x{selector}-{arg_size}" with hex::encode (lowercase) matches geth — correct.
  • arg_size = input.len() - 4 (argument bytes, not total input) — correct.

Precompile address check is fork-agnostic (tracing.rs:449–461).

The is_precompile_address function hardcodes the range 0x01..=0x11 for L1 precompiles. This suppresses selectors for addresses 0x0b0x11 on forks where those precompiles don't yet exist (e.g., BLS12-381 was not active before Prague). Geth uses a fork-aware isPrecompiled(to) check. The comment documents this deliberate divergence as "defensible," which is fair — but it's worth noting this is a correctness gap relative to geth on historical block traces.

A stronger fix would thread the block's fork through to collect_four_byte_selectors, then call the existing is_precompile(address, fork, vm_type) from crates/vm/levm/src/precompiles.rs. That avoids duplicating the precompile table.

Precompile table duplication (tracing.rs:443–461).

is_precompile_address duplicates knowledge already in levm/src/precompiles.rs. If a new precompile is added (e.g., at 0x12), only one of the two sites will be updated. Consider at least a doc-comment linking the two, or a #[cfg(test)] assertion comparing ranges.

ok_or eagerly allocates (tracing.rs:237).

.ok_or(RpcErr::Internal("Empty call trace".to_string()))

The String is allocated even when the Option is Some. Prefer ok_or_else(|| ...) for consistency with the block-trace branch at line 399 (which already uses ok_or_else).


Design

Post-processing via call tracer is correct but runs the full call tracer.

trace_transaction_calls with only_top_call: false runs the entire call tracer. For a transaction with thousands of nested calls, this builds a large CallTraceFrame tree just to walk it once for selectors. A dedicated tracer would be more efficient, but given current usage patterns this is acceptable. Worth a comment explaining the trade-off.

collect_four_byte_selectors / collect_four_byte_recursive split is clear — the naming makes the depth-0 skip intent explicit.

Recursion depth is bounded by the EVM call depth limit (1024), so stack overflow is not a practical concern.


Test Coverage

Unit tests are thorough. The #[cfg(test)] block covers: top-level skip, short calldata, arg-size vs total-size, nested calls, deduplication, call-type filtering, and precompile address boundaries. Good.

Integration tests only cover empty results. Both trace_tx_four_byte_tracer_value_transfer_is_empty and trace_block_four_byte_tracer use simple ETH transfers, which produce empty maps. There is no integration test exercising a transaction that produces a non-empty selector map (i.e., a contract call). This leaves the happy path untested at the integration level.

helpers.rs:191#![allow(dead_code)] is a reasonable approach for shared test infrastructure, but flagging unused items rather than suppressing would catch accidental dead helpers earlier.


Minor / Style

  • tracing.rs:433: hex::encode(&frame.input[..4]) — the & on a slice is required (unsized type), but worth noting for readability that this is always 8 lowercase hex chars.
  • tracing.rs:60–68: Doc comment on FourByteTracer variant links to geth's source; the URL may drift over time as geth reorganises, but it's still helpful for now.
  • helpers.rs:37: TEST_PRIVATE_KEY is clearly test-only. Confirm this key has no corresponding mainnet/testnet funds (standard hygiene for test fixtures with embedded secrets).
  • CallType variants use SCREAMING_SNAKE_CASE (pre-existing, not introduced here) — the matches! macro call in collect_four_byte_recursive is idiomatic Rust regardless.

Summary

The implementation is correct for the common case and well-documented. Two items worth addressing before merge:

  1. Fork-agnostic precompile check — either thread the fork through and call the existing is_precompile, or add a clear comment that this is intentional and a known deviation from geth on pre-Prague historical traces.
  2. Integration test with a contract call — add at least one test producing a non-empty selector map to verify the non-trivial path end-to-end.

Everything else is minor polish.


Automated review by Claude (Anthropic) · sonnet · custom prompt

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 26, 2026

Greptile Summary

This PR implements the 4byteTracer for debug_traceTransaction and debug_traceBlockByNumber. It works by post-processing the existing call-tracer output — walking the call tree to extract CALL/DELEGATECALL frames with ≥4 bytes of calldata, skipping the top-level transaction frame and precompile targets, and returning a "0xSELECTOR-argsize" → count map matching geth's format.

  • Adds FourByteTracer variant to TracerType enum and corresponding handle() branches in both TraceTransactionRequest and TraceBlockByNumberRequest.
  • Introduces collect_four_byte_selectors / collect_four_byte_recursive helpers and a fork-agnostic is_precompile_address check covering L1 precompiles (0x01–0x11) and the L2 P256VERIFY precompile (0x100).
  • Adds comprehensive unit tests for the selector-collection logic and three RPC-level integration tests (all using plain ETH transfers, so only the empty-map output path is exercised end-to-end).

Confidence Score: 3/5

Safe to merge for the wired-up plumbing; the main open question is whether the STATICCALL/CALLCODE filtering matches geth's reference implementation.

The selector-collection logic is well unit-tested and the RPC plumbing is straightforward. However, the claim that geth's 4byteTracer also skips STATICCALL and CALLCODE frames deserves verification against the linked geth source — if geth's CaptureEnter records all call types (filtering only on input length and precompile status), then view-function calls via STATICCALL would be silently dropped and the tracer would return a different map than geth for any transaction that makes read-only contract calls.

crates/networking/rpc/tracing.rs — specifically the call-type filter in collect_four_byte_recursive and the is_precompile_address boundary at 0x11.

Important Files Changed

Filename Overview
crates/networking/rpc/tracing.rs Adds FourByteTracer to TraceTransactionRequest and TraceBlockByNumberRequest; implements collect_four_byte_selectors/collect_four_byte_recursive helpers plus is_precompile_address; potential behavioral divergence from geth regarding STATICCALL/CALLCODE recording
test/tests/rpc/debug_four_byte_tracer_tests.rs New integration tests for 4byteTracer; only covers the empty-map case via ETH transfers, not actual selector recording
test/tests/rpc/helpers.rs New shared test utilities: in-memory store setup, block building, RPC dispatch helpers; clean and well-structured
test/tests/rpc/mod.rs Trivial module registration for new test files

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[RPC debug_traceTransaction or debug_traceBlockByNumber] --> B{TracerType}
    B -->|FourByteTracer| C[trace_transaction_calls with only_top_call=false]
    C --> D[Take first CallTraceFrame - top-level tx call]
    D --> E[collect_four_byte_selectors - skip top frame itself]
    E --> F[iterate top_frame.calls at depth 1+]
    F --> G[collect_four_byte_recursive per sub-call]
    G --> H{CallType is CALL or DELEGATECALL?}
    H -->|No| J[skip recording - still recurse into children]
    H -->|Yes| I{input length at least 4 and not precompile?}
    I -->|No| J
    I -->|Yes| K[build key 0xSELECTOR-argsize and increment count]
    K --> L[recurse into frame.calls]
    J --> L
    L -->|more sub-calls| G
    L -->|no more| M[return HashMap String to u64]
    M --> N[serialize to JSON object]
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
crates/networking/rpc/tracing.rs:430-442
**STATICCALL/CALLCODE filtering may diverge from geth**

The implementation skips `STATICCALL` and `CALLCODE` frames entirely when recording selectors. Geth's native `4byteTracer` uses the `CaptureEnter` hook, which is called for *all* call types (CALL, DELEGATECALL, STATICCALL, CALLCODE, CREATE, CREATE2). It only gates on `len(input) < 4` and `isPrecompiled(to)` — there is no op-type filter. If that is still the case in the current geth source, every STATICCALL with ≥4 bytes of calldata to a non-precompile would be recorded by geth but silently skipped here, producing a different selector map for any transaction that makes view-function calls (which commonly use STATICCALL).

### Issue 2 of 2
test/tests/rpc/debug_four_byte_tracer_tests.rs:1-71
**Integration tests only exercise the empty-map code path**

All three integration tests are built on `setup_single_transfer_block`, which creates a plain ETH transfer. Because the tracer skips the top-level call and the transfer has no nested calls, the selector map is always empty. This means the RPC serialization path for a non-empty map (the case that actually exercises `collect_four_byte_selectors` through the full dispatch chain) has no integration-level coverage. A test with a transaction that invokes a deployed contract — so at least one nested CALL/DELEGATECALL with ≥4 bytes of calldata appears in the result — would catch any wire-format or plumbing regression that the unit tests in `tracing.rs` cannot.

Reviews (1): Last reviewed commit: "fix(rpc): align 4byteTracer with geth's ..." | Re-trigger Greptile

Comment on lines +430 to +442
&& frame.input.len() >= 4
&& !is_precompile_address(&frame.to)
{
let selector = hex::encode(&frame.input[..4]);
let arg_size = frame.input.len() - 4;
let key = format!("0x{selector}-{arg_size}");
*selectors.entry(key).or_insert(0) += 1;
}
for sub_call in &frame.calls {
collect_four_byte_recursive(sub_call, selectors);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 STATICCALL/CALLCODE filtering may diverge from geth

The implementation skips STATICCALL and CALLCODE frames entirely when recording selectors. Geth's native 4byteTracer uses the CaptureEnter hook, which is called for all call types (CALL, DELEGATECALL, STATICCALL, CALLCODE, CREATE, CREATE2). It only gates on len(input) < 4 and isPrecompiled(to) — there is no op-type filter. If that is still the case in the current geth source, every STATICCALL with ≥4 bytes of calldata to a non-precompile would be recorded by geth but silently skipped here, producing a different selector map for any transaction that makes view-function calls (which commonly use STATICCALL).

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/networking/rpc/tracing.rs
Line: 430-442

Comment:
**STATICCALL/CALLCODE filtering may diverge from geth**

The implementation skips `STATICCALL` and `CALLCODE` frames entirely when recording selectors. Geth's native `4byteTracer` uses the `CaptureEnter` hook, which is called for *all* call types (CALL, DELEGATECALL, STATICCALL, CALLCODE, CREATE, CREATE2). It only gates on `len(input) < 4` and `isPrecompiled(to)` — there is no op-type filter. If that is still the case in the current geth source, every STATICCALL with ≥4 bytes of calldata to a non-precompile would be recorded by geth but silently skipped here, producing a different selector map for any transaction that makes view-function calls (which commonly use STATICCALL).

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +1 to +71
use ethrex_common::H256;
use serde_json::{Value, json};

use super::helpers::{rpc_call, rpc_call_expect_err, setup_single_transfer_block};

#[tokio::test]
async fn trace_tx_four_byte_tracer_value_transfer_is_empty() {
let env = setup_single_transfer_block().await;

let result = rpc_call(
&env.store,
"debug_traceTransaction",
vec![
json!(format!("{:#x}", env.tx_hash)),
json!({"tracer": "4byteTracer"}),
],
)
.await;

// A simple ETH transfer has no nested calls and the tracer skips the
// top-level call, so the result must be the empty map.
let obj = result.as_object().expect("response should be an object");
assert!(obj.is_empty(), "simple transfer has no 4-byte selectors");
}

#[tokio::test]
async fn trace_tx_four_byte_tracer_unknown_hash_errors() {
let env = setup_single_transfer_block().await;

let err = rpc_call_expect_err(
&env.store,
"debug_traceTransaction",
vec![
json!(format!("{:#x}", H256::from_low_u64_be(0xdeadbeef))),
json!({"tracer": "4byteTracer"}),
],
)
.await;
let msg = format!("{err:?}");
assert!(
msg.contains("Transaction not Found"),
"expected tx-not-found error, got: {msg}"
);
}

#[tokio::test]
async fn trace_block_four_byte_tracer() {
let env = setup_single_transfer_block().await;

let result: Value = rpc_call(
&env.store,
"debug_traceBlockByNumber",
vec![
json!(format!("{:#x}", env.block.header.number)),
json!({"tracer": "4byteTracer"}),
],
)
.await;

let arr = result.as_array().expect("response should be an array");
assert_eq!(arr.len(), 1, "one tx in block");
let entry = arr[0].as_object().expect("entry should be an object");
assert_eq!(
entry["txHash"].as_str().unwrap().to_lowercase(),
format!("{:#x}", env.tx_hash).to_lowercase()
);
let selectors = entry["result"]
.as_object()
.expect("result should be a selector map");
assert!(selectors.is_empty(), "value transfer yields no selectors");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Integration tests only exercise the empty-map code path

All three integration tests are built on setup_single_transfer_block, which creates a plain ETH transfer. Because the tracer skips the top-level call and the transfer has no nested calls, the selector map is always empty. This means the RPC serialization path for a non-empty map (the case that actually exercises collect_four_byte_selectors through the full dispatch chain) has no integration-level coverage. A test with a transaction that invokes a deployed contract — so at least one nested CALL/DELEGATECALL with ≥4 bytes of calldata appears in the result — would catch any wire-format or plumbing regression that the unit tests in tracing.rs cannot.

Prompt To Fix With AI
This is a comment left during a code review.
Path: test/tests/rpc/debug_four_byte_tracer_tests.rs
Line: 1-71

Comment:
**Integration tests only exercise the empty-map code path**

All three integration tests are built on `setup_single_transfer_block`, which creates a plain ETH transfer. Because the tracer skips the top-level call and the transfer has no nested calls, the selector map is always empty. This means the RPC serialization path for a non-empty map (the case that actually exercises `collect_four_byte_selectors` through the full dispatch chain) has no integration-level coverage. A test with a transaction that invokes a deployed contract — so at least one nested CALL/DELEGATECALL with ≥4 bytes of calldata appears in the result — would catch any wire-format or plumbing regression that the unit tests in `tracing.rs` cannot.

How can I resolve this? If you propose a fix, please make it concise.

… tracing

Add support for the `4byteTracer` tracer type which records all function
selectors (first 4 bytes of calldata) and calldata sizes for every call
in a transaction. Returns a map of "0xSELECTOR-SIZE" -> count.

Implemented by post-processing the existing call tracer output, walking
the call trace tree to extract selector information from each frame.
Trace a real transfer with 4byteTracer and assert the response
is an empty selector map (no calldata in a plain transfer).
Three correctness bugs versus geth's built-in 4byteTracer
(eth/tracers/native/4byte.go), each of which would cause clients matching
against geth output to mismatch every entry:

- The reported size was `len(calldata)` instead of `len(calldata) - 4`.
  Geth's key shape is `"0xSELECTOR-N"` where N is the argument-bytes
  length excluding the selector itself.
- The top-level transaction call was being counted. Geth skips depth 0
  and only records nested invocations.
- Every CallType was counted, including STATICCALL, CALLCODE, CREATE,
  CREATE2, and SELFDESTRUCT. Geth filters on the opcode and only counts
  CALL and DELEGATECALL — for create variants and selfdestruct the input
  isn't a function selector, and the geth tracer doesn't surface
  STATICCALL or CALLCODE either.

Also skip precompile-targeted invocations (addresses 0x01..=0x11 plus
P256VERIFY at 0x100) — matching geth's per-fork `isPrecompiled` check
fork-agnostically, since any precompile address is a precompile once its
fork activates.

Unit tests now lock in each rule: top-frame skip, short-input skip,
call-type filter, precompile skip, arg-size key, duplicate counting.
Integration tests gain block-level coverage and a missing-tx failure
case. Variant doc clarifies the size semantics and skipped categories.

Test setup boilerplate moves out of the misnamed `debug_trace_tests.rs`
into a shared `rpc::helpers` module so #6694, #6701, #6705, #6704 can
all rebase to drop their duplicate copies; the file is renamed to
`debug_four_byte_tracer_tests.rs` to fit its contents.
@azteca1998 azteca1998 force-pushed the feat/four-byte-tracer branch from 64a9cbd to 0963449 Compare May 27, 2026 15:25
Geth's 4byteTracer (CaptureEnter) counts all call types with >= 4
bytes of input data. Only CREATE/CREATE2/SELFDESTRUCT are skipped
(their input is init-code, not ABI-encoded calls).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L1 Ethereum client

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

Implement 4byteTracer for debug_trace*

1 participant