From d58b060ba40da6c5e06586ed71163ef51031e1ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isabel=20Sch=C3=B6ps=20Thiel=20=40IsabelSchoepd?= <155141998+IST-Github@users.noreply.github.com> Date: Thu, 4 Jan 2024 04:29:24 +0100 Subject: [PATCH] Delete ethclient directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Isabel Schöps Thiel @IsabelSchoepd <155141998+IST-Github@users.noreply.github.com> --- ethclient/ethclient.go | 716 ----------------------- ethclient/ethclient_test.go | 745 ------------------------ ethclient/gethclient/gethclient.go | 335 ----------- ethclient/gethclient/gethclient_test.go | 570 ------------------ ethclient/signer.go | 62 -- 5 files changed, 2428 deletions(-) delete mode 100644 ethclient/ethclient.go delete mode 100644 ethclient/ethclient_test.go delete mode 100644 ethclient/gethclient/gethclient.go delete mode 100644 ethclient/gethclient/gethclient_test.go delete mode 100644 ethclient/signer.go diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go deleted file mode 100644 index e8a201f71b..0000000000 --- a/ethclient/ethclient.go +++ /dev/null @@ -1,716 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package ethclient provides a client for the Ethereum RPC API. -package ethclient - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rpc" -) - -// Client defines typed wrappers for the Ethereum RPC API. -type Client struct { - c *rpc.Client -} - -// Dial connects a client to the given URL. -func Dial(rawurl string) (*Client, error) { - return DialContext(context.Background(), rawurl) -} - -// DialContext connects a client to the given URL with context. -func DialContext(ctx context.Context, rawurl string) (*Client, error) { - c, err := rpc.DialContext(ctx, rawurl) - if err != nil { - return nil, err - } - return NewClient(c), nil -} - -// NewClient creates a client that uses the given RPC client. -func NewClient(c *rpc.Client) *Client { - return &Client{c} -} - -// Close closes the underlying RPC connection. -func (ec *Client) Close() { - ec.c.Close() -} - -// Client gets the underlying RPC client. -func (ec *Client) Client() *rpc.Client { - return ec.c -} - -// Blockchain Access - -// ChainID retrieves the current chain ID for transaction replay protection. -func (ec *Client) ChainID(ctx context.Context) (*big.Int, error) { - var result hexutil.Big - err := ec.c.CallContext(ctx, &result, "eth_chainId") - if err != nil { - return nil, err - } - return (*big.Int)(&result), err -} - -// BlockByHash returns the given full block. -// -// Note that loading full blocks requires two requests. Use HeaderByHash -// if you don't need all transactions or uncle headers. -func (ec *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { - return ec.getBlock(ctx, "eth_getBlockByHash", hash, true) -} - -// BlockByNumber returns a block from the current canonical chain. If number is nil, the -// latest known block is returned. -// -// Note that loading full blocks requires two requests. Use HeaderByNumber -// if you don't need all transactions or uncle headers. -func (ec *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) { - return ec.getBlock(ctx, "eth_getBlockByNumber", toBlockNumArg(number), true) -} - -// BlockNumber returns the most recent block number -func (ec *Client) BlockNumber(ctx context.Context) (uint64, error) { - var result hexutil.Uint64 - err := ec.c.CallContext(ctx, &result, "eth_blockNumber") - return uint64(result), err -} - -// PeerCount returns the number of p2p peers as reported by the net_peerCount method. -func (ec *Client) PeerCount(ctx context.Context) (uint64, error) { - var result hexutil.Uint64 - err := ec.c.CallContext(ctx, &result, "net_peerCount") - return uint64(result), err -} - -// BlockReceipts returns the receipts of a given block number or hash. -func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]*types.Receipt, error) { - var r []*types.Receipt - err := ec.c.CallContext(ctx, &r, "eth_getBlockReceipts", blockNrOrHash.String()) - if err == nil && r == nil { - return nil, ethereum.NotFound - } - return r, err -} - -type rpcBlock struct { - Hash common.Hash `json:"hash"` - Transactions []rpcTransaction `json:"transactions"` - UncleHashes []common.Hash `json:"uncles"` - Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` -} - -func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { - var raw json.RawMessage - err := ec.c.CallContext(ctx, &raw, method, args...) - if err != nil { - return nil, err - } - - // Decode header and transactions. - var head *types.Header - if err := json.Unmarshal(raw, &head); err != nil { - return nil, err - } - // When the block is not found, the API returns JSON null. - if head == nil { - return nil, ethereum.NotFound - } - - var body rpcBlock - if err := json.Unmarshal(raw, &body); err != nil { - return nil, err - } - // Quick-verify transaction and uncle lists. This mostly helps with debugging the server. - if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 { - return nil, errors.New("server returned non-empty uncle list but block header indicates no uncles") - } - if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 { - return nil, errors.New("server returned empty uncle list but block header indicates uncles") - } - if head.TxHash == types.EmptyTxsHash && len(body.Transactions) > 0 { - return nil, errors.New("server returned non-empty transaction list but block header indicates no transactions") - } - if head.TxHash != types.EmptyTxsHash && len(body.Transactions) == 0 { - return nil, errors.New("server returned empty transaction list but block header indicates transactions") - } - // Load uncles because they are not included in the block response. - var uncles []*types.Header - if len(body.UncleHashes) > 0 { - uncles = make([]*types.Header, len(body.UncleHashes)) - reqs := make([]rpc.BatchElem, len(body.UncleHashes)) - for i := range reqs { - reqs[i] = rpc.BatchElem{ - Method: "eth_getUncleByBlockHashAndIndex", - Args: []interface{}{body.Hash, hexutil.EncodeUint64(uint64(i))}, - Result: &uncles[i], - } - } - if err := ec.c.BatchCallContext(ctx, reqs); err != nil { - return nil, err - } - for i := range reqs { - if reqs[i].Error != nil { - return nil, reqs[i].Error - } - if uncles[i] == nil { - return nil, fmt.Errorf("got null header for uncle %d of block %x", i, body.Hash[:]) - } - } - } - // Fill the sender cache of transactions in the block. - txs := make([]*types.Transaction, len(body.Transactions)) - for i, tx := range body.Transactions { - if tx.From != nil { - setSenderFromServer(tx.tx, *tx.From, body.Hash) - } - txs[i] = tx.tx - } - return types.NewBlockWithHeader(head).WithBody(txs, uncles).WithWithdrawals(body.Withdrawals), nil -} - -// HeaderByHash returns the block header with the given hash. -func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { - var head *types.Header - err := ec.c.CallContext(ctx, &head, "eth_getBlockByHash", hash, false) - if err == nil && head == nil { - err = ethereum.NotFound - } - return head, err -} - -// HeaderByNumber returns a block header from the current canonical chain. If number is -// nil, the latest known header is returned. -func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { - var head *types.Header - err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false) - if err == nil && head == nil { - err = ethereum.NotFound - } - return head, err -} - -type rpcTransaction struct { - tx *types.Transaction - txExtraInfo -} - -type txExtraInfo struct { - BlockNumber *string `json:"blockNumber,omitempty"` - BlockHash *common.Hash `json:"blockHash,omitempty"` - From *common.Address `json:"from,omitempty"` -} - -func (tx *rpcTransaction) UnmarshalJSON(msg []byte) error { - if err := json.Unmarshal(msg, &tx.tx); err != nil { - return err - } - return json.Unmarshal(msg, &tx.txExtraInfo) -} - -// TransactionByHash returns the transaction with the given hash. -func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) { - var json *rpcTransaction - err = ec.c.CallContext(ctx, &json, "eth_getTransactionByHash", hash) - if err != nil { - return nil, false, err - } else if json == nil { - return nil, false, ethereum.NotFound - } else if _, r, _ := json.tx.RawSignatureValues(); r == nil { - return nil, false, errors.New("server returned transaction without signature") - } - if json.From != nil && json.BlockHash != nil { - setSenderFromServer(json.tx, *json.From, *json.BlockHash) - } - return json.tx, json.BlockNumber == nil, nil -} - -// TransactionSender returns the sender address of the given transaction. The transaction -// must be known to the remote node and included in the blockchain at the given block and -// index. The sender is the one derived by the protocol at the time of inclusion. -// -// There is a fast-path for transactions retrieved by TransactionByHash and -// TransactionInBlock. Getting their sender address can be done without an RPC interaction. -func (ec *Client) TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) { - // Try to load the address from the cache. - sender, err := types.Sender(&senderFromServer{blockhash: block}, tx) - if err == nil { - return sender, nil - } - - // It was not found in cache, ask the server. - var meta struct { - Hash common.Hash - From common.Address - } - if err = ec.c.CallContext(ctx, &meta, "eth_getTransactionByBlockHashAndIndex", block, hexutil.Uint64(index)); err != nil { - return common.Address{}, err - } - if meta.Hash == (common.Hash{}) || meta.Hash != tx.Hash() { - return common.Address{}, errors.New("wrong inclusion block/index") - } - return meta.From, nil -} - -// TransactionCount returns the total number of transactions in the given block. -func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { - var num hexutil.Uint - err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash) - return uint(num), err -} - -// TransactionInBlock returns a single transaction at index in the given block. -func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { - var json *rpcTransaction - err := ec.c.CallContext(ctx, &json, "eth_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index)) - if err != nil { - return nil, err - } - if json == nil { - return nil, ethereum.NotFound - } else if _, r, _ := json.tx.RawSignatureValues(); r == nil { - return nil, errors.New("server returned transaction without signature") - } - if json.From != nil && json.BlockHash != nil { - setSenderFromServer(json.tx, *json.From, *json.BlockHash) - } - return json.tx, err -} - -// TransactionReceipt returns the receipt of a transaction by transaction hash. -// Note that the receipt is not available for pending transactions. -func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { - var r *types.Receipt - err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash) - if err == nil { - if r == nil { - return nil, ethereum.NotFound - } - } - return r, err -} - -// SyncProgress retrieves the current progress of the sync algorithm. If there's -// no sync currently running, it returns nil. -func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { - var raw json.RawMessage - if err := ec.c.CallContext(ctx, &raw, "eth_syncing"); err != nil { - return nil, err - } - // Handle the possible response types - var syncing bool - if err := json.Unmarshal(raw, &syncing); err == nil { - return nil, nil // Not syncing (always false) - } - var p *rpcProgress - if err := json.Unmarshal(raw, &p); err != nil { - return nil, err - } - return p.toSyncProgress(), nil -} - -// SubscribeNewHead subscribes to notifications about the current blockchain head -// on the given channel. -func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) { - sub, err := ec.c.EthSubscribe(ctx, ch, "newHeads") - if err != nil { - // Defensively prefer returning nil interface explicitly on error-path, instead - // of letting default golang behavior wrap it with non-nil interface that stores - // nil concrete type value. - return nil, err - } - return sub, nil -} - -// State Access - -// NetworkID returns the network ID for this client. -func (ec *Client) NetworkID(ctx context.Context) (*big.Int, error) { - version := new(big.Int) - var ver string - if err := ec.c.CallContext(ctx, &ver, "net_version"); err != nil { - return nil, err - } - if _, ok := version.SetString(ver, 10); !ok { - return nil, fmt.Errorf("invalid net_version result %q", ver) - } - return version, nil -} - -// BalanceAt returns the wei balance of the given account. -// The block number can be nil, in which case the balance is taken from the latest known block. -func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { - var result hexutil.Big - err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber)) - return (*big.Int)(&result), err -} - -// BalanceAtHash returns the wei balance of the given account. -func (ec *Client) BalanceAtHash(ctx context.Context, account common.Address, blockHash common.Hash) (*big.Int, error) { - var result hexutil.Big - err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, rpc.BlockNumberOrHashWithHash(blockHash, false)) - return (*big.Int)(&result), err -} - -// StorageAt returns the value of key in the contract storage of the given account. -// The block number can be nil, in which case the value is taken from the latest known block. -func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { - var result hexutil.Bytes - err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, toBlockNumArg(blockNumber)) - return result, err -} - -// StorageAtHash returns the value of key in the contract storage of the given account. -func (ec *Client) StorageAtHash(ctx context.Context, account common.Address, key common.Hash, blockHash common.Hash) ([]byte, error) { - var result hexutil.Bytes - err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, rpc.BlockNumberOrHashWithHash(blockHash, false)) - return result, err -} - -// CodeAt returns the contract code of the given account. -// The block number can be nil, in which case the code is taken from the latest known block. -func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { - var result hexutil.Bytes - err := ec.c.CallContext(ctx, &result, "eth_getCode", account, toBlockNumArg(blockNumber)) - return result, err -} - -// CodeAtHash returns the contract code of the given account. -func (ec *Client) CodeAtHash(ctx context.Context, account common.Address, blockHash common.Hash) ([]byte, error) { - var result hexutil.Bytes - err := ec.c.CallContext(ctx, &result, "eth_getCode", account, rpc.BlockNumberOrHashWithHash(blockHash, false)) - return result, err -} - -// NonceAt returns the account nonce of the given account. -// The block number can be nil, in which case the nonce is taken from the latest known block. -func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { - var result hexutil.Uint64 - err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber)) - return uint64(result), err -} - -// NonceAtHash returns the account nonce of the given account. -func (ec *Client) NonceAtHash(ctx context.Context, account common.Address, blockHash common.Hash) (uint64, error) { - var result hexutil.Uint64 - err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, rpc.BlockNumberOrHashWithHash(blockHash, false)) - return uint64(result), err -} - -// Filters - -// FilterLogs executes a filter query. -func (ec *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { - var result []types.Log - arg, err := toFilterArg(q) - if err != nil { - return nil, err - } - err = ec.c.CallContext(ctx, &result, "eth_getLogs", arg) - return result, err -} - -// SubscribeFilterLogs subscribes to the results of a streaming filter query. -func (ec *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { - arg, err := toFilterArg(q) - if err != nil { - return nil, err - } - sub, err := ec.c.EthSubscribe(ctx, ch, "logs", arg) - if err != nil { - // Defensively prefer returning nil interface explicitly on error-path, instead - // of letting default golang behavior wrap it with non-nil interface that stores - // nil concrete type value. - return nil, err - } - return sub, nil -} - -func toFilterArg(q ethereum.FilterQuery) (interface{}, error) { - arg := map[string]interface{}{ - "address": q.Addresses, - "topics": q.Topics, - } - if q.BlockHash != nil { - arg["blockHash"] = *q.BlockHash - if q.FromBlock != nil || q.ToBlock != nil { - return nil, errors.New("cannot specify both BlockHash and FromBlock/ToBlock") - } - } else { - if q.FromBlock == nil { - arg["fromBlock"] = "0x0" - } else { - arg["fromBlock"] = toBlockNumArg(q.FromBlock) - } - arg["toBlock"] = toBlockNumArg(q.ToBlock) - } - return arg, nil -} - -// Pending State - -// PendingBalanceAt returns the wei balance of the given account in the pending state. -func (ec *Client) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) { - var result hexutil.Big - err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, "pending") - return (*big.Int)(&result), err -} - -// PendingStorageAt returns the value of key in the contract storage of the given account in the pending state. -func (ec *Client) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) { - var result hexutil.Bytes - err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, "pending") - return result, err -} - -// PendingCodeAt returns the contract code of the given account in the pending state. -func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { - var result hexutil.Bytes - err := ec.c.CallContext(ctx, &result, "eth_getCode", account, "pending") - return result, err -} - -// PendingNonceAt returns the account nonce of the given account in the pending state. -// This is the nonce that should be used for the next transaction. -func (ec *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { - var result hexutil.Uint64 - err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending") - return uint64(result), err -} - -// PendingTransactionCount returns the total number of transactions in the pending state. -func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) { - var num hexutil.Uint - err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", "pending") - return uint(num), err -} - -// Contract Calling - -// CallContract 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. -func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { - var hex hexutil.Bytes - err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber)) - if err != nil { - return nil, err - } - return hex, nil -} - -// CallContractAtHash is almost the same as CallContract except that it selects -// the block by block hash instead of block height. -func (ec *Client) CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error) { - var hex hexutil.Bytes - err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), rpc.BlockNumberOrHashWithHash(blockHash, false)) - 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) { - var hex hexutil.Bytes - err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending") - if err != nil { - return nil, err - } - return hex, nil -} - -// SuggestGasPrice retrieves the currently suggested gas price to allow a timely -// execution of a transaction. -func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - var hex hexutil.Big - if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil { - return nil, err - } - return (*big.Int)(&hex), nil -} - -// SuggestGasTipCap retrieves the currently suggested gas tip cap after 1559 to -// allow a timely execution of a transaction. -func (ec *Client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { - var hex hexutil.Big - if err := ec.c.CallContext(ctx, &hex, "eth_maxPriorityFeePerGas"); err != nil { - return nil, err - } - return (*big.Int)(&hex), nil -} - -type feeHistoryResultMarshaling struct { - OldestBlock *hexutil.Big `json:"oldestBlock"` - Reward [][]*hexutil.Big `json:"reward,omitempty"` - BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"` - GasUsedRatio []float64 `json:"gasUsedRatio"` -} - -// FeeHistory retrieves the fee market history. -func (ec *Client) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) { - var res feeHistoryResultMarshaling - if err := ec.c.CallContext(ctx, &res, "eth_feeHistory", hexutil.Uint(blockCount), toBlockNumArg(lastBlock), rewardPercentiles); err != nil { - return nil, err - } - reward := make([][]*big.Int, len(res.Reward)) - for i, r := range res.Reward { - reward[i] = make([]*big.Int, len(r)) - for j, r := range r { - reward[i][j] = (*big.Int)(r) - } - } - baseFee := make([]*big.Int, len(res.BaseFee)) - for i, b := range res.BaseFee { - baseFee[i] = (*big.Int)(b) - } - return ðereum.FeeHistory{ - OldestBlock: (*big.Int)(res.OldestBlock), - Reward: reward, - BaseFee: baseFee, - GasUsedRatio: res.GasUsedRatio, - }, nil -} - -// EstimateGas tries to estimate the gas needed to execute a specific transaction based on -// the current pending state of the backend blockchain. There is no guarantee that this is -// the true gas limit requirement as other transactions may be added or removed by miners, -// but it should provide a basis for setting a reasonable default. -func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) { - var hex hexutil.Uint64 - err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg)) - if err != nil { - return 0, err - } - return uint64(hex), nil -} - -// SendTransaction injects a signed transaction into the pending pool for execution. -// -// If the transaction was a contract creation use the TransactionReceipt method to get the -// contract address after the transaction has been mined. -func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error { - data, err := tx.MarshalBinary() - if err != nil { - return err - } - return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", hexutil.Encode(data)) -} - -func toBlockNumArg(number *big.Int) string { - if number == nil { - return "latest" - } - if number.Sign() >= 0 { - return hexutil.EncodeBig(number) - } - // It's negative. - if number.IsInt64() { - return rpc.BlockNumber(number.Int64()).String() - } - // It's negative and large, which is invalid. - return fmt.Sprintf("", number) -} - -func toCallArg(msg ethereum.CallMsg) interface{} { - arg := map[string]interface{}{ - "from": msg.From, - "to": msg.To, - } - if len(msg.Data) > 0 { - arg["input"] = hexutil.Bytes(msg.Data) - } - if msg.Value != nil { - arg["value"] = (*hexutil.Big)(msg.Value) - } - if msg.Gas != 0 { - arg["gas"] = hexutil.Uint64(msg.Gas) - } - if msg.GasPrice != nil { - arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) - } - if msg.GasFeeCap != nil { - arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap) - } - if msg.GasTipCap != nil { - arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap) - } - return arg -} - -// rpcProgress is a copy of SyncProgress with hex-encoded fields. -type rpcProgress struct { - StartingBlock hexutil.Uint64 - CurrentBlock hexutil.Uint64 - HighestBlock hexutil.Uint64 - - PulledStates hexutil.Uint64 - KnownStates hexutil.Uint64 - - SyncedAccounts hexutil.Uint64 - SyncedAccountBytes hexutil.Uint64 - SyncedBytecodes hexutil.Uint64 - SyncedBytecodeBytes hexutil.Uint64 - SyncedStorage hexutil.Uint64 - SyncedStorageBytes hexutil.Uint64 - HealedTrienodes hexutil.Uint64 - HealedTrienodeBytes hexutil.Uint64 - HealedBytecodes hexutil.Uint64 - HealedBytecodeBytes hexutil.Uint64 - HealingTrienodes hexutil.Uint64 - HealingBytecode hexutil.Uint64 -} - -func (p *rpcProgress) toSyncProgress() *ethereum.SyncProgress { - if p == nil { - return nil - } - return ðereum.SyncProgress{ - StartingBlock: uint64(p.StartingBlock), - CurrentBlock: uint64(p.CurrentBlock), - HighestBlock: uint64(p.HighestBlock), - PulledStates: uint64(p.PulledStates), - KnownStates: uint64(p.KnownStates), - SyncedAccounts: uint64(p.SyncedAccounts), - SyncedAccountBytes: uint64(p.SyncedAccountBytes), - SyncedBytecodes: uint64(p.SyncedBytecodes), - SyncedBytecodeBytes: uint64(p.SyncedBytecodeBytes), - SyncedStorage: uint64(p.SyncedStorage), - SyncedStorageBytes: uint64(p.SyncedStorageBytes), - HealedTrienodes: uint64(p.HealedTrienodes), - HealedTrienodeBytes: uint64(p.HealedTrienodeBytes), - HealedBytecodes: uint64(p.HealedBytecodes), - HealedBytecodeBytes: uint64(p.HealedBytecodeBytes), - HealingTrienodes: uint64(p.HealingTrienodes), - HealingBytecode: uint64(p.HealingBytecode), - } -} diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go deleted file mode 100644 index 0f87ad5f5c..0000000000 --- a/ethclient/ethclient_test.go +++ /dev/null @@ -1,745 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package ethclient - -import ( - "bytes" - "context" - "errors" - "math/big" - "reflect" - "testing" - "time" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" -) - -// Verify that Client implements the ethereum interfaces. -var ( - _ = ethereum.ChainReader(&Client{}) - _ = ethereum.TransactionReader(&Client{}) - _ = ethereum.ChainStateReader(&Client{}) - _ = ethereum.ChainSyncReader(&Client{}) - _ = ethereum.ContractCaller(&Client{}) - _ = ethereum.GasEstimator(&Client{}) - _ = ethereum.GasPricer(&Client{}) - _ = ethereum.LogFilterer(&Client{}) - _ = ethereum.PendingStateReader(&Client{}) - // _ = ethereum.PendingStateEventer(&Client{}) - _ = ethereum.PendingContractCaller(&Client{}) -) - -func TestToFilterArg(t *testing.T) { - blockHashErr := errors.New("cannot specify both BlockHash and FromBlock/ToBlock") - addresses := []common.Address{ - common.HexToAddress("0xD36722ADeC3EdCB29c8e7b5a47f352D701393462"), - } - blockHash := common.HexToHash( - "0xeb94bb7d78b73657a9d7a99792413f50c0a45c51fc62bdcb08a53f18e9a2b4eb", - ) - - for _, testCase := range []struct { - name string - input ethereum.FilterQuery - output interface{} - err error - }{ - { - "without BlockHash", - ethereum.FilterQuery{ - Addresses: addresses, - FromBlock: big.NewInt(1), - ToBlock: big.NewInt(2), - Topics: [][]common.Hash{}, - }, - map[string]interface{}{ - "address": addresses, - "fromBlock": "0x1", - "toBlock": "0x2", - "topics": [][]common.Hash{}, - }, - nil, - }, - { - "with nil fromBlock and nil toBlock", - ethereum.FilterQuery{ - Addresses: addresses, - Topics: [][]common.Hash{}, - }, - map[string]interface{}{ - "address": addresses, - "fromBlock": "0x0", - "toBlock": "latest", - "topics": [][]common.Hash{}, - }, - nil, - }, - { - "with negative fromBlock and negative toBlock", - ethereum.FilterQuery{ - Addresses: addresses, - FromBlock: big.NewInt(-1), - ToBlock: big.NewInt(-1), - Topics: [][]common.Hash{}, - }, - map[string]interface{}{ - "address": addresses, - "fromBlock": "pending", - "toBlock": "pending", - "topics": [][]common.Hash{}, - }, - nil, - }, - { - "with blockhash", - ethereum.FilterQuery{ - Addresses: addresses, - BlockHash: &blockHash, - Topics: [][]common.Hash{}, - }, - map[string]interface{}{ - "address": addresses, - "blockHash": blockHash, - "topics": [][]common.Hash{}, - }, - nil, - }, - { - "with blockhash and from block", - ethereum.FilterQuery{ - Addresses: addresses, - BlockHash: &blockHash, - FromBlock: big.NewInt(1), - Topics: [][]common.Hash{}, - }, - nil, - blockHashErr, - }, - { - "with blockhash and to block", - ethereum.FilterQuery{ - Addresses: addresses, - BlockHash: &blockHash, - ToBlock: big.NewInt(1), - Topics: [][]common.Hash{}, - }, - nil, - blockHashErr, - }, - { - "with blockhash and both from / to block", - ethereum.FilterQuery{ - Addresses: addresses, - BlockHash: &blockHash, - FromBlock: big.NewInt(1), - ToBlock: big.NewInt(2), - Topics: [][]common.Hash{}, - }, - nil, - blockHashErr, - }, - } { - t.Run(testCase.name, func(t *testing.T) { - output, err := toFilterArg(testCase.input) - if (testCase.err == nil) != (err == nil) { - t.Fatalf("expected error %v but got %v", testCase.err, err) - } - if testCase.err != nil { - if testCase.err.Error() != err.Error() { - t.Fatalf("expected error %v but got %v", testCase.err, err) - } - } else if !reflect.DeepEqual(testCase.output, output) { - t.Fatalf("expected filter arg %v but got %v", testCase.output, output) - } - }) - } -} - -var ( - testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testAddr = crypto.PubkeyToAddress(testKey.PublicKey) - testBalance = big.NewInt(2e15) -) - -var genesis = &core.Genesis{ - Config: params.AllEthashProtocolChanges, - Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, - ExtraData: []byte("test genesis"), - Timestamp: 9000, - BaseFee: big.NewInt(params.InitialBaseFee), -} - -var testTx1 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{ - Nonce: 0, - Value: big.NewInt(12), - GasPrice: big.NewInt(params.InitialBaseFee), - Gas: params.TxGas, - To: &common.Address{2}, -}) - -var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{ - Nonce: 1, - Value: big.NewInt(8), - GasPrice: big.NewInt(params.InitialBaseFee), - Gas: params.TxGas, - To: &common.Address{2}, -}) - -func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { - // Generate test chain. - blocks := generateTestChain() - - // Create node - n, err := node.New(&node.Config{}) - if err != nil { - t.Fatalf("can't create new node: %v", err) - } - // Create Ethereum Service - config := ðconfig.Config{Genesis: genesis} - ethservice, err := eth.New(n, config) - if err != nil { - t.Fatalf("can't create new ethereum service: %v", err) - } - // Import the test chain. - if err := n.Start(); err != nil { - t.Fatalf("can't start test node: %v", err) - } - if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil { - t.Fatalf("can't import test blocks: %v", err) - } - return n, blocks -} - -func generateTestChain() []*types.Block { - generate := func(i int, g *core.BlockGen) { - g.OffsetTime(5) - g.SetExtra([]byte("test")) - if i == 1 { - // Test transactions are included in block #2. - g.AddTx(testTx1) - g.AddTx(testTx2) - } - } - _, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 2, generate) - return append([]*types.Block{genesis.ToBlock()}, blocks...) -} - -func TestEthClient(t *testing.T) { - backend, chain := newTestBackend(t) - client := backend.Attach() - defer backend.Close() - defer client.Close() - - tests := map[string]struct { - test func(t *testing.T) - }{ - "Header": { - func(t *testing.T) { testHeader(t, chain, client) }, - }, - "BalanceAt": { - func(t *testing.T) { testBalanceAt(t, client) }, - }, - "TxInBlockInterrupted": { - func(t *testing.T) { testTransactionInBlockInterrupted(t, client) }, - }, - "ChainID": { - func(t *testing.T) { testChainID(t, client) }, - }, - "GetBlock": { - func(t *testing.T) { testGetBlock(t, client) }, - }, - "StatusFunctions": { - func(t *testing.T) { testStatusFunctions(t, client) }, - }, - "CallContract": { - func(t *testing.T) { testCallContract(t, client) }, - }, - "CallContractAtHash": { - func(t *testing.T) { testCallContractAtHash(t, client) }, - }, - "AtFunctions": { - func(t *testing.T) { testAtFunctions(t, client) }, - }, - "TransactionSender": { - func(t *testing.T) { testTransactionSender(t, client) }, - }, - } - - t.Parallel() - for name, tt := range tests { - t.Run(name, tt.test) - } -} - -func testHeader(t *testing.T, chain []*types.Block, client *rpc.Client) { - tests := map[string]struct { - block *big.Int - want *types.Header - wantErr error - }{ - "genesis": { - block: big.NewInt(0), - want: chain[0].Header(), - }, - "first_block": { - block: big.NewInt(1), - want: chain[1].Header(), - }, - "future_block": { - block: big.NewInt(1000000000), - want: nil, - wantErr: ethereum.NotFound, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - ec := NewClient(client) - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) - defer cancel() - - got, err := ec.HeaderByNumber(ctx, tt.block) - if !errors.Is(err, tt.wantErr) { - t.Fatalf("HeaderByNumber(%v) error = %q, want %q", tt.block, err, tt.wantErr) - } - if got != nil && got.Number != nil && got.Number.Sign() == 0 { - got.Number = big.NewInt(0) // hack to make DeepEqual work - } - if !reflect.DeepEqual(got, tt.want) { - t.Fatalf("HeaderByNumber(%v)\n = %v\nwant %v", tt.block, got, tt.want) - } - }) - } -} - -func testBalanceAt(t *testing.T, client *rpc.Client) { - tests := map[string]struct { - account common.Address - block *big.Int - want *big.Int - wantErr error - }{ - "valid_account_genesis": { - account: testAddr, - block: big.NewInt(0), - want: testBalance, - }, - "valid_account": { - account: testAddr, - block: big.NewInt(1), - want: testBalance, - }, - "non_existent_account": { - account: common.Address{1}, - block: big.NewInt(1), - want: big.NewInt(0), - }, - "future_block": { - account: testAddr, - block: big.NewInt(1000000000), - want: big.NewInt(0), - wantErr: errors.New("header not found"), - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - ec := NewClient(client) - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) - defer cancel() - - got, err := ec.BalanceAt(ctx, tt.account, tt.block) - if tt.wantErr != nil && (err == nil || err.Error() != tt.wantErr.Error()) { - t.Fatalf("BalanceAt(%x, %v) error = %q, want %q", tt.account, tt.block, err, tt.wantErr) - } - if got.Cmp(tt.want) != 0 { - t.Fatalf("BalanceAt(%x, %v) = %v, want %v", tt.account, tt.block, got, tt.want) - } - }) - } -} - -func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) { - ec := NewClient(client) - - // Get current block by number. - block, err := ec.BlockByNumber(context.Background(), nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - // Test tx in block interrupted. - ctx, cancel := context.WithCancel(context.Background()) - cancel() - <-ctx.Done() // Ensure the close of the Done channel - tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0) - if tx != nil { - t.Fatal("transaction should be nil") - } - if err == nil || err == ethereum.NotFound { - t.Fatal("error should not be nil/notfound") - } - - // Test tx in block not found. - if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 20); err != ethereum.NotFound { - t.Fatal("error should be ethereum.NotFound") - } -} - -func testChainID(t *testing.T, client *rpc.Client) { - ec := NewClient(client) - id, err := ec.ChainID(context.Background()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if id == nil || id.Cmp(params.AllEthashProtocolChanges.ChainID) != 0 { - t.Fatalf("ChainID returned wrong number: %+v", id) - } -} - -func testGetBlock(t *testing.T, client *rpc.Client) { - ec := NewClient(client) - - // Get current block number - blockNumber, err := ec.BlockNumber(context.Background()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if blockNumber != 2 { - t.Fatalf("BlockNumber returned wrong number: %d", blockNumber) - } - // Get current block by number - block, err := ec.BlockByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if block.NumberU64() != blockNumber { - t.Fatalf("BlockByNumber returned wrong block: want %d got %d", blockNumber, block.NumberU64()) - } - // Get current block by hash - blockH, err := ec.BlockByHash(context.Background(), block.Hash()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if block.Hash() != blockH.Hash() { - t.Fatalf("BlockByHash returned wrong block: want %v got %v", block.Hash().Hex(), blockH.Hash().Hex()) - } - // Get header by number - header, err := ec.HeaderByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if block.Header().Hash() != header.Hash() { - t.Fatalf("HeaderByNumber returned wrong header: want %v got %v", block.Header().Hash().Hex(), header.Hash().Hex()) - } - // Get header by hash - headerH, err := ec.HeaderByHash(context.Background(), block.Hash()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if block.Header().Hash() != headerH.Hash() { - t.Fatalf("HeaderByHash returned wrong header: want %v got %v", block.Header().Hash().Hex(), headerH.Hash().Hex()) - } -} - -func testStatusFunctions(t *testing.T, client *rpc.Client) { - ec := NewClient(client) - - // Sync progress - progress, err := ec.SyncProgress(context.Background()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if progress != nil { - t.Fatalf("unexpected progress: %v", progress) - } - - // NetworkID - networkID, err := ec.NetworkID(context.Background()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if networkID.Cmp(big.NewInt(1337)) != 0 { - t.Fatalf("unexpected networkID: %v", networkID) - } - - // SuggestGasPrice - gasPrice, err := ec.SuggestGasPrice(context.Background()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if gasPrice.Cmp(big.NewInt(1000000000)) != 0 { - t.Fatalf("unexpected gas price: %v", gasPrice) - } - - // SuggestGasTipCap - gasTipCap, err := ec.SuggestGasTipCap(context.Background()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if gasTipCap.Cmp(big.NewInt(234375000)) != 0 { - t.Fatalf("unexpected gas tip cap: %v", gasTipCap) - } - - // FeeHistory - history, err := ec.FeeHistory(context.Background(), 1, big.NewInt(2), []float64{95, 99}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - want := ðereum.FeeHistory{ - OldestBlock: big.NewInt(2), - Reward: [][]*big.Int{ - { - big.NewInt(234375000), - big.NewInt(234375000), - }, - }, - BaseFee: []*big.Int{ - big.NewInt(765625000), - big.NewInt(671627818), - }, - GasUsedRatio: []float64{0.008912678667376286}, - } - if !reflect.DeepEqual(history, want) { - t.Fatalf("FeeHistory result doesn't match expected: (got: %v, want: %v)", history, want) - } -} - -func testCallContractAtHash(t *testing.T, client *rpc.Client) { - ec := NewClient(client) - - // EstimateGas - msg := ethereum.CallMsg{ - From: testAddr, - To: &common.Address{}, - Gas: 21000, - Value: big.NewInt(1), - } - gas, err := ec.EstimateGas(context.Background(), msg) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if gas != 21000 { - t.Fatalf("unexpected gas price: %v", gas) - } - block, err := ec.HeaderByNumber(context.Background(), big.NewInt(1)) - if err != nil { - t.Fatalf("BlockByNumber error: %v", err) - } - // CallContract - if _, err := ec.CallContractAtHash(context.Background(), msg, block.Hash()); err != nil { - t.Fatalf("unexpected error: %v", err) - } -} - -func testCallContract(t *testing.T, client *rpc.Client) { - ec := NewClient(client) - - // EstimateGas - msg := ethereum.CallMsg{ - From: testAddr, - To: &common.Address{}, - Gas: 21000, - Value: big.NewInt(1), - } - gas, err := ec.EstimateGas(context.Background(), msg) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if gas != 21000 { - t.Fatalf("unexpected gas price: %v", gas) - } - // CallContract - if _, err := ec.CallContract(context.Background(), msg, big.NewInt(1)); err != nil { - t.Fatalf("unexpected error: %v", err) - } - // PendingCallContract - if _, err := ec.PendingCallContract(context.Background(), msg); err != nil { - t.Fatalf("unexpected error: %v", err) - } -} - -func testAtFunctions(t *testing.T, client *rpc.Client) { - ec := NewClient(client) - - block, err := ec.HeaderByNumber(context.Background(), big.NewInt(1)) - if err != nil { - t.Fatalf("BlockByNumber error: %v", err) - } - - // send a transaction for some interesting pending status - sendTransaction(ec) - time.Sleep(100 * time.Millisecond) - - // Check pending transaction count - pending, err := ec.PendingTransactionCount(context.Background()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if pending != 1 { - t.Fatalf("unexpected pending, wanted 1 got: %v", pending) - } - // Query balance - balance, err := ec.BalanceAt(context.Background(), testAddr, nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - hashBalance, err := ec.BalanceAtHash(context.Background(), testAddr, block.Hash()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if balance.Cmp(hashBalance) == 0 { - t.Fatalf("unexpected balance at hash: %v %v", balance, hashBalance) - } - penBalance, err := ec.PendingBalanceAt(context.Background(), testAddr) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if balance.Cmp(penBalance) == 0 { - t.Fatalf("unexpected balance: %v %v", balance, penBalance) - } - // NonceAt - nonce, err := ec.NonceAt(context.Background(), testAddr, nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - hashNonce, err := ec.NonceAtHash(context.Background(), testAddr, block.Hash()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if hashNonce == nonce { - t.Fatalf("unexpected nonce at hash: %v %v", nonce, hashNonce) - } - penNonce, err := ec.PendingNonceAt(context.Background(), testAddr) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if penNonce != nonce+1 { - t.Fatalf("unexpected nonce: %v %v", nonce, penNonce) - } - // StorageAt - storage, err := ec.StorageAt(context.Background(), testAddr, common.Hash{}, nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - hashStorage, err := ec.StorageAtHash(context.Background(), testAddr, common.Hash{}, block.Hash()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !bytes.Equal(storage, hashStorage) { - t.Fatalf("unexpected storage at hash: %v %v", storage, hashStorage) - } - penStorage, err := ec.PendingStorageAt(context.Background(), testAddr, common.Hash{}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !bytes.Equal(storage, penStorage) { - t.Fatalf("unexpected storage: %v %v", storage, penStorage) - } - // CodeAt - code, err := ec.CodeAt(context.Background(), testAddr, nil) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - hashCode, err := ec.CodeAtHash(context.Background(), common.Address{}, block.Hash()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !bytes.Equal(code, hashCode) { - t.Fatalf("unexpected code at hash: %v %v", code, hashCode) - } - penCode, err := ec.PendingCodeAt(context.Background(), testAddr) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !bytes.Equal(code, penCode) { - t.Fatalf("unexpected code: %v %v", code, penCode) - } -} - -func testTransactionSender(t *testing.T, client *rpc.Client) { - ec := NewClient(client) - ctx := context.Background() - - // Retrieve testTx1 via RPC. - block2, err := ec.HeaderByNumber(ctx, big.NewInt(2)) - if err != nil { - t.Fatal("can't get block 1:", err) - } - tx1, err := ec.TransactionInBlock(ctx, block2.Hash(), 0) - if err != nil { - t.Fatal("can't get tx:", err) - } - if tx1.Hash() != testTx1.Hash() { - t.Fatalf("wrong tx hash %v, want %v", tx1.Hash(), testTx1.Hash()) - } - - // The sender address is cached in tx1, so no additional RPC should be required in - // TransactionSender. Ensure the server is not asked by canceling the context here. - canceledCtx, cancel := context.WithCancel(context.Background()) - cancel() - <-canceledCtx.Done() // Ensure the close of the Done channel - sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0) - if err != nil { - t.Fatal(err) - } - if sender1 != testAddr { - t.Fatal("wrong sender:", sender1) - } - - // Now try to get the sender of testTx2, which was not fetched through RPC. - // TransactionSender should query the server here. - sender2, err := ec.TransactionSender(ctx, testTx2, block2.Hash(), 1) - if err != nil { - t.Fatal(err) - } - if sender2 != testAddr { - t.Fatal("wrong sender:", sender2) - } -} - -func sendTransaction(ec *Client) error { - chainID, err := ec.ChainID(context.Background()) - if err != nil { - return err - } - nonce, err := ec.PendingNonceAt(context.Background(), testAddr) - if err != nil { - return err - } - - signer := types.LatestSignerForChainID(chainID) - tx, err := types.SignNewTx(testKey, signer, &types.LegacyTx{ - Nonce: nonce, - To: &common.Address{2}, - Value: big.NewInt(1), - Gas: 22000, - GasPrice: big.NewInt(params.InitialBaseFee), - }) - if err != nil { - return err - } - return ec.SendTransaction(context.Background(), tx) -} diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go deleted file mode 100644 index e2c0ef3ed0..0000000000 --- a/ethclient/gethclient/gethclient.go +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package gethclient provides an RPC client for geth-specific APIs. -package gethclient - -import ( - "context" - "encoding/json" - "fmt" - "math/big" - "runtime" - "runtime/debug" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rpc" -) - -// Client is a wrapper around rpc.Client that implements geth-specific functionality. -// -// If you want to use the standardized Ethereum RPC functionality, use ethclient.Client instead. -type Client struct { - c *rpc.Client -} - -// New creates a client that uses the given RPC client. -func New(c *rpc.Client) *Client { - return &Client{c} -} - -// CreateAccessList tries to create an access list for a specific transaction based on the -// current pending state of the blockchain. -func (ec *Client) CreateAccessList(ctx context.Context, msg ethereum.CallMsg) (*types.AccessList, uint64, string, error) { - type accessListResult struct { - Accesslist *types.AccessList `json:"accessList"` - Error string `json:"error,omitempty"` - GasUsed hexutil.Uint64 `json:"gasUsed"` - } - var result accessListResult - if err := ec.c.CallContext(ctx, &result, "eth_createAccessList", toCallArg(msg)); err != nil { - return nil, 0, "", err - } - return result.Accesslist, uint64(result.GasUsed), result.Error, nil -} - -// AccountResult is the result of a GetProof operation. -type AccountResult struct { - Address common.Address `json:"address"` - AccountProof []string `json:"accountProof"` - Balance *big.Int `json:"balance"` - CodeHash common.Hash `json:"codeHash"` - Nonce uint64 `json:"nonce"` - StorageHash common.Hash `json:"storageHash"` - StorageProof []StorageResult `json:"storageProof"` -} - -// StorageResult provides a proof for a key-value pair. -type StorageResult struct { - Key string `json:"key"` - Value *big.Int `json:"value"` - Proof []string `json:"proof"` -} - -// GetProof returns the account and storage values of the specified account including the Merkle-proof. -// The block number can be nil, in which case the value is taken from the latest known block. -func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []string, blockNumber *big.Int) (*AccountResult, error) { - type storageResult struct { - Key string `json:"key"` - Value *hexutil.Big `json:"value"` - Proof []string `json:"proof"` - } - - type accountResult struct { - Address common.Address `json:"address"` - AccountProof []string `json:"accountProof"` - Balance *hexutil.Big `json:"balance"` - CodeHash common.Hash `json:"codeHash"` - Nonce hexutil.Uint64 `json:"nonce"` - StorageHash common.Hash `json:"storageHash"` - StorageProof []storageResult `json:"storageProof"` - } - - // Avoid keys being 'null'. - if keys == nil { - keys = []string{} - } - - var res accountResult - err := ec.c.CallContext(ctx, &res, "eth_getProof", account, keys, toBlockNumArg(blockNumber)) - // Turn hexutils back to normal datatypes - storageResults := make([]StorageResult, 0, len(res.StorageProof)) - for _, st := range res.StorageProof { - storageResults = append(storageResults, StorageResult{ - Key: st.Key, - Value: st.Value.ToInt(), - Proof: st.Proof, - }) - } - result := AccountResult{ - Address: res.Address, - AccountProof: res.AccountProof, - Balance: res.Balance.ToInt(), - Nonce: uint64(res.Nonce), - CodeHash: res.CodeHash, - StorageHash: res.StorageHash, - StorageProof: storageResults, - } - return &result, err -} - -// CallContract 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. -// Please use ethclient.CallContract instead if you don't need the override functionality. -func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int, overrides *map[common.Address]OverrideAccount) ([]byte, error) { - var hex hexutil.Bytes - err := ec.c.CallContext( - ctx, &hex, "eth_call", toCallArg(msg), - toBlockNumArg(blockNumber), overrides, - ) - 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. -func (ec *Client) GCStats(ctx context.Context) (*debug.GCStats, error) { - var result debug.GCStats - err := ec.c.CallContext(ctx, &result, "debug_gcStats") - return &result, err -} - -// MemStats retrieves the current memory stats from a geth node. -func (ec *Client) MemStats(ctx context.Context) (*runtime.MemStats, error) { - var result runtime.MemStats - err := ec.c.CallContext(ctx, &result, "debug_memStats") - return &result, err -} - -// SetHead sets the current head of the local chain by block number. -// Note, this is a destructive action and may severely damage your chain. -// Use with extreme caution. -func (ec *Client) SetHead(ctx context.Context, number *big.Int) error { - return ec.c.CallContext(ctx, nil, "debug_setHead", toBlockNumArg(number)) -} - -// GetNodeInfo retrieves the node info of a geth node. -func (ec *Client) GetNodeInfo(ctx context.Context) (*p2p.NodeInfo, error) { - var result p2p.NodeInfo - err := ec.c.CallContext(ctx, &result, "admin_nodeInfo") - return &result, err -} - -// SubscribeFullPendingTransactions subscribes to new pending transactions. -func (ec *Client) SubscribeFullPendingTransactions(ctx context.Context, ch chan<- *types.Transaction) (*rpc.ClientSubscription, error) { - return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions", true) -} - -// SubscribePendingTransactions subscribes to new pending transaction hashes. -func (ec *Client) SubscribePendingTransactions(ctx context.Context, ch chan<- common.Hash) (*rpc.ClientSubscription, error) { - return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions") -} - -func toBlockNumArg(number *big.Int) string { - if number == nil { - return "latest" - } - if number.Sign() >= 0 { - return hexutil.EncodeBig(number) - } - // It's negative. - if number.IsInt64() { - return rpc.BlockNumber(number.Int64()).String() - } - // It's negative and large, which is invalid. - return fmt.Sprintf("", number) -} - -func toCallArg(msg ethereum.CallMsg) interface{} { - arg := map[string]interface{}{ - "from": msg.From, - "to": msg.To, - } - if len(msg.Data) > 0 { - arg["input"] = hexutil.Bytes(msg.Data) - } - if msg.Value != nil { - arg["value"] = (*hexutil.Big)(msg.Value) - } - if msg.Gas != 0 { - arg["gas"] = hexutil.Uint64(msg.Gas) - } - if msg.GasPrice != nil { - arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) - } - return arg -} - -// OverrideAccount specifies the state of an account to be overridden. -type OverrideAccount struct { - // Nonce sets nonce of the account. Note: the nonce override will only - // be applied when it is set to a non-zero value. - Nonce uint64 - - // Code sets the contract code. The override will be applied - // when the code is non-nil, i.e. setting empty code is possible - // using an empty slice. - Code []byte - - // Balance sets the account balance. - Balance *big.Int - - // State sets the complete storage. The override will be applied - // when the given map is non-nil. Using an empty map wipes the - // entire contract storage during the call. - State map[common.Hash]common.Hash - - // StateDiff allows overriding individual storage slots. - StateDiff map[common.Hash]common.Hash -} - -func (a OverrideAccount) MarshalJSON() ([]byte, error) { - type acc struct { - Nonce hexutil.Uint64 `json:"nonce,omitempty"` - Code string `json:"code,omitempty"` - Balance *hexutil.Big `json:"balance,omitempty"` - State interface{} `json:"state,omitempty"` - StateDiff map[common.Hash]common.Hash `json:"stateDiff,omitempty"` - } - - output := acc{ - Nonce: hexutil.Uint64(a.Nonce), - Balance: (*hexutil.Big)(a.Balance), - StateDiff: a.StateDiff, - } - if a.Code != nil { - output.Code = hexutil.Encode(a.Code) - } - if a.State != nil { - output.State = a.State - } - 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) -} diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go deleted file mode 100644 index fdd94a7d73..0000000000 --- a/ethclient/gethclient/gethclient_test.go +++ /dev/null @@ -1,570 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package gethclient - -import ( - "bytes" - "context" - "encoding/json" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/eth/filters" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" -) - -var ( - testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testAddr = crypto.PubkeyToAddress(testKey.PublicKey) - testContract = common.HexToAddress("0xbeef") - testEmpty = common.HexToAddress("0xeeee") - testSlot = common.HexToHash("0xdeadbeef") - testValue = crypto.Keccak256Hash(testSlot[:]) - testBalance = big.NewInt(2e15) -) - -func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { - // Generate test chain. - genesis, blocks := generateTestChain() - // Create node - n, err := node.New(&node.Config{}) - if err != nil { - t.Fatalf("can't create new node: %v", err) - } - // Create Ethereum Service - config := ðconfig.Config{Genesis: genesis} - ethservice, err := eth.New(n, config) - if err != nil { - t.Fatalf("can't create new ethereum service: %v", err) - } - filterSystem := filters.NewFilterSystem(ethservice.APIBackend, filters.Config{}) - n.RegisterAPIs([]rpc.API{{ - Namespace: "eth", - Service: filters.NewFilterAPI(filterSystem, false), - }}) - - // Import the test chain. - if err := n.Start(); err != nil { - t.Fatalf("can't start test node: %v", err) - } - if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil { - t.Fatalf("can't import test blocks: %v", err) - } - return n, blocks -} - -func generateTestChain() (*core.Genesis, []*types.Block) { - genesis := &core.Genesis{ - Config: params.AllEthashProtocolChanges, - Alloc: core.GenesisAlloc{ - testAddr: {Balance: testBalance, Storage: map[common.Hash]common.Hash{testSlot: testValue}}, - testContract: {Nonce: 1, Code: []byte{0x13, 0x37}}, - testEmpty: {Balance: big.NewInt(1)}, - }, - ExtraData: []byte("test genesis"), - Timestamp: 9000, - } - generate := func(i int, g *core.BlockGen) { - g.OffsetTime(5) - g.SetExtra([]byte("test")) - } - _, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 1, generate) - blocks = append([]*types.Block{genesis.ToBlock()}, blocks...) - return genesis, blocks -} - -func TestGethClient(t *testing.T) { - backend, _ := newTestBackend(t) - client := backend.Attach() - defer backend.Close() - defer client.Close() - - tests := []struct { - name string - test func(t *testing.T) - }{ - { - "TestGetProof1", - func(t *testing.T) { testGetProof(t, client, testAddr) }, - }, { - "TestGetProof2", - func(t *testing.T) { testGetProof(t, client, testContract) }, - }, { - "TestGetProofEmpty", - func(t *testing.T) { testGetProof(t, client, testEmpty) }, - }, { - "TestGetProofNonExistent", - func(t *testing.T) { testGetProofNonExistent(t, client) }, - }, { - "TestGetProofCanonicalizeKeys", - func(t *testing.T) { testGetProofCanonicalizeKeys(t, client) }, - }, { - "TestGCStats", - func(t *testing.T) { testGCStats(t, client) }, - }, { - "TestMemStats", - func(t *testing.T) { testMemStats(t, client) }, - }, { - "TestGetNodeInfo", - func(t *testing.T) { testGetNodeInfo(t, client) }, - }, { - "TestSubscribePendingTxHashes", - func(t *testing.T) { testSubscribePendingTransactions(t, client) }, - }, { - "TestSubscribePendingTxs", - func(t *testing.T) { testSubscribeFullPendingTransactions(t, client) }, - }, { - "TestCallContract", - 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 - // one block. The `testAcessList` fails if the miner has not yet created a - // new pending-block after the import event. - // Hence: this test should be last, execute the tests serially. - { - "TestAccessList", - func(t *testing.T) { testAccessList(t, client) }, - }, { - "TestSetHead", - func(t *testing.T) { testSetHead(t, client) }, - }, - } - for _, tt := range tests { - t.Run(tt.name, tt.test) - } -} - -func testAccessList(t *testing.T, client *rpc.Client) { - ec := New(client) - // Test transfer - msg := ethereum.CallMsg{ - From: testAddr, - To: &common.Address{}, - Gas: 21000, - GasPrice: big.NewInt(765625000), - Value: big.NewInt(1), - } - al, gas, vmErr, err := ec.CreateAccessList(context.Background(), msg) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if vmErr != "" { - t.Fatalf("unexpected vm error: %v", vmErr) - } - if gas != 21000 { - t.Fatalf("unexpected gas used: %v", gas) - } - if len(*al) != 0 { - t.Fatalf("unexpected length of accesslist: %v", len(*al)) - } - // Test reverting transaction - msg = ethereum.CallMsg{ - From: testAddr, - To: nil, - Gas: 100000, - GasPrice: big.NewInt(1000000000), - Value: big.NewInt(1), - Data: common.FromHex("0x608060806080608155fd"), - } - al, gas, vmErr, err = ec.CreateAccessList(context.Background(), msg) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if vmErr == "" { - t.Fatalf("wanted vmErr, got none") - } - if gas == 21000 { - t.Fatalf("unexpected gas used: %v", gas) - } - if len(*al) != 1 || al.StorageKeys() != 1 { - t.Fatalf("unexpected length of accesslist: %v", len(*al)) - } - // address changes between calls, so we can't test for it. - if (*al)[0].Address == common.HexToAddress("0x0") { - t.Fatalf("unexpected address: %v", (*al)[0].Address) - } - if (*al)[0].StorageKeys[0] != common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000081") { - t.Fatalf("unexpected storage key: %v", (*al)[0].StorageKeys[0]) - } -} - -func testGetProof(t *testing.T, client *rpc.Client, addr common.Address) { - ec := New(client) - ethcl := ethclient.NewClient(client) - result, err := ec.GetProof(context.Background(), addr, []string{testSlot.String()}, nil) - if err != nil { - t.Fatal(err) - } - if result.Address != addr { - t.Fatalf("unexpected address, have: %v want: %v", result.Address, addr) - } - // test nonce - if nonce, _ := ethcl.NonceAt(context.Background(), addr, nil); result.Nonce != nonce { - t.Fatalf("invalid nonce, want: %v got: %v", nonce, result.Nonce) - } - // test balance - if balance, _ := ethcl.BalanceAt(context.Background(), addr, nil); result.Balance.Cmp(balance) != 0 { - t.Fatalf("invalid balance, want: %v got: %v", balance, result.Balance) - } - // test storage - if len(result.StorageProof) != 1 { - t.Fatalf("invalid storage proof, want 1 proof, got %v proof(s)", len(result.StorageProof)) - } - for _, proof := range result.StorageProof { - if proof.Key != testSlot.String() { - t.Fatalf("invalid storage proof key, want: %q, got: %q", testSlot.String(), proof.Key) - } - slotValue, _ := ethcl.StorageAt(context.Background(), addr, common.HexToHash(proof.Key), nil) - if have, want := common.BigToHash(proof.Value), common.BytesToHash(slotValue); have != want { - t.Fatalf("addr %x, invalid storage proof value: have: %v, want: %v", addr, have, want) - } - } - // test code - code, _ := ethcl.CodeAt(context.Background(), addr, nil) - if have, want := result.CodeHash, crypto.Keccak256Hash(code); have != want { - t.Fatalf("codehash wrong, have %v want %v ", have, want) - } -} - -func testGetProofCanonicalizeKeys(t *testing.T, client *rpc.Client) { - ec := New(client) - - // Tests with non-canon input for storage keys. - // Here we check that the storage key is canonicalized. - result, err := ec.GetProof(context.Background(), testAddr, []string{"0x0dEadbeef"}, nil) - if err != nil { - t.Fatal(err) - } - if result.StorageProof[0].Key != "0xdeadbeef" { - t.Fatalf("wrong storage key encoding in proof: %q", result.StorageProof[0].Key) - } - if result, err = ec.GetProof(context.Background(), testAddr, []string{"0x000deadbeef"}, nil); err != nil { - t.Fatal(err) - } - if result.StorageProof[0].Key != "0xdeadbeef" { - t.Fatalf("wrong storage key encoding in proof: %q", result.StorageProof[0].Key) - } - - // If the requested storage key is 32 bytes long, it will be returned as is. - hashSizedKey := "0x00000000000000000000000000000000000000000000000000000000deadbeef" - result, err = ec.GetProof(context.Background(), testAddr, []string{hashSizedKey}, nil) - if err != nil { - t.Fatal(err) - } - if result.StorageProof[0].Key != hashSizedKey { - t.Fatalf("wrong storage key encoding in proof: %q", result.StorageProof[0].Key) - } -} - -func testGetProofNonExistent(t *testing.T, client *rpc.Client) { - addr := common.HexToAddress("0x0001") - ec := New(client) - result, err := ec.GetProof(context.Background(), addr, nil, nil) - if err != nil { - t.Fatal(err) - } - if result.Address != addr { - t.Fatalf("unexpected address, have: %v want: %v", result.Address, addr) - } - // test nonce - if result.Nonce != 0 { - t.Fatalf("invalid nonce, want: %v got: %v", 0, result.Nonce) - } - // test balance - if result.Balance.Cmp(big.NewInt(0)) != 0 { - t.Fatalf("invalid balance, want: %v got: %v", 0, result.Balance) - } - // test storage - if have := len(result.StorageProof); have != 0 { - t.Fatalf("invalid storage proof, want 0 proof, got %v proof(s)", have) - } - // test codeHash - if have, want := result.CodeHash, (common.Hash{}); have != want { - t.Fatalf("codehash wrong, have %v want %v ", have, want) - } - // test codeHash - if have, want := result.StorageHash, (common.Hash{}); have != want { - t.Fatalf("storagehash wrong, have %v want %v ", have, want) - } -} - -func testGCStats(t *testing.T, client *rpc.Client) { - ec := New(client) - _, err := ec.GCStats(context.Background()) - if err != nil { - t.Fatal(err) - } -} - -func testMemStats(t *testing.T, client *rpc.Client) { - ec := New(client) - stats, err := ec.MemStats(context.Background()) - if err != nil { - t.Fatal(err) - } - if stats.Alloc == 0 { - t.Fatal("Invalid mem stats retrieved") - } -} - -func testGetNodeInfo(t *testing.T, client *rpc.Client) { - ec := New(client) - info, err := ec.GetNodeInfo(context.Background()) - if err != nil { - t.Fatal(err) - } - - if info.Name == "" { - t.Fatal("Invalid node info retrieved") - } -} - -func testSetHead(t *testing.T, client *rpc.Client) { - ec := New(client) - err := ec.SetHead(context.Background(), big.NewInt(0)) - if err != nil { - t.Fatal(err) - } -} - -func testSubscribePendingTransactions(t *testing.T, client *rpc.Client) { - ec := New(client) - ethcl := ethclient.NewClient(client) - // Subscribe to Transactions - ch := make(chan common.Hash) - ec.SubscribePendingTransactions(context.Background(), ch) - // Send a transaction - chainID, err := ethcl.ChainID(context.Background()) - if err != nil { - t.Fatal(err) - } - // Create transaction - tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil) - signer := types.LatestSignerForChainID(chainID) - signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) - if err != nil { - t.Fatal(err) - } - signedTx, err := tx.WithSignature(signer, signature) - if err != nil { - t.Fatal(err) - } - // Send transaction - err = ethcl.SendTransaction(context.Background(), signedTx) - if err != nil { - t.Fatal(err) - } - // Check that the transaction was sent over the channel - hash := <-ch - if hash != signedTx.Hash() { - t.Fatalf("Invalid tx hash received, got %v, want %v", hash, signedTx.Hash()) - } -} - -func testSubscribeFullPendingTransactions(t *testing.T, client *rpc.Client) { - ec := New(client) - ethcl := ethclient.NewClient(client) - // Subscribe to Transactions - ch := make(chan *types.Transaction) - ec.SubscribeFullPendingTransactions(context.Background(), ch) - // Send a transaction - chainID, err := ethcl.ChainID(context.Background()) - if err != nil { - t.Fatal(err) - } - // Create transaction - tx := types.NewTransaction(1, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil) - signer := types.LatestSignerForChainID(chainID) - signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) - if err != nil { - t.Fatal(err) - } - signedTx, err := tx.WithSignature(signer, signature) - if err != nil { - t.Fatal(err) - } - // Send transaction - err = ethcl.SendTransaction(context.Background(), signedTx) - if err != nil { - t.Fatal(err) - } - // Check that the transaction was sent over the channel - tx = <-ch - if tx.Hash() != signedTx.Hash() { - t.Fatalf("Invalid tx hash received, got %v, want %v", tx.Hash(), signedTx.Hash()) - } -} - -func testCallContract(t *testing.T, client *rpc.Client) { - ec := New(client) - msg := ethereum.CallMsg{ - From: testAddr, - To: &common.Address{}, - Gas: 21000, - GasPrice: big.NewInt(1000000000), - Value: big.NewInt(1), - } - // CallContract without override - if _, err := ec.CallContract(context.Background(), msg, big.NewInt(0), nil); err != nil { - t.Fatalf("unexpected error: %v", err) - } - // CallContract with override - override := OverrideAccount{ - Nonce: 1, - } - mapAcc := make(map[common.Address]OverrideAccount) - mapAcc[testAddr] = override - if _, err := ec.CallContract(context.Background(), msg, big.NewInt(0), &mapAcc); err != nil { - t.Fatalf("unexpected error: %v", err) - } -} - -func TestOverrideAccountMarshal(t *testing.T) { - om := map[common.Address]OverrideAccount{ - {0x11}: { - // Zero-valued nonce is not overridden, but simply dropped by the encoder. - Nonce: 0, - }, - {0xaa}: { - Nonce: 5, - }, - {0xbb}: { - Code: []byte{1}, - }, - {0xcc}: { - // 'code', 'balance', 'state' should be set when input is - // a non-nil but empty value. - Code: []byte{}, - Balance: big.NewInt(0), - State: map[common.Hash]common.Hash{}, - // For 'stateDiff' the behavior is different, empty map - // is ignored because it makes no difference. - StateDiff: map[common.Hash]common.Hash{}, - }, - } - - marshalled, err := json.MarshalIndent(&om, "", " ") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - expected := `{ - "0x1100000000000000000000000000000000000000": {}, - "0xaa00000000000000000000000000000000000000": { - "nonce": "0x5" - }, - "0xbb00000000000000000000000000000000000000": { - "code": "0x01" - }, - "0xcc00000000000000000000000000000000000000": { - "code": "0x", - "balance": "0x0", - "state": {} - } -}` - - if string(marshalled) != expected { - t.Error("wrong output:", string(marshalled)) - 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) - } -} diff --git a/ethclient/signer.go b/ethclient/signer.go deleted file mode 100644 index f827d4eb56..0000000000 --- a/ethclient/signer.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package ethclient - -import ( - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - -// senderFromServer is a types.Signer that remembers the sender address returned by the RPC -// server. It is stored in the transaction's sender address cache to avoid an additional -// request in TransactionSender. -type senderFromServer struct { - addr common.Address - blockhash common.Hash -} - -var errNotCached = errors.New("sender not cached") - -func setSenderFromServer(tx *types.Transaction, addr common.Address, block common.Hash) { - // Use types.Sender for side-effect to store our signer into the cache. - types.Sender(&senderFromServer{addr, block}, tx) -} - -func (s *senderFromServer) Equal(other types.Signer) bool { - os, ok := other.(*senderFromServer) - return ok && os.blockhash == s.blockhash -} - -func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) { - if s.addr == (common.Address{}) { - return common.Address{}, errNotCached - } - return s.addr, nil -} - -func (s *senderFromServer) ChainID() *big.Int { - panic("can't sign with senderFromServer") -} -func (s *senderFromServer) Hash(tx *types.Transaction) common.Hash { - panic("can't sign with senderFromServer") -} -func (s *senderFromServer) SignatureValues(tx *types.Transaction, sig []byte) (R, S, V *big.Int, err error) { - panic("can't sign with senderFromServer") -}