github.com/ethereum/go-ethereum@v1.16.1/internal/ethapi/simulate.go (about)

     1  // Copyright 2023 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package ethapi
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"math/big"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/common/hexutil"
    29  	"github.com/ethereum/go-ethereum/consensus"
    30  	"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
    31  	"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
    32  	"github.com/ethereum/go-ethereum/core"
    33  	"github.com/ethereum/go-ethereum/core/state"
    34  	"github.com/ethereum/go-ethereum/core/types"
    35  	"github.com/ethereum/go-ethereum/core/vm"
    36  	"github.com/ethereum/go-ethereum/internal/ethapi/override"
    37  	"github.com/ethereum/go-ethereum/params"
    38  	"github.com/ethereum/go-ethereum/rpc"
    39  )
    40  
    41  const (
    42  	// maxSimulateBlocks is the maximum number of blocks that can be simulated
    43  	// in a single request.
    44  	maxSimulateBlocks = 256
    45  
    46  	// timestampIncrement is the default increment between block timestamps.
    47  	timestampIncrement = 12
    48  )
    49  
    50  // simBlock is a batch of calls to be simulated sequentially.
    51  type simBlock struct {
    52  	BlockOverrides *override.BlockOverrides
    53  	StateOverrides *override.StateOverride
    54  	Calls          []TransactionArgs
    55  }
    56  
    57  // simCallResult is the result of a simulated call.
    58  type simCallResult struct {
    59  	ReturnValue hexutil.Bytes  `json:"returnData"`
    60  	Logs        []*types.Log   `json:"logs"`
    61  	GasUsed     hexutil.Uint64 `json:"gasUsed"`
    62  	Status      hexutil.Uint64 `json:"status"`
    63  	Error       *callError     `json:"error,omitempty"`
    64  }
    65  
    66  func (r *simCallResult) MarshalJSON() ([]byte, error) {
    67  	type callResultAlias simCallResult
    68  	// Marshal logs to be an empty array instead of nil when empty
    69  	if r.Logs == nil {
    70  		r.Logs = []*types.Log{}
    71  	}
    72  	return json.Marshal((*callResultAlias)(r))
    73  }
    74  
    75  // simBlockResult is the result of a simulated block.
    76  type simBlockResult struct {
    77  	fullTx      bool
    78  	chainConfig *params.ChainConfig
    79  	Block       *types.Block
    80  	Calls       []simCallResult
    81  	// senders is a map of transaction hashes to their senders.
    82  	senders map[common.Hash]common.Address
    83  }
    84  
    85  func (r *simBlockResult) MarshalJSON() ([]byte, error) {
    86  	blockData := RPCMarshalBlock(r.Block, true, r.fullTx, r.chainConfig)
    87  	blockData["calls"] = r.Calls
    88  	// Set tx sender if user requested full tx objects.
    89  	if r.fullTx {
    90  		if raw, ok := blockData["transactions"].([]any); ok {
    91  			for _, tx := range raw {
    92  				if tx, ok := tx.(*RPCTransaction); ok {
    93  					tx.From = r.senders[tx.Hash]
    94  				} else {
    95  					return nil, errors.New("simulated transaction result has invalid type")
    96  				}
    97  			}
    98  		}
    99  	}
   100  	return json.Marshal(blockData)
   101  }
   102  
   103  // simOpts are the inputs to eth_simulateV1.
   104  type simOpts struct {
   105  	BlockStateCalls        []simBlock
   106  	TraceTransfers         bool
   107  	Validation             bool
   108  	ReturnFullTransactions bool
   109  }
   110  
   111  // simChainHeadReader implements ChainHeaderReader which is needed as input for FinalizeAndAssemble.
   112  type simChainHeadReader struct {
   113  	context.Context
   114  	Backend
   115  }
   116  
   117  func (m *simChainHeadReader) Config() *params.ChainConfig {
   118  	return m.Backend.ChainConfig()
   119  }
   120  
   121  func (m *simChainHeadReader) CurrentHeader() *types.Header {
   122  	return m.Backend.CurrentHeader()
   123  }
   124  
   125  func (m *simChainHeadReader) GetHeader(hash common.Hash, number uint64) *types.Header {
   126  	header, err := m.Backend.HeaderByNumber(m.Context, rpc.BlockNumber(number))
   127  	if err != nil || header == nil {
   128  		return nil
   129  	}
   130  	if header.Hash() != hash {
   131  		return nil
   132  	}
   133  	return header
   134  }
   135  
   136  func (m *simChainHeadReader) GetHeaderByNumber(number uint64) *types.Header {
   137  	header, err := m.Backend.HeaderByNumber(m.Context, rpc.BlockNumber(number))
   138  	if err != nil {
   139  		return nil
   140  	}
   141  	return header
   142  }
   143  
   144  func (m *simChainHeadReader) GetHeaderByHash(hash common.Hash) *types.Header {
   145  	header, err := m.Backend.HeaderByHash(m.Context, hash)
   146  	if err != nil {
   147  		return nil
   148  	}
   149  	return header
   150  }
   151  
   152  // simulator is a stateful object that simulates a series of blocks.
   153  // it is not safe for concurrent use.
   154  type simulator struct {
   155  	b              Backend
   156  	state          *state.StateDB
   157  	base           *types.Header
   158  	chainConfig    *params.ChainConfig
   159  	gp             *core.GasPool
   160  	traceTransfers bool
   161  	validate       bool
   162  	fullTx         bool
   163  }
   164  
   165  // execute runs the simulation of a series of blocks.
   166  func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlockResult, error) {
   167  	if err := ctx.Err(); err != nil {
   168  		return nil, err
   169  	}
   170  	var (
   171  		cancel  context.CancelFunc
   172  		timeout = sim.b.RPCEVMTimeout()
   173  	)
   174  	if timeout > 0 {
   175  		ctx, cancel = context.WithTimeout(ctx, timeout)
   176  	} else {
   177  		ctx, cancel = context.WithCancel(ctx)
   178  	}
   179  	// Make sure the context is cancelled when the call has completed
   180  	// this makes sure resources are cleaned up.
   181  	defer cancel()
   182  
   183  	var err error
   184  	blocks, err = sim.sanitizeChain(blocks)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  	// Prepare block headers with preliminary fields for the response.
   189  	headers, err := sim.makeHeaders(blocks)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  	var (
   194  		results = make([]*simBlockResult, len(blocks))
   195  		parent  = sim.base
   196  	)
   197  	for bi, block := range blocks {
   198  		result, callResults, senders, err := sim.processBlock(ctx, &block, headers[bi], parent, headers[:bi], timeout)
   199  		if err != nil {
   200  			return nil, err
   201  		}
   202  		headers[bi] = result.Header()
   203  		results[bi] = &simBlockResult{fullTx: sim.fullTx, chainConfig: sim.chainConfig, Block: result, Calls: callResults, senders: senders}
   204  		parent = result.Header()
   205  	}
   206  	return results, nil
   207  }
   208  
   209  func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, parent *types.Header, headers []*types.Header, timeout time.Duration) (*types.Block, []simCallResult, map[common.Hash]common.Address, error) {
   210  	// Set header fields that depend only on parent block.
   211  	// Parent hash is needed for evm.GetHashFn to work.
   212  	header.ParentHash = parent.Hash()
   213  	if sim.chainConfig.IsLondon(header.Number) {
   214  		// In non-validation mode base fee is set to 0 if it is not overridden.
   215  		// This is because it creates an edge case in EVM where gasPrice < baseFee.
   216  		// Base fee could have been overridden.
   217  		if header.BaseFee == nil {
   218  			if sim.validate {
   219  				header.BaseFee = eip1559.CalcBaseFee(sim.chainConfig, parent)
   220  			} else {
   221  				header.BaseFee = big.NewInt(0)
   222  			}
   223  		}
   224  	}
   225  	if sim.chainConfig.IsCancun(header.Number, header.Time) {
   226  		var excess uint64
   227  		if sim.chainConfig.IsCancun(parent.Number, parent.Time) {
   228  			excess = eip4844.CalcExcessBlobGas(sim.chainConfig, parent, header.Time)
   229  		}
   230  		header.ExcessBlobGas = &excess
   231  	}
   232  	blockContext := core.NewEVMBlockContext(header, sim.newSimulatedChainContext(ctx, headers), nil)
   233  	if block.BlockOverrides.BlobBaseFee != nil {
   234  		blockContext.BlobBaseFee = block.BlockOverrides.BlobBaseFee.ToInt()
   235  	}
   236  	precompiles := sim.activePrecompiles(sim.base)
   237  	// State overrides are applied prior to execution of a block
   238  	if err := block.StateOverrides.Apply(sim.state, precompiles); err != nil {
   239  		return nil, nil, nil, err
   240  	}
   241  	var (
   242  		gasUsed, blobGasUsed uint64
   243  		txes                 = make([]*types.Transaction, len(block.Calls))
   244  		callResults          = make([]simCallResult, len(block.Calls))
   245  		receipts             = make([]*types.Receipt, len(block.Calls))
   246  		// Block hash will be repaired after execution.
   247  		tracer   = newTracer(sim.traceTransfers, blockContext.BlockNumber.Uint64(), common.Hash{}, common.Hash{}, 0)
   248  		vmConfig = &vm.Config{
   249  			NoBaseFee: !sim.validate,
   250  			Tracer:    tracer.Hooks(),
   251  		}
   252  		// senders is a map of transaction hashes to their senders.
   253  		// Transaction objects contain only the signature, and we lose track
   254  		// of the sender when translating the arguments into a transaction object.
   255  		senders = make(map[common.Hash]common.Address)
   256  	)
   257  	tracingStateDB := vm.StateDB(sim.state)
   258  	if hooks := tracer.Hooks(); hooks != nil {
   259  		tracingStateDB = state.NewHookedState(sim.state, hooks)
   260  	}
   261  	evm := vm.NewEVM(blockContext, tracingStateDB, sim.chainConfig, *vmConfig)
   262  	// It is possible to override precompiles with EVM bytecode, or
   263  	// move them to another address.
   264  	if precompiles != nil {
   265  		evm.SetPrecompiles(precompiles)
   266  	}
   267  	if sim.chainConfig.IsPrague(header.Number, header.Time) || sim.chainConfig.IsVerkle(header.Number, header.Time) {
   268  		core.ProcessParentBlockHash(header.ParentHash, evm)
   269  	}
   270  	if header.ParentBeaconRoot != nil {
   271  		core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, evm)
   272  	}
   273  	var allLogs []*types.Log
   274  	for i, call := range block.Calls {
   275  		if err := ctx.Err(); err != nil {
   276  			return nil, nil, nil, err
   277  		}
   278  		if err := sim.sanitizeCall(&call, sim.state, header, blockContext, &gasUsed); err != nil {
   279  			return nil, nil, nil, err
   280  		}
   281  		var (
   282  			tx     = call.ToTransaction(types.DynamicFeeTxType)
   283  			txHash = tx.Hash()
   284  		)
   285  		txes[i] = tx
   286  		senders[txHash] = call.from()
   287  		tracer.reset(txHash, uint(i))
   288  		sim.state.SetTxContext(txHash, i)
   289  		// EoA check is always skipped, even in validation mode.
   290  		msg := call.ToMessage(header.BaseFee, !sim.validate, true)
   291  		result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp)
   292  		if err != nil {
   293  			txErr := txValidationError(err)
   294  			return nil, nil, nil, txErr
   295  		}
   296  		// Update the state with pending changes.
   297  		var root []byte
   298  		if sim.chainConfig.IsByzantium(blockContext.BlockNumber) {
   299  			tracingStateDB.Finalise(true)
   300  		} else {
   301  			root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes()
   302  		}
   303  		gasUsed += result.UsedGas
   304  		receipts[i] = core.MakeReceipt(evm, result, sim.state, blockContext.BlockNumber, common.Hash{}, blockContext.Time, tx, gasUsed, root)
   305  		blobGasUsed += receipts[i].BlobGasUsed
   306  		logs := tracer.Logs()
   307  		callRes := simCallResult{ReturnValue: result.Return(), Logs: logs, GasUsed: hexutil.Uint64(result.UsedGas)}
   308  		if result.Failed() {
   309  			callRes.Status = hexutil.Uint64(types.ReceiptStatusFailed)
   310  			if errors.Is(result.Err, vm.ErrExecutionReverted) {
   311  				// If the result contains a revert reason, try to unpack it.
   312  				revertErr := newRevertError(result.Revert())
   313  				callRes.Error = &callError{Message: revertErr.Error(), Code: errCodeReverted, Data: revertErr.ErrorData().(string)}
   314  			} else {
   315  				callRes.Error = &callError{Message: result.Err.Error(), Code: errCodeVMError}
   316  			}
   317  		} else {
   318  			callRes.Status = hexutil.Uint64(types.ReceiptStatusSuccessful)
   319  			allLogs = append(allLogs, callRes.Logs...)
   320  		}
   321  		callResults[i] = callRes
   322  	}
   323  	header.GasUsed = gasUsed
   324  	if sim.chainConfig.IsCancun(header.Number, header.Time) {
   325  		header.BlobGasUsed = &blobGasUsed
   326  	}
   327  	var requests [][]byte
   328  	// Process EIP-7685 requests
   329  	if sim.chainConfig.IsPrague(header.Number, header.Time) {
   330  		requests = [][]byte{}
   331  		// EIP-6110
   332  		if err := core.ParseDepositLogs(&requests, allLogs, sim.chainConfig); err != nil {
   333  			return nil, nil, nil, err
   334  		}
   335  		// EIP-7002
   336  		if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
   337  			return nil, nil, nil, err
   338  		}
   339  		// EIP-7251
   340  		if err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
   341  			return nil, nil, nil, err
   342  		}
   343  	}
   344  	if requests != nil {
   345  		reqHash := types.CalcRequestsHash(requests)
   346  		header.RequestsHash = &reqHash
   347  	}
   348  	blockBody := &types.Body{Transactions: txes, Withdrawals: *block.BlockOverrides.Withdrawals}
   349  	chainHeadReader := &simChainHeadReader{ctx, sim.b}
   350  	b, err := sim.b.Engine().FinalizeAndAssemble(chainHeadReader, header, sim.state, blockBody, receipts)
   351  	if err != nil {
   352  		return nil, nil, nil, err
   353  	}
   354  	repairLogs(callResults, b.Hash())
   355  	return b, callResults, senders, nil
   356  }
   357  
   358  // repairLogs updates the block hash in the logs present in the result of
   359  // a simulated block. This is needed as during execution when logs are collected
   360  // the block hash is not known.
   361  func repairLogs(calls []simCallResult, hash common.Hash) {
   362  	for i := range calls {
   363  		for j := range calls[i].Logs {
   364  			calls[i].Logs[j].BlockHash = hash
   365  		}
   366  	}
   367  }
   368  
   369  func (sim *simulator) sanitizeCall(call *TransactionArgs, state vm.StateDB, header *types.Header, blockContext vm.BlockContext, gasUsed *uint64) error {
   370  	if call.Nonce == nil {
   371  		nonce := state.GetNonce(call.from())
   372  		call.Nonce = (*hexutil.Uint64)(&nonce)
   373  	}
   374  	// Let the call run wild unless explicitly specified.
   375  	if call.Gas == nil {
   376  		remaining := blockContext.GasLimit - *gasUsed
   377  		call.Gas = (*hexutil.Uint64)(&remaining)
   378  	}
   379  	if *gasUsed+uint64(*call.Gas) > blockContext.GasLimit {
   380  		return &blockGasLimitReachedError{fmt.Sprintf("block gas limit reached: %d >= %d", gasUsed, blockContext.GasLimit)}
   381  	}
   382  	if err := call.CallDefaults(sim.gp.Gas(), header.BaseFee, sim.chainConfig.ChainID); err != nil {
   383  		return err
   384  	}
   385  	return nil
   386  }
   387  
   388  func (sim *simulator) activePrecompiles(base *types.Header) vm.PrecompiledContracts {
   389  	var (
   390  		isMerge = (base.Difficulty.Sign() == 0)
   391  		rules   = sim.chainConfig.Rules(base.Number, isMerge, base.Time)
   392  	)
   393  	return vm.ActivePrecompiledContracts(rules)
   394  }
   395  
   396  // sanitizeChain checks the chain integrity. Specifically it checks that
   397  // block numbers and timestamp are strictly increasing, setting default values
   398  // when necessary. Gaps in block numbers are filled with empty blocks.
   399  // Note: It modifies the block's override object.
   400  func (sim *simulator) sanitizeChain(blocks []simBlock) ([]simBlock, error) {
   401  	var (
   402  		res           = make([]simBlock, 0, len(blocks))
   403  		base          = sim.base
   404  		prevNumber    = base.Number
   405  		prevTimestamp = base.Time
   406  	)
   407  	for _, block := range blocks {
   408  		if block.BlockOverrides == nil {
   409  			block.BlockOverrides = new(override.BlockOverrides)
   410  		}
   411  		if block.BlockOverrides.Number == nil {
   412  			n := new(big.Int).Add(prevNumber, big.NewInt(1))
   413  			block.BlockOverrides.Number = (*hexutil.Big)(n)
   414  		}
   415  		if block.BlockOverrides.Withdrawals == nil {
   416  			block.BlockOverrides.Withdrawals = &types.Withdrawals{}
   417  		}
   418  		diff := new(big.Int).Sub(block.BlockOverrides.Number.ToInt(), prevNumber)
   419  		if diff.Cmp(common.Big0) <= 0 {
   420  			return nil, &invalidBlockNumberError{fmt.Sprintf("block numbers must be in order: %d <= %d", block.BlockOverrides.Number.ToInt().Uint64(), prevNumber)}
   421  		}
   422  		if total := new(big.Int).Sub(block.BlockOverrides.Number.ToInt(), base.Number); total.Cmp(big.NewInt(maxSimulateBlocks)) > 0 {
   423  			return nil, &clientLimitExceededError{message: "too many blocks"}
   424  		}
   425  		if diff.Cmp(big.NewInt(1)) > 0 {
   426  			// Fill the gap with empty blocks.
   427  			gap := new(big.Int).Sub(diff, big.NewInt(1))
   428  			// Assign block number to the empty blocks.
   429  			for i := uint64(0); i < gap.Uint64(); i++ {
   430  				n := new(big.Int).Add(prevNumber, big.NewInt(int64(i+1)))
   431  				t := prevTimestamp + timestampIncrement
   432  				b := simBlock{
   433  					BlockOverrides: &override.BlockOverrides{
   434  						Number:      (*hexutil.Big)(n),
   435  						Time:        (*hexutil.Uint64)(&t),
   436  						Withdrawals: &types.Withdrawals{},
   437  					},
   438  				}
   439  				prevTimestamp = t
   440  				res = append(res, b)
   441  			}
   442  		}
   443  		// Only append block after filling a potential gap.
   444  		prevNumber = block.BlockOverrides.Number.ToInt()
   445  		var t uint64
   446  		if block.BlockOverrides.Time == nil {
   447  			t = prevTimestamp + timestampIncrement
   448  			block.BlockOverrides.Time = (*hexutil.Uint64)(&t)
   449  		} else {
   450  			t = uint64(*block.BlockOverrides.Time)
   451  			if t <= prevTimestamp {
   452  				return nil, &invalidBlockTimestampError{fmt.Sprintf("block timestamps must be in order: %d <= %d", t, prevTimestamp)}
   453  			}
   454  		}
   455  		prevTimestamp = t
   456  		res = append(res, block)
   457  	}
   458  	return res, nil
   459  }
   460  
   461  // makeHeaders makes header object with preliminary fields based on a simulated block.
   462  // Some fields have to be filled post-execution.
   463  // It assumes blocks are in order and numbers have been validated.
   464  func (sim *simulator) makeHeaders(blocks []simBlock) ([]*types.Header, error) {
   465  	var (
   466  		res    = make([]*types.Header, len(blocks))
   467  		base   = sim.base
   468  		header = base
   469  	)
   470  	for bi, block := range blocks {
   471  		if block.BlockOverrides == nil || block.BlockOverrides.Number == nil {
   472  			return nil, errors.New("empty block number")
   473  		}
   474  		overrides := block.BlockOverrides
   475  
   476  		var withdrawalsHash *common.Hash
   477  		if sim.chainConfig.IsShanghai(overrides.Number.ToInt(), (uint64)(*overrides.Time)) {
   478  			withdrawalsHash = &types.EmptyWithdrawalsHash
   479  		}
   480  		var parentBeaconRoot *common.Hash
   481  		if sim.chainConfig.IsCancun(overrides.Number.ToInt(), (uint64)(*overrides.Time)) {
   482  			parentBeaconRoot = &common.Hash{}
   483  			if overrides.BeaconRoot != nil {
   484  				parentBeaconRoot = overrides.BeaconRoot
   485  			}
   486  		}
   487  		header = overrides.MakeHeader(&types.Header{
   488  			UncleHash:        types.EmptyUncleHash,
   489  			ReceiptHash:      types.EmptyReceiptsHash,
   490  			TxHash:           types.EmptyTxsHash,
   491  			Coinbase:         header.Coinbase,
   492  			Difficulty:       header.Difficulty,
   493  			GasLimit:         header.GasLimit,
   494  			WithdrawalsHash:  withdrawalsHash,
   495  			ParentBeaconRoot: parentBeaconRoot,
   496  		})
   497  		res[bi] = header
   498  	}
   499  	return res, nil
   500  }
   501  
   502  func (sim *simulator) newSimulatedChainContext(ctx context.Context, headers []*types.Header) *ChainContext {
   503  	return NewChainContext(ctx, &simBackend{base: sim.base, b: sim.b, headers: headers})
   504  }
   505  
   506  type simBackend struct {
   507  	b       ChainContextBackend
   508  	base    *types.Header
   509  	headers []*types.Header
   510  }
   511  
   512  func (b *simBackend) Engine() consensus.Engine {
   513  	return b.b.Engine()
   514  }
   515  
   516  func (b *simBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
   517  	if uint64(number) == b.base.Number.Uint64() {
   518  		return b.base, nil
   519  	}
   520  	if uint64(number) < b.base.Number.Uint64() {
   521  		// Resolve canonical header.
   522  		return b.b.HeaderByNumber(ctx, number)
   523  	}
   524  	// Simulated block.
   525  	for _, header := range b.headers {
   526  		if header.Number.Uint64() == uint64(number) {
   527  			return header, nil
   528  		}
   529  	}
   530  	return nil, errors.New("header not found")
   531  }
   532  
   533  func (b *simBackend) ChainConfig() *params.ChainConfig {
   534  	return b.b.ChainConfig()
   535  }