skyfffire 3 anos atrás
pai
commit
9d7229df9c

+ 4 - 1
eth/api_backend.go

@@ -212,13 +212,16 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
 	return nil
 }
 
-func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
+func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) (*vm.EVM, func() error, error) {
 	vmError := func() error { return nil }
 	if vmConfig == nil {
 		vmConfig = b.eth.blockchain.GetVMConfig()
 	}
 	txContext := core.NewEVMTxContext(msg)
 	context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
+	if blockContext != nil {
+		context = *blockContext
+	}
 	return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), vmError, nil
 }
 

+ 1 - 25
eth/tracers/api.go

@@ -92,34 +92,10 @@ func NewAPI(backend Backend) *API {
 	return &API{backend: backend}
 }
 
-type chainContext struct {
-	api *API
-	ctx context.Context
-}
-
-func (context *chainContext) Engine() consensus.Engine {
-	return context.api.backend.Engine()
-}
-
-func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
-	header, err := context.api.backend.HeaderByNumber(context.ctx, rpc.BlockNumber(number))
-	if err != nil {
-		return nil
-	}
-	if header.Hash() == hash {
-		return header
-	}
-	header, err = context.api.backend.HeaderByHash(context.ctx, hash)
-	if err != nil {
-		return nil
-	}
-	return header
-}
-
 // chainContext constructs the context reader which is used by the evm for reading
 // the necessary chain context.
 func (api *API) chainContext(ctx context.Context) core.ChainContext {
-	return &chainContext{api: api, ctx: ctx}
+	return ethapi.NewChainContext(ctx, api.backend)
 }
 
 // blockByNumber is the wrapper of the chain access function offered by the backend.

+ 1 - 1
ethclient/ethclient_test.go

@@ -221,7 +221,7 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
 		t.Fatalf("can't create new node: %v", err)
 	}
 	// Create Ethereum Service
-	config := &ethconfig.Config{Genesis: genesis}
+	config := &ethconfig.Config{Genesis: genesis, RPCGasCap: 50000000}
 	config.Ethash.PowMode = ethash.ModeFake
 	ethservice, err := eth.New(n, config)
 	if err != nil {

+ 1 - 1
ethclient/gethclient/gethclient_test.go

@@ -55,7 +55,7 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
 		t.Fatalf("can't create new node: %v", err)
 	}
 	// Create Ethereum Service
-	config := &ethconfig.Config{Genesis: genesis}
+	config := &ethconfig.Config{Genesis: genesis, RPCGasCap: 50000000}
 	config.Ethash.PowMode = ethash.ModeFake
 	ethservice, err := eth.New(n, config)
 	if err != nil {

+ 1 - 0
graphql/graphql_test.go

@@ -251,6 +251,7 @@ func createGQLService(t *testing.T, stack *node.Node) {
 		TrieDirtyCache:          5,
 		TrieTimeout:             60 * time.Minute,
 		SnapshotCache:           5,
+		RPCGasCap:               50000000,
 	}
 	ethBackend, err := eth.New(stack, ethConf)
 	if err != nil {

+ 113 - 4
internal/ethapi/api.go

@@ -22,6 +22,7 @@ import (
 	"encoding/hex"
 	"errors"
 	"fmt"
+	"github.com/ethereum/go-ethereum/consensus"
 	"math/big"
 	"strings"
 	"time"
@@ -932,6 +933,37 @@ func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
 	}
 }
 
+type ChainContextBackend interface {
+	Engine() consensus.Engine
+	HeaderByNumber(context.Context, rpc.BlockNumber) (*types.Header, error)
+}
+
+// ChainContext is an implementation of core.ChainContext. It's main use-case
+// is instantiating a vm.BlockContext without having access to the BlockChain object.
+type ChainContext struct {
+	b   ChainContextBackend
+	ctx context.Context
+}
+
+// NewChainContext creates a new ChainContext object.
+func NewChainContext(ctx context.Context, backend ChainContextBackend) *ChainContext {
+	return &ChainContext{ctx: ctx, b: backend}
+}
+
+func (context *ChainContext) Engine() consensus.Engine {
+	return context.b.Engine()
+}
+
+func (context *ChainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
+	// This method is called to get the hash for a block number when executing the BLOCKHASH
+	// opcode. Hence no need to search for non-canonical blocks.
+	header, err := context.b.HeaderByNumber(context.ctx, rpc.BlockNumber(number))
+	if err != nil || header.Hash() != hash {
+		return nil
+	}
+	return header
+}
+
 func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
 	defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
 
@@ -954,12 +986,16 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
 	// this makes sure resources are cleaned up.
 	defer cancel()
 
+	return doCall(ctx, b, args, state, header, timeout, new(core.GasPool).AddGas(globalGasCap), nil)
+}
+
+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) {
 	// Get a new instance of the EVM.
-	msg, err := args.ToMessage(globalGasCap, header.BaseFee)
+	msg, err := args.ToMessage(gp.Gas(), header.BaseFee)
 	if err != nil {
 		return nil, err
 	}
-	evm, vmError, err := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true})
+	evm, vmError, err := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}, blockContext)
 	if err != nil {
 		return nil, err
 	}
