mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-26 15:47:21 +00:00
eth/tracers: align traceBlockParallel with pre-exec system calls
traceBlockParallel rebuilt per-tx prestate by replaying transactions but did not execute the same pre-exec system calls that traceBlock/traceChain apply before transaction execution. This created a semantic mismatch under post- merge forks (EIP-4788/Prague), where tracer-visible state could diverge from the canonical block execution context. Root cause - The parallel fast-replay path initialized EVM and immediately started tx replay. - It skipped: - ProcessBeaconBlockRoot (EIP-4788) - ProcessParentBlockHash (Prague / EIP-2935) Fix - In traceBlockParallel, after constructing EVM on parent state and before tx replay, execute the same pre-exec system calls as other tracing paths: - ProcessBeaconBlockRoot when block has a beacon root - ProcessParentBlockHash when Prague rules are active - This makes parallel tracing state preparation behaviorally equivalent to traceBlock and traceChain. Regression test - Add TestTraceBlockParallelPragueParentHashSystemCall. - Build a post-merge test chain (beacon+ethash faker), seed history storage contract in genesis, run traceBlockParallel, and assert that the EIP-2935 ring-buffer slot for block-1 stores block.ParentHash(). - The test fails without the fix and passes with it. Validation - go test ./eth/tracers -run TestTraceBlockParallelPragueParentHashSystemCall - go test ./eth/tracers -run 'TestTraceBlock|TestTraceCall|TestTraceTransaction'
This commit is contained in:
parent
406a852ec8
commit
429d7aca05
2 changed files with 71 additions and 0 deletions
|
|
@ -692,6 +692,12 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
|
|||
var failed error
|
||||
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||
evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{})
|
||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||
}
|
||||
if api.backend.ChainConfig().IsPrague(block.Number(), block.Time()) {
|
||||
core.ProcessParentBlockHash(block.ParentHash(), evm)
|
||||
}
|
||||
|
||||
txloop:
|
||||
for i, tx := range txs {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package tracers
|
|||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -284,6 +285,70 @@ func TestStateHooks(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTraceBlockParallelPragueParentHashSystemCall(t *testing.T) {
|
||||
var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
from = crypto.PubkeyToAddress(key.PublicKey)
|
||||
to = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
|
||||
cfg = *params.AllDevChainProtocolChanges
|
||||
nonce = uint64(0)
|
||||
)
|
||||
|
||||
genesis := &core.Genesis{
|
||||
Config: &cfg,
|
||||
Alloc: types.GenesisAlloc{
|
||||
from: {Balance: big.NewInt(params.Ether)},
|
||||
to: {Balance: big.NewInt(0)},
|
||||
params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0},
|
||||
},
|
||||
}
|
||||
backend := newTestMergedBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
|
||||
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||
Nonce: nonce,
|
||||
To: &to,
|
||||
Value: big.NewInt(1),
|
||||
Gas: params.TxGas,
|
||||
GasPrice: b.BaseFee(),
|
||||
}), types.HomesteadSigner{}, key)
|
||||
b.AddTx(tx)
|
||||
nonce++
|
||||
})
|
||||
defer backend.teardown()
|
||||
|
||||
api := NewAPI(backend)
|
||||
block := backend.chain.GetBlockByNumber(1)
|
||||
if block == nil {
|
||||
t.Fatal("failed to retrieve test block")
|
||||
}
|
||||
parent := backend.chain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
t.Fatal("failed to retrieve parent block")
|
||||
}
|
||||
statedb, release, err := backend.StateAtBlock(context.Background(), parent, defaultTraceReexec, nil, true, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create parent state: %v", err)
|
||||
}
|
||||
defer release()
|
||||
|
||||
results, err := api.traceBlockParallel(context.Background(), block, statedb, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("traceBlockParallel failed: %v", err)
|
||||
}
|
||||
if len(results) != 1 {
|
||||
t.Fatalf("unexpected result length: have %d want 1", len(results))
|
||||
}
|
||||
if results[0].Error != "" {
|
||||
t.Fatalf("unexpected trace error: %v", results[0].Error)
|
||||
}
|
||||
var historyKey common.Hash
|
||||
binary.BigEndian.PutUint64(historyKey[24:], (block.NumberU64()-1)%params.HistoryServeWindow)
|
||||
have := statedb.GetState(params.HistoryStorageAddress, historyKey)
|
||||
want := block.ParentHash()
|
||||
if have != want {
|
||||
t.Fatalf("unexpected parent hash in storage: have %v want %v", have, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTraceCall(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue