aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/types/transaction.go14
-rw-r--r--core/types/transaction_signing.go174
-rw-r--r--core/types/transaction_test.go5
3 files changed, 67 insertions, 126 deletions
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 7f54860fc..a46521236 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -209,12 +209,6 @@ func (tx *Transaction) Hash() common.Hash {
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 {
if size := tx.size.Load(); size != nil {
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.
// This signature needs to be formatted as described in the yellow paper (v+27).
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.
diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go
index ba4f2aa03..dfc84fdac 100644
--- a/core/types/transaction_signing.go
+++ b/core/types/transaction_signing.go
@@ -29,9 +29,6 @@ import (
var (
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
@@ -62,12 +59,9 @@ func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, err
if err != nil {
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
// elliptic curve and an error if it failed deriving or upon an incorrect
// 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 {
return common.Address{}, err
}
- var addr common.Address
- copy(addr[:], crypto.Keccak256(pubkey[1:])[12:])
tx.from.Store(sigCache{signer: signer, from: addr})
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 {
- // 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
- // PubilcKey returns the public key derived from the signature
- 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 returns true if the given signer is the same as the receiver.
Equal(Signer) bool
}
-// EIP155Transaction implements TransactionInterface using the
-// EIP155 rules
+// EIP155Transaction implements Signer using the EIP155 rules.
type EIP155Signer struct {
- HomesteadSigner
-
chainId, chainIdMul *big.Int
}
@@ -131,55 +122,32 @@ func (s EIP155Signer) Equal(s2 Signer) bool {
return ok && eip155.chainId.Cmp(s.chainId) == 0
}
-func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) {
- // if the transaction is not protected fall back to homestead signer
+var big8 = big.NewInt(8)
+
+func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
if !tx.Protected() {
- return (HomesteadSigner{}).PublicKey(tx)
+ return HomesteadSigner{}.Sender(tx)
}
-
if tx.ChainId().Cmp(s.chainId) != 0 {
- return nil, ErrInvalidChainId
- }
-
- V := byte(new(big.Int).Sub(tx.data.V, s.chainIdMul).Uint64() - 35)
- if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, 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 common.Address{}, ErrInvalidChainId
}
- return pub, nil
+ V := new(big.Int).Sub(tx.data.V, s.chainIdMul)
+ V.Sub(V, big8)
+ return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true)
}
// 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.
-func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
- if len(sig) != 65 {
- panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
+func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
+ R, S, V, err = HomesteadSigner{}.SignatureValues(tx, 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 {
- cpy.data.V = big.NewInt(int64(sig[64] + 35))
- cpy.data.V.Add(cpy.data.V, s.chainIdMul)
+ V = big.NewInt(int64(sig[64] + 35))
+ V.Add(V, s.chainIdMul)
}
- return cpy, nil
+ return R, S, V, nil
}
// Hash returns the hash to be signed by the sender.
@@ -205,44 +173,14 @@ func (s HomesteadSigner) Equal(s2 Signer) bool {
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.
-func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
- if len(sig) != 65 {
- 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) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
+ return hs.FrontierSigner.SignatureValues(tx, sig)
}
-func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) {
- if tx.data.V.BitLen() > 8 {
- 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
+func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
+ return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true)
}
type FrontierSigner struct{}
@@ -252,20 +190,19 @@ func (s FrontierSigner) Equal(s2 Signer) bool {
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.
-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 {
- 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}
- 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
+ r = new(big.Int).SetBytes(sig[:32])
+ s = new(big.Int).SetBytes(sig[32:64])
+ v = new(big.Int).SetBytes([]byte{sig[64] + 27})
+ return r, s, v, 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.
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
@@ -278,32 +215,35 @@ func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
})
}
-func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) {
- if tx.data.V.BitLen() > 8 {
- return nil, ErrInvalidSig
- }
+func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
+ return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false)
+}
- V := byte(tx.data.V.Uint64() - 27)
- if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, false) {
- return nil, ErrInvalidSig
+func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) {
+ if Vb.BitLen() > 8 {
+ 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
- r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
+ r, s := R.Bytes(), 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 := fs.Hash(tx)
- pub, err := crypto.Ecrecover(hash[:], sig)
+ pub, err := crypto.Ecrecover(sighash[:], sig)
if err != nil {
- return nil, err
+ return common.Address{}, err
}
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
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index df9d7ffd1..30ecb84dd 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -52,10 +52,11 @@ var (
)
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())
}
- if rightvrsTx.SigHash(HomesteadSigner{}) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
+ if homestead.Hash(rightvrsTx) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash())
}
}