aboutsummaryrefslogtreecommitdiffstats
path: root/ethclient
diff options
context:
space:
mode:
Diffstat (limited to 'ethclient')
-rw-r--r--ethclient/ethclient.go85
-rw-r--r--ethclient/signer.go59
2 files changed, 124 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.
diff --git a/ethclient/signer.go b/ethclient/signer.go
new file mode 100644
index 000000000..74a93f1e2
--- /dev/null
+++ b/ethclient/signer.go
@@ -0,0 +1,59 @@
+// 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 <http://www.gnu.org/licenses/>.
+
+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.blockhash == (common.Hash{}) {
+ return common.Address{}, errNotCached
+ }
+ return s.addr, nil
+}
+
+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")
+}