Skip to content

Commit d78ad22

Browse files
authored
ethclient, mobile: add TransactionSender (ethereum#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.
1 parent a660685 commit d78ad22

File tree

7 files changed

+204
-149
lines changed

7 files changed

+204
-149
lines changed

core/types/transaction.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,6 @@ func (tx *Transaction) Hash() common.Hash {
209209
return v
210210
}
211211

212-
// SigHash returns the hash to be signed by the sender.
213-
// It does not uniquely identify the transaction.
214-
func (tx *Transaction) SigHash(signer Signer) common.Hash {
215-
return signer.Hash(tx)
216-
}
217-
218212
func (tx *Transaction) Size() common.StorageSize {
219213
if size := tx.size.Load(); size != nil {
220214
return size.(common.StorageSize)
@@ -249,7 +243,13 @@ func (tx *Transaction) AsMessage(s Signer) (Message, error) {
249243
// WithSignature returns a new transaction with the given signature.
250244
// This signature needs to be formatted as described in the yellow paper (v+27).
251245
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
252-
return signer.WithSignature(tx, sig)
246+
r, s, v, err := signer.SignatureValues(tx, sig)
247+
if err != nil {
248+
return nil, err
249+
}
250+
cpy := &Transaction{data: tx.data}
251+
cpy.data.R, cpy.data.S, cpy.data.V = r, s, v
252+
return cpy, nil
253253
}
254254

255255
// Cost returns amount + gasprice * gaslimit.

core/types/transaction_signing.go

Lines changed: 57 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ import (
2929

3030
var (
3131
ErrInvalidChainId = errors.New("invalid chain id for signer")
32-
33-
errAbstractSigner = errors.New("abstract signer")
34-
abstractSignerAddress = common.HexToAddress("ffffffffffffffffffffffffffffffffffffffff")
3532
)
3633

3734
// 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
6259
if err != nil {
6360
return nil, err
6461
}
65-
return s.WithSignature(tx, sig)
62+
return tx.WithSignature(s, sig)
6663
}
6764

68-
// Sender derives the sender from the tx using the signer derivation
69-
// functions.
70-
7165
// Sender returns the address derived from the signature (V, R, S) using secp256k1
7266
// elliptic curve and an error if it failed deriving or upon an incorrect
7367
// signature.
@@ -86,33 +80,30 @@ func Sender(signer Signer, tx *Transaction) (common.Address, error) {
8680
}
8781
}
8882

89-
pubkey, err := signer.PublicKey(tx)
83+
addr, err := signer.Sender(tx)
9084
if err != nil {
9185
return common.Address{}, err
9286
}
93-
var addr common.Address
94-
copy(addr[:], crypto.Keccak256(pubkey[1:])[12:])
9587
tx.from.Store(sigCache{signer: signer, from: addr})
9688
return addr, nil
9789
}
9890

91+
// Signer encapsulates transaction signature handling. Note that this interface is not a
92+
// stable API and may change at any time to accommodate new protocol rules.
9993
type Signer interface {
100-
// Hash returns the rlp encoded hash for signatures
94+
// Sender returns the sender address of the transaction.
95+
Sender(tx *Transaction) (common.Address, error)
96+
// SignatureValues returns the raw R, S, V values corresponding to the
97+
// given signature.
98+
SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error)
99+
// Hash returns the hash to be signed.
101100
Hash(tx *Transaction) common.Hash
102-
// PubilcKey returns the public key derived from the signature
103-
PublicKey(tx *Transaction) ([]byte, error)
104-
// WithSignature returns a copy of the transaction with the given signature.
105-
// The signature must be encoded in [R || S || V] format where V is 0 or 1.
106-
WithSignature(tx *Transaction, sig []byte) (*Transaction, error)
107-
// Checks for equality on the signers
101+
// Equal returns true if the given signer is the same as the receiver.
108102
Equal(Signer) bool
109103
}
110104

