@@ -33,6 +33,7 @@ import (
3333 "github.com/ethereum/go-ethereum/common"
3434 "github.com/ethereum/go-ethereum/common/hexutil"
3535 "github.com/ethereum/go-ethereum/common/math"
36+ "github.com/ethereum/go-ethereum/consensus"
3637 "github.com/ethereum/go-ethereum/consensus/ethash"
3738 "github.com/ethereum/go-ethereum/consensus/misc"
3839 "github.com/ethereum/go-ethereum/core"
@@ -946,6 +947,38 @@ func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
946947 }
947948}
948949
950+ // ChainContextBackend provides methods required to implement ChainContext.
951+ type ChainContextBackend interface {
952+ Engine () consensus.Engine
953+ HeaderByNumber (context.Context , rpc.BlockNumber ) (* types.Header , error )
954+ }
955+
956+ // ChainContext is an implementation of core.ChainContext. It's main use-case
957+ // is instantiating a vm.BlockContext without having access to the BlockChain object.
958+ type ChainContext struct {
959+ b ChainContextBackend
960+ ctx context.Context
961+ }
962+
963+ // NewChainContext creates a new ChainContext object.
964+ func NewChainContext (ctx context.Context , backend ChainContextBackend ) * ChainContext {
965+ return & ChainContext {ctx : ctx , b : backend }
966+ }
967+
968+ func (context * ChainContext ) Engine () consensus.Engine {
969+ return context .b .Engine ()
970+ }
971+
972+ func (context * ChainContext ) GetHeader (hash common.Hash , number uint64 ) * types.Header {
973+ // This method is called to get the hash for a block number when executing the BLOCKHASH
974+ // opcode. Hence no need to search for non-canonical blocks.
975+ header , err := context .b .HeaderByNumber (context .ctx , rpc .BlockNumber (number ))
976+ if err != nil || header .Hash () != hash {
977+ return nil
978+ }
979+ return header
980+ }
981+
949982func DoCall (ctx context.Context , b Backend , args TransactionArgs , blockNrOrHash rpc.BlockNumberOrHash , overrides * StateOverride , timeout time.Duration , globalGasCap uint64 ) (* core.ExecutionResult , error ) {
950983 defer func (start time.Time ) { log .Debug ("Executing EVM call finished" , "runtime" , time .Since (start )) }(time .Now ())
951984
@@ -967,13 +1000,16 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
9671000 // Make sure the context is cancelled when the call has completed
9681001 // this makes sure resources are cleaned up.
9691002 defer cancel ()
1003+ return doCall (ctx , b , args , state , header , timeout , new (core.GasPool ).AddGas (globalGasCap ), nil )
1004+ }
9701005
1006+ func doCall (ctx context.Context , b Backend , args TransactionArgs , state * state.StateDB , header * types.Header , timeout time.Duration , gp * core.GasPool , blockContext * vm.BlockContext ) (* core.ExecutionResult , error ) {
9711007 // Get a new instance of the EVM.
972- msg , err := args .ToMessage (globalGasCap , header .BaseFee )
1008+ msg , err := args .ToMessage (gp . Gas () , header .BaseFee )
9731009 if err != nil {
9741010 return nil , err
9751011 }
976- evm , vmError , err := b .GetEVM (ctx , msg , state , header , & vm.Config {NoBaseFee : true })
1012+ evm , vmError , err := b .GetEVM (ctx , msg , state , header , & vm.Config {NoBaseFee : true }, blockContext )
9771013 if err != nil {
9781014 return nil , err
9791015 }
@@ -985,7 +1021,6 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
9851021 }()
9861022
9871023 // Execute the message.
988- gp := new (core.GasPool ).AddGas (math .MaxUint64 )
9891024 result , err := core .ApplyMessage (evm , msg , gp )
9901025 if err := vmError (); err != nil {
9911026 return nil , err
@@ -1049,6 +1084,80 @@ func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrO
10491084 return result .Return (), result .Err
10501085}
10511086
1087+ // BatchCallConfig is the config object to be passed to eth_batchCall.
1088+ type BatchCallConfig struct {
1089+ Block rpc.BlockNumberOrHash
1090+ StateOverrides * StateOverride
1091+ Calls []BatchCallArgs
1092+ }
1093+
1094+ // BatchCallArgs is the object specifying each call within eth_batchCall. It
1095+ // extends TransactionArgs with the list of block metadata overrides.
1096+ type BatchCallArgs struct {
1097+ TransactionArgs
1098+ BlockOverrides * BlockOverrides
1099+ }
1100+
1101+ // CallResult is the result of one call.
1102+ type CallResult struct {
1103+ Return hexutil.Bytes
1104+ Error error
1105+ }
1106+
1107+ // BatchCall executes a series of transactions on the state of a given block as base.
1108+ // The base state can be overridden once before transactions are executed.
1109+ //
1110+ // Additionally, each call can override block context fields such as number.
1111+ //
1112+ // Note, this function doesn't make any changes in the state/blockchain and is
1113+ // useful to execute and retrieve values.
1114+ func (s * BlockChainAPI ) BatchCall (ctx context.Context , config BatchCallConfig ) ([]CallResult , error ) {
1115+ state , header , err := s .b .StateAndHeaderByNumberOrHash (ctx , config .Block )
1116+ if state == nil || err != nil {
1117+ return nil , err
1118+ }
1119+ // State overrides are applied once before all calls
1120+ if err := config .StateOverrides .Apply (state ); err != nil {
1121+ return nil , err
1122+ }
1123+ // Setup context so it may be cancelled before the calls completed
1124+ // or, in case of unmetered gas, setup a context with a timeout.
1125+ var (
1126+ cancel context.CancelFunc
1127+ timeout = s .b .RPCEVMTimeout ()
1128+ )
1129+ if timeout > 0 {
1130+ ctx , cancel = context .WithTimeout (ctx , timeout )
1131+ } else {
1132+ ctx , cancel = context .WithCancel (ctx )
1133+ }
1134+ // Make sure the context is cancelled when the call has completed
1135+ // this makes sure resources are cleaned up.
1136+ defer cancel ()
1137+ var (
1138+ results []CallResult
1139+ // Each tx and all the series of txes shouldn't consume more gas than cap
1140+ globalGasCap = s .b .RPCGasCap ()
1141+ gp = new (core.GasPool ).AddGas (globalGasCap )
1142+ )
1143+ for _ , call := range config .Calls {
1144+ blockContext := core .NewEVMBlockContext (header , NewChainContext (ctx , s .b ), nil )
1145+ if call .BlockOverrides != nil {
1146+ call .BlockOverrides .Apply (& blockContext )
1147+ }
1148+ result , err := doCall (ctx , s .b , call .TransactionArgs , state , header , timeout , gp , & blockContext )
1149+ if err != nil {
1150+ return nil , err
1151+ }
1152+ // If the result contains a revert reason, try to unpack and return it.
1153+ if len (result .Revert ()) > 0 {
1154+ return nil , newRevertError (result )
1155+ }
1156+ results = append (results , CallResult {Return : result .Return (), Error : result .Err })
1157+ }
1158+ return results , nil
1159+ }
1160+
10521161func DoEstimateGas (ctx context.Context , b Backend , args TransactionArgs , blockNrOrHash rpc.BlockNumberOrHash , gasCap uint64 ) (hexutil.Uint64 , error ) {
10531162 // Binary search the gas requirement, as it may be higher than the amount used
10541163 var (
@@ -1459,7 +1568,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
14591568 // Apply the transaction with the access list tracer
14601569 tracer := logger .NewAccessListTracer (accessList , args .from (), to , precompiles )
14611570 config := vm.Config {Tracer : tracer , Debug : true , NoBaseFee : true }
1462- vmenv , _ , err := b .GetEVM (ctx , msg , statedb , header , & config )
1571+ vmenv , _ , err := b .GetEVM (ctx , msg , statedb , header , & config , nil )
14631572 if err != nil {
14641573 return nil , 0 , nil , err
14651574 }
0 commit comments