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.
This commit is contained in:
Felix Lange 2017-09-11 16:02:53 +02:00
parent 794741b8b2
commit be5feae14f
4 changed files with 72 additions and 129 deletions

View file

@ -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.

View file

@ -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

View file

@ -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())
} }
} }

View file

@ -262,9 +262,11 @@ 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)} }
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 {