From d1ce690e763f9ae4f0aa0cf247faf58c7b997947 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 10 Oct 2023 16:34:48 +0200 Subject: [PATCH] move multicall logic to struct --- internal/ethapi/api.go | 308 +------------------------------ internal/ethapi/multicall.go | 348 +++++++++++++++++++++++++++++++++++ 2 files changed, 350 insertions(+), 306 deletions(-) create mode 100644 internal/ethapi/multicall.go diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 5fe169cb4d..3a91dde4de 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -19,7 +19,6 @@ package ethapi import ( "context" "encoding/hex" - "encoding/json" "errors" "fmt" "math/big" @@ -1222,62 +1221,6 @@ func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrO return result.Return(), result.Err } -// CallBatch is a batch of calls to be simulated sequentially. -type CallBatch struct { - BlockOverrides *BlockOverrides - StateOverrides *StateOverride - Calls []TransactionArgs -} - -type blockResult struct { - Number hexutil.Uint64 `json:"number"` - Hash common.Hash `json:"hash"` - Time hexutil.Uint64 `json:"timestamp"` - GasLimit hexutil.Uint64 `json:"gasLimit"` - GasUsed hexutil.Uint64 `json:"gasUsed"` - FeeRecipient common.Address `json:"feeRecipient"` - BaseFee *hexutil.Big `json:"baseFeePerGas"` - PrevRandao common.Hash `json:"prevRandao"` - Calls []callResult `json:"calls"` -} - -func mcBlockResultFromHeader(header *types.Header, callResults []callResult) blockResult { - return blockResult{ - Number: hexutil.Uint64(header.Number.Uint64()), - Hash: header.Hash(), - Time: hexutil.Uint64(header.Time), - GasLimit: hexutil.Uint64(header.GasLimit), - GasUsed: hexutil.Uint64(header.GasUsed), - FeeRecipient: header.Coinbase, - BaseFee: (*hexutil.Big)(header.BaseFee), - PrevRandao: header.MixDigest, - Calls: callResults, - } -} - -type callResult struct { - ReturnValue hexutil.Bytes `json:"returnData"` - Logs []*types.Log `json:"logs"` - GasUsed hexutil.Uint64 `json:"gasUsed"` - Status hexutil.Uint64 `json:"status"` - Error *callError `json:"error,omitempty"` -} - -func (r *callResult) MarshalJSON() ([]byte, error) { - type callResultAlias callResult - // Marshal logs to be an empty array instead of nil when empty - if r.Logs == nil { - r.Logs = []*types.Log{} - } - return json.Marshal((*callResultAlias)(r)) -} - -type multicallOpts struct { - BlockStateCalls []CallBatch - TraceTransfers bool - Validation bool -} - // MulticallV1 executes series of transactions on top of a base state. // The transactions are packed into blocks. For each block, block header // fields can be overridden. The state can also be overridden prior to @@ -1290,255 +1233,8 @@ func (s *BlockChainAPI) MulticallV1(ctx context.Context, opts multicallOpts, blo n := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) blockNrOrHash = &n } - state, base, err := s.b.StateAndHeaderByNumberOrHash(ctx, *blockNrOrHash) - if state == nil || err != nil { - return nil, err - } - // Setup context so it may be cancelled before the calls completed - // or, in case of unmetered gas, setup a context with a timeout. - var ( - cancel context.CancelFunc - timeout = s.b.RPCEVMTimeout() - blocks = opts.BlockStateCalls - ) - if timeout > 0 { - ctx, cancel = context.WithTimeout(ctx, timeout) - } else { - ctx, cancel = context.WithCancel(ctx) - } - // Make sure the context is cancelled when the call has completed - // this makes sure resources are cleaned up. - defer cancel() - headers, err := makeHeaders(ctx, s.b.ChainConfig(), blocks, base) - if err != nil { - return nil, err - } - var ( - results = make([]blockResult, len(blocks)) - // Each tx and all the series of txes shouldn't consume more gas than cap - globalGasCap = s.b.RPCGasCap() - gp = new(core.GasPool).AddGas(globalGasCap) - header = base - blockContext = core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil) - rules = s.b.ChainConfig().Rules(blockContext.BlockNumber, blockContext.Random != nil, blockContext.Time) - precompiles = vm.ActivePrecompiledContracts(rules).Copy() - numHashes = headers[len(headers)-1].Number.Uint64() - base.Number.Uint64() + 256 - // Cache for the block hashes. - hashes = make([]common.Hash, numHashes) - ) - for bi, block := range blocks { - header = headers[bi] - blockContext = core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil) - // Respond to BLOCKHASH requests. - blockContext.GetHash = func(n uint64) common.Hash { - var ( - h common.Hash - err error - ) - h, hashes, err = getHash(ctx, n, base, headers, s.b, hashes) - if err != nil { - log.Warn(err.Error()) - return common.Hash{} - } - return h - } - // State overrides are applied prior to execution of a block - if err := block.StateOverrides.Apply(state, precompiles); err != nil { - return nil, err - } - var ( - gasUsed uint64 - txes = make([]*types.Transaction, len(block.Calls)) - callResults = make([]callResult, len(block.Calls)) - ) - for i, call := range block.Calls { - if call.Nonce == nil { - nonce := state.GetNonce(call.from()) - call.Nonce = (*hexutil.Uint64)(&nonce) - } - // Let the call run wild unless explicitly specified. - if call.Gas == nil { - remaining := blockContext.GasLimit - gasUsed - call.Gas = (*hexutil.Uint64)(&remaining) - } - if call.GasPrice == nil && call.MaxFeePerGas == nil && call.MaxPriorityFeePerGas == nil { - call.MaxFeePerGas = (*hexutil.Big)(big.NewInt(0)) - call.MaxPriorityFeePerGas = (*hexutil.Big)(big.NewInt(0)) - } - // TODO: check chainID and against current header for london fees - if err := call.validateAll(); err != nil { - return nil, err - } - tx := call.ToTransaction(true) - txes[i] = tx - // TODO: repair log block hashes post execution. - vmConfig := &vm.Config{ - NoBaseFee: true, - // Block hash will be repaired after execution. - Tracer: newTracer(opts.TraceTransfers, blockContext.BlockNumber.Uint64(), common.Hash{}, tx.Hash(), uint(i)), - } - result, err := applyMessage(ctx, s.b, call, state, header, timeout, gp, &blockContext, vmConfig, precompiles, opts.Validation) - if err != nil { - callErr := callErrorFromError(err) - callResults[i] = callResult{Error: callErr, Status: hexutil.Uint64(types.ReceiptStatusFailed)} - continue - } - // If the result contains a revert reason, try to unpack it. - if len(result.Revert()) > 0 { - result.Err = newRevertError(result) - } - logs := vmConfig.Tracer.(*tracer).Logs() - callRes := callResult{ReturnValue: result.Return(), Logs: logs, GasUsed: hexutil.Uint64(result.UsedGas)} - if result.Failed() { - callRes.Status = hexutil.Uint64(types.ReceiptStatusFailed) - if errors.Is(result.Err, vm.ErrExecutionReverted) { - callRes.Error = &callError{Message: result.Err.Error(), Code: -32000} - } else { - callRes.Error = &callError{Message: result.Err.Error(), Code: -32015} - } - } else { - callRes.Status = hexutil.Uint64(types.ReceiptStatusSuccessful) - } - callResults[i] = callRes - gasUsed += result.UsedGas - state.Finalise(true) - } - var ( - parentHash common.Hash - err error - ) - parentHash, hashes, err = getHash(ctx, header.Number.Uint64()-1, base, headers, s.b, hashes) - if err != nil { - return nil, err - } - header.ParentHash = parentHash - header.Root = state.IntermediateRoot(true) - header.GasUsed = gasUsed - header.TxHash = types.DeriveSha(types.Transactions(txes), trie.NewStackTrie(nil)) - results[bi] = mcBlockResultFromHeader(header, callResults) - repairLogs(results, header.Hash()) - } - return results, nil -} - -// getHash returns the hash for the block of the given number. Block can be -// part of the canonical chain, a simulated block or a phantom block. -// Note getHash assumes `n` is smaller than the last already simulated block -// and smaller than the last block to be simulated. -func getHash(ctx context.Context, n uint64, base *types.Header, headers []*types.Header, backend Backend, hashes []common.Hash) (common.Hash, []common.Hash, error) { - // getIndex returns the index of the hash in the hashes cache. - // The cache potentially includes 255 blocks prior to the base. - getIndex := func(n uint64) int { - first := base.Number.Uint64() - 255 - return int(n - first) - } - index := getIndex(n) - if h := hashes[index]; h != (common.Hash{}) { - return h, hashes, nil - } - h, err := computeHash(ctx, n, base, headers, backend, hashes) - if err != nil { - return common.Hash{}, hashes, err - } - if h != (common.Hash{}) { - hashes[index] = h - } - return h, hashes, nil -} - -func computeHash(ctx context.Context, n uint64, base *types.Header, headers []*types.Header, backend Backend, hashes []common.Hash) (common.Hash, error) { - if n == base.Number.Uint64() { - return base.Hash(), nil - } else if n < base.Number.Uint64() { - h, err := backend.HeaderByNumber(ctx, rpc.BlockNumber(n)) - if err != nil { - return common.Hash{}, fmt.Errorf("failed to load block hash for number %d. Err: %v\n", n, err) - } - return h.Hash(), nil - } - h := base - for i, _ := range headers { - tmp := headers[i] - // BLOCKHASH will only allow numbers prior to current block - // so no need to check that condition. - if tmp.Number.Uint64() == n { - hash := tmp.Hash() - return hash, nil - } else if tmp.Number.Uint64() > n { - // Phantom block. - lastNonPhantomHash, _, err := getHash(ctx, h.Number.Uint64(), base, headers, backend, hashes) - if err != nil { - return common.Hash{}, err - } - // keccak(rlp(lastNonPhantomBlockHash, blockNumber)) - hashData, err := rlp.EncodeToBytes([][]byte{lastNonPhantomHash.Bytes(), big.NewInt(int64(n)).Bytes()}) - if err != nil { - return common.Hash{}, err - } - return crypto.Keccak256Hash(hashData), nil - } - h = tmp - } - return common.Hash{}, errors.New("requested block is in future") -} - -// repairLogs updates the block hash in the logs present in a multicall -// result object. This is needed as during execution when logs are collected -// the block hash is not known. -func repairLogs(results []blockResult, blockHash common.Hash) { - for i := range results { - for j := range results[i].Calls { - for k := range results[i].Calls[j].Logs { - results[i].Calls[j].Logs[k].BlockHash = blockHash - } - } - } -} -func makeHeaders(ctx context.Context, config *params.ChainConfig, blocks []CallBatch, base *types.Header) ([]*types.Header, error) { - res := make([]*types.Header, len(blocks)) - var ( - prevNumber = base.Number.Uint64() - prevTimestamp = base.Time - header = base - ) - for bi, block := range blocks { - overrides := new(BlockOverrides) - if block.BlockOverrides != nil { - overrides = block.BlockOverrides - } - // Sanitize block number and timestamp - if overrides.Number == nil { - n := new(big.Int).Add(big.NewInt(int64(prevNumber)), big.NewInt(1)) - overrides.Number = (*hexutil.Big)(n) - } else if overrides.Number.ToInt().Uint64() <= prevNumber { - return nil, fmt.Errorf("block numbers must be in order") - } - prevNumber = overrides.Number.ToInt().Uint64() - - if overrides.Time == nil { - t := prevTimestamp + 1 - overrides.Time = (*hexutil.Uint64)(&t) - } else if time := (*uint64)(overrides.Time); *time <= prevTimestamp { - return nil, fmt.Errorf("timestamps must be in order") - } - prevTimestamp = uint64(*overrides.Time) - - var baseFee *big.Int - if config.IsLondon(overrides.Number.ToInt()) { - baseFee = eip1559.CalcBaseFee(config, header) - } - header = &types.Header{ - UncleHash: types.EmptyUncleHash, - Coinbase: base.Coinbase, - Difficulty: base.Difficulty, - GasLimit: base.GasLimit, - //MixDigest: header.MixDigest, - BaseFee: baseFee, - } - overrides.ApplyToHeader(header) - res[bi] = header - } - return res, nil + mc := &multicall{b: s.b, blockNrOrHash: *blockNrOrHash} + return mc.execute(ctx, opts) } // executeEstimate is a helper that executes the transaction under a given gas limit and returns diff --git a/internal/ethapi/multicall.go b/internal/ethapi/multicall.go new file mode 100644 index 0000000000..0da48541af --- /dev/null +++ b/internal/ethapi/multicall.go @@ -0,0 +1,348 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethapi + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/consensus/misc/eip1559" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/trie" +) + +// CallBatch is a batch of calls to be simulated sequentially. +type CallBatch struct { + BlockOverrides *BlockOverrides + StateOverrides *StateOverride + Calls []TransactionArgs +} + +type blockResult struct { + Number hexutil.Uint64 `json:"number"` + Hash common.Hash `json:"hash"` + Time hexutil.Uint64 `json:"timestamp"` + GasLimit hexutil.Uint64 `json:"gasLimit"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + FeeRecipient common.Address `json:"feeRecipient"` + BaseFee *hexutil.Big `json:"baseFeePerGas"` + PrevRandao common.Hash `json:"prevRandao"` + Calls []callResult `json:"calls"` +} + +func mcBlockResultFromHeader(header *types.Header, callResults []callResult) blockResult { + return blockResult{ + Number: hexutil.Uint64(header.Number.Uint64()), + Hash: header.Hash(), + Time: hexutil.Uint64(header.Time), + GasLimit: hexutil.Uint64(header.GasLimit), + GasUsed: hexutil.Uint64(header.GasUsed), + FeeRecipient: header.Coinbase, + BaseFee: (*hexutil.Big)(header.BaseFee), + PrevRandao: header.MixDigest, + Calls: callResults, + } +} + +type callResult struct { + ReturnValue hexutil.Bytes `json:"returnData"` + Logs []*types.Log `json:"logs"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Status hexutil.Uint64 `json:"status"` + Error *callError `json:"error,omitempty"` +} + +func (r *callResult) MarshalJSON() ([]byte, error) { + type callResultAlias callResult + // Marshal logs to be an empty array instead of nil when empty + if r.Logs == nil { + r.Logs = []*types.Log{} + } + return json.Marshal((*callResultAlias)(r)) +} + +type multicallOpts struct { + BlockStateCalls []CallBatch + TraceTransfers bool + Validation bool +} + +type multicall struct { + blockNrOrHash rpc.BlockNumberOrHash + b Backend + hashes []common.Hash +} + +func (mc *multicall) execute(ctx context.Context, opts multicallOpts) ([]blockResult, error) { + state, base, err := mc.b.StateAndHeaderByNumberOrHash(ctx, mc.blockNrOrHash) + if state == nil || err != nil { + return nil, err + } + // Setup context so it may be cancelled before the calls completed + // or, in case of unmetered gas, setup a context with a timeout. + var ( + cancel context.CancelFunc + timeout = mc.b.RPCEVMTimeout() + blocks = opts.BlockStateCalls + ) + if timeout > 0 { + ctx, cancel = context.WithTimeout(ctx, timeout) + } else { + ctx, cancel = context.WithCancel(ctx) + } + // Make sure the context is cancelled when the call has completed + // this makes sure resources are cleaned up. + defer cancel() + headers, err := makeHeaders(mc.b.ChainConfig(), blocks, base) + if err != nil { + return nil, err + } + var ( + results = make([]blockResult, len(blocks)) + // Each tx and all the series of txes shouldn't consume more gas than cap + globalGasCap = mc.b.RPCGasCap() + gp = new(core.GasPool).AddGas(globalGasCap) + header = base + blockContext = core.NewEVMBlockContext(header, NewChainContext(ctx, mc.b), nil) + rules = mc.b.ChainConfig().Rules(blockContext.BlockNumber, blockContext.Random != nil, blockContext.Time) + precompiles = vm.ActivePrecompiledContracts(rules).Copy() + numHashes = headers[len(headers)-1].Number.Uint64() - base.Number.Uint64() + 256 + ) + // Cache for the block hashes. + mc.hashes = make([]common.Hash, numHashes) + for bi, block := range blocks { + header = headers[bi] + blockContext = core.NewEVMBlockContext(header, NewChainContext(ctx, mc.b), nil) + // Respond to BLOCKHASH requests. + blockContext.GetHash = func(n uint64) common.Hash { + h, err := mc.getBlockHash(ctx, n, base, headers) + if err != nil { + log.Warn(err.Error()) + return common.Hash{} + } + return h + } + // State overrides are applied prior to execution of a block + if err := block.StateOverrides.Apply(state, precompiles); err != nil { + return nil, err + } + var ( + gasUsed uint64 + txes = make([]*types.Transaction, len(block.Calls)) + callResults = make([]callResult, len(block.Calls)) + ) + for i, call := range block.Calls { + if call.Nonce == nil { + nonce := state.GetNonce(call.from()) + call.Nonce = (*hexutil.Uint64)(&nonce) + } + // Let the call run wild unless explicitly specified. + if call.Gas == nil { + remaining := blockContext.GasLimit - gasUsed + call.Gas = (*hexutil.Uint64)(&remaining) + } + if call.GasPrice == nil && call.MaxFeePerGas == nil && call.MaxPriorityFeePerGas == nil { + call.MaxFeePerGas = (*hexutil.Big)(big.NewInt(0)) + call.MaxPriorityFeePerGas = (*hexutil.Big)(big.NewInt(0)) + } + // TODO: check chainID and against current header for london fees + if err := call.validateAll(); err != nil { + return nil, err + } + tx := call.ToTransaction(true) + txes[i] = tx + // TODO: repair log block hashes post execution. + vmConfig := &vm.Config{ + NoBaseFee: true, + // Block hash will be repaired after execution. + Tracer: newTracer(opts.TraceTransfers, blockContext.BlockNumber.Uint64(), common.Hash{}, tx.Hash(), uint(i)), + } + result, err := applyMessage(ctx, mc.b, call, state, header, timeout, gp, &blockContext, vmConfig, precompiles, opts.Validation) + if err != nil { + callErr := callErrorFromError(err) + callResults[i] = callResult{Error: callErr, Status: hexutil.Uint64(types.ReceiptStatusFailed)} + continue + } + // If the result contains a revert reason, try to unpack it. + if len(result.Revert()) > 0 { + result.Err = newRevertError(result) + } + logs := vmConfig.Tracer.(*tracer).Logs() + callRes := callResult{ReturnValue: result.Return(), Logs: logs, GasUsed: hexutil.Uint64(result.UsedGas)} + if result.Failed() { + callRes.Status = hexutil.Uint64(types.ReceiptStatusFailed) + if errors.Is(result.Err, vm.ErrExecutionReverted) { + callRes.Error = &callError{Message: result.Err.Error(), Code: -32000} + } else { + callRes.Error = &callError{Message: result.Err.Error(), Code: -32015} + } + } else { + callRes.Status = hexutil.Uint64(types.ReceiptStatusSuccessful) + } + callResults[i] = callRes + gasUsed += result.UsedGas + state.Finalise(true) + } + var ( + parentHash common.Hash + err error + ) + parentHash, err = mc.getBlockHash(ctx, header.Number.Uint64()-1, base, headers) + if err != nil { + return nil, err + } + header.ParentHash = parentHash + header.Root = state.IntermediateRoot(true) + header.GasUsed = gasUsed + header.TxHash = types.DeriveSha(types.Transactions(txes), trie.NewStackTrie(nil)) + results[bi] = mcBlockResultFromHeader(header, callResults) + repairLogs(results, header.Hash()) + } + return results, nil +} + +// getBlockHash returns the hash for the block of the given number. Block can be +// part of the canonical chain, a simulated block or a phantom block. +// Note getBlockHash assumes `n` is smaller than the last already simulated block +// and smaller than the last block to be simulated. +func (mc *multicall) getBlockHash(ctx context.Context, n uint64, base *types.Header, headers []*types.Header) (common.Hash, error) { + // getIndex returns the index of the hash in the hashes cache. + // The cache potentially includes 255 blocks prior to the base. + getIndex := func(n uint64) int { + first := base.Number.Uint64() - 255 + return int(n - first) + } + index := getIndex(n) + if h := mc.hashes[index]; h != (common.Hash{}) { + return h, nil + } + h, err := mc.computeBlockHash(ctx, n, base, headers) + if err != nil { + return common.Hash{}, err + } + if h != (common.Hash{}) { + mc.hashes[index] = h + } + return h, nil +} + +func (mc *multicall) computeBlockHash(ctx context.Context, n uint64, base *types.Header, headers []*types.Header) (common.Hash, error) { + if n == base.Number.Uint64() { + return base.Hash(), nil + } else if n < base.Number.Uint64() { + h, err := mc.b.HeaderByNumber(ctx, rpc.BlockNumber(n)) + if err != nil { + return common.Hash{}, fmt.Errorf("failed to load block hash for number %d. Err: %v\n", n, err) + } + return h.Hash(), nil + } + h := base + for i, _ := range headers { + tmp := headers[i] + // BLOCKHASH will only allow numbers prior to current block + // so no need to check that condition. + if tmp.Number.Uint64() == n { + hash := tmp.Hash() + return hash, nil + } else if tmp.Number.Uint64() > n { + // Phantom block. + lastNonPhantomHash, err := mc.getBlockHash(ctx, h.Number.Uint64(), base, headers) + if err != nil { + return common.Hash{}, err + } + // keccak(rlp(lastNonPhantomBlockHash, blockNumber)) + hashData, err := rlp.EncodeToBytes([][]byte{lastNonPhantomHash.Bytes(), big.NewInt(int64(n)).Bytes()}) + if err != nil { + return common.Hash{}, err + } + return crypto.Keccak256Hash(hashData), nil + } + h = tmp + } + return common.Hash{}, errors.New("requested block is in future") +} + +// repairLogs updates the block hash in the logs present in a multicall +// result object. This is needed as during execution when logs are collected +// the block hash is not known. +func repairLogs(results []blockResult, blockHash common.Hash) { + for i := range results { + for j := range results[i].Calls { + for k := range results[i].Calls[j].Logs { + results[i].Calls[j].Logs[k].BlockHash = blockHash + } + } + } +} +func makeHeaders(config *params.ChainConfig, blocks []CallBatch, base *types.Header) ([]*types.Header, error) { + res := make([]*types.Header, len(blocks)) + var ( + prevNumber = base.Number.Uint64() + prevTimestamp = base.Time + header = base + ) + for bi, block := range blocks { + overrides := new(BlockOverrides) + if block.BlockOverrides != nil { + overrides = block.BlockOverrides + } + // Sanitize block number and timestamp + if overrides.Number == nil { + n := new(big.Int).Add(big.NewInt(int64(prevNumber)), big.NewInt(1)) + overrides.Number = (*hexutil.Big)(n) + } else if overrides.Number.ToInt().Uint64() <= prevNumber { + return nil, fmt.Errorf("block numbers must be in order") + } + prevNumber = overrides.Number.ToInt().Uint64() + + if overrides.Time == nil { + t := prevTimestamp + 1 + overrides.Time = (*hexutil.Uint64)(&t) + } else if time := (*uint64)(overrides.Time); *time <= prevTimestamp { + return nil, fmt.Errorf("timestamps must be in order") + } + prevTimestamp = uint64(*overrides.Time) + + var baseFee *big.Int + if config.IsLondon(overrides.Number.ToInt()) { + baseFee = eip1559.CalcBaseFee(config, header) + } + header = &types.Header{ + UncleHash: types.EmptyUncleHash, + Coinbase: base.Coinbase, + Difficulty: base.Difficulty, + GasLimit: base.GasLimit, + //MixDigest: header.MixDigest, + BaseFee: baseFee, + } + overrides.ApplyToHeader(header) + res[bi] = header + } + return res, nil +}