@@ -20,11 +20,13 @@ import (
20
20
"crypto/ecdsa"
21
21
"errors"
22
22
"fmt"
23
+ "maps"
23
24
"math/big"
24
25
25
26
"github.com/ethereum/go-ethereum/common"
26
27
"github.com/ethereum/go-ethereum/crypto"
27
28
"github.com/ethereum/go-ethereum/params"
29
+ "github.com/ethereum/go-ethereum/params/forks"
28
30
)
29
31
30
32
var ErrInvalidChainId = errors .New ("invalid chain id for signer" )
@@ -178,245 +180,141 @@ type Signer interface {
178
180
Equal (Signer ) bool
179
181
}
180
182
181
- type pragueSigner struct { cancunSigner }
182
-
183
- // NewPragueSigner returns a signer that accepts
184
- // - EIP-7702 set code transactions
185
- // - EIP-4844 blob transactions
186
- // - EIP-1559 dynamic fee transactions
187
- // - EIP-2930 access list transactions,
188
- // - EIP-155 replay protected transactions, and
189
- // - legacy Homestead transactions.
190
- func NewPragueSigner (chainId * big.Int ) Signer {
191
- signer , _ := NewCancunSigner (chainId ).(cancunSigner )
192
- return pragueSigner {signer }
183
+ // modernSigner is the signer implementation that handles non-legacy transaction types.
184
+ // For legacy transactions, it defers to one of the legacy signers (frontier, homestead, eip155).
185
+ type modernSigner struct {
186
+ txtypes map [byte ]struct {}
187
+ chainID * big.Int
188
+ legacy Signer
193
189
}
194
190
195
- func ( s pragueSigner ) Sender ( tx * Transaction ) (common. Address , error ) {
196
- if tx . Type () != SetCodeTxType {
197
- return s . cancunSigner . Sender ( tx )
191
+ func newModernSigner ( chainID * big. Int , fork forks. Fork ) Signer {
192
+ if chainID == nil || chainID . Sign () <= 0 {
193
+ panic ( fmt . Sprintf ( "invalid chainID %v" , chainID ) )
198
194
}
199
- V , R , S := tx .RawSignatureValues ()
200
-
201
- // Set code txs are defined to use 0 and 1 as their recovery
202
- // id, add 27 to become equivalent to unprotected Homestead signatures.
203
- V = new (big.Int ).Add (V , big .NewInt (27 ))
204
- if tx .ChainId ().Cmp (s .chainId ) != 0 {
205
- return common.Address {}, fmt .Errorf ("%w: have %d want %d" , ErrInvalidChainId , tx .ChainId (), s .chainId )
195
+ s := & modernSigner {
196
+ chainID : chainID ,
197
+ txtypes : make (map [byte ]struct {}, 4 ),
206
198
}
207
- return recoverPlain (s .Hash (tx ), R , S , V , true )
199
+ // configure legacy signer
200
+ switch {
201
+ case fork >= forks .SpuriousDragon :
202
+ s .legacy = NewEIP155Signer (chainID )
203
+ case fork >= forks .Homestead :
204
+ s .legacy = HomesteadSigner {}
205
+ default :
206
+ s .legacy = FrontierSigner {}
207
+ }
208
+ s .txtypes [LegacyTxType ] = struct {}{}
209
+ // configure tx types
210
+ if fork >= forks .Berlin {
211
+ s .txtypes [AccessListTxType ] = struct {}{}
212
+ }
213
+ if fork >= forks .London {
214
+ s .txtypes [DynamicFeeTxType ] = struct {}{}
215
+ }
216
+ if fork >= forks .Cancun {
217
+ s .txtypes [BlobTxType ] = struct {}{}
218
+ }
219
+ if fork >= forks .Prague {
220
+ s .txtypes [SetCodeTxType ] = struct {}{}
221
+ }
222
+ return s
208
223
}
209
224
210
- func (s pragueSigner ) Equal (s2 Signer ) bool {
211
- x , ok := s2 .(pragueSigner )
212
- return ok && x .chainId .Cmp (s .chainId ) == 0
225
+ func (s * modernSigner ) ChainID () * big.Int {
226
+ return s .chainID
213
227
}
214
228
215
- func (s pragueSigner ) SignatureValues (tx * Transaction , sig []byte ) (R , S , V * big.Int , err error ) {
216
- txdata , ok := tx .inner .(* SetCodeTx )
217
- if ! ok {
218
- return s .cancunSigner .SignatureValues (tx , sig )
219
- }
220
- // Check that chain ID of tx matches the signer. We also accept ID zero here,
221
- // because it indicates that the chain ID was not specified in the tx.
222
- if txdata .ChainID .Sign () != 0 && txdata .ChainID .CmpBig (s .chainId ) != 0 {
223
- return nil , nil , nil , fmt .Errorf ("%w: have %d want %d" , ErrInvalidChainId , txdata .ChainID , s .chainId )
224
- }
225
- R , S , _ = decodeSignature (sig )
226
- V = big .NewInt (int64 (sig [64 ]))
227
- return R , S , V , nil
229
+ func (s * modernSigner ) Equal (s2 Signer ) bool {
230
+ other , ok := s2 .(* modernSigner )
231
+ return ok && s .chainID .Cmp (other .chainID ) == 0 && maps .Equal (s .txtypes , other .txtypes ) && s .legacy .Equal (other .legacy )
228
232
}
229
233
230
- // Hash returns the hash to be signed by the sender.
231
- // It does not uniquely identify the transaction.
232
- func (s pragueSigner ) Hash (tx * Transaction ) common.Hash {
233
- if tx .Type () != SetCodeTxType {
234
- return s .cancunSigner .Hash (tx )
235
- }
236
- return tx .inner .sigHash (s .chainId )
234
+ func (s * modernSigner ) Hash (tx * Transaction ) common.Hash {
235
+ return tx .inner .sigHash (s .chainID )
237
236
}
238
237
239
- type cancunSigner struct { londonSigner }
240
-
241
- // NewCancunSigner returns a signer that accepts
242
- // - EIP-4844 blob transactions
243
- // - EIP-1559 dynamic fee transactions
244
- // - EIP-2930 access list transactions,
245
- // - EIP-155 replay protected transactions, and
246
- // - legacy Homestead transactions.
247
- func NewCancunSigner (chainId * big.Int ) Signer {
248
- return cancunSigner {londonSigner {eip2930Signer {NewEIP155Signer (chainId )}}}
238
+ func (s * modernSigner ) supportsType (txtype byte ) bool {
239
+ _ , ok := s .txtypes [txtype ]
240
+ return ok
249
241
}
250
242
251
- func (s cancunSigner ) Sender (tx * Transaction ) (common.Address , error ) {
252
- if tx .Type () != BlobTxType {
253
- return s .londonSigner .Sender (tx )
243
+ func (s * modernSigner ) Sender (tx * Transaction ) (common.Address , error ) {
244
+ tt := tx .Type ()
245
+ if ! s .supportsType (tt ) {
246
+ return common.Address {}, ErrTxTypeNotSupported
254
247
}
255
- V , R , S := tx .RawSignatureValues ()
256
- // Blob txs are defined to use 0 and 1 as their recovery
248
+ if tt == LegacyTxType {
249
+ return s .legacy .Sender (tx )
250
+ }
251
+ if tx .ChainId ().Cmp (s .chainID ) != 0 {
252
+ return common.Address {}, fmt .Errorf ("%w: have %d want %d" , ErrInvalidChainId , tx .ChainId (), s .chainID )
253
+ }
254
+ // 'modern' txs are defined to use 0 and 1 as their recovery
257
255
// id, add 27 to become equivalent to unprotected Homestead signatures.
256
+ V , R , S := tx .RawSignatureValues ()
258
257
V = new (big.Int ).Add (V , big .NewInt (27 ))
259
- if tx .ChainId ().Cmp (s .chainId ) != 0 {
260
- return common.Address {}, fmt .Errorf ("%w: have %d want %d" , ErrInvalidChainId , tx .ChainId (), s .chainId )
261
- }
262
258
return recoverPlain (s .Hash (tx ), R , S , V , true )
263
259
}
264
260
265
- func (s cancunSigner ) Equal (s2 Signer ) bool {
266
- x , ok := s2 .(cancunSigner )
267
- return ok && x .chainId .Cmp (s .chainId ) == 0
268
- }
269
-
270
- func (s cancunSigner ) SignatureValues (tx * Transaction , sig []byte ) (R , S , V * big.Int , err error ) {
271
- txdata , ok := tx .inner .(* BlobTx )
272
- if ! ok {
273
- return s .londonSigner .SignatureValues (tx , sig )
261
+ func (s * modernSigner ) SignatureValues (tx * Transaction , sig []byte ) (R , S , V * big.Int , err error ) {
262
+ tt := tx .Type ()
263
+ if ! s .supportsType (tt ) {
264
+ return nil , nil , nil , ErrTxTypeNotSupported
265
+ }
266
+ if tt == LegacyTxType {
267
+ return s .legacy .SignatureValues (tx , sig )
274
268
}
275
269
// Check that chain ID of tx matches the signer. We also accept ID zero here,
276
270
// because it indicates that the chain ID was not specified in the tx.
277
- if txdata . ChainID . Sign () != 0 && txdata . ChainID . CmpBig ( s . chainId ) != 0 {
278
- return nil , nil , nil , fmt .Errorf ("%w: have %d want %d" , ErrInvalidChainId , txdata . ChainID , s .chainId )
271
+ if tx . inner . chainID (). Sign () != 0 && tx . inner . chainID (). Cmp ( s . chainID ) != 0 {
272
+ return nil , nil , nil , fmt .Errorf ("%w: have %d want %d" , ErrInvalidChainId , tx . inner . chainID () , s .chainID )
279
273
}
280
274
R , S , _ = decodeSignature (sig )
281
275
V = big .NewInt (int64 (sig [64 ]))
282
276
return R , S , V , nil
283
277
}
284
278
285
- // Hash returns the hash to be signed by the sender.
286
- // It does not uniquely identify the transaction.
287
- func (s cancunSigner ) Hash (tx * Transaction ) common.Hash {
288
- if tx .Type () != BlobTxType {
289
- return s .londonSigner .Hash (tx )
290
- }
291
- return tx .inner .sigHash (s .chainId )
279
+ // NewPragueSigner returns a signer that accepts
280
+ // - EIP-7702 set code transactions
281
+ // - EIP-4844 blob transactions
282
+ // - EIP-1559 dynamic fee transactions
283
+ // - EIP-2930 access list transactions,
284
+ // - EIP-155 replay protected transactions, and
285
+ // - legacy Homestead transactions.
286
+ func NewPragueSigner (chainId * big.Int ) Signer {
287
+ return newModernSigner (chainId , forks .Prague )
292
288
}
293
289
294
- type londonSigner struct { eip2930Signer }
290
+ // NewCancunSigner returns a signer that accepts
291
+ // - EIP-4844 blob transactions
292
+ // - EIP-1559 dynamic fee transactions
293
+ // - EIP-2930 access list transactions,
294
+ // - EIP-155 replay protected transactions, and
295
+ // - legacy Homestead transactions.
296
+ func NewCancunSigner (chainId * big.Int ) Signer {
297
+ return newModernSigner (chainId , forks .Cancun )
298
+ }
295
299
296
300
// NewLondonSigner returns a signer that accepts
297
301
// - EIP-1559 dynamic fee transactions
298
302
// - EIP-2930 access list transactions,
299
303
// - EIP-155 replay protected transactions, and
300
304
// - legacy Homestead transactions.
301
305
func NewLondonSigner (chainId * big.Int ) Signer {
302
- return londonSigner { eip2930Signer { NewEIP155Signer (chainId )}}
306
+ return newModernSigner (chainId , forks . London )
303
307
}
304
308
305
- func (s londonSigner ) Sender (tx * Transaction ) (common.Address , error ) {
306
- if tx .Type () != DynamicFeeTxType {
307
- return s .eip2930Signer .Sender (tx )
308
- }
309
- V , R , S := tx .RawSignatureValues ()
310
- // DynamicFee txs are defined to use 0 and 1 as their recovery
311
- // id, add 27 to become equivalent to unprotected Homestead signatures.
312
- V = new (big.Int ).Add (V , big .NewInt (27 ))
313
- if tx .ChainId ().Cmp (s .chainId ) != 0 {
314
- return common.Address {}, fmt .Errorf ("%w: have %d want %d" , ErrInvalidChainId , tx .ChainId (), s .chainId )
315
- }
316
- return recoverPlain (s .Hash (tx ), R , S , V , true )
317
- }
318
-
319
- func (s londonSigner ) Equal (s2 Signer ) bool {
320
- x , ok := s2 .(londonSigner )
321
- return ok && x .chainId .Cmp (s .chainId ) == 0
322
- }
323
-
324
- func (s londonSigner ) SignatureValues (tx * Transaction , sig []byte ) (R , S , V * big.Int , err error ) {
325
- txdata , ok := tx .inner .(* DynamicFeeTx )
326
- if ! ok {
327
- return s .eip2930Signer .SignatureValues (tx , sig )
328
- }
329
- // Check that chain ID of tx matches the signer. We also accept ID zero here,
330
- // because it indicates that the chain ID was not specified in the tx.
331
- if txdata .ChainID .Sign () != 0 && txdata .ChainID .Cmp (s .chainId ) != 0 {
332
- return nil , nil , nil , fmt .Errorf ("%w: have %d want %d" , ErrInvalidChainId , txdata .ChainID , s .chainId )
333
- }
334
- R , S , _ = decodeSignature (sig )
335
- V = big .NewInt (int64 (sig [64 ]))
336
- return R , S , V , nil
337
- }
338
-
339
- // Hash returns the hash to be signed by the sender.
340
- // It does not uniquely identify the transaction.
341
- func (s londonSigner ) Hash (tx * Transaction ) common.Hash {
342
- if tx .Type () != DynamicFeeTxType {
343
- return s .eip2930Signer .Hash (tx )
344
- }
345
- return tx .inner .sigHash (s .chainId )
346
- }
347
-
348
- type eip2930Signer struct { EIP155Signer }
349
-
350
309
// NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions,
351
310
// EIP-155 replay protected transactions, and legacy Homestead transactions.
352
311
func NewEIP2930Signer (chainId * big.Int ) Signer {
353
- return eip2930Signer {NewEIP155Signer (chainId )}
354
- }
355
-
356
- func (s eip2930Signer ) ChainID () * big.Int {
357
- return s .chainId
358
- }
359
-
360
- func (s eip2930Signer ) Equal (s2 Signer ) bool {
361
- x , ok := s2 .(eip2930Signer )
362
- return ok && x .chainId .Cmp (s .chainId ) == 0
363
- }
364
-
365
- func (s eip2930Signer ) Sender (tx * Transaction ) (common.Address , error ) {
366
- V , R , S := tx .RawSignatureValues ()
367
- switch tx .Type () {
368
- case LegacyTxType :
369
- return s .EIP155Signer .Sender (tx )
370
- case AccessListTxType :
371
- // AL txs are defined to use 0 and 1 as their recovery
372
- // id, add 27 to become equivalent to unprotected Homestead signatures.
373
- V = new (big.Int ).Add (V , big .NewInt (27 ))
374
- default :
375
- return common.Address {}, ErrTxTypeNotSupported
376
- }
377
- if tx .ChainId ().Cmp (s .chainId ) != 0 {
378
- return common.Address {}, fmt .Errorf ("%w: have %d want %d" , ErrInvalidChainId , tx .ChainId (), s .chainId )
379
- }
380
- return recoverPlain (s .Hash (tx ), R , S , V , true )
381
- }
382
-
383
- func (s eip2930Signer ) SignatureValues (tx * Transaction , sig []byte ) (R , S , V * big.Int , err error ) {
384
- switch txdata := tx .inner .(type ) {
385
- case * LegacyTx :
386
- return s .EIP155Signer .SignatureValues (tx , sig )
387
- case * AccessListTx :
388
- // Check that chain ID of tx matches the signer. We also accept ID zero here,
389
- // because it indicates that the chain ID was not specified in the tx.
390
- if txdata .ChainID .Sign () != 0 && txdata .ChainID .Cmp (s .chainId ) != 0 {
391
- return nil , nil , nil , fmt .Errorf ("%w: have %d want %d" , ErrInvalidChainId , txdata .ChainID , s .chainId )
392
- }
393
- R , S , _ = decodeSignature (sig )
394
- V = big .NewInt (int64 (sig [64 ]))
395
- default :
396
- return nil , nil , nil , ErrTxTypeNotSupported
397
- }
398
- return R , S , V , nil
399
- }
400
-
401
- // Hash returns the hash to be signed by the sender.
402
- // It does not uniquely identify the transaction.
403
- func (s eip2930Signer ) Hash (tx * Transaction ) common.Hash {
404
- switch tx .Type () {
405
- case LegacyTxType :
406
- return s .EIP155Signer .Hash (tx )
407
- case AccessListTxType :
408
- return tx .inner .sigHash (s .chainId )
409
- default :
410
- // This _should_ not happen, but in case someone sends in a bad
411
- // json struct via RPC, it's probably more prudent to return an
412
- // empty hash instead of killing the node with a panic
413
- //panic("Unsupported transaction type: %d", tx.typ)
414
- return common.Hash {}
415
- }
312
+ return newModernSigner (chainId , forks .Berlin )
416
313
}
417
314
418
315
// EIP155Signer implements Signer using the EIP-155 rules. This accepts transactions which
419
316
// are replay-protected as well as unprotected homestead transactions.
317
+ // Deprecated: always use the Signer interface type
420
318
type EIP155Signer struct {
421
319
chainId , chainIdMul * big.Int
422
320
}
@@ -478,8 +376,9 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
478
376
return tx .inner .sigHash (s .chainId )
479
377
}
480
378
481
- // HomesteadSigner implements Signer interface using the
482
- // homestead rules.
379
+ // HomesteadSigner implements Signer using the homestead rules. The only valid reason to
380
+ // use this type is creating legacy transactions which are intentionally not
381
+ // replay-protected.
483
382
type HomesteadSigner struct { FrontierSigner }
484
383
485
384
func (hs HomesteadSigner ) ChainID () * big.Int {
@@ -505,8 +404,8 @@ func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
505
404
return recoverPlain (hs .Hash (tx ), r , s , v , true )
506
405
}
507
406
508
- // FrontierSigner implements Signer interface using the
509
- // frontier rules.
407
+ // FrontierSigner implements Signer using the frontier rules.
408
+ // Deprecated: always use the Signer interface type
510
409
type FrontierSigner struct {}
511
410
512
411
func (fs FrontierSigner ) ChainID () * big.Int {
0 commit comments