Skip to content

Commit 3d28958

Browse files
lupin012claude
andcommitted
rpc: unify BlockOverrides into a single type with three override methods
Two separate BlockOverrides structs existed in the codebase (ethapi.BlockOverrides and transactions.BlockOverrides) with overlapping but divergent fields, different field types, and different JSON tags. This caused inconsistencies across eth_call, eth_estimateGas, eth_simulateV1, and eth_callMany. Changes: - Merge into a single ethapi.BlockOverrides with all 11 fields: Number, Difficulty, Time, GasLimit, FeeRecipient, PrevRandao, BaseFeePerGas, BlobBaseFee, BeaconRoot, BlockHash, Withdrawals - Add ethapi.BlockHashOverrides (was only in transactions package) - Three methods with clearly separated responsibilities: - Override(*BlockContext) error — for eth_call/eth_estimateGas; rejects BeaconRoot and Withdrawals (aligned with Geth); now propagates error (was silently swallowed before) - OverrideHeader(*Header) *Header — for eth_simulateV1 block assembly - OverrideBlockContext(*BlockContext, BlockHashOverrides) — for eth_simulateV1/eth_callMany; applies all fields including BlobBaseFee and BlockHash - Remove duplicate struct from transactions/call.go - Replace manual field-by-field application in eth_callMany with OverrideBlockContext call - Add complete unit test suite: 20 tests covering all three methods, nil receivers, field isolation, overflow checks, rejection of unsupported fields, and BlockHash map merging Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f3c7817 commit 3d28958

File tree

6 files changed

+436
-168
lines changed

6 files changed

+436
-168
lines changed

rpc/ethapi/block_overrides.go

Lines changed: 113 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ package ethapi
1818

