mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-16 17:03:46 +00:00
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
be5feae14f
commit
e4ae593927
4 changed files with 114 additions and 16 deletions
|
|
@ -153,25 +153,57 @@ 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
|
||||
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)
|
||||
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. 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) (common.Address, error) {
|
||||
// Try to load the address from the cache.
|
||||
sender, err := types.Sender((*senderFromServer)(nil), tx)
|
||||
if err == nil {
|
||||
return sender, nil
|
||||
}
|
||||
return tx, block.BlockNumber == nil, nil
|
||||
// TODO It'd better to use GetTransactionByBlockHashAndIndex because it works with the
|
||||
// light client. This will be even more important with EIP 208 because there can be
|
||||
// multiple inclusions of the same tx. The downside is that users would need to supply
|
||||
// the inclusion block.
|
||||
var meta struct{ From common.Address }
|
||||
err = ec.c.CallContext(ctx, &meta, "eth_getTransactionByHash", tx.Hash())
|
||||
return meta.From, err
|
||||
}
|
||||
|
||||
// TransactionCount returns the total number of transactions in the given block.
|
||||
|
|
@ -183,16 +215,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)
|
||||
return json.tx, err
|
||||
}
|
||||
|
||||
// TransactionReceipt returns the receipt of a transaction by transaction hash.
|
||||
|
|
|
|||
58
ethclient/signer.go
Normal file
58
ethclient/signer.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// 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
|
||||
}
|
||||
|
||||
var errNotCached = errors.New("sender not cached")
|
||||
|
||||
func setSenderFromServer(tx *types.Transaction, addr common.Address) {
|
||||
// Use types.Sender for side-effect to store our signer into the cache.
|
||||
types.Sender(&senderFromServer{addr}, tx)
|
||||
}
|
||||
|
||||
func (s *senderFromServer) Equal(other types.Signer) bool {
|
||||
_, ok := other.(*senderFromServer)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) {
|
||||
if s == nil {
|
||||
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,12 @@ func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (tx *Tr
|
|||
return &Transaction{rawTx}, err
|
||||
}
|
||||
|
||||
// GetTransactionSender returns the sender address of a transaction.
|
||||
func (ec *EthereumClient) GetTransactionSender(ctx *Context, tx *Transaction) (sender *Address, _ error) {
|
||||
addr, err := ec.client.TransactionSender(ctx.context, tx.tx)
|
||||
return &Address{addr}, err
|
||||
}
|
||||
|
||||
// GetTransactionCount returns the total number of transactions in the given block.
|
||||
func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (count int, _ error) {
|
||||
rawCount, err := ec.client.TransactionCount(ctx.context, hash.hash)
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ 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) {
|
||||
var signer types.Signer = types.HomesteadSigner{}
|
||||
if chainID != nil {
|
||||
|
|
|
|||
Loading…
Reference in a new issue