forked from cerc-io/plugeth
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.
This commit is contained in:
parent
a660685746
commit
d78ad226c2
@ -209,12 +209,6 @@ func (tx *Transaction) Hash() common.Hash {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// SigHash returns the hash to be signed by the sender.
|
|
||||||
// It does not uniquely identify the transaction.
|
|
||||||
func (tx *Transaction) SigHash(signer Signer) common.Hash {
|
|
||||||
return signer.Hash(tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx *Transaction) Size() common.StorageSize {
|
func (tx *Transaction) Size() common.StorageSize {
|
||||||
if size := tx.size.Load(); size != nil {
|
if size := tx.size.Load(); size != nil {
|
||||||
return size.(common.StorageSize)
|
return size.(common.StorageSize)
|
||||||
@ -249,7 +243,13 @@ func (tx *Transaction) AsMessage(s Signer) (Message, error) {
|
|||||||
// WithSignature returns a new transaction with the given signature.
|
// WithSignature returns a new transaction with the given signature.
|
||||||
// This signature needs to be formatted as described in the yellow paper (v+27).
|
// This signature needs to be formatted as described in the yellow paper (v+27).
|
||||||
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
|
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
|
||||||
return signer.WithSignature(tx, sig)
|
r, s, v, err := signer.SignatureValues(tx, sig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cpy := &Transaction{data: tx.data}
|
||||||
|
cpy.data.R, cpy.data.S, cpy.data.V = r, s, v
|
||||||
|
return cpy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cost returns amount + gasprice * gaslimit.
|
// Cost returns amount + gasprice * gaslimit.
|
||||||
|
@ -29,9 +29,6 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidChainId = errors.New("invalid chain id for signer")
|
ErrInvalidChainId = errors.New("invalid chain id for signer")
|
||||||
|
|
||||||
errAbstractSigner = errors.New("abstract signer")
|
|
||||||
abstractSignerAddress = common.HexToAddress("ffffffffffffffffffffffffffffffffffffffff")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// sigCache is used to cache the derived sender and contains
|
// sigCache is used to cache the derived sender and contains
|
||||||
@ -62,12 +59,9 @@ func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return s.WithSignature(tx, sig)
|
return tx.WithSignature(s, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sender derives the sender from the tx using the signer derivation
|
|
||||||
// functions.
|
|
||||||
|
|
||||||
// Sender returns the address derived from the signature (V, R, S) using secp256k1
|
// Sender returns the address derived from the signature (V, R, S) using secp256k1
|
||||||
// elliptic curve and an error if it failed deriving or upon an incorrect
|
// elliptic curve and an error if it failed deriving or upon an incorrect
|
||||||
// signature.
|
// signature.
|
||||||
@ -86,33 +80,30 @@ func Sender(signer Signer, tx *Transaction) (common.Address, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pubkey, err := signer.PublicKey(tx)
|
addr, err := signer.Sender(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
var addr common.Address
|
|
||||||
copy(addr[:], crypto.Keccak256(pubkey[1:])[12:])
|
|
||||||
tx.from.Store(sigCache{signer: signer, from: addr})
|
tx.from.Store(sigCache{signer: signer, from: addr})
|
||||||
return addr, nil
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Signer encapsulates transaction signature handling. Note that this interface is not a
|
||||||
|
// stable API and may change at any time to accommodate new protocol rules.
|
||||||
type Signer interface {
|
type Signer interface {
|
||||||
// Hash returns the rlp encoded hash for signatures
|
// Sender returns the sender address of the transaction.
|
||||||
|
Sender(tx *Transaction) (common.Address, error)
|
||||||
|
// SignatureValues returns the raw R, S, V values corresponding to the
|
||||||
|
// given signature.
|
||||||
|
SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error)
|
||||||
|
// Hash returns the hash to be signed.
|
||||||
Hash(tx *Transaction) common.Hash
|
Hash(tx *Transaction) common.Hash
|
||||||
// PubilcKey returns the public key derived from the signature
|
// Equal returns true if the given signer is the same as the receiver.
|
||||||
PublicKey(tx *Transaction) ([]byte, error)
|
|
||||||
// WithSignature returns a copy of the transaction with the given signature.
|
|
||||||
// The signature must be encoded in [R || S || V] format where V is 0 or 1.
|
|
||||||
WithSignature(tx *Transaction, sig []byte) (*Transaction, error)
|
|
||||||
// Checks for equality on the signers
|
|
||||||
Equal(Signer) bool
|
Equal(Signer) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// EIP155Transaction implements TransactionInterface using the
|
// EIP155Transaction implements Signer using the EIP155 rules.
|
||||||
// EIP155 rules
|
|
||||||
type EIP155Signer struct {
|
type EIP155Signer struct {
|
||||||
HomesteadSigner
|
|
||||||
|
|
||||||
chainId, chainIdMul *big.Int
|
chainId, chainIdMul *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,55 +122,32 @@ func (s EIP155Signer) Equal(s2 Signer) bool {
|
|||||||
return ok && eip155.chainId.Cmp(s.chainId) == 0
|
return ok && eip155.chainId.Cmp(s.chainId) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) {
|
var big8 = big.NewInt(8)
|
||||||
// if the transaction is not protected fall back to homestead signer
|
|
||||||
|
func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
|
||||||
if !tx.Protected() {
|
if !tx.Protected() {
|
||||||
return (HomesteadSigner{}).PublicKey(tx)
|
return HomesteadSigner{}.Sender(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.ChainId().Cmp(s.chainId) != 0 {
|
if tx.ChainId().Cmp(s.chainId) != 0 {
|
||||||
return nil, ErrInvalidChainId
|
return common.Address{}, ErrInvalidChainId
|
||||||
}
|
}
|
||||||
|
V := new(big.Int).Sub(tx.data.V, s.chainIdMul)
|
||||||
V := byte(new(big.Int).Sub(tx.data.V, s.chainIdMul).Uint64() - 35)
|
V.Sub(V, big8)
|
||||||
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
|
return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true)
|
||||||
return nil, ErrInvalidSig
|
|
||||||
}
|
|
||||||
// encode the signature in uncompressed format
|
|
||||||
R, S := tx.data.R.Bytes(), tx.data.S.Bytes()
|
|
||||||
sig := make([]byte, 65)
|
|
||||||
copy(sig[32-len(R):32], R)
|
|
||||||
copy(sig[64-len(S):64], S)
|
|
||||||
sig[64] = V
|
|
||||||
|
|
||||||
// recover the public key from the signature
|
|
||||||
hash := s.Hash(tx)
|
|
||||||
pub, err := crypto.Ecrecover(hash[:], sig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(pub) == 0 || pub[0] != 4 {
|
|
||||||
return nil, errors.New("invalid public key")
|
|
||||||
}
|
|
||||||
return pub, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSignature returns a new transaction with the given signature. This signature
|
// WithSignature returns a new transaction with the given signature. This signature
|
||||||
// needs to be in the [R || S || V] format where V is 0 or 1.
|
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||||
func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
|
func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||||
if len(sig) != 65 {
|
R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig)
|
||||||
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cpy := &Transaction{data: tx.data}
|
|
||||||
cpy.data.R = new(big.Int).SetBytes(sig[:32])
|
|
||||||
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
|
|
||||||
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]})
|
|
||||||
if s.chainId.Sign() != 0 {
|
if s.chainId.Sign() != 0 {
|
||||||
cpy.data.V = big.NewInt(int64(sig[64] + 35))
|
V = big.NewInt(int64(sig[64] + 35))
|
||||||
cpy.data.V.Add(cpy.data.V, s.chainIdMul)
|
V.Add(V, s.chainIdMul)
|
||||||
}
|
}
|
||||||
return cpy, nil
|
return R, S, V, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash returns the hash to be signed by the sender.
|
// Hash returns the hash to be signed by the sender.
|
||||||
@ -205,44 +173,14 @@ func (s HomesteadSigner) Equal(s2 Signer) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSignature returns a new transaction with the given signature. This signature
|
// SignatureValues returns signature values. This signature
|
||||||
// needs to be in the [R || S || V] format where V is 0 or 1.
|
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||||
func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
|
func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
|
||||||
if len(sig) != 65 {
|
return hs.FrontierSigner.SignatureValues(tx, sig)
|
||||||
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
|
|
||||||
}
|
|
||||||
cpy := &Transaction{data: tx.data}
|
|
||||||
cpy.data.R = new(big.Int).SetBytes(sig[:32])
|
|
||||||
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
|
|
||||||
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27})
|
|
||||||
return cpy, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) {
|
func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||||
if tx.data.V.BitLen() > 8 {
|
return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true)
|
||||||
return nil, ErrInvalidSig
|
|
||||||
}
|
|
||||||
V := byte(tx.data.V.Uint64() - 27)
|
|
||||||
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
|
|
||||||
return nil, ErrInvalidSig
|
|
||||||
}
|
|
||||||
// encode the snature in uncompressed format
|
|
||||||
r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
|
|
||||||
sig := make([]byte, 65)
|
|
||||||
copy(sig[32-len(r):32], r)
|
|
||||||
copy(sig[64-len(s):64], s)
|
|
||||||
sig[64] = V
|
|
||||||
|
|
||||||
// recover the public key from the snature
|
|
||||||
hash := hs.Hash(tx)
|
|
||||||
pub, err := crypto.Ecrecover(hash[:], sig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(pub) == 0 || pub[0] != 4 {
|
|
||||||
return nil, errors.New("invalid public key")
|
|
||||||
}
|
|
||||||
return pub, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FrontierSigner struct{}
|
type FrontierSigner struct{}
|
||||||
@ -252,20 +190,19 @@ func (s FrontierSigner) Equal(s2 Signer) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSignature returns a new transaction with the given signature. This signature
|
// SignatureValues returns signature values. This signature
|
||||||
// needs to be in the [R || S || V] format where V is 0 or 1.
|
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||||
func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
|
func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
|
||||||
if len(sig) != 65 {
|
if len(sig) != 65 {
|
||||||
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
|
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
|
||||||
}
|
}
|
||||||
cpy := &Transaction{data: tx.data}
|
r = new(big.Int).SetBytes(sig[:32])
|
||||||
cpy.data.R = new(big.Int).SetBytes(sig[:32])
|
s = new(big.Int).SetBytes(sig[32:64])
|
||||||
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
|
v = new(big.Int).SetBytes([]byte{sig[64] + 27})
|
||||||
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27})
|
return r, s, v, nil
|
||||||
return cpy, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash returns the hash to be sned by the sender.
|
// Hash returns the hash to be signed by the sender.
|
||||||
// It does not uniquely identify the transaction.
|
// It does not uniquely identify the transaction.
|
||||||
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
|
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
|
||||||
return rlpHash([]interface{}{
|
return rlpHash([]interface{}{
|
||||||
@ -278,32 +215,35 @@ func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) {
|
func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||||
if tx.data.V.BitLen() > 8 {
|
return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false)
|
||||||
return nil, ErrInvalidSig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
V := byte(tx.data.V.Uint64() - 27)
|
func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) {
|
||||||
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, false) {
|
if Vb.BitLen() > 8 {
|
||||||
return nil, ErrInvalidSig
|
return common.Address{}, ErrInvalidSig
|
||||||
|
}
|
||||||
|
V := byte(Vb.Uint64() - 27)
|
||||||
|
if !crypto.ValidateSignatureValues(V, R, S, homestead) {
|
||||||
|
return common.Address{}, ErrInvalidSig
|
||||||
}
|
}
|
||||||
// encode the snature in uncompressed format
|
// encode the snature in uncompressed format
|
||||||
r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
|
r, s := R.Bytes(), S.Bytes()
|
||||||
sig := make([]byte, 65)
|
sig := make([]byte, 65)
|
||||||
copy(sig[32-len(r):32], r)
|
copy(sig[32-len(r):32], r)
|
||||||
copy(sig[64-len(s):64], s)
|
copy(sig[64-len(s):64], s)
|
||||||
sig[64] = V
|
sig[64] = V
|
||||||
|
|
||||||
// recover the public key from the snature
|
// recover the public key from the snature
|
||||||
hash := fs.Hash(tx)
|
pub, err := crypto.Ecrecover(sighash[:], sig)
|
||||||
pub, err := crypto.Ecrecover(hash[:], sig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
if len(pub) == 0 || pub[0] != 4 {
|
if len(pub) == 0 || pub[0] != 4 {
|
||||||
return nil, errors.New("invalid public key")
|
return common.Address{}, errors.New("invalid public key")
|
||||||
}
|
}
|
||||||
return pub, nil
|
var addr common.Address
|
||||||
|
copy(addr[:], crypto.Keccak256(pub[1:])[12:])
|
||||||
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deriveChainId derives the chain id from the given v parameter
|
// deriveChainId derives the chain id from the given v parameter
|
||||||
|
@ -52,10 +52,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestTransactionSigHash(t *testing.T) {
|
func TestTransactionSigHash(t *testing.T) {
|
||||||
if emptyTx.SigHash(HomesteadSigner{}) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
|
var homestead HomesteadSigner
|
||||||
|
if homestead.Hash(emptyTx) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
|
||||||
t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash())
|
t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash())
|
||||||
}
|
}
|
||||||
if rightvrsTx.SigHash(HomesteadSigner{}) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
|
if homestead.Hash(rightvrsTx) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
|
||||||
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash())
|
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ package ethclient
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ func (ec *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Bl
|
|||||||
|
|
||||||
type rpcBlock struct {
|
type rpcBlock struct {
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
Transactions []*types.Transaction `json:"transactions"`
|
Transactions []rpcTransaction `json:"transactions"`
|
||||||
UncleHashes []common.Hash `json:"uncles"`
|
UncleHashes []common.Hash `json:"uncles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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.
|
// 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
|
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.
|
// 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) {
|
func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
|
||||||
var raw json.RawMessage
|
var json *rpcTransaction
|
||||||
err = ec.c.CallContext(ctx, &raw, "eth_getTransactionByHash", hash)
|
err = ec.c.CallContext(ctx, &json, "eth_getTransactionByHash", hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
} else if len(raw) == 0 {
|
} else if json == nil {
|
||||||
return nil, false, ethereum.NotFound
|
return nil, false, ethereum.NotFound
|
||||||
}
|
} else if _, r, _ := json.tx.RawSignatureValues(); r == nil {
|
||||||
if err := json.Unmarshal(raw, &tx); err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
} else if _, r, _ := tx.RawSignatureValues(); r == nil {
|
|
||||||
return nil, false, fmt.Errorf("server returned transaction without signature")
|
return nil, false, fmt.Errorf("server returned transaction without signature")
|
||||||
}
|
}
|
||||||
var block struct{ BlockNumber *string }
|
setSenderFromServer(json.tx, json.From, json.BlockHash)
|
||||||
if err := json.Unmarshal(raw, &block); err != nil {
|
return json.tx, json.BlockNumber == nil, nil
|
||||||
return nil, false, err
|
|
||||||
}
|
}
|
||||||
return tx, block.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 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.
|
||||||
@ -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.
|
// 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) {
|
func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
|
||||||
var tx *types.Transaction
|
var json *rpcTransaction
|
||||||
err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index))
|
err := ec.c.CallContext(ctx, &json, "eth_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if tx == nil {
|
if json == nil {
|
||||||
return nil, ethereum.NotFound
|
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 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.
|
// TransactionReceipt returns the receipt of a transaction by transaction hash.
|
||||||
|
59
ethclient/signer.go
Normal file
59
ethclient/signer.go
Normal file
@ -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")
|
||||||
|
}
|
@ -77,6 +77,13 @@ func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (tx *Tr
|
|||||||
return &Transaction{rawTx}, err
|
return &Transaction{rawTx}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTransactionSender returns the sender address of a transaction. The transaction must
|
||||||
|
// be included in blockchain at the given block and index.
|
||||||
|
func (ec *EthereumClient) GetTransactionSender(ctx *Context, tx *Transaction, blockhash *Hash, index int) (sender *Address, _ error) {
|
||||||
|
addr, err := ec.client.TransactionSender(ctx.context, tx.tx, blockhash.hash, uint(index))
|
||||||
|
return &Address{addr}, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetTransactionCount returns the total number of transactions in the given block.
|
// GetTransactionCount returns the total number of transactions in the given block.
|
||||||
func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (count int, _ error) {
|
func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (count int, _ error) {
|
||||||
rawCount, err := ec.client.TransactionCount(ctx.context, hash.hash)
|
rawCount, err := ec.client.TransactionCount(ctx.context, hash.hash)
|
||||||
|
@ -262,9 +262,12 @@ func (tx *Transaction) GetValue() *BigInt { return &BigInt{tx.tx.Value()} }
|
|||||||
func (tx *Transaction) GetNonce() int64 { return int64(tx.tx.Nonce()) }
|
func (tx *Transaction) GetNonce() int64 { return int64(tx.tx.Nonce()) }
|
||||||
|
|
||||||
func (tx *Transaction) GetHash() *Hash { return &Hash{tx.tx.Hash()} }
|
func (tx *Transaction) GetHash() *Hash { return &Hash{tx.tx.Hash()} }
|
||||||
func (tx *Transaction) GetSigHash() *Hash { return &Hash{tx.tx.SigHash(types.HomesteadSigner{})} }
|
|
||||||
func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} }
|
func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} }
|
||||||
|
|
||||||
|
// Deprecated: GetSigHash cannot know which signer to use.
|
||||||
|
func (tx *Transaction) GetSigHash() *Hash { return &Hash{types.HomesteadSigner{}.Hash(tx.tx)} }
|
||||||
|
|
||||||
|
// Deprecated: use EthereumClient.TransactionSender
|
||||||
func (tx *Transaction) GetFrom(chainID *BigInt) (address *Address, _ error) {
|
func (tx *Transaction) GetFrom(chainID *BigInt) (address *Address, _ error) {
|
||||||
var signer types.Signer = types.HomesteadSigner{}
|
var signer types.Signer = types.HomesteadSigner{}
|
||||||
if chainID != nil {
|
if chainID != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user