fix(debug): honour the tracer's Execute↔Trace swap on the EIP-7928 BAL path - #12972
fix(debug): honour the tracer's Execute↔Trace swap on the EIP-7928 BAL path#12972hudem1 wants to merge 6 commits into
Conversation
…L path Part of #12599 — fixes #12723 (follow-up from #12721). Under EIP-7928 the block-access-list pool runs transactions through its own per-worker tx-processor adapters, built from the scope's TransactionProcessorAdapterFactory. Every scope registers that factory except debug, which registered ChangeableTransactionProcessorAdapter directly — so its BAL workers stayed on Execute and GethStyleTracer's runtime swap to TraceTransactionProcessorAdapter was bypassed. A debug_traceCall from a state-overridden contract sender on an Amsterdam block was therefore rejected with EIP-3607 "sender has deployed code" instead of being traced. Debug now registers ChangeableTransactionProcessorAdapter.ForProcessor as its factory: each worker mirrors the shared changeable adapter's current mode (re-read per call) onto its own processor, so the BAL pool honours the swap. Adds a regression test tracing a contract sender through the BAL path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @hudem1's task in 7m 43s —— View job Review: fix(debug): honour the tracer's Execute↔Trace swap on the EIP-7928 BAL path
Verdict: the fix itself is correct and minimal. No Critical or High findings. All findings below are on the test and on the robustness of the mode-detection contract. What I verified
Findings
Not verifiedI could not compile or run anything — |
EVM Opcode Benchmark DiffAggregated runs: base=1, pr=1 No significant regressions or improvements detected. |
…unit test - Move the BAL debug_traceCall regression into a DebugRpcModuleTests partial that reuses Context.Create (disposes the chain) instead of a standalone, undisposed fixture; drop the redundant AddBlock (AddBlocksOnStart already gives a non-genesis head) and the misleading EIP-3607 "enable" comment. - Add ChangeableTransactionProcessorAdapterTests pinning the "re-read the mode on every call" invariant: the BAL pool builds its worker once, before the tracer swaps the mode, so a snapshot implementation would silently keep validating on later non-swapping traces. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
wurdum
left a comment
There was a problem hiding this comment.
The only thing, out of the scope of changes:
GethStyleTracer clones the target block with WithReplacedBodyCloned, which carries the original BlockAccessList forward, and BlockchainProcessorFacade.Process forwards options without ForceSequentialBlockAccessList. BranchProcessor adds that flag whenever a tracer is present, so on a real Amsterdam block debug_traceCall takes the parallel BAL executor, where the sequential-retry exception has no handler. Mirroring BranchProcessor in the facade would put this fix on the path users hit.
// Mirror BranchProcessor: traced processing runs the sequential BAL path, since this facade
// bypasses BranchProcessor and therefore its sequential retry.
ProcessingOptions blockOptions = tracer == NullBlockTracer.Instance
? options
: options | ProcessingOptions.ForceSequentialBlockAccessList;
(Block? processedBlock, TxReceipt[] _) = blockProcessor.ProcessOne(block, blockOptions, tracer, spec, token);…-sniff Review follow-up (benaadams): - ChangeableTransactionProcessorAdapter now stores the runtime mode as an explicit TransactionProcessorAdapterFactory (CurrentAdapterFactory) that GethStyleTracer swaps, instead of inferring the mode from `CurrentAdapter is TraceTransactionProcessorAdapter`. A decorated or alternative tracing adapter is no longer misclassified as Execute. Each ForProcessor worker applies the current factory to its own processor, re-read on every call. - Remove the substitute-based ChangeableTransactionProcessorAdapterTests: the factory shape makes "re-read per call on the worker's own processor" structural (no snapshot possible), and test-infrastructure.md forbids Substitute.For for components TestBlockchain provides. The production-DI e2e test remains the guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…essorFacade Review follow-up (wurdum): GethStyleTracer clones the target block with WithReplacedBodyCloned, carrying the original BlockAccessList forward, and this facade forwarded options unchanged. BranchProcessor adds ForceSequentialBlockAccessList whenever a tracer is present (and handles the parallel BlockAccessListSequentialRetryException); the facade bypasses BranchProcessor, so on a real Amsterdam block debug_traceCall took the parallel BAL executor whose sequential-retry exception has no handler here. Mirror BranchProcessor: force the sequential BAL path when a tracer is present. This also lands the adapter fix on the path users actually hit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@wurdum good catch on the parallel BAL path — applied in 51bb387.
ProcessingOptions blockOptions = tracer == NullBlockTracer.Instance
? options
: options | ProcessingOptions.ForceSequentialBlockAccessList;
(Block? processedBlock, TxReceipt[] _) = blockProcessor.ProcessOne(block, blockOptions, tracer, spec, token);Debug suites ( |
IDE0005: Nethermind.JsonRpc is an ancestor of the file's namespace (Nethermind.JsonRpc.Test.Modules), so ResultWrapper resolves without the using. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes #12723 (follow-up from #12721, part of #12599 glamsterdam devnet-8).
Changes
Under EIP-7928 the
BlockAccessListManagerruns transactions through its own per-worker tx-processor adapters, built from the block-processing scope'sTransactionProcessorAdapterFactory. Every scope registers that factory (production, trace, proof, simulate) except debug, which registeredChangeableTransactionProcessorAdapterdirectly — so its BAL-pool workers stayed onExecuteandGethStyleTracer's runtime swap toTraceTransactionProcessorAdapterwas bypassed on the BAL path.Consequence: on an Amsterdam block a
debug_traceCallwhose sender has deployed code (a state-overridden contract, or any call that relies on skipped sender validation) was rejected with EIP-3607sender has deployed codeinstead of being traced —Executevalidates,Traceskips validation.The fix mirrors what every other scope does, adapted for debug's shared-mutable adapter:
ChangeableTransactionProcessorAdapter.ForProcessor(processor)returns a per-worker adapter that mirrors the shared adapter's current mode (Execute vs Trace), re-read on every call — required because the sequential BAL manager builds its worker adapter once.DebugModuleFactoryregistersTransactionProcessorAdapterFactory = changeable.ForProcessor, so the BAL pool's workers track the same changeable mode the tracer swaps. The scopedChangeableTransactionProcessorAdapterregistration stays for the non-BAL path and as the tracer's swap target.TransactionProcessorAdapterFactory<remarks>that described debug as the un-fixed exception.Types of changes
Testing
DebugTraceCallBalTests.debug_traceCall_from_contract_sender_traces_on_bal_path: traces a contract-sender call through the EIP-7928 BAL path on an Amsterdam chain.InvalidTransactionException: … failed with error sender has deployed code.callTracer) so the buffered/eager path runs the transaction instead of the lazily-streamed default struct-log path.DebugRpcModuleTests,DebugModuleTests,DebugSimulateTests) pass — no regressions.Documentation
No user-facing changes.
🤖 Generated with Claude Code