aboutsummaryrefslogtreecommitdiffstats
path: root/ethclient/ethclient.go
diff options
context:
space:
mode:
authorFelix Lange <fjl@users.noreply.github.com>2017-10-01 17:03:28 +0800
committerGitHub <noreply@github.com>2017-10-01 17:03:28 +0800
commitd78ad226c26c84635c60fad233de9e6e438a5599 (patch)
treec78572046e8d95dfa50a48b0d980e2ee1727f18b /ethclient/ethclient.go
parenta660685746db17a41cd67b05c614cdb29e49340c (diff)
downloadgo-tangerine-d78ad226c26c84635c60fad233de9e6e438a5599.tar
go-tangerine-d78ad226c26c84635c60fad233de9e6e438a5599.tar.gz
go-tangerine-d78ad226c26c84635c60fad233de9e6e438a5599.tar.bz2
go-tangerine-d78ad226c26c84635c60fad233de9e6e438a5599.tar.lz
go-tangerine-d78ad226c26c84635c60fad233de9e6e438a5599.tar.xz
go-tangerine-d78ad226c26c84635c60fad233de9e6e438a5599.tar.zst
go-tangerine-d78ad226c26c84635c60fad233de9e6e438a5599.zip
ethclient, mobile: add TransactionSender (#15127)
* core/types: make Signer derive address instead of public key There are two reasons to do this now: The upcoming ethclient signer doesn't know the public key, just the address. EIP 208 will introduce a new signer which derives the 'entry point' address for transactions with zero signature. The entry point has no public key. Other changes to the interface ease the path make to moving signature crypto out of core/types later. * ethclient, mobile: add TransactionSender The new method can get the right signer without any crypto, and without knowledge of the signature scheme that was used when the transaction was included.
Diffstat (limited to 'ethclient/ethclient.go')
-rw-r--r--ethclient/ethclient.go85
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.