Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 113 additions & 26 deletions rpc/ethapi/block_overrides.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ package ethapi

import (
"errors"
"maps"

"github.com/holiman/uint256"

"github.com/erigontech/erigon/common"
"github.com/erigontech/erigon/common/hexutil"
Expand All @@ -26,55 +29,139 @@ import (
"github.com/erigontech/erigon/execution/vm/evmtypes"
)

// BlockHashOverrides maps block numbers to their hash overrides,
// used to intercept BLOCKHASH opcode calls during simulation.
type BlockHashOverrides map[uint64]common.Hash

// BlockOverrides is the unified set of block-level fields that can be
// overridden during simulation or call execution (eth_call, eth_estimateGas,
// eth_simulateV1, eth_callMany).
type BlockOverrides struct {
Number *hexutil.Big `json:"number"`
PrevRanDao *common.Hash `json:"prevRandao"`
Time *hexutil.Uint64 `json:"time"`
GasLimit *hexutil.Uint64 `json:"gasLimit"`
FeeRecipient *common.Address `json:"feeRecipient"`
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas"`
BlobBaseFee *hexutil.Big `json:"blobBaseFee"`
Withdrawals []*types.Withdrawal `json:"withdrawals"`
Number *hexutil.Big `json:"number"`
Difficulty *hexutil.Big `json:"difficulty"`
Time *hexutil.Uint64 `json:"time"`
GasLimit *hexutil.Uint64 `json:"gasLimit"`
FeeRecipient *common.Address `json:"feeRecipient"`
PrevRandao *common.Hash `json:"prevRandao"`
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas"`
BlobBaseFee *hexutil.Big `json:"blobBaseFee"`
BeaconRoot *common.Hash `json:"beaconRoot"`
BlockHash *map[uint64]common.Hash `json:"blockHash"`
Withdrawals *types.Withdrawals `json:"withdrawals"`
}

// Override applies overrides to an EVM block context used in single-call
// methods (eth_call, eth_estimateGas). BeaconRoot and Withdrawals are
// rejected because they are not meaningful in a single-call context.
func (overrides *BlockOverrides) Override(context *evmtypes.BlockContext) error {

if overrides == nil {
return nil
}
if overrides.BeaconRoot != nil {
return errors.New(`block override "beaconRoot" is not supported for this RPC method`)
}
if overrides.Withdrawals != nil {
return errors.New(`block override "withdrawals" is not supported for this RPC method`)
}
if overrides.Number != nil {
context.BlockNumber = overrides.Number.Uint64()
}

if overrides.PrevRanDao != nil {
context.PrevRanDao = overrides.PrevRanDao
if overrides.Difficulty != nil {
context.Difficulty.SetFromBig(overrides.Difficulty.ToInt())
}
if overrides.PrevRandao != nil {
context.PrevRanDao = overrides.PrevRandao
}

if overrides.Time != nil {
context.Time = overrides.Time.Uint64()
context.Time = uint64(*overrides.Time)
}

if overrides.GasLimit != nil {
context.GasLimit = overrides.GasLimit.Uint64()
context.GasLimit = uint64(*overrides.GasLimit)
}

if overrides.FeeRecipient != nil {
context.Coinbase = accounts.InternAddress(*overrides.FeeRecipient)
}

if overrides.BaseFeePerGas != nil {
overflow := context.BaseFee.SetFromBig(overrides.BaseFeePerGas.ToInt())
if overflow {
if overflow := context.BaseFee.SetFromBig(overrides.BaseFeePerGas.ToInt()); overflow {
return errors.New("BlockOverrides.BaseFee uint256 overflow")
}
}

if overrides.BlobBaseFee != nil {
overflow := context.BlobBaseFee.SetFromBig(overrides.BlobBaseFee.ToInt())
if overflow {
if overflow := context.BlobBaseFee.SetFromBig(overrides.BlobBaseFee.ToInt()); overflow {
return errors.New("BlockOverrides.BlobBaseFee uint256 overflow")
}
}
return nil
}

if overrides.Withdrawals != nil {
return errors.New("BlockOverrides.Withdrawals not supported")
// OverrideHeader returns a modified copy of header with the overridden fields
// applied. Used by eth_simulateV1 to build block headers before execution.
// BeaconRoot and Withdrawals are handled separately by the caller at the
// block-assembly level.
func (overrides *BlockOverrides) OverrideHeader(header *types.Header) *types.Header {
if overrides == nil {
return header
}
h := types.CopyHeader(header)
if overrides.Number != nil {
h.Number.SetFromBig(overrides.Number.ToInt())
}
if overrides.Difficulty != nil {
h.Difficulty.SetFromBig(overrides.Difficulty.ToInt())
}
if overrides.Time != nil {
h.Time = uint64(*overrides.Time)
}
if overrides.GasLimit != nil {
h.GasLimit = uint64(*overrides.GasLimit)
}
if overrides.FeeRecipient != nil {
h.Coinbase = *overrides.FeeRecipient
}
if overrides.BaseFeePerGas != nil {
baseFee := new(uint256.Int)
baseFee.SetFromBig(overrides.BaseFeePerGas.ToInt())
h.BaseFee = baseFee
}
if overrides.PrevRandao != nil {
h.MixDigest = *overrides.PrevRandao
}
return h
}

// OverrideBlockContext applies overrides to an EVM block context used in
// simulation (eth_simulateV1, eth_callMany). Unlike Override, it does not
// reject BeaconRoot or Withdrawals — those are handled at the block-assembly
// level by the caller.
func (overrides *BlockOverrides) OverrideBlockContext(blockCtx *evmtypes.BlockContext, blockHashOverrides BlockHashOverrides) {
if overrides == nil {
return
}
if overrides.Number != nil {
blockCtx.BlockNumber = overrides.Number.Uint64()
}
if overrides.Difficulty != nil {
blockCtx.Difficulty.SetFromBig(overrides.Difficulty.ToInt())
}
if overrides.Time != nil {
blockCtx.Time = uint64(*overrides.Time)
}
if overrides.GasLimit != nil {
blockCtx.GasLimit = uint64(*overrides.GasLimit)
}
if overrides.FeeRecipient != nil {
blockCtx.Coinbase = accounts.InternAddress(*overrides.FeeRecipient)
}
if overrides.PrevRandao != nil {
blockCtx.PrevRanDao = overrides.PrevRandao
}
if overrides.BaseFeePerGas != nil {
blockCtx.BaseFee.SetFromBig(overrides.BaseFeePerGas.ToInt())
}
if overrides.BlobBaseFee != nil {
blockCtx.BlobBaseFee.SetFromBig(overrides.BlobBaseFee.ToInt())
}
if overrides.BlockHash != nil {
maps.Copy(blockHashOverrides, *overrides.BlockHash)
}
return nil
}
Loading
Loading