diff options
Diffstat (limited to 'ethclient/ethclient.go')
-rw-r--r-- | ethclient/ethclient.go | 85 |
1 files changed, 65 insertions, 20 deletions
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 48639d949..7f73ab113 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -20,6 +20,7 @@ package ethclient import ( "context" "encoding/json" + "errors" "fmt" "math/big" @@ -70,9 +71,9 @@ func (ec *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Bl } type rpcBlock struct { - Hash common.Hash `json:"hash"` - Transactions []*types.Transaction `json:"transactions"` - UncleHashes []common.Hash `json:"uncles"` + Hash common.Hash `json:"hash"` + Transactions []rpcTransaction `json:"transactions"` + UncleHashes []common.Hash `json:"uncles"` } func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { @@ -129,7 +130,13 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface } } } - return types.NewBlockWithHeader(head).WithBody(body.Transactions, uncles), nil + // Fill the sender cache of transactions in the block. + txs := make([]*types.Transaction, len(body.Transactions)) + for i, tx := range body.Transactions { + setSenderFromServer(tx.tx, tx.From, body.Hash) + txs[i] = tx.tx + } + return types.NewBlockWithHeader(head).WithBody(txs, uncles), nil } // HeaderByHash returns the block header with the given hash. @@ -153,25 +160,62 @@ func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H return head, err } +type rpcTransaction struct { + tx *types.Transaction + txExtraInfo +} + +type txExtraInfo struct { + BlockNumber *string + BlockHash common.Hash + From common.Address +} + +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 raw json.RawMessage - err = ec.c.CallContext(ctx, &raw, "eth_getTransactionByHash", hash) + var json *rpcTransaction + err = ec.c.CallContext(ctx, &json, "eth_getTransactionByHash", hash) if err != nil { return nil, false, err - } else if len(raw) == 0 { + } else if json == nil { return nil, false, ethereum.NotFound - } - if err := json.Unmarshal(raw, &tx); err != nil { - return nil, false, err - } else if _, r, _ := tx.RawSignatureValues(); r == nil { + } else if _, r, _ := json.tx.RawSignatureValues(); r == nil { return nil, false, fmt.Errorf("server returned transaction without signature") } - var block struct{ BlockNumber *string } - if err := json.Unmarshal(raw, &block); err != nil { - return nil, false, err + 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 + } + 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 tx, block.BlockNumber == nil, nil + return meta.From, nil } // TransactionCount returns the total number of transactions in the given block. @@ -183,16 +227,17 @@ func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) ( // 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 tx *types.Transaction - err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index)) + var json *rpcTransaction + err := ec.c.CallContext(ctx, &json, "eth_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index)) if err == nil { - if tx == nil { + if json == nil { return nil, ethereum.NotFound - } else if _, r, _ := tx.RawSignatureValues(); r == nil { + } else if _, r, _ := json.tx.RawSignatureValues(); r == nil { return nil, fmt.Errorf("server returned transaction without signature") } } - return tx, err + setSenderFromServer(json.tx, json.From, json.BlockHash) + return json.tx, err } // TransactionReceipt returns the receipt of a transaction by transaction hash. |