@@ -20,11 +20,13 @@ import (
2020 "crypto/ecdsa"
2121 "errors"
2222 "fmt"
23+ "maps"
2324 "math/big"
2425
2526 "github.com/ethereum/go-ethereum/common"
2627 "github.com/ethereum/go-ethereum/crypto"
2728 "github.com/ethereum/go-ethereum/params"
29+ "github.com/ethereum/go-ethereum/params/forks"
2830)
2931
3032var ErrInvalidChainId = errors .New ("invalid chain id for signer" )
@@ -178,245 +180,141 @@ type Signer interface {
178180 Equal (Signer ) bool
179181}
180182
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
193189}
194190
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 ) )
198194 }
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 ),
206198 }
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
208223}
209224
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
213227}
214228
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 )
228232}
229233
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 )
237236}
238237
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
249241}
250242
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
254247 }
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
257255 // id, add 27 to become equivalent to unprotected Homestead signatures.
256+ V , R , S := tx .RawSignatureValues ()
258257 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- }
262258 return recoverPlain (s .Hash (tx ), R , S , V , true )
263259}
264260
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 )
274268 }
275269 // Check that chain ID of tx matches the signer. We also accept ID zero here,
276270 // 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 )
279273 }
280274 R , S , _ = decodeSignature (sig )
281275 V = big .NewInt (int64 (sig [64 ]))
282276 return R , S , V , nil
283277}
284278
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 )
292288}
293289
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+ }
295299
296300// NewLondonSigner returns a signer that accepts
297301// - EIP-1559 dynamic fee transactions
298302// - EIP-2930 access list transactions,
299303// - EIP-155 replay protected transactions, and
300304// - legacy Homestead transactions.
301305func NewLondonSigner (chainId * big.Int ) Signer {
302- return londonSigner { eip2930Signer { NewEIP155Signer (chainId )}}
306+ return newModernSigner (chainId , forks . London )
303307}
304308
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-
350309// NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions,
351310// EIP-155 replay protected transactions, and legacy Homestead transactions.
352311func 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 )
416313}
417314
418315// EIP155Signer implements Signer using the EIP-155 rules. This accepts transactions which
419316// are replay-protected as well as unprotected homestead transactions.
317+ // Deprecated: always use the Signer interface type
420318type EIP155Signer struct {
421319 chainId , chainIdMul * big.Int
422320}
@@ -478,8 +376,9 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
478376 return tx .inner .sigHash (s .chainId )
479377}
480378
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.
483382type HomesteadSigner struct { FrontierSigner }
484383
485384func (hs HomesteadSigner ) ChainID () * big.Int {
@@ -505,8 +404,8 @@ func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) {
505404 return recoverPlain (hs .Hash (tx ), r , s , v , true )
506405}
507406
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
510409type FrontierSigner struct {}
511410
512411func (fs FrontierSigner ) ChainID () * big.Int {
0 commit comments