111-
// EIP155Transaction implements TransactionInterface using the
112-
// EIP155 rules
105+
// EIP155Transaction implements Signer using the EIP155 rules.
113106
type EIP155Signer struct {
114-
HomesteadSigner
115-
116107
chainId, chainIdMul *big.Int
117108
}
118109

@@ -131,55 +122,32 @@ func (s EIP155Signer) Equal(s2 Signer) bool {
131122
return ok && eip155.chainId.Cmp(s.chainId) == 0
132123
}
133124

134-
func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) {
135-
// if the transaction is not protected fall back to homestead signer
125+
var big8 = big.NewInt(8)
126+
127+
func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
136128
if !tx.Protected() {
137-
return (HomesteadSigner{}).PublicKey(tx)
129+
return HomesteadSigner{}.Sender(tx)
138130
}
139-
140131
if tx.ChainId().Cmp(s.chainId) != 0 {
141-
return nil, ErrInvalidChainId
142-
}
143-
144-
V := byte(new(big.Int).Sub(tx.data.V, s.chainIdMul).Uint64() - 35)
145-
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
146-
return nil, ErrInvalidSig
147-
}
148-
// encode the signature in uncompressed format
149-
R, S := tx.data.R.Bytes(), tx.data.S.Bytes()
150-
sig := make([]byte, 65)
151-
copy(sig[32-len(R):32], R)
152-
copy(sig[64-len(S):64], S)
153-
sig[64] = V
154-
155-
// recover the public key from the signature
156-
hash := s.Hash(tx)
157-
pub, err := crypto.Ecrecover(hash[:], sig)
158-
if err != nil {
159-
return nil, err
160-
}
161-
if len(pub) == 0 || pub[0] != 4 {
162-
return nil, errors.New("invalid public key")
132+
return common.Address{}, ErrInvalidChainId
163133
}
164-
return pub, nil
134+
V := new(big.Int).Sub(tx.data.V, s.chainIdMul)
135+
V.Sub(V, big8)
136+
return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true)
165137
}
166138

167139
// WithSignature returns a new transaction with the given signature. This signature
168140
// needs to be in the [R || S || V] format where V is 0 or 1.
169-
func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
170-
if len(sig) != 65 {
171-
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
141+
func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
142+
R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig)
143+
if err != nil {
144+
return nil, nil, nil, err
172145
}
173-
174-
cpy := &Transaction{data: tx.data}
175-
cpy.data.R = new(big.Int).SetBytes(sig[:32])
176-
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
177-
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]})
178146
if s.chainId.Sign() != 0 {
179-
cpy.data.V = big.NewInt(int64(sig[64] + 35))
180-
cpy.data.V.Add(cpy.data.V, s.chainIdMul)
147+
V = big.NewInt(int64(sig[64] + 35))
148+
V.Add(V, s.chainIdMul)
181149
}
182-
return cpy, nil
150+
return R, S, V, nil
183151
}
184152

185153
// Hash returns the hash to be signed by the sender.
@@ -205,44 +173,14 @@ func (s HomesteadSigner) Equal(s2 Signer) bool {
205173
return ok
206174
}
207175

208-
// WithSignature returns a new transaction with the given signature. This signature
176+
// SignatureValues returns signature values. This signature
209177
// needs to be in the [R || S || V] format where V is 0 or 1.
210-
func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
211-
if len(sig) != 65 {
212-
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
213-
}
214-
cpy := &Transaction{data: tx.data}
215-
cpy.data.R = new(big.Int).SetBytes(sig[:32])
216-
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
217-
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27})
218-
return cpy, nil
178+
func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
179+
return hs.FrontierSigner.SignatureValues(tx, sig)
219180
}
220181

221-
func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) {
222-
if tx.data.V.BitLen() > 8 {
223-
return nil, ErrInvalidSig
224-
}
225-
V := byte(tx.data.V.Uint64() - 27)
226-
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
227-
return nil, ErrInvalidSig
228-
}
229-
// encode the snature in uncompressed format
230-
r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
231-
sig := make([]byte, 65)
232-
copy(sig[32-len(r):32], r)
233-
copy(sig[64-len(s):64], s)
234-
sig[64] = V
235-
236-
// recover the public key from the snature
237-
hash := hs.Hash(tx)
238-
pub, err := crypto.Ecrecover(hash[:], sig)
239-
if err != nil {
240-
return nil, err
241-
}
242-
if len(pub) == 0 || pub[0] != 4 {
243-
return nil, errors.New("invalid public key")
244-
}
245-
return pub, nil
182+
func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
183+
return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true)
246184
}
247185

248186
type FrontierSigner struct{}
@@ -252,20 +190,19 @@ func (s FrontierSigner) Equal(s2 Signer) bool {
252190
return ok
253191
}
254192

255-
// WithSignature returns a new transaction with the given signature. This signature
193+
// SignatureValues returns signature values. This signature
256194
// needs to be in the [R || S || V] format where V is 0 or 1.
257-
func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
195+
func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
258196
if len(sig) != 65 {
259-
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
197+
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
260198
}
261-
cpy := &Transaction{data: tx.data}
262-
cpy.data.R = new(big.Int).SetBytes(sig[:32])
263-
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
264-
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27})
265-
return cpy, nil
199+
r = new(big.Int).SetBytes(sig[:32])
200+
s = new(big.Int).SetBytes(sig[32:64])
201+
v = new(big.Int).SetBytes([]byte{sig[64] + 27})
202+
return r, s, v, nil
266203
}
267204

268-
// Hash returns the hash to be sned by the sender.
205+
// Hash returns the hash to be signed by the sender.
269206
// It does not uniquely identify the transaction.
270207
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
271208
return rlpHash([]interface{}{
@@ -278,32 +215,35 @@ func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
278215
})
279216
}
280217

281-
func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) {
282-
if tx.data.V.BitLen() > 8 {
283-
return nil, ErrInvalidSig
284-
}
218+
func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) {
219+
return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false)
220+
}
285221

286-
V := byte(tx.data.V.Uint64() - 27)
287-
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, false) {
288-
return nil, ErrInvalidSig
222+
func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) {
223+
if Vb.BitLen() > 8 {
224+
return common.Address{}, ErrInvalidSig
225+
}
226+
V := byte(Vb.Uint64() - 27)
227+
if !crypto.ValidateSignatureValues(V, R, S, homestead) {
228+
return common.Address{}, ErrInvalidSig
289229
}
290230
// encode the snature in uncompressed format
291-
r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
231+
r, s := R.Bytes(), S.Bytes()
292232
sig := make([]byte, 65)
293233
copy(sig[32-len(r):32], r)
294234
copy(sig[64-len(s):64], s)
295235
sig[64] = V
296-
297236
// recover the public key from the snature
298-
hash := fs.Hash(tx)
299-
pub, err := crypto.Ecrecover(hash[:], sig)
237+
pub, err := crypto.Ecrecover(sighash[:], sig)
300238
if err != nil {
301-
return nil, err
239+
return common.Address{}, err
302240
}
303241
if len(pub) == 0 || pub[0] != 4 {
304-
return nil, errors.New("invalid public key")
242+
return common.Address{}, errors.New("invalid public key")
305243
}
306-
return pub, nil
244+
var addr common.Address
245+
copy(addr[:], crypto.Keccak256(pub[1:])[12:])
246+
return addr, nil
307247
}
308248

309249
// deriveChainId derives the chain id from the given v parameter

core/types/transaction_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,11 @@ var (
5252
)
5353

5454
func TestTransactionSigHash(t *testing.T) {
55-
if emptyTx.SigHash(HomesteadSigner{}) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
55+
var homestead HomesteadSigner
56+
if homestead.Hash(emptyTx) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
5657
t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash())
5758
}
58-
if rightvrsTx.SigHash(HomesteadSigner{}) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
59+
if homestead.Hash(rightvrsTx) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
5960
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash())
6061
}
6162
}

0 commit comments

Comments
 (0)