internal/ethapi: add block overrides to eth_call (#26414)
Adds an optional config parameter to eth_call which allows users to override block context fields (same functionality that was added to traceCall in #24871) --------- Co-authored-by: Martin Holst Swende <martin@swende.se>
This commit is contained in:
parent
52c246fac3
commit
ae66009640
@ -246,12 +246,17 @@ func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
|
|||||||
return nil
|
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, blockCtx *vm.BlockContext) (*vm.EVM, func() error, error) {
|
||||||
if vmConfig == nil {
|
if vmConfig == nil {
|
||||||
vmConfig = b.eth.blockchain.GetVMConfig()
|
vmConfig = b.eth.blockchain.GetVMConfig()
|
||||||
}
|
}
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
|
var context vm.BlockContext
|
||||||
|
if blockCtx != nil {
|
||||||
|
context = *blockCtx
|
||||||
|
} else {
|
||||||
|
context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
|
||||||
|
}
|
||||||
return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), state.Error, nil
|
return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), state.Error, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,34 +100,10 @@ func NewAPI(backend Backend) *API {
|
|||||||
return &API{backend: backend}
|
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
|
// chainContext constructs the context reader which is used by the evm for reading
|
||||||
// the necessary chain context.
|
// the necessary chain context.
|
||||||
func (api *API) chainContext(ctx context.Context) core.ChainContext {
|
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.
|
// blockByNumber is the wrapper of the chain access function offered by the backend.
|
||||||
|
@ -143,6 +143,28 @@ func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockN
|
|||||||
return hex, err
|
return hex, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CallContractWithBlockOverrides executes a message call transaction, which is directly executed
|
||||||
|
// in the VM of the node, but never mined into the blockchain.
|
||||||
|
//
|
||||||
|
// blockNumber selects the block height at which the call runs. It can be nil, in which
|
||||||
|
// case the code is taken from the latest known block. Note that state from very old
|
||||||
|
// blocks might not be available.
|
||||||
|
//
|
||||||
|
// overrides specifies a map of contract states that should be overwritten before executing
|
||||||
|
// the message call.
|
||||||
|
//
|
||||||
|
// blockOverrides specifies block fields exposed to the EVM that can be overridden for the call.
|
||||||
|
//
|
||||||
|
// Please use ethclient.CallContract instead if you don't need the override functionality.
|
||||||
|
func (ec *Client) CallContractWithBlockOverrides(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int, overrides *map[common.Address]OverrideAccount, blockOverrides BlockOverrides) ([]byte, error) {
|
||||||
|
var hex hexutil.Bytes
|
||||||
|
err := ec.c.CallContext(
|
||||||
|
ctx, &hex, "eth_call", toCallArg(msg),
|
||||||
|
toBlockNumArg(blockNumber), overrides, blockOverrides,
|
||||||
|
)
|
||||||
|
return hex, err
|
||||||
|
}
|
||||||
|
|
||||||
// GCStats retrieves the current garbage collection stats from a geth node.
|
// GCStats retrieves the current garbage collection stats from a geth node.
|
||||||
func (ec *Client) GCStats(ctx context.Context) (*debug.GCStats, error) {
|
func (ec *Client) GCStats(ctx context.Context) (*debug.GCStats, error) {
|
||||||
var result debug.GCStats
|
var result debug.GCStats
|
||||||
@ -265,3 +287,52 @@ func (a OverrideAccount) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return json.Marshal(output)
|
return json.Marshal(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlockOverrides specifies the set of header fields to override.
|
||||||
|
type BlockOverrides struct {
|
||||||
|
// Number overrides the block number.
|
||||||
|
Number *big.Int
|
||||||
|
// Difficulty overrides the block difficulty.
|
||||||
|
Difficulty *big.Int
|
||||||
|
// Time overrides the block timestamp. Time is applied only when
|
||||||
|
// it is non-zero.
|
||||||
|
Time uint64
|
||||||
|
// GasLimit overrides the block gas limit. GasLimit is applied only when
|
||||||
|
// it is non-zero.
|
||||||
|
GasLimit uint64
|
||||||
|
// Coinbase overrides the block coinbase. Coinbase is applied only when
|
||||||
|
// it is different from the zero address.
|
||||||
|
Coinbase common.Address
|
||||||
|
// Random overrides the block extra data which feeds into the RANDOM opcode.
|
||||||
|
// Random is applied only when it is a non-zero hash.
|
||||||
|
Random common.Hash
|
||||||
|
// BaseFee overrides the block base fee.
|
||||||
|
BaseFee *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o BlockOverrides) MarshalJSON() ([]byte, error) {
|
||||||
|
type override struct {
|
||||||
|
Number *hexutil.Big `json:"number,omitempty"`
|
||||||
|
Difficulty *hexutil.Big `json:"difficulty,omitempty"`
|
||||||
|
Time hexutil.Uint64 `json:"time,omitempty"`
|
||||||
|
GasLimit hexutil.Uint64 `json:"gasLimit,omitempty"`
|
||||||
|
Coinbase *common.Address `json:"coinbase,omitempty"`
|
||||||
|
Random *common.Hash `json:"random,omitempty"`
|
||||||
|
BaseFee *hexutil.Big `json:"baseFee,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
output := override{
|
||||||
|
Number: (*hexutil.Big)(o.Number),
|
||||||
|
Difficulty: (*hexutil.Big)(o.Difficulty),
|
||||||
|
Time: hexutil.Uint64(o.Time),
|
||||||
|
GasLimit: hexutil.Uint64(o.GasLimit),
|
||||||
|
BaseFee: (*hexutil.Big)(o.BaseFee),
|
||||||
|
}
|
||||||
|
if o.Coinbase != (common.Address{}) {
|
||||||
|
output.Coinbase = &o.Coinbase
|
||||||
|
}
|
||||||
|
if o.Random != (common.Hash{}) {
|
||||||
|
output.Random = &o.Random
|
||||||
|
}
|
||||||
|
return json.Marshal(output)
|
||||||
|
}
|
||||||
|
@ -127,6 +127,9 @@ func TestGethClient(t *testing.T) {
|
|||||||
}, {
|
}, {
|
||||||
"TestCallContract",
|
"TestCallContract",
|
||||||
func(t *testing.T) { testCallContract(t, client) },
|
func(t *testing.T) { testCallContract(t, client) },
|
||||||
|
}, {
|
||||||
|
"TestCallContractWithBlockOverrides",
|
||||||
|
func(t *testing.T) { testCallContractWithBlockOverrides(t, client) },
|
||||||
},
|
},
|
||||||
// The testaccesslist is a bit time-sensitive: the newTestBackend imports
|
// The testaccesslist is a bit time-sensitive: the newTestBackend imports
|
||||||
// one block. The `testAcessList` fails if the miner has not yet created a
|
// one block. The `testAcessList` fails if the miner has not yet created a
|
||||||
@ -413,3 +416,75 @@ func TestOverrideAccountMarshal(t *testing.T) {
|
|||||||
t.Error("want:", expected)
|
t.Error("want:", expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBlockOverridesMarshal(t *testing.T) {
|
||||||
|
for i, tt := range []struct {
|
||||||
|
bo BlockOverrides
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
bo: BlockOverrides{},
|
||||||
|
want: `{}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
bo: BlockOverrides{
|
||||||
|
Coinbase: common.HexToAddress("0x1111111111111111111111111111111111111111"),
|
||||||
|
},
|
||||||
|
want: `{"coinbase":"0x1111111111111111111111111111111111111111"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
bo: BlockOverrides{
|
||||||
|
Number: big.NewInt(1),
|
||||||
|
Difficulty: big.NewInt(2),
|
||||||
|
Time: 3,
|
||||||
|
GasLimit: 4,
|
||||||
|
BaseFee: big.NewInt(5),
|
||||||
|
},
|
||||||
|
want: `{"number":"0x1","difficulty":"0x2","time":"0x3","gasLimit":"0x4","baseFee":"0x5"}`,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
marshalled, err := json.Marshal(&tt.bo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if string(marshalled) != tt.want {
|
||||||
|
t.Errorf("Testcase #%d failed. expected\n%s\ngot\n%s", i, tt.want, string(marshalled))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCallContractWithBlockOverrides(t *testing.T, client *rpc.Client) {
|
||||||
|
ec := New(client)
|
||||||
|
msg := ethereum.CallMsg{
|
||||||
|
From: testAddr,
|
||||||
|
To: &common.Address{},
|
||||||
|
Gas: 50000,
|
||||||
|
GasPrice: big.NewInt(1000000000),
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
}
|
||||||
|
override := OverrideAccount{
|
||||||
|
// Returns coinbase address.
|
||||||
|
Code: common.FromHex("0x41806000526014600cf3"),
|
||||||
|
}
|
||||||
|
mapAcc := make(map[common.Address]OverrideAccount)
|
||||||
|
mapAcc[common.Address{}] = override
|
||||||
|
res, err := ec.CallContract(context.Background(), msg, big.NewInt(0), &mapAcc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(res, common.FromHex("0x0000000000000000000000000000000000000000")) {
|
||||||
|
t.Fatalf("unexpected result: %x", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now test with block overrides
|
||||||
|
bo := BlockOverrides{
|
||||||
|
Coinbase: common.HexToAddress("0x1111111111111111111111111111111111111111"),
|
||||||
|
}
|
||||||
|
res, err = ec.CallContractWithBlockOverrides(context.Background(), msg, big.NewInt(0), &mapAcc, bo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(res, common.FromHex("0x1111111111111111111111111111111111111111")) {
|
||||||
|
t.Fatalf("unexpected result: %x", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1068,7 +1068,7 @@ func (c *CallResult) Status() hexutil.Uint64 {
|
|||||||
func (b *Block) Call(ctx context.Context, args struct {
|
func (b *Block) Call(ctx context.Context, args struct {
|
||||||
Data ethapi.TransactionArgs
|
Data ethapi.TransactionArgs
|
||||||
}) (*CallResult, error) {
|
}) (*CallResult, error) {
|
||||||
result, err := ethapi.DoCall(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, b.r.backend.RPCEVMTimeout(), b.r.backend.RPCGasCap())
|
result, err := ethapi.DoCall(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, nil, b.r.backend.RPCEVMTimeout(), b.r.backend.RPCGasCap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1131,7 +1131,7 @@ func (p *Pending) Call(ctx context.Context, args struct {
|
|||||||
Data ethapi.TransactionArgs
|
Data ethapi.TransactionArgs
|
||||||
}) (*CallResult, error) {
|
}) (*CallResult, error) {
|
||||||
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
|
||||||
result, err := ethapi.DoCall(ctx, p.r.backend, args.Data, pendingBlockNr, nil, p.r.backend.RPCEVMTimeout(), p.r.backend.RPCGasCap())
|
result, err := ethapi.DoCall(ctx, p.r.backend, args.Data, pendingBlockNr, nil, nil, p.r.backend.RPCEVMTimeout(), p.r.backend.RPCGasCap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
@ -955,7 +956,39 @@ func (diff *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
|
// ChainContextBackend provides methods required to implement ChainContext.
|
||||||
|
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, blockOverrides *BlockOverrides, 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())
|
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||||
|
|
||||||
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
@ -982,7 +1015,11 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
evm, vmError, err := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true})
|
blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
|
||||||
|
if blockOverrides != nil {
|
||||||
|
blockOverrides.Apply(&blockCtx)
|
||||||
|
}
|
||||||
|
evm, vmError, err := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}, &blockCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1046,8 +1083,8 @@ func (e *revertError) ErrorData() interface{} {
|
|||||||
//
|
//
|
||||||
// Note, this function doesn't make and changes in the state/blockchain and is
|
// Note, this function doesn't make and changes in the state/blockchain and is
|
||||||
// useful to execute and retrieve values.
|
// useful to execute and retrieve values.
|
||||||
func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) {
|
func (s *BlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides) (hexutil.Bytes, error) {
|
||||||
result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
|
result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, blockOverrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1132,7 +1169,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
|||||||
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
||||||
args.Gas = (*hexutil.Uint64)(&gas)
|
args.Gas = (*hexutil.Uint64)(&gas)
|
||||||
|
|
||||||
result, err := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap)
|
result, err := DoCall(ctx, b, args, blockNrOrHash, nil, nil, 0, gasCap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, core.ErrIntrinsicGas) {
|
if errors.Is(err, core.ErrIntrinsicGas) {
|
||||||
return true, nil, nil // Special case, raise gas limit
|
return true, nil, nil // Special case, raise gas limit
|
||||||
@ -1478,7 +1515,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
|||||||
// Apply the transaction with the access list tracer
|
// Apply the transaction with the access list tracer
|
||||||
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
|
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
|
||||||
config := vm.Config{Tracer: tracer, NoBaseFee: true}
|
config := vm.Config{Tracer: tracer, NoBaseFee: true}
|
||||||
vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, &config)
|
vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, &config, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, err
|
return nil, 0, nil, err
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,34 @@
|
|||||||
package ethapi
|
package ethapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTransaction_RoundTripRpcJSON(t *testing.T) {
|
func TestTransaction_RoundTripRpcJSON(t *testing.T) {
|
||||||
@ -157,3 +177,450 @@ func allTransactionTypes(addr common.Address, config *params.ChainConfig) []type
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testBackend struct {
|
||||||
|
db ethdb.Database
|
||||||
|
chain *core.BlockChain
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend {
|
||||||
|
var (
|
||||||
|
engine = ethash.NewFaker()
|
||||||
|
backend = &testBackend{
|
||||||
|
db: rawdb.NewMemoryDatabase(),
|
||||||
|
}
|
||||||
|
cacheConfig = &core.CacheConfig{
|
||||||
|
TrieCleanLimit: 256,
|
||||||
|
TrieDirtyLimit: 256,
|
||||||
|
TrieTimeLimit: 5 * time.Minute,
|
||||||
|
SnapshotLimit: 0,
|
||||||
|
TrieDirtyDisabled: true, // Archive mode
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// Generate blocks for testing
|
||||||
|
_, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, n, generator)
|
||||||
|
chain, err := core.NewBlockChain(backend.db, cacheConfig, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
|
}
|
||||||
|
if n, err := chain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
|
}
|
||||||
|
backend.chain = chain
|
||||||
|
return backend
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b testBackend) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} }
|
||||||
|
func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
|
return big.NewInt(0), nil
|
||||||
|
}
|
||||||
|
func (b testBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
|
||||||
|
return nil, nil, nil, nil, nil
|
||||||
|
}
|
||||||
|
func (b testBackend) ChainDb() ethdb.Database { return b.db }
|
||||||
|
func (b testBackend) AccountManager() *accounts.Manager { return nil }
|
||||||
|
func (b testBackend) ExtRPCEnabled() bool { return false }
|
||||||
|
func (b testBackend) RPCGasCap() uint64 { return 10000000 }
|
||||||
|
func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second }
|
||||||
|
func (b testBackend) RPCTxFeeCap() float64 { return 0 }
|
||||||
|
func (b testBackend) UnprotectedAllowed() bool { return false }
|
||||||
|
func (b testBackend) SetHead(number uint64) {}
|
||||||
|
func (b testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
|
||||||
|
if number == rpc.LatestBlockNumber {
|
||||||
|
return b.chain.CurrentBlock(), nil
|
||||||
|
}
|
||||||
|
return b.chain.GetHeaderByNumber(uint64(number)), nil
|
||||||
|
}
|
||||||
|
func (b testBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) CurrentHeader() *types.Header { panic("implement me") }
|
||||||
|
func (b testBackend) CurrentBlock() *types.Header { panic("implement me") }
|
||||||
|
func (b testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
|
||||||
|
if number == rpc.LatestBlockNumber {
|
||||||
|
head := b.chain.CurrentBlock()
|
||||||
|
return b.chain.GetBlock(head.Hash(), head.Number.Uint64()), nil
|
||||||
|
}
|
||||||
|
return b.chain.GetBlockByNumber(uint64(number)), nil
|
||||||
|
}
|
||||||
|
func (b testBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
|
||||||
|
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||||
|
return b.BlockByNumber(ctx, blockNr)
|
||||||
|
}
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
|
||||||
|
return b.chain.GetBlock(hash, uint64(number.Int64())).Body(), nil
|
||||||
|
}
|
||||||
|
func (b testBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
||||||
|
if number == rpc.PendingBlockNumber {
|
||||||
|
panic("pending state not implemented")
|
||||||
|
}
|
||||||
|
header, err := b.HeaderByNumber(ctx, number)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if header == nil {
|
||||||
|
return nil, nil, errors.New("header not found")
|
||||||
|
}
|
||||||
|
stateDb, err := b.chain.StateAt(header.Root)
|
||||||
|
return stateDb, header, err
|
||||||
|
}
|
||||||
|
func (b testBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
|
||||||
|
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||||
|
return b.StateAndHeaderByNumber(ctx, blockNr)
|
||||||
|
}
|
||||||
|
panic("only implemented for number")
|
||||||
|
}
|
||||||
|
func (b testBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) { panic("implement me") }
|
||||||
|
func (b testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { panic("implement me") }
|
||||||
|
func (b testBackend) 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.chain.GetVMConfig()
|
||||||
|
}
|
||||||
|
txContext := core.NewEVMTxContext(msg)
|
||||||
|
context := core.NewEVMBlockContext(header, b.chain, nil)
|
||||||
|
if blockContext != nil {
|
||||||
|
context = *blockContext
|
||||||
|
}
|
||||||
|
return vm.NewEVM(context, txContext, state, b.chain.Config(), *vmConfig), vmError, nil
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) GetPoolTransactions() (types.Transactions, error) { panic("implement me") }
|
||||||
|
func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { panic("implement me") }
|
||||||
|
func (b testBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) Stats() (pending int, queued int) { panic("implement me") }
|
||||||
|
func (b testBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeNewTxsEvent(events chan<- core.NewTxsEvent) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) ChainConfig() *params.ChainConfig { return b.chain.Config() }
|
||||||
|
func (b testBackend) Engine() consensus.Engine { return b.chain.Engine() }
|
||||||
|
func (b testBackend) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
func (b testBackend) BloomStatus() (uint64, uint64) { panic("implement me") }
|
||||||
|
func (b testBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEstimateGas(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// Initialize test accounts
|
||||||
|
var (
|
||||||
|
accounts = newAccounts(2)
|
||||||
|
genesis = &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: core.GenesisAlloc{
|
||||||
|
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genBlocks = 10
|
||||||
|
signer = types.HomesteadSigner{}
|
||||||
|
randomAccounts = newAccounts(2)
|
||||||
|
)
|
||||||
|
api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
|
||||||
|
// Transfer from account[0] to account[1]
|
||||||
|
// value: 1000 wei
|
||||||
|
// fee: 0 wei
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
}))
|
||||||
|
var testSuite = []struct {
|
||||||
|
blockNumber rpc.BlockNumber
|
||||||
|
call TransactionArgs
|
||||||
|
expectErr error
|
||||||
|
want uint64
|
||||||
|
}{
|
||||||
|
// simple transfer on latest block
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &accounts[1].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
|
},
|
||||||
|
expectErr: nil,
|
||||||
|
want: 21000,
|
||||||
|
},
|
||||||
|
// simple transfer with insufficient funds on latest block
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &randomAccounts[0].addr,
|
||||||
|
To: &accounts[1].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
|
},
|
||||||
|
expectErr: core.ErrInsufficientFunds,
|
||||||
|
want: 21000,
|
||||||
|
},
|
||||||
|
// empty create
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{},
|
||||||
|
expectErr: nil,
|
||||||
|
want: 53000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, tc := range testSuite {
|
||||||
|
result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber})
|
||||||
|
if tc.expectErr != nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !errors.Is(err, tc.expectErr) {
|
||||||
|
t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d: want no error, have %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if uint64(result) != tc.want {
|
||||||
|
t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, uint64(result), tc.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCall(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// Initialize test accounts
|
||||||
|
var (
|
||||||
|
accounts = newAccounts(3)
|
||||||
|
genesis = &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: core.GenesisAlloc{
|
||||||
|
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genBlocks = 10
|
||||||
|
signer = types.HomesteadSigner{}
|
||||||
|
)
|
||||||
|
api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
|
||||||
|
// Transfer from account[0] to account[1]
|
||||||
|
// value: 1000 wei
|
||||||
|
// fee: 0 wei
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
}))
|
||||||
|
randomAccounts := newAccounts(3)
|
||||||
|
var testSuite = []struct {
|
||||||
|
blockNumber rpc.BlockNumber
|
||||||
|
overrides StateOverride
|
||||||
|
call TransactionArgs
|
||||||
|
blockOverrides BlockOverrides
|
||||||
|
expectErr error
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
// transfer on genesis
|
||||||
|
{
|
||||||
|
blockNumber: rpc.BlockNumber(0),
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &accounts[1].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
|
},
|
||||||
|
expectErr: nil,
|
||||||
|
want: "0x",
|
||||||
|
},
|
||||||
|
// transfer on the head
|
||||||
|
{
|
||||||
|
blockNumber: rpc.BlockNumber(genBlocks),
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &accounts[1].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
|
},
|
||||||
|
expectErr: nil,
|
||||||
|
want: "0x",
|
||||||
|
},
|
||||||
|
// transfer on a non-existent block, error expects
|
||||||
|
{
|
||||||
|
blockNumber: rpc.BlockNumber(genBlocks + 1),
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &accounts[1].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
|
},
|
||||||
|
expectErr: errors.New("header not found"),
|
||||||
|
},
|
||||||
|
// transfer on the latest block
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[0].addr,
|
||||||
|
To: &accounts[1].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
|
},
|
||||||
|
expectErr: nil,
|
||||||
|
want: "0x",
|
||||||
|
},
|
||||||
|
// Call which can only succeed if state is state overridden
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &randomAccounts[0].addr,
|
||||||
|
To: &randomAccounts[1].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
|
},
|
||||||
|
overrides: StateOverride{
|
||||||
|
randomAccounts[0].addr: OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
|
||||||
|
},
|
||||||
|
want: "0x",
|
||||||
|
},
|
||||||
|
// Invalid call without state overriding
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &randomAccounts[0].addr,
|
||||||
|
To: &randomAccounts[1].addr,
|
||||||
|
Value: (*hexutil.Big)(big.NewInt(1000)),
|
||||||
|
},
|
||||||
|
expectErr: core.ErrInsufficientFunds,
|
||||||
|
},
|
||||||
|
// Successful simple contract call
|
||||||
|
//
|
||||||
|
// // SPDX-License-Identifier: GPL-3.0
|
||||||
|
//
|
||||||
|
// pragma solidity >=0.7.0 <0.8.0;
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * @title Storage
|
||||||
|
// * @dev Store & retrieve value in a variable
|
||||||
|
// */
|
||||||
|
// contract Storage {
|
||||||
|
// uint256 public number;
|
||||||
|
// constructor() {
|
||||||
|
// number = block.number;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &randomAccounts[0].addr,
|
||||||
|
To: &randomAccounts[2].addr,
|
||||||
|
Data: hex2Bytes("8381f58a"), // call number()
|
||||||
|
},
|
||||||
|
overrides: StateOverride{
|
||||||
|
randomAccounts[2].addr: OverrideAccount{
|
||||||
|
Code: hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"),
|
||||||
|
StateDiff: &map[common.Hash]common.Hash{common.Hash{}: common.BigToHash(big.NewInt(123))},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: "0x000000000000000000000000000000000000000000000000000000000000007b",
|
||||||
|
},
|
||||||
|
// Block overrides should work
|
||||||
|
{
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
call: TransactionArgs{
|
||||||
|
From: &accounts[1].addr,
|
||||||
|
Input: &hexutil.Bytes{
|
||||||
|
0x43, // NUMBER
|
||||||
|
0x60, 0x00, 0x52, // MSTORE offset 0
|
||||||
|
0x60, 0x20, 0x60, 0x00, 0xf3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
blockOverrides: BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))},
|
||||||
|
want: "0x000000000000000000000000000000000000000000000000000000000000000b",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, tc := range testSuite {
|
||||||
|
result, err := api.Call(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides)
|
||||||
|
if tc.expectErr != nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !errors.Is(err, tc.expectErr) {
|
||||||
|
// Second try
|
||||||
|
if !reflect.DeepEqual(err, tc.expectErr) {
|
||||||
|
t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d: want no error, have %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(result.String(), tc.want) {
|
||||||
|
t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, result.String(), tc.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
addr common.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
type Accounts []Account
|
||||||
|
|
||||||
|
func (a Accounts) Len() int { return len(a) }
|
||||||
|
func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 }
|
||||||
|
|
||||||
|
func newAccounts(n int) (accounts Accounts) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
accounts = append(accounts, Account{key: key, addr: addr})
|
||||||
|
}
|
||||||
|
sort.Sort(accounts)
|
||||||
|
return accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRPCBalance(balance *big.Int) **hexutil.Big {
|
||||||
|
rpcBalance := (*hexutil.Big)(balance)
|
||||||
|
return &rpcBalance
|
||||||
|
}
|
||||||
|
|
||||||
|
func hex2Bytes(str string) *hexutil.Bytes {
|
||||||
|
rpcBytes := hexutil.Bytes(common.Hex2Bytes(str))
|
||||||
|
return &rpcBytes
|
||||||
|
}
|
||||||
|
@ -68,7 +68,7 @@ type Backend interface {
|
|||||||
PendingBlockAndReceipts() (*types.Block, types.Receipts)
|
PendingBlockAndReceipts() (*types.Block, types.Receipts)
|
||||||
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
||||||
GetTd(ctx context.Context, hash common.Hash) *big.Int
|
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, blockCtx *vm.BlockContext) (*vm.EVM, func() error, error)
|
||||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
||||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||||
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
|
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
|
||||||
|
@ -305,7 +305,7 @@ func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
func (b *backendMock) GetTd(ctx context.Context, hash common.Hash) *big.Int { return 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, blockCtx *vm.BlockContext) (*vm.EVM, func() error, error) {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
func (b *backendMock) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { return nil }
|
func (b *backendMock) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { return nil }
|
||||||
|
@ -608,8 +608,8 @@ web3._extend({
|
|||||||
new web3._extend.Method({
|
new web3._extend.Method({
|
||||||
name: 'call',
|
name: 'call',
|
||||||
call: 'eth_call',
|
call: 'eth_call',
|
||||||
params: 3,
|
params: 4,
|
||||||
inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputDefaultBlockNumberFormatter, null],
|
inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputDefaultBlockNumberFormatter, null, null],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
properties: [
|
properties: [
|
||||||
|
@ -184,12 +184,15 @@ func (b *LesApiBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
|
|||||||
return nil
|
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, blockCtx *vm.BlockContext) (*vm.EVM, func() error, error) {
|
||||||
if vmConfig == nil {
|
if vmConfig == nil {
|
||||||
vmConfig = new(vm.Config)
|
vmConfig = new(vm.Config)
|
||||||
}
|
}
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
context := core.NewEVMBlockContext(header, b.eth.blockchain, nil)
|
context := core.NewEVMBlockContext(header, b.eth.blockchain, nil)
|
||||||
|
if blockCtx != nil {
|
||||||
|
context = *blockCtx
|
||||||
|
}
|
||||||
return vm.NewEVM(context, txContext, state, b.eth.chainConfig, *vmConfig), state.Error, nil
|
return vm.NewEVM(context, txContext, state, b.eth.chainConfig, *vmConfig), state.Error, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user