ethclient: require inclusion block for TransactionSender

This commit is contained in:
Felix Lange 2017-09-25 11:10:37 +02:00
parent 81651c38da
commit 302bc9f7fb
2 changed files with 26 additions and 19 deletions

View file

@ -20,6 +20,7 @@ package ethclient
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"math/big" "math/big"
@ -166,6 +167,7 @@ type rpcTransaction struct {
type txExtraInfo struct { type txExtraInfo struct {
BlockNumber *string BlockNumber *string
BlockHash common.Hash
From common.Address From common.Address
} }
@ -187,29 +189,33 @@ func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *
} else if _, r, _ := json.tx.RawSignatureValues(); r == nil { } else if _, r, _ := json.tx.RawSignatureValues(); r == nil {
return nil, false, fmt.Errorf("server returned transaction without signature") return nil, false, fmt.Errorf("server returned transaction without signature")
} }
setSenderFromServer(json.tx, json.From) setSenderFromServer(json.tx, json.From, json.BlockHash)
return json.tx, json.BlockNumber == nil, nil return json.tx, json.BlockNumber == nil, nil
} }
// TransactionSender returns the sender address of the given transaction. The transaction // TransactionSender returns the sender address of the given transaction. The transaction
// must be known to the remote node. The sender is the one derived by the protocol at the // must be known to the remote node and included in the blockchain. The sender is the one
// time of inclusion. // derived by the protocol at the time of inclusion.
// //
// There is a fast-path for transactions retrieved by TransactionByHash and // There is a fast-path for transactions retrieved by TransactionByHash and
// TransactionInBlock. Getting their sender address can be done without an RPC interaction. // TransactionInBlock. Getting their sender address can be done without an RPC interaction.
func (ec *Client) TransactionSender(ctx context.Context, tx *types.Transaction) (common.Address, error) { 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. // Try to load the address from the cache.
sender, err := types.Sender((*senderFromServer)(nil), tx) sender, err := types.Sender(&senderFromServer{blockhash: block}, tx)
if err == nil { if err == nil {
return sender, nil return sender, nil
} }
// TODO It'd better to use GetTransactionByBlockHashAndIndex because it works with the var meta struct {
// light client. This will be even more important with EIP 208 because there can be Hash common.Hash
// multiple inclusions of the same tx. The downside is that users would need to supply From common.Address
// the inclusion block. }
var meta struct{ From common.Address } if err = ec.c.CallContext(ctx, &meta, "eth_getTransactionByBlockHashAndIndex", block, hexutil.Uint64(index)); err != nil {
err = ec.c.CallContext(ctx, &meta, "eth_getTransactionByHash", tx.Hash()) return common.Address{}, err
return meta.From, 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. // TransactionCount returns the total number of transactions in the given block.
@ -230,7 +236,7 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash,
return nil, fmt.Errorf("server returned transaction without signature") return nil, fmt.Errorf("server returned transaction without signature")
} }
} }
setSenderFromServer(json.tx, json.From) setSenderFromServer(json.tx, json.From, json.BlockHash)
return json.tx, err return json.tx, err
} }

View file

@ -29,22 +29,23 @@ import (
// request in TransactionSender. // request in TransactionSender.
type senderFromServer struct { type senderFromServer struct {
addr common.Address addr common.Address
blockhash common.Hash
} }
var errNotCached = errors.New("sender not cached") var errNotCached = errors.New("sender not cached")
func setSenderFromServer(tx *types.Transaction, addr common.Address) { func setSenderFromServer(tx *types.Transaction, addr common.Address, block common.Hash) {
// Use types.Sender for side-effect to store our signer into the cache. // Use types.Sender for side-effect to store our signer into the cache.
types.Sender(&senderFromServer{addr}, tx) types.Sender(&senderFromServer{addr, block}, tx)
} }
func (s *senderFromServer) Equal(other types.Signer) bool { func (s *senderFromServer) Equal(other types.Signer) bool {
_, ok := other.(*senderFromServer) os, ok := other.(*senderFromServer)
return ok return ok && os.blockhash == s.blockhash
} }
func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) { func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) {
if s == nil { if s.blockhash == (common.Hash{}) {
return common.Address{}, errNotCached return common.Address{}, errNotCached
} }
return s.addr, nil return s.addr, nil