IC-1033: add Rabby Pipeline block tracing - #7
Conversation
📝 WalkthroughWalkthroughAdds Pipeline Mode 2 tracing through ChangesPipeline tracing
Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant TraceAPI
participant EVMBackend
participant DebankBuilder
participant StateReader
Client->>TraceAPI: trace_debankBlock(blockNumberOrHash)
TraceAPI->>EVMBackend: TraceBlock(block, TraceConfig)
EVMBackend-->>TraceAPI: TraceResults
TraceAPI->>DebankBuilder: Build(block, parent, receipts, TraceResults, StateReader)
DebankBuilder->>StateReader: GetProof and GetCode at traced height
StateReader-->>DebankBuilder: Historical account state
DebankBuilder-->>TraceAPI: debank.Output
TraceAPI-->>Client: Pipeline-compatible response
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/evm/rpc/namespaces/ethereum/trace/api.go (1)
31-84: 🚀 Performance & Scalability | 🔵 TrivialConsider guarding the cost of this endpoint.
DebankBlockperforms a full-block mux replay plus oneeth_getProofand oneeth_getCodeper touched account, all on the request path. On busy blocks this is a heavy, unbounded operation exposed via a public namespace. Consider a per-request timeout viactx, and/or rate limiting / result caching keyed by block hash, so a burst oftrace_debankBlockcalls can't degrade the node.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/evm/rpc/namespaces/ethereum/trace/api.go` around lines 31 - 84, Bound the expensive DebankBlock request path by applying a per-request timeout through the existing ctx flow and adding protection against repeated work, such as rate limiting or caching results by block hash. Ensure bursts of trace_debankBlock calls cannot trigger unbounded full-block replay and proof/code lookups, while preserving the existing successful response and error handling behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@internal/evm/rpc/namespaces/ethereum/trace/api.go`:
- Around line 31-84: Bound the expensive DebankBlock request path by applying a
per-request timeout through the existing ctx flow and adding protection against
repeated work, such as rate limiting or caching results by block hash. Ensure
bursts of trace_debankBlock calls cannot trigger unbounded full-block replay and
proof/code lookups, while preserving the existing successful response and error
handling behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4a5b7e6c-eb75-46cd-a013-3e6aada3c06a
📒 Files selected for processing (10)
.env.exampleREADME.mddocs/PIPELINE_TRACING.mde2e/debank_pipeline_test.gointernal/evm/rpc/apis.gointernal/evm/rpc/apis_test.gointernal/evm/rpc/debank/builder.gointernal/evm/rpc/debank/builder_test.gointernal/evm/rpc/debank/types.gointernal/evm/rpc/namespaces/ethereum/trace/api.go
There was a problem hiding this comment.
Pull request overview
This PR adds Chaintable Pipeline “Mode 2” block tracing support to evm-gateway by introducing a new trace JSON-RPC namespace with trace_debankBlock, returning a Pipeline wire-compatible response that merges EVM tracing results with the gateway’s virtualized Cosmos bank events.
Changes:
- Added a new
traceJSON-RPC namespace exposingtrace_debankBlock(blockNumberOrHash). - Implemented Pipeline-compatible response building, including mux-tracer decoding, trace/event shaping, and RLP-encoded state diff generation via post-state
eth_getProof/eth_getCode. - Added unit + live e2e compatibility coverage and documentation for configuration/usage.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| README.md | Adds a short pointer to Pipeline tracing docs and the new RPC method. |
| internal/evm/rpc/namespaces/ethereum/trace/api.go | Implements trace_debankBlock handler and block/receipt/trace loading. |
| internal/evm/rpc/debank/types.go | Introduces Pipeline wire-compatible JSON/RLP types and validation hash helper. |
| internal/evm/rpc/debank/builder.go | Core builder that converts blocks/receipts/traces/virtual logs into Pipeline output + state diff. |
| internal/evm/rpc/debank/builder_test.go | Unit tests for mux config, trace/event shaping, state diff encoding, and revert/virtual-log handling. |
| internal/evm/rpc/apis.go | Registers the new trace namespace. |
| internal/evm/rpc/apis_test.go | Verifies trace namespace registration. |
| e2e/debank_pipeline_test.go | Adds an opt-in live e2e test validating the full Mode 2 wire contract. |
| docs/PIPELINE_TRACING.md | Documents configuration and a focused live compatibility test command. |
| .env.example | Adds trace to the example enabled JSON-RPC namespaces. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/evm/rpc/backend/tracing.go (1)
181-222: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winReplay-order checks should not be skipped on cache reads
- In
OfflineRPCOnly, cached trace results can still be returned without the replay-order guard when the block isn’t already supplied, so stale pre-fix entries can slip through.- For cacheable requests that arrive without a resolved block, the current flow fetches and validates the block before checking the cache, so a cache hit can still fail on a transient RPC error. Consider deferring that fetch or storing a cache version/validation marker.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/evm/rpc/backend/tracing.go` around lines 181 - 222, Update the trace-cache path around validateTraceBlockReplayOrder so cache lookup does not require an upfront Tendermint block fetch, avoiding transient RPC failures before cache hits. Ensure cached results carry and validate a replay-order/version marker, reject legacy or unvalidated entries (including in OfflineRPCOnly), and only resolve and validate the block when required before returning a result.
🧹 Nitpick comments (1)
internal/evm/rpc/backend/tracing.go (1)
292-373: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winTransaction decode is now duplicated on every
TraceBlockcall.
validateTraceBlockReplayOrderdecodes every tx/message in the block, andtraceBlockEthereumTransactions(Lines 348-373) decodes the same block's txs again immediately afterward. Every trace request now pays the tx-decode cost twice. Consider havingvalidateTraceBlockReplayOrderreturn the decoded Ethereum messages/hashes (or a shared decode helper) sotraceBlockEthereumTransactionscan reuse that work instead of re-decoding.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/evm/rpc/backend/tracing.go` around lines 292 - 373, Avoid decoding block transactions twice during TraceBlock handling by sharing the decoded transaction/message results between validateTraceBlockReplayOrder and traceBlockEthereumTransactions. Update these methods, or introduce a focused shared helper, so validation and Ethereum message/hash extraction reuse one decode pass while preserving existing validation errors and returned ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@internal/evm/rpc/backend/tracing.go`:
- Around line 181-222: Update the trace-cache path around
validateTraceBlockReplayOrder so cache lookup does not require an upfront
Tendermint block fetch, avoiding transient RPC failures before cache hits.
Ensure cached results carry and validate a replay-order/version marker, reject
legacy or unvalidated entries (including in OfflineRPCOnly), and only resolve
and validate the block when required before returning a result.
---
Nitpick comments:
In `@internal/evm/rpc/backend/tracing.go`:
- Around line 292-373: Avoid decoding block transactions twice during TraceBlock
handling by sharing the decoded transaction/message results between
validateTraceBlockReplayOrder and traceBlockEthereumTransactions. Update these
methods, or introduce a focused shared helper, so validation and Ethereum
message/hash extraction reuse one decode pass while preserving existing
validation errors and returned ordering.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0bfca92f-13da-452d-9790-24daa74ff159
📒 Files selected for processing (5)
docs/PIPELINE_TRACING.mdinternal/evm/rpc/backend/trace_helpers_test.gointernal/evm/rpc/backend/tracing.gointernal/evm/rpc/debank/builder.gointernal/evm/rpc/debank/builder_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
- docs/PIPELINE_TRACING.md
- internal/evm/rpc/debank/builder.go
Summary
Adds Chaintable Pipeline Mode 2 support to the Injective EVM gateway through
trace_debankBlock(blockNumberOrHash). The response is wire-compatible withtypes.DebankOutPutand combines EVM execution data with the gateway's virtualized Cosmos bank events so Rabby/Pipeline can observe nativex/bank, MTS, ERC20, and Circle USDC movement.This revision also closes the audit's response-correctness findings:
0x0000000000000000000000000000000000000000tostorage_contracts;Related deployment references:
Design and implementation
RPC surface
tracenamespace and exposestrace_debankBlock(blockNumberOrHash).block_file, an Ethereum-compatibleheader, RLP-encodedstate_diff, and Pipeline'svalidation_hash.EVM tracing and state diff
For traceable layouts, the endpoint requests one native geth
muxTracerreplay containing:erc7562Tracerwith logs enabled for nested call/create frames, EVM logs, errors, gas/output data, and exact per-frameSSTOREmarkers;prestateTracerfor touched accounts and storage slots.Injective's current gRPC trace path does not provide a usable prestate-tracer post-state diff. The gateway therefore queries historical
eth_getProofandeth_getCodeat the traced height, compares those values with the captured prestate, and RLP-encodes Pipeline'sBlockStorageDiff. This covers account balance/nonce/code changes, creation/deletion, storage changes, new bytecode, and root-only Cosmos state transitions.The tracer's effective
stackTopItemsSize=3default is explicit in the request. This also versions the trace-cache key, preventing an offline gateway from reusing a Pipeline trace cached before the ordered-replay validation was introduced.Ordered Cosmos/EVM replay safety
The current injective-core
QueryTraceBlockRequestandQueryTraceTxRequestprotobuf contracts accept onlyMsgEthereumTx. They cannot execute a native Cosmos message in the same mutable replay context. Starting at H-1 and stripping a mixed block down to EVM messages can therefore produce different frames, logs, touched slots, and state diffs when an EVM transaction reads state changed by an earlier native message.The gateway now fails closed instead of returning data from that different transition:
ordered Cosmos/EVM block replay is unavailablebefore trace-cache lookup and before the gRPC replay.Full support for every mixed ordering requires a follow-up injective-core trace API that accepts and executes complete ordered Cosmos transactions from beginning-of-block state. The gateway cannot reproduce native keeper transitions remotely with the current EVM-only request contract.
Pipeline wire compatibility
storage_contractssemantics for reverted or net-zeroSSTOREoperations when an execution address is known.CALL/DELEGATECALL/CALLCODE/EXTDELEGATECALL, logs, revert reasons, and dynamic-fee metadata.Correct receipt log indexes
The opcode trace may contain logs from frames whose effects were reverted, while the receipt contains only persisted logs. Receipt indexes are now consumed only by frames that are neither failed nor parent-failed. A reverted child log stays in
error_eventswith index zero, and the next surviving trace log receives the actual nonzero receipt index. Tracer positions beyond the call count are clamped to the same terminal bucket used by timeline assignment, preventing duplicate unconsumed receipt events.Correct storage-contract addresses for failed creates
erc7562Tracercan clear a reverted CREATE/CREATE2 frame'stoaddress. Storage-address resolution now returns(address, known)and only inserts a contract when the execution address is known. The trace still reportsself_storage_change=true, but a missing creation destination is skipped instead of being represented as the zero address. Real calls to the zero address remain distinguishable because a non-niltopointer is still accepted.Cosmos and token movement integration
0x0000000000000000000000000000000000000800.error_traces.x/banktransfers, canonical Injective MTS/BankERC20 transfers, storage-backed ERC20 logs, and Circle's Injective USDC implementation for supported replay layouts.Short usage guide
Enable EVM gRPC tracing on the Injective node:
or:
Enable the gateway namespace and Cosmos event virtualization:
Query by height or hash:
See
docs/PIPELINE_TRACING.mdfor the focused live compatibility test and the ordered-replay safety boundary.Test coverage
New focused regressions cover:
SSTORE, a missingto, retained storage flags, and no zero-address storage contract.Validation commands:
All commands pass.
Live verification
Validated against a running local Injective chain with gRPC tracing and Cosmos event virtualization enabled.
Audit-specific fixtures
MsgSendat tx 0, EVM transfer at tx 10x3294)trace_debankBlockanddebug_traceBlockByNumberreject before replay with the exact tx/message position; no divergent response is emittedSSTOREthenREVERTself_storage_change=true,storage_change=true, one error trace, andstorage_contracts=[](no zero address)0x1275)Existing compatibility fixtures
x/banktransfer, block 4162SSTORE, storage diffFixedSupplyBankERC20, blocks 4282/4314self_storage_change=true, token instorage_contracts, zero net RLP storage diffsSupported fixtures pass number and hash lookup. Responses were unmarshaled into the real
github.com/Chaintable/pipeline/types.DebankOutPut, had validation hashes recomputed by Pipeline code, and hadstate_diffdecoded using Pipeline's RLP types.The gated live compatibility command is:
Summary by CodeRabbit
New Features
trace_debankBlockRPC method for Pipeline Mode 2 block tracing.Documentation
Tests