diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index a819537c7b9f..51053491a131 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -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" @@ -345,7 +346,7 @@ func TestOverridenTraceCall(t *testing.T) { config: &TraceCallConfig{ Tracer: &tracer, StateOverrides: ðapi.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, @@ -398,7 +399,7 @@ func TestOverridenTraceCall(t *testing.T) { config: &TraceCallConfig{ Tracer: &tracer, StateOverrides: ðapi.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))}), }, diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 80240dcb9eba..25efd46bafb6 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -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 } diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index a958c1e32ad1..672161b4c470 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -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" @@ -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) }, }, @@ -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 diff --git a/interfaces.go b/interfaces.go index b9d0bb880e58..7f6b2fd5d9fb 100644 --- a/interfaces.go +++ b/interfaces.go @@ -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" ) @@ -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 diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 913cc0b974f9..1744a6946f0f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -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" @@ -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 {