mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-14 03:56:36 +00:00
internal/ethapi: support for beacon root and withdrawals in simulate api (#31304)
Adds block override fields for beacon block root and withdrawals to the eth_simulateV1. Addresses https://github.com/ethereum/go-ethereum/issues/31264
This commit is contained in:
parent
8e3b94da1e
commit
71e9c9b8a7
6 changed files with 106 additions and 17 deletions
|
|
@ -223,7 +223,9 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio
|
||||||
dirtyState = opts.State.Copy()
|
dirtyState = opts.State.Copy()
|
||||||
)
|
)
|
||||||
if opts.BlockOverrides != nil {
|
if opts.BlockOverrides != nil {
|
||||||
opts.BlockOverrides.Apply(&evmContext)
|
if err := opts.BlockOverrides.Apply(&evmContext); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Lower the basefee to 0 to avoid breaking EVM
|
// Lower the basefee to 0 to avoid breaking EVM
|
||||||
// invariants (basefee < feecap).
|
// invariants (basefee < feecap).
|
||||||
|
|
|
||||||
|
|
@ -950,7 +950,9 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
||||||
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||||
// Apply the customization rules if required.
|
// Apply the customization rules if required.
|
||||||
if config != nil {
|
if config != nil {
|
||||||
config.BlockOverrides.Apply(&vmctx)
|
if overrideErr := config.BlockOverrides.Apply(&vmctx); overrideErr != nil {
|
||||||
|
return nil, overrideErr
|
||||||
|
}
|
||||||
rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber, vmctx.Random != nil, vmctx.Time)
|
rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber, vmctx.Random != nil, vmctx.Time)
|
||||||
|
|
||||||
precompiles = vm.ActivePrecompiledContracts(rules)
|
precompiles = vm.ActivePrecompiledContracts(rules)
|
||||||
|
|
|
||||||
|
|
@ -660,7 +660,9 @@ func (context *ChainContext) Config() *params.ChainConfig {
|
||||||
func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
|
func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.StateDB, header *types.Header, overrides *override.StateOverride, blockOverrides *override.BlockOverrides, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
|
||||||
blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
|
blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
|
||||||
if blockOverrides != nil {
|
if blockOverrides != nil {
|
||||||
blockOverrides.Apply(&blockCtx)
|
if err := blockOverrides.Apply(&blockCtx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rules := b.ChainConfig().Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time)
|
rules := b.ChainConfig().Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time)
|
||||||
precompiles := vm.ActivePrecompiledContracts(rules)
|
precompiles := vm.ActivePrecompiledContracts(rules)
|
||||||
|
|
|
||||||
|
|
@ -1134,6 +1134,24 @@ func TestCall(t *testing.T) {
|
||||||
},
|
},
|
||||||
want: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
want: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "unsupported block override beaconRoot",
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{},
|
||||||
|
blockOverrides: override.BlockOverrides{
|
||||||
|
BeaconRoot: &common.Hash{0, 1, 2},
|
||||||
|
},
|
||||||
|
expectErr: errors.New(`block override "beaconRoot" is not supported for this RPC method`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unsupported block override withdrawals",
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{},
|
||||||
|
blockOverrides: override.BlockOverrides{
|
||||||
|
Withdrawals: &types.Withdrawals{},
|
||||||
|
},
|
||||||
|
expectErr: errors.New(`block override "withdrawals" is not supported for this RPC method`),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range testSuite {
|
for _, tc := range testSuite {
|
||||||
result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides)
|
result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package override
|
package override
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
|
@ -128,12 +129,20 @@ type BlockOverrides struct {
|
||||||
PrevRandao *common.Hash
|
PrevRandao *common.Hash
|
||||||
BaseFeePerGas *hexutil.Big
|
BaseFeePerGas *hexutil.Big
|
||||||
BlobBaseFee *hexutil.Big
|
BlobBaseFee *hexutil.Big
|
||||||
|
BeaconRoot *common.Hash
|
||||||
|
Withdrawals *types.Withdrawals
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply overrides the given header fields into the given block context.
|
// Apply overrides the given header fields into the given block context.
|
||||||
func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
|
func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) error {
|
||||||
if o == nil {
|
if o == nil {
|
||||||
return
|
return nil
|
||||||
|
}
|
||||||
|
if o.BeaconRoot != nil {
|
||||||
|
return errors.New(`block override "beaconRoot" is not supported for this RPC method`)
|
||||||
|
}
|
||||||
|
if o.Withdrawals != nil {
|
||||||
|
return errors.New(`block override "withdrawals" is not supported for this RPC method`)
|
||||||
}
|
}
|
||||||
if o.Number != nil {
|
if o.Number != nil {
|
||||||
blockCtx.BlockNumber = o.Number.ToInt()
|
blockCtx.BlockNumber = o.Number.ToInt()
|
||||||
|
|
@ -159,6 +168,7 @@ func (o *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
|
||||||
if o.BlobBaseFee != nil {
|
if o.BlobBaseFee != nil {
|
||||||
blockCtx.BlobBaseFee = o.BlobBaseFee.ToInt()
|
blockCtx.BlobBaseFee = o.BlobBaseFee.ToInt()
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeHeader returns a new header object with the overridden
|
// MakeHeader returns a new header object with the overridden
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi/override"
|
"github.com/ethereum/go-ethereum/internal/ethapi/override"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -95,6 +94,47 @@ type simOpts struct {
|
||||||
ReturnFullTransactions bool
|
ReturnFullTransactions bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simChainHeadReader implements ChainHeaderReader which is needed as input for FinalizeAndAssemble.
|
||||||
|
type simChainHeadReader struct {
|
||||||
|
context.Context
|
||||||
|
Backend
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *simChainHeadReader) Config() *params.ChainConfig {
|
||||||
|
return m.Backend.ChainConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *simChainHeadReader) CurrentHeader() *types.Header {
|
||||||
|
return m.Backend.CurrentHeader()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *simChainHeadReader) GetHeader(hash common.Hash, number uint64) *types.Header {
|
||||||
|
header, err := m.Backend.HeaderByNumber(m.Context, rpc.BlockNumber(number))
|
||||||
|
if err != nil || header == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if header.Hash() != hash {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *simChainHeadReader) GetHeaderByNumber(number uint64) *types.Header {
|
||||||
|
header, err := m.Backend.HeaderByNumber(m.Context, rpc.BlockNumber(number))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *simChainHeadReader) GetHeaderByHash(hash common.Hash) *types.Header {
|
||||||
|
header, err := m.Backend.HeaderByHash(m.Context, hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
// simulator is a stateful object that simulates a series of blocks.
|
// simulator is a stateful object that simulates a series of blocks.
|
||||||
// it is not safe for concurrent use.
|
// it is not safe for concurrent use.
|
||||||
type simulator struct {
|
type simulator struct {
|
||||||
|
|
@ -209,6 +249,9 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
if sim.chainConfig.IsPrague(header.Number, header.Time) || sim.chainConfig.IsVerkle(header.Number, header.Time) {
|
if sim.chainConfig.IsPrague(header.Number, header.Time) || sim.chainConfig.IsVerkle(header.Number, header.Time) {
|
||||||
core.ProcessParentBlockHash(header.ParentHash, evm)
|
core.ProcessParentBlockHash(header.ParentHash, evm)
|
||||||
}
|
}
|
||||||
|
if header.ParentBeaconRoot != nil {
|
||||||
|
core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, evm)
|
||||||
|
}
|
||||||
var allLogs []*types.Log
|
var allLogs []*types.Log
|
||||||
for i, call := range block.Calls {
|
for i, call := range block.Calls {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
|
|
@ -258,6 +301,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
}
|
}
|
||||||
callResults[i] = callRes
|
callResults[i] = callRes
|
||||||
}
|
}
|
||||||
|
header.GasUsed = gasUsed
|
||||||
|
if sim.chainConfig.IsCancun(header.Number, header.Time) {
|
||||||
|
header.BlobGasUsed = &blobGasUsed
|
||||||
|
}
|
||||||
var requests [][]byte
|
var requests [][]byte
|
||||||
// Process EIP-7685 requests
|
// Process EIP-7685 requests
|
||||||
if sim.chainConfig.IsPrague(header.Number, header.Time) {
|
if sim.chainConfig.IsPrague(header.Number, header.Time) {
|
||||||
|
|
@ -271,20 +318,16 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
// EIP-7251
|
// EIP-7251
|
||||||
core.ProcessConsolidationQueue(&requests, evm)
|
core.ProcessConsolidationQueue(&requests, evm)
|
||||||
}
|
}
|
||||||
header.Root = sim.state.IntermediateRoot(true)
|
|
||||||
header.GasUsed = gasUsed
|
|
||||||
if sim.chainConfig.IsCancun(header.Number, header.Time) {
|
|
||||||
header.BlobGasUsed = &blobGasUsed
|
|
||||||
}
|
|
||||||
var withdrawals types.Withdrawals
|
|
||||||
if sim.chainConfig.IsShanghai(header.Number, header.Time) {
|
|
||||||
withdrawals = make([]*types.Withdrawal, 0)
|
|
||||||
}
|
|
||||||
if requests != nil {
|
if requests != nil {
|
||||||
reqHash := types.CalcRequestsHash(requests)
|
reqHash := types.CalcRequestsHash(requests)
|
||||||
header.RequestsHash = &reqHash
|
header.RequestsHash = &reqHash
|
||||||
}
|
}
|
||||||
b := types.NewBlock(header, &types.Body{Transactions: txes, Withdrawals: withdrawals}, receipts, trie.NewStackTrie(nil))
|
blockBody := &types.Body{Transactions: txes, Withdrawals: *block.BlockOverrides.Withdrawals}
|
||||||
|
chainHeadReader := &simChainHeadReader{ctx, sim.b}
|
||||||
|
b, err := sim.b.Engine().FinalizeAndAssemble(chainHeadReader, header, sim.state, blockBody, receipts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
repairLogs(callResults, b.Hash())
|
repairLogs(callResults, b.Hash())
|
||||||
return b, callResults, nil
|
return b, callResults, nil
|
||||||
}
|
}
|
||||||
|
|
@ -346,6 +389,9 @@ func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) {
|
||||||
n := new(big.Int).Add(prevNumber, big.NewInt(1))
|
n := new(big.Int).Add(prevNumber, big.NewInt(1))
|
||||||
block.BlockOverrides.Number = (*hexutil.Big)(n)
|
block.BlockOverrides.Number = (*hexutil.Big)(n)
|
||||||
}
|
}
|
||||||
|
if block.BlockOverrides.Withdrawals == nil {
|
||||||
|
block.BlockOverrides.Withdrawals = &types.Withdrawals{}
|
||||||
|
}
|
||||||
diff := new(big.Int).Sub(block.BlockOverrides.Number.ToInt(), prevNumber)
|
diff := new(big.Int).Sub(block.BlockOverrides.Number.ToInt(), prevNumber)
|
||||||
if diff.Cmp(common.Big0) <= 0 {
|
if diff.Cmp(common.Big0) <= 0 {
|
||||||
return nil, &invalidBlockNumberError{fmt.Sprintf("block numbers must be in order: %d <= %d", block.BlockOverrides.Number.ToInt().Uint64(), prevNumber)}
|
return nil, &invalidBlockNumberError{fmt.Sprintf("block numbers must be in order: %d <= %d", block.BlockOverrides.Number.ToInt().Uint64(), prevNumber)}
|
||||||
|
|
@ -360,7 +406,13 @@ func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) {
|
||||||
for i := uint64(0); i < gap.Uint64(); i++ {
|
for i := uint64(0); i < gap.Uint64(); i++ {
|
||||||
n := new(big.Int).Add(prevNumber, big.NewInt(int64(i+1)))
|
n := new(big.Int).Add(prevNumber, big.NewInt(int64(i+1)))
|
||||||
t := prevTimestamp + timestampIncrement
|
t := prevTimestamp + timestampIncrement
|
||||||
b := simBlock{BlockOverrides: &override.BlockOverrides{Number: (*hexutil.Big)(n), Time: (*hexutil.Uint64)(&t)}}
|
b := simBlock{
|
||||||
|
BlockOverrides: &override.BlockOverrides{
|
||||||
|
Number: (*hexutil.Big)(n),
|
||||||
|
Time: (*hexutil.Uint64)(&t),
|
||||||
|
Withdrawals: &types.Withdrawals{},
|
||||||
|
},
|
||||||
|
}
|
||||||
prevTimestamp = t
|
prevTimestamp = t
|
||||||
res = append(res, b)
|
res = append(res, b)
|
||||||
}
|
}
|
||||||
|
|
@ -405,6 +457,9 @@ func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) {
|
||||||
var parentBeaconRoot *common.Hash
|
var parentBeaconRoot *common.Hash
|
||||||
if sim.chainConfig.IsCancun(overrides.Number.ToInt(), (uint64)(*overrides.Time)) {
|
if sim.chainConfig.IsCancun(overrides.Number.ToInt(), (uint64)(*overrides.Time)) {
|
||||||
parentBeaconRoot = &common.Hash{}
|
parentBeaconRoot = &common.Hash{}
|
||||||
|
if overrides.BeaconRoot != nil {
|
||||||
|
parentBeaconRoot = overrides.BeaconRoot
|
||||||
|
}
|
||||||
}
|
}
|
||||||
header = overrides.MakeHeader(&types.Header{
|
header = overrides.MakeHeader(&types.Header{
|
||||||
UncleHash: types.EmptyUncleHash,
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue