Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions eth/tracers/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"testing"
"time"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
Expand Down Expand Up @@ -345,7 +346,7 @@ func TestOverridenTraceCall(t *testing.T) {
config: &TraceCallConfig{
Tracer: &tracer,
StateOverrides: &ethapi.StateOverride{
randomAccounts[0].addr: ethapi.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
randomAccounts[0].addr: ethereum.OverrideAccount{Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether)))},
},
},
expectErr: nil,
Expand Down Expand Up @@ -398,7 +399,7 @@ func TestOverridenTraceCall(t *testing.T) {
config: &TraceCallConfig{
Tracer: &tracer,
StateOverrides: &ethapi.StateOverride{
randomAccounts[2].addr: ethapi.OverrideAccount{
randomAccounts[2].addr: ethereum.OverrideAccount{
Code: newRPCBytes(common.Hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033")),
StateDiff: newStates([]common.Hash{{}}, []common.Hash{common.BigToHash(big.NewInt(123))}),
},
Expand Down
28 changes: 23 additions & 5 deletions ethclient/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,19 +473,37 @@ func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
// case the code is taken from the latest known block. Note that state from very old
// blocks might not be available.
func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
return ec.CallContractWithOverrides(ctx, msg, blockNumber, nil)
}

// PendingCallContract executes a message call transaction using the EVM.
// The state seen by the contract call is the pending state.
func (ec *Client) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
return ec.PendingCallContractWithOverrides(ctx, msg, nil)
}

// CallContractWithOverrides 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 accounts whose state will be overridden prior to execution.
func (ec *Client) CallContractWithOverrides(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int, overrides ethereum.StateOverride) ([]byte, error) {
var hex hexutil.Bytes
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber))
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber), overrides)
if err != nil {
return nil, err
}
return hex, nil
}

// PendingCallContract executes a message call transaction using the EVM.
// The state seen by the contract call is the pending state.
func (ec *Client) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
// PendingCallContractWithOverrides executes a message call transaction using the EVM.
// The state seen by the contract call is the pending state after account overrides have been applied.
func (ec *Client) PendingCallContractWithOverrides(ctx context.Context, msg ethereum.CallMsg, overrides ethereum.StateOverride) ([]byte, error) {
var hex hexutil.Bytes
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending")
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending", overrides)
if err != nil {
return nil, err
}
Expand Down
43 changes: 42 additions & 1 deletion ethclient/ethclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
Expand Down Expand Up @@ -263,6 +264,9 @@ func TestEthClient(t *testing.T) {
"TestCallContract": {
func(t *testing.T) { testCallContract(t, client) },
},
"TestCallContractWithOverrides": {
func(t *testing.T) { testCallContractWithOverrides(t, client) },
},
"TestAtFunctions": {
func(t *testing.T) { testAtFunctions(t, client) },
},
Expand Down Expand Up @@ -492,12 +496,49 @@ func testCallContract(t *testing.T, client *rpc.Client) {
if _, err := ec.CallContract(context.Background(), msg, big.NewInt(1)); err != nil {
t.Fatalf("unexpected error: %v", err)
}
// PendingCallCOntract
// PendingCallContract
if _, err := ec.PendingCallContract(context.Background(), msg); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}

func testCallContractWithOverrides(t *testing.T, client *rpc.Client) {
ec := NewClient(client)

nonExistentAccount := common.Address{1}

msg := ethereum.CallMsg{
From: nonExistentAccount,
To: &common.Address{},
Gas: 21000,
Value: big.NewInt(1),
}

// CallContract should fail due to lack of funds
if _, err := ec.CallContract(context.Background(), msg, big.NewInt(1)); err == nil {
t.Fatalf("expected error sending from empty account")
}
// PendingCallContract should fail due to lack of funds
if _, err := ec.PendingCallContract(context.Background(), msg); err == nil {
t.Fatalf("expected error sending from empty account")
}

// Override
overrides := make(ethereum.StateOverride)
balance := new(*hexutil.Big)
*balance = (*hexutil.Big)(big.NewInt(1))
overrides[nonExistentAccount] = ethereum.OverrideAccount{Balance: balance}

// CallContractWithOverrides should succeed with overridden balance
if _, err := ec.CallContractWithOverrides(context.Background(), msg, big.NewInt(1), overrides); err != nil {
t.Fatalf("unexpected error: %v", err)
}
// PendingCallContractWithOverrides should succeed with overridden balance
if _, err := ec.PendingCallContractWithOverrides(context.Background(), msg, overrides); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}

func testAtFunctions(t *testing.T, client *rpc.Client) {
ec := NewClient(client)
// send a transaction for some interesting pending status
Expand Down
18 changes: 18 additions & 0 deletions interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)

Expand Down Expand Up @@ -133,6 +134,23 @@ type ContractCaller interface {
CallContract(ctx context.Context, call CallMsg, blockNumber *big.Int) ([]byte, error)
}

// OverrideAccount indicates the overriding fields of account during the execution
// of a message call.
// Note, state and stateDiff can't be specified at the same time. If state is
// set, message execution will only use the data in the given state. Otherwise
// if statDiff is set, all diff will be applied first and then execute the call
// message.
type OverrideAccount struct {
Nonce *hexutil.Uint64 `json:"nonce"`
Code *hexutil.Bytes `json:"code"`
Balance **hexutil.Big `json:"balance"`
State *map[common.Hash]common.Hash `json:"state"`
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
}

// StateOverride is the collection of overridden accounts.
type StateOverride map[common.Address]OverrideAccount

// FilterQuery contains options for contract log filtering.
type FilterQuery struct {
BlockHash *common.Hash // used by eth_getLogs, return logs only from block with this hash
Expand Down
17 changes: 2 additions & 15 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"time"

"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/keystore"
Expand Down Expand Up @@ -804,22 +805,8 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A
return res[:], state.Error()
}

// OverrideAccount indicates the overriding fields of account during the execution
// of a message call.
// Note, state and stateDiff can't be specified at the same time. If state is
// set, message execution will only use the data in the given state. Otherwise
// if statDiff is set, all diff will be applied first and then execute the call
// message.
type OverrideAccount struct {
Nonce *hexutil.Uint64 `json:"nonce"`
Code *hexutil.Bytes `json:"code"`
Balance **hexutil.Big `json:"balance"`
State *map[common.Hash]common.Hash `json:"state"`
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
}

// StateOverride is the collection of overridden accounts.
type StateOverride map[common.Address]OverrideAccount
type StateOverride ethereum.StateOverride

// Apply overrides the fields of specified accounts into the given state.
func (diff *StateOverride) Apply(state *state.StateDB) error {
Expand Down