1919
import (
2020
"errors"
21+
"maps"
22+
23+
"github.com/holiman/uint256"
2124

2225
"github.com/erigontech/erigon/common"
2326
"github.com/erigontech/erigon/common/hexutil"
@@ -26,55 +29,139 @@ import (
2629
"github.com/erigontech/erigon/execution/vm/evmtypes"
2730
)
2831

32+
// BlockHashOverrides maps block numbers to their hash overrides,
33+
// used to intercept BLOCKHASH opcode calls during simulation.
34+
type BlockHashOverrides map[uint64]common.Hash
35+
36+
// BlockOverrides is the unified set of block-level fields that can be
37+
// overridden during simulation or call execution (eth_call, eth_estimateGas,
38+
// eth_simulateV1, eth_callMany).
2939
type BlockOverrides struct {
30-
Number *hexutil.Big `json:"number"`
31-
PrevRanDao *common.Hash `json:"prevRandao"`
32-
Time *hexutil.Uint64 `json:"time"`
33-
GasLimit *hexutil.Uint64 `json:"gasLimit"`
34-
FeeRecipient *common.Address `json:"feeRecipient"`
35-
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas"`
36-
BlobBaseFee *hexutil.Big `json:"blobBaseFee"`
37-
Withdrawals []*types.Withdrawal `json:"withdrawals"`
40+
Number *hexutil.Big `json:"number"`
41+
Difficulty *hexutil.Big `json:"difficulty"`
42+
Time *hexutil.Uint64 `json:"time"`
43+
GasLimit *hexutil.Uint64 `json:"gasLimit"`
44+
FeeRecipient *common.Address `json:"feeRecipient"`
45+
PrevRandao *common.Hash `json:"prevRandao"`
46+
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas"`
47+
BlobBaseFee *hexutil.Big `json:"blobBaseFee"`
48+
BeaconRoot *common.Hash `json:"beaconRoot"`
49+
BlockHash *map[uint64]common.Hash `json:"blockHash"`
50+
Withdrawals *types.Withdrawals `json:"withdrawals"`
3851
}
3952

53+
// Override applies overrides to an EVM block context used in single-call
54+
// methods (eth_call, eth_estimateGas). BeaconRoot and Withdrawals are
55+
// rejected because they are not meaningful in a single-call context.
4056
func (overrides *BlockOverrides) Override(context *evmtypes.BlockContext) error {
41-
57+
if overrides == nil {
58+
return nil
59+
}
60+
if overrides.BeaconRoot != nil {
61+
return errors.New(`block override "beaconRoot" is not supported for this RPC method`)
62+
}
63+
if overrides.Withdrawals != nil {
64+
return errors.New(`block override "withdrawals" is not supported for this RPC method`)
65+
}
4266
if overrides.Number != nil {
4367
context.BlockNumber = overrides.Number.Uint64()
4468
}
45-
46-
if overrides.PrevRanDao != nil {
47-
context.PrevRanDao = overrides.PrevRanDao
69+
if overrides.Difficulty != nil {
70+
context.Difficulty.SetFromBig(overrides.Difficulty.ToInt())
71+
}
72+
if overrides.PrevRandao != nil {
73+
context.PrevRanDao = overrides.PrevRandao
4874
}
49-
5075
if overrides.Time != nil {
51-
context.Time = overrides.Time.Uint64()
76+
context.Time = uint64(*overrides.Time)
5277
}
53-
5478
if overrides.GasLimit != nil {
55-
context.GasLimit = overrides.GasLimit.Uint64()
79+
context.GasLimit = uint64(*overrides.GasLimit)
5680
}
57-
5881
if overrides.FeeRecipient != nil {
5982
context.Coinbase = accounts.InternAddress(*overrides.FeeRecipient)
6083
}
61-
6284
if overrides.BaseFeePerGas != nil {
63-
overflow := context.BaseFee.SetFromBig(overrides.BaseFeePerGas.ToInt())
64-
if overflow {
85+
if overflow := context.BaseFee.SetFromBig(overrides.BaseFeePerGas.ToInt()); overflow {
6586
return errors.New("BlockOverrides.BaseFee uint256 overflow")
6687
}
6788
}
68-
6989
if overrides.BlobBaseFee != nil {
70-
overflow := context.BlobBaseFee.SetFromBig(overrides.BlobBaseFee.ToInt())
71-
if overflow {
90+
if overflow := context.BlobBaseFee.SetFromBig(overrides.BlobBaseFee.ToInt()); overflow {
7291
return errors.New("BlockOverrides.BlobBaseFee uint256 overflow")
7392
}
7493
}
94+
return nil
95+
}
7596

76-
if overrides.Withdrawals != nil {
77-
return errors.New("BlockOverrides.Withdrawals not supported")
97+
// OverrideHeader returns a modified copy of header with the overridden fields
98+
// applied. Used by eth_simulateV1 to build block headers before execution.
99+
// BeaconRoot and Withdrawals are handled separately by the caller at the
100+
// block-assembly level.
101+
func (overrides *BlockOverrides) OverrideHeader(header *types.Header) *types.Header {
102+
if overrides == nil {
103+
return header
104+
}
105+
h := types.CopyHeader(header)
106+
if overrides.Number != nil {
107+
h.Number.SetFromBig(overrides.Number.ToInt())
108+
}
109+
if overrides.Difficulty != nil {
110+
h.Difficulty.SetFromBig(overrides.Difficulty.ToInt())
111+
}
112+
if overrides.Time != nil {
113+
h.Time = uint64(*overrides.Time)
114+
}
115+
if overrides.GasLimit != nil {
116+
h.GasLimit = uint64(*overrides.GasLimit)
117+
}
118+
if overrides.FeeRecipient != nil {
119+
h.Coinbase = *overrides.FeeRecipient
120+
}
121+
if overrides.BaseFeePerGas != nil {
122+
baseFee := new(uint256.Int)
123+
baseFee.SetFromBig(overrides.BaseFeePerGas.ToInt())
124+
h.BaseFee = baseFee
125+
}
126+
if overrides.PrevRandao != nil {
127+
h.MixDigest = *overrides.PrevRandao
128+
}
129+
return h
130+
}
131+
132+
// OverrideBlockContext applies overrides to an EVM block context used in
133+
// simulation (eth_simulateV1, eth_callMany). Unlike Override, it does not
134+
// reject BeaconRoot or Withdrawals — those are handled at the block-assembly
135+
// level by the caller.
136+
func (overrides *BlockOverrides) OverrideBlockContext(blockCtx *evmtypes.BlockContext, blockHashOverrides BlockHashOverrides) {
137+
if overrides == nil {
138+
return
139+
}
140+
if overrides.Number != nil {
141+
blockCtx.BlockNumber = overrides.Number.Uint64()
142+
}
143+
if overrides.Difficulty != nil {
144+
blockCtx.Difficulty.SetFromBig(overrides.Difficulty.ToInt())
145+
}
146+
if overrides.Time != nil {
147+
blockCtx.Time = uint64(*overrides.Time)
148+
}
149+
if overrides.GasLimit != nil {
150+
blockCtx.GasLimit = uint64(*overrides.GasLimit)
151+
}
152+
if overrides.FeeRecipient != nil {
153+
blockCtx.Coinbase = accounts.InternAddress(*overrides.FeeRecipient)
154+
}
155+
if overrides.PrevRandao != nil {
156+
blockCtx.PrevRanDao = overrides.PrevRandao
157+
}
158+
if overrides.BaseFeePerGas != nil {
159+
blockCtx.BaseFee.SetFromBig(overrides.BaseFeePerGas.ToInt())
160+
}
161+
if overrides.BlobBaseFee != nil {
162+
blockCtx.BlobBaseFee.SetFromBig(overrides.BlobBaseFee.ToInt())
163+
}
164+
if overrides.BlockHash != nil {
165+
maps.Copy(blockHashOverrides, *overrides.BlockHash)
78166
}
79-
return nil
80167
}

0 commit comments

Comments
 (0)