@@ -971,7 +1007,6 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
 	}()
 
 	// Execute the message.
-	gp := new(core.GasPool).AddGas(math.MaxUint64)
 	result, err := core.ApplyMessage(evm, msg, gp)
 	if err := vmError(); err != nil {
 		return nil, err
@@ -987,6 +1022,80 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
 	return result, nil
 }
 
+// BatchCallConfig is the config object to be passed to eth_batchCall.
+type BatchCallConfig struct {
+	Block          rpc.BlockNumberOrHash
+	StateOverrides *StateOverride
+	Calls          []BatchCallArgs
+}
+
+// BatchCallArgs is the object specifying each call within eth_batchCall. It
+// extends TransactionArgs with the list of block metadata overrides.
+type BatchCallArgs struct {
+	TransactionArgs
+	BlockOverrides *BlockOverrides
+}
+
+// CallResult is the result of one call.
+type CallResult struct {
+	Return hexutil.Bytes
+	Error  error
+}
+
+// BatchCall executes a series of transactions on the state of a given block as base.
+// The base state can be overridden once before transactions are executed.
+//
+// Additionally, each call can override block context fields such as number.
+//
+// Note, this function doesn't make any changes in the state/blockchain and is
+// useful to execute and retrieve values.
+func (s *BlockChainAPI) BatchCall(ctx context.Context, config BatchCallConfig) ([]CallResult, error) {
+	state, header, err := s.b.StateAndHeaderByNumberOrHash(ctx, config.Block)
+	if state == nil || err != nil {
+		return nil, err
+	}
+	// State overrides are applied once before all calls
+	if err := config.StateOverrides.Apply(state); 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()
+	)
+	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()
+	var (
+		results []CallResult
+		// 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)
+	)
+	for _, call := range config.Calls {
+		blockContext := core.NewEVMBlockContext(header, NewChainContext(ctx, s.b), nil)
+		if call.BlockOverrides != nil {
+			call.BlockOverrides.Apply(&blockContext)
+		}
+		result, err := doCall(ctx, s.b, call.TransactionArgs, state, header, timeout, gp, &blockContext)
+		if err != nil {
+			return nil, err
+		}
+		// If the result contains a revert reason, try to unpack it.
+		if len(result.Revert()) > 0 {
+			result.Err = newRevertError(result)
+		}
+		results = append(results, CallResult{Return: result.Return(), Error: result.Err})
+	}
+	return results, nil
+}
+
 func newRevertError(result *core.ExecutionResult) *revertError {
 	reason, errUnpack := abi.UnpackRevert(result.Revert())
 	err := errors.New("execution reverted")
@@ -1445,7 +1554,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
 		// Apply the transaction with the access list tracer
 		tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
 		config := vm.Config{Tracer: tracer, Debug: true, NoBaseFee: true}
-		vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, &config)
+		vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, &config, nil)
 		if err != nil {
 			return nil, 0, nil, err
 		}

+ 1 - 1
internal/ethapi/backend.go

@@ -68,7 +68,7 @@ type Backend interface {
 	PendingBlockAndReceipts() (*types.Block, types.Receipts)
 	GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
 	GetTd(ctx context.Context, hash common.Hash) *big.Int
-	GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error)
+	GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) (*vm.EVM, func() error, error)
 	SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
 	SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
 	SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription

+ 1 - 1
internal/ethapi/transaction_args_test.go

@@ -302,7 +302,7 @@ func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number
 	return nil, nil
 }
 func (b *backendMock) GetTd(ctx context.Context, hash common.Hash) *big.Int { return nil }
-func (b *backendMock) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
+func (b *backendMock) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) (*vm.EVM, func() error, error) {
 	return nil, nil, nil
 }
 func (b *backendMock) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { return nil }

+ 5 - 0
internal/web3ext/web3ext.go

@@ -595,6 +595,11 @@ web3._extend({
 			call: 'eth_getLogs',
 			params: 1,
 		}),
+		new web3._extend.Method({
+			name: 'batchCall',
+			call: 'eth_batchCall',
+			params: 1,
+		}),
 		new web3._extend.Method({
 			name: 'callBundle',
 			call: 'eth_callBundle',

+ 4 - 1
les/api_backend.go

@@ -179,12 +179,15 @@ func (b *LesApiBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
 	return nil
 }
 
-func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
+func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) (*vm.EVM, func() error, error) {
 	if vmConfig == nil {
 		vmConfig = new(vm.Config)
 	}
 	txContext := core.NewEVMTxContext(msg)
 	context := core.NewEVMBlockContext(header, b.eth.blockchain, nil)
+	if blockContext != nil {
+		context = *blockContext
+	}
 	return vm.NewEVM(context, txContext, state, b.eth.chainConfig, *vmConfig), state.Error, nil
 }