Overview
After EIP-6780, SELFDESTRUCT only deletes a contract's code in the same transaction it was created. But before the merge, contracts could be destroyed and recreated at the same address — creating a subtle security surface: a contract that looks safe at address X might be a different contract than the one audited.
block-tracer detects these reinitialized contracts by scanning a range of Ethereum blocks for SELFDESTRUCT + CREATE pairs at the same address.
How it works
cargo run <start_block> <end_block>
# output: reinitialized_contracts.json
For each block in range:
- Call
trace_blockvia reth's RPC to get all create/selfdestruct actions - Collect self-destructed addresses and subsequently created addresses
- Find the intersection — addresses that appear in both sets
- For addresses where the reinit falls outside the scan range, query reth's
PlainAccountStatetable directly to check current status
Performance design
Tracing blocks via RPC is the bottleneck (90% of runtime). The implementation spawns one tokio task per block, with a semaphore capping concurrency at 32 to avoid overwhelming the reth node.
DB queries are batched: self-destructed addresses are chunked and queried in parallel via separate tokio tasks. The gain here is modest (reth's MDBX is fast), but it avoids serializing on large scan ranges.
Accessing reth internals
The interesting part is the direct DB access: rather than making an RPC call for every address, we open reth's MDBX database directly and query the PlainAccountState table. This requires knowing reth's internal table layout — which is stable but not formally documented.