@@ -33,6 +33,7 @@ import (
33
33
"github.com/ethereum/go-ethereum/common"
34
34
"github.com/ethereum/go-ethereum/common/hexutil"
35
35
"github.com/ethereum/go-ethereum/common/math"
36
+ "github.com/ethereum/go-ethereum/consensus"
36
37
"github.com/ethereum/go-ethereum/consensus/ethash"
37
38
"github.com/ethereum/go-ethereum/consensus/misc"
38
39
"github.com/ethereum/go-ethereum/core"
@@ -946,6 +947,38 @@ func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
946
947
}
947
948
}
948
949
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
+
949
982
func DoCall (ctx context.Context , b Backend , args TransactionArgs , blockNrOrHash rpc.BlockNumberOrHash , overrides * StateOverride , timeout time.Duration , globalGasCap uint64 ) (* core.ExecutionResult , error ) {
950
983
defer func (start time.Time ) { log .Debug ("Executing EVM call finished" , "runtime" , time .Since (start )) }(time .Now ())
951
984
@@ -967,13 +1000,16 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
967
1000
// Make sure the context is cancelled when the call has completed
968
1001
// this makes sure resources are cleaned up.
969
1002
defer cancel ()
1003
+ return doCall (ctx , b , args , state , header , timeout , new (core.GasPool ).AddGas (globalGasCap ), nil )
1004
+ }
970
1005
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 ) {
971
1007
// Get a new instance of the EVM.
972
- msg , err := args .ToMessage (globalGasCap , header .BaseFee )
1008
+ msg , err := args .ToMessage (gp . Gas () , header .BaseFee )
973
1009
if err != nil {
974
1010
return nil , err
975
1011
}
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 )
977
1013
if err != nil {
978
1014
return nil , err
979
1015
}
@@ -985,7 +1021,6 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
985
1021
}()
986
1022
987
1023
// Execute the message.
988
- gp := new (core.GasPool ).AddGas (math .MaxUint64 )
989
1024
result , err := core .ApplyMessage (evm , msg , gp )
990
1025
if err := vmError (); err != nil {
991
1026
return nil , err
@@ -1049,6 +1084,80 @@ func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrO
1049
1084
return result .Return (), result .Err
1050
1085
}
1051
1086
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
+
1052
1161
func DoEstimateGas (ctx context.Context , b Backend , args TransactionArgs , blockNrOrHash rpc.BlockNumberOrHash , gasCap uint64 ) (hexutil.Uint64 , error ) {
1053
1162
// Binary search the gas requirement, as it may be higher than the amount used
1054
1163
var (
@@ -1459,7 +1568,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
1459
1568
// Apply the transaction with the access list tracer
1460
1569
tracer := logger .NewAccessListTracer (accessList , args .from (), to , precompiles )
1461
1570
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 )
1463
1572
if err != nil {
1464
1573
return nil , 0 , nil , err
1465
1574
}
0 commit comments