mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-20 11:46:44 +00:00
internal/ethapi: make difficulty override a no-op post-merge in eth_simulateV1
A difficulty block override is documented as a no-op for post-merge blocks
(see override.BlockOverrides.Difficulty), but makeHeaders sets the simulated
header's difficulty to zero only before calling overrides.MakeHeader, which
then re-applies the override unconditionally.
The resulting non-zero difficulty makes the simulated header fail
beacon.IsPoSHeader, so block assembly (Finalize/AssembleBlock) is routed
through the legacy PoW engine even though the block carries post-merge fields.
On a live post-merge chain this panics, and the RPC returns
-32603 "method handler crashed" for any eth_simulateV1 call that overrides
difficulty (e.g. {"blockOverrides":{"difficulty":"0x1"}}). eth_call is
unaffected because it does not assemble a block.
Re-zero the difficulty after MakeHeader for post-merge blocks to honour the
documented no-op and keep the header on the PoS assembly path. Add a
regression test asserting the simulated post-merge block reports difficulty
0x0 when the override is supplied.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ab134b2101
commit
49fba66f63
2 changed files with 56 additions and 1 deletions
|
|
@ -2745,6 +2745,52 @@ func TestSimulateV1WithdrawalsByFork(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestSimulateV1DifficultyOverridePostMerge checks that a difficulty block
|
||||||
|
// override is a no-op for post-merge blocks. Applying it would set a non-zero
|
||||||
|
// difficulty on the simulated header, which fails IsPoSHeader and routes block
|
||||||
|
// assembly through the legacy (PoW) engine even though the block carries
|
||||||
|
// post-merge fields, leading to a panic.
|
||||||
|
func TestSimulateV1DifficultyOverridePostMerge(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
gspec := &core.Genesis{Config: params.MergedTestChainConfig, Alloc: types.GenesisAlloc{}}
|
||||||
|
backend := newTestBackend(t, 1, gspec, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) { b.SetPoS() })
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
stateDB, baseHeader, err := backend.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get state and header: %v", err)
|
||||||
|
}
|
||||||
|
sim := &simulator{
|
||||||
|
b: backend,
|
||||||
|
state: stateDB,
|
||||||
|
base: baseHeader,
|
||||||
|
chainConfig: backend.ChainConfig(),
|
||||||
|
budget: newGasBudget(0),
|
||||||
|
}
|
||||||
|
|
||||||
|
diff := (*hexutil.Big)(big.NewInt(0xCAFE0000))
|
||||||
|
results, err := sim.execute(ctx, []simBlock{{
|
||||||
|
BlockOverrides: &override.BlockOverrides{Difficulty: diff},
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("simulation execution failed: %v", err)
|
||||||
|
}
|
||||||
|
require.Len(t, results, 1)
|
||||||
|
|
||||||
|
enc, err := json.Marshal(results[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to marshal result: %v", err)
|
||||||
|
}
|
||||||
|
var raw map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(enc, &raw); err != nil {
|
||||||
|
t.Fatalf("failed to unmarshal result: %v", err)
|
||||||
|
}
|
||||||
|
if got := string(raw["difficulty"]); got != `"0x0"` {
|
||||||
|
t.Fatalf("difficulty override was not a no-op post-merge: got %s, want \"0x0\"\n%s", got, enc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSignTransaction(t *testing.T) {
|
func TestSignTransaction(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Initialize test accounts
|
// Initialize test accounts
|
||||||
|
|
|
||||||
|
|
@ -562,8 +562,9 @@ func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) {
|
||||||
}
|
}
|
||||||
// Set difficulty to zero if the given block is post-merge. Without this, all post-merge hardforks would remain inactive.
|
// Set difficulty to zero if the given block is post-merge. Without this, all post-merge hardforks would remain inactive.
|
||||||
// For example, calling eth_simulateV1(..., blockParameter: 0x0) on hoodi network will cause all blocks to have a difficulty of 1 and be treated as pre-merge.
|
// For example, calling eth_simulateV1(..., blockParameter: 0x0) on hoodi network will cause all blocks to have a difficulty of 1 and be treated as pre-merge.
|
||||||
|
isPostMerge := sim.chainConfig.IsPostMerge(number.Uint64(), timestamp)
|
||||||
difficulty := header.Difficulty
|
difficulty := header.Difficulty
|
||||||
if sim.chainConfig.IsPostMerge(number.Uint64(), timestamp) {
|
if isPostMerge {
|
||||||
difficulty = big.NewInt(0)
|
difficulty = big.NewInt(0)
|
||||||
}
|
}
|
||||||
header = overrides.MakeHeader(&types.Header{
|
header = overrides.MakeHeader(&types.Header{
|
||||||
|
|
@ -576,6 +577,14 @@ func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) {
|
||||||
WithdrawalsHash: withdrawalsHash,
|
WithdrawalsHash: withdrawalsHash,
|
||||||
ParentBeaconRoot: parentBeaconRoot,
|
ParentBeaconRoot: parentBeaconRoot,
|
||||||
})
|
})
|
||||||
|
// A difficulty override is documented as a no-op for post-merge blocks.
|
||||||
|
// MakeHeader applies it unconditionally though, so re-zero it here: a
|
||||||
|
// non-zero difficulty makes the simulated header fail IsPoSHeader, which
|
||||||
|
// routes block assembly through the legacy (PoW) engine even though the
|
||||||
|
// block carries post-merge fields, leading to a panic.
|
||||||
|
if isPostMerge {
|
||||||
|
header.Difficulty = big.NewInt(0)
|
||||||
|
}
|
||||||
res[bi] = header
|
res[bi] = header
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue