diff --git a/cmd/evm/internal/t8ntool/tx_iterator.go b/cmd/evm/internal/t8ntool/tx_iterator.go index d4ebb4b399ea..047626c56b7d 100644 --- a/cmd/evm/internal/t8ntool/tx_iterator.go +++ b/cmd/evm/internal/t8ntool/tx_iterator.go @@ -102,7 +102,7 @@ func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Tran if tx.protected { signed, err = types.SignTx(tx.tx, signer, tx.key) } else { - signed, err = types.SignTx(tx.tx, types.FrontierSigner{}, tx.key) + signed, err = types.SignTx(tx.tx, types.HomesteadSigner{}, tx.key) } if err != nil { return nil, NewError(ErrorJson, fmt.Errorf("tx %d: failed to sign tx: %v", i, err)) diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 89c08aeddd58..01aa67c6ba44 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -20,11 +20,13 @@ import ( "crypto/ecdsa" "errors" "fmt" + "maps" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/params/forks" ) var ErrInvalidChainId = errors.New("invalid chain id for signer") @@ -178,120 +180,122 @@ type Signer interface { Equal(Signer) bool } -type pragueSigner struct{ cancunSigner } - -// NewPragueSigner returns a signer that accepts -// - EIP-7702 set code transactions -// - EIP-4844 blob transactions -// - EIP-1559 dynamic fee transactions -// - EIP-2930 access list transactions, -// - EIP-155 replay protected transactions, and -// - legacy Homestead transactions. -func NewPragueSigner(chainId *big.Int) Signer { - signer, _ := NewCancunSigner(chainId).(cancunSigner) - return pragueSigner{signer} +// modernSigner is the signer implementation that handles non-legacy transaction types. +// For legacy transactions, it defers to one of the legacy signers (frontier, homestead, eip155). +type modernSigner struct { + txtypes map[byte]struct{} + chainID *big.Int + legacy Signer } -func (s pragueSigner) Sender(tx *Transaction) (common.Address, error) { - if tx.Type() != SetCodeTxType { - return s.cancunSigner.Sender(tx) +func newModernSigner(chainID *big.Int, fork forks.Fork) Signer { + if chainID == nil || chainID.Sign() <= 0 { + panic(fmt.Sprintf("invalid chainID %v", chainID)) } - V, R, S := tx.RawSignatureValues() - - // Set code txs are defined to use 0 and 1 as their recovery - // id, add 27 to become equivalent to unprotected Homestead signatures. - V = new(big.Int).Add(V, big.NewInt(27)) - if tx.ChainId().Cmp(s.chainId) != 0 { - return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId) + s := &modernSigner{ + chainID: chainID, + txtypes: make(map[byte]struct{}, 4), } - return recoverPlain(s.Hash(tx), R, S, V, true) + // configure legacy signer + switch { + case fork >= forks.SpuriousDragon: + s.legacy = NewEIP155Signer(chainID) + case fork >= forks.Homestead: + s.legacy = HomesteadSigner{} + default: + s.legacy = FrontierSigner{} + } + s.txtypes[LegacyTxType] = struct{}{} + // configure tx types + if fork >= forks.Berlin { + s.txtypes[AccessListTxType] = struct{}{} + } + if fork >= forks.London { + s.txtypes[DynamicFeeTxType] = struct{}{} + } + if fork >= forks.Cancun { + s.txtypes[BlobTxType] = struct{}{} + } + if fork >= forks.Prague { + s.txtypes[SetCodeTxType] = struct{}{} + } + return s } -func (s pragueSigner) Equal(s2 Signer) bool { - x, ok := s2.(pragueSigner) - return ok && x.chainId.Cmp(s.chainId) == 0 +func (s *modernSigner) ChainID() *big.Int { + return s.chainID } -func (s pragueSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { - txdata, ok := tx.inner.(*SetCodeTx) - if !ok { - return s.cancunSigner.SignatureValues(tx, sig) - } - // Check that chain ID of tx matches the signer. We also accept ID zero here, - // because it indicates that the chain ID was not specified in the tx. - if txdata.ChainID.Sign() != 0 && txdata.ChainID.CmpBig(s.chainId) != 0 { - return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId) - } - R, S, _ = decodeSignature(sig) - V = big.NewInt(int64(sig[64])) - return R, S, V, nil +func (s *modernSigner) Equal(s2 Signer) bool { + other, ok := s2.(*modernSigner) + return ok && s.chainID.Cmp(other.chainID) == 0 && maps.Equal(s.txtypes, other.txtypes) && s.legacy.Equal(other.legacy) } -// Hash returns the hash to be signed by the sender. -// It does not uniquely identify the transaction. -func (s pragueSigner) Hash(tx *Transaction) common.Hash { - if tx.Type() != SetCodeTxType { - return s.cancunSigner.Hash(tx) - } - return tx.inner.sigHash(s.chainId) +func (s *modernSigner) Hash(tx *Transaction) common.Hash { + return tx.inner.sigHash(s.chainID) } -type cancunSigner struct{ londonSigner } - -// NewCancunSigner returns a signer that accepts -// - EIP-4844 blob transactions -// - EIP-1559 dynamic fee transactions -// - EIP-2930 access list transactions, -// - EIP-155 replay protected transactions, and -// - legacy Homestead transactions. -func NewCancunSigner(chainId *big.Int) Signer { - return cancunSigner{londonSigner{eip2930Signer{NewEIP155Signer(chainId)}}} +func (s *modernSigner) supportsType(txtype byte) bool { + _, ok := s.txtypes[txtype] + return ok } -func (s cancunSigner) Sender(tx *Transaction) (common.Address, error) { - if tx.Type() != BlobTxType { - return s.londonSigner.Sender(tx) +func (s *modernSigner) Sender(tx *Transaction) (common.Address, error) { + tt := tx.Type() + if !s.supportsType(tt) { + return common.Address{}, ErrTxTypeNotSupported } - V, R, S := tx.RawSignatureValues() - // Blob txs are defined to use 0 and 1 as their recovery + if tt == LegacyTxType { + return s.legacy.Sender(tx) + } + if tx.ChainId().Cmp(s.chainID) != 0 { + return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainID) + } + // 'modern' txs are defined to use 0 and 1 as their recovery // id, add 27 to become equivalent to unprotected Homestead signatures. + V, R, S := tx.RawSignatureValues() V = new(big.Int).Add(V, big.NewInt(27)) - if tx.ChainId().Cmp(s.chainId) != 0 { - return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId) - } return recoverPlain(s.Hash(tx), R, S, V, true) } -func (s cancunSigner) Equal(s2 Signer) bool { - x, ok := s2.(cancunSigner) - return ok && x.chainId.Cmp(s.chainId) == 0 -} - -func (s cancunSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { - txdata, ok := tx.inner.(*BlobTx) - if !ok { - return s.londonSigner.SignatureValues(tx, sig) +func (s *modernSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { + tt := tx.Type() + if !s.supportsType(tt) { + return nil, nil, nil, ErrTxTypeNotSupported + } + if tt == LegacyTxType { + return s.legacy.SignatureValues(tx, sig) } // Check that chain ID of tx matches the signer. We also accept ID zero here, // because it indicates that the chain ID was not specified in the tx. - if txdata.ChainID.Sign() != 0 && txdata.ChainID.CmpBig(s.chainId) != 0 { - return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId) + if tx.inner.chainID().Sign() != 0 && tx.inner.chainID().Cmp(s.chainID) != 0 { + return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.inner.chainID(), s.chainID) } R, S, _ = decodeSignature(sig) V = big.NewInt(int64(sig[64])) return R, S, V, nil } -// Hash returns the hash to be signed by the sender. -// It does not uniquely identify the transaction. -func (s cancunSigner) Hash(tx *Transaction) common.Hash { - if tx.Type() != BlobTxType { - return s.londonSigner.Hash(tx) - } - return tx.inner.sigHash(s.chainId) +// NewPragueSigner returns a signer that accepts +// - EIP-7702 set code transactions +// - EIP-4844 blob transactions +// - EIP-1559 dynamic fee transactions +// - EIP-2930 access list transactions, +// - EIP-155 replay protected transactions, and +// - legacy Homestead transactions. +func NewPragueSigner(chainId *big.Int) Signer { + return newModernSigner(chainId, forks.Prague) } -type londonSigner struct{ eip2930Signer } +// NewCancunSigner returns a signer that accepts +// - EIP-4844 blob transactions +// - EIP-1559 dynamic fee transactions +// - EIP-2930 access list transactions, +// - EIP-155 replay protected transactions, and +// - legacy Homestead transactions. +func NewCancunSigner(chainId *big.Int) Signer { + return newModernSigner(chainId, forks.Cancun) +} // NewLondonSigner returns a signer that accepts // - EIP-1559 dynamic fee transactions @@ -299,124 +303,18 @@ type londonSigner struct{ eip2930Signer } // - EIP-155 replay protected transactions, and // - legacy Homestead transactions. func NewLondonSigner(chainId *big.Int) Signer { - return londonSigner{eip2930Signer{NewEIP155Signer(chainId)}} + return newModernSigner(chainId, forks.London) } -func (s londonSigner) Sender(tx *Transaction) (common.Address, error) { - if tx.Type() != DynamicFeeTxType { - return s.eip2930Signer.Sender(tx) - } - V, R, S := tx.RawSignatureValues() - // DynamicFee txs are defined to use 0 and 1 as their recovery - // id, add 27 to become equivalent to unprotected Homestead signatures. - V = new(big.Int).Add(V, big.NewInt(27)) - if tx.ChainId().Cmp(s.chainId) != 0 { - return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId) - } - return recoverPlain(s.Hash(tx), R, S, V, true) -} - -func (s londonSigner) Equal(s2 Signer) bool { - x, ok := s2.(londonSigner) - return ok && x.chainId.Cmp(s.chainId) == 0 -} - -func (s londonSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { - txdata, ok := tx.inner.(*DynamicFeeTx) - if !ok { - return s.eip2930Signer.SignatureValues(tx, sig) - } - // Check that chain ID of tx matches the signer. We also accept ID zero here, - // because it indicates that the chain ID was not specified in the tx. - if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 { - return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId) - } - R, S, _ = decodeSignature(sig) - V = big.NewInt(int64(sig[64])) - return R, S, V, nil -} - -// Hash returns the hash to be signed by the sender. -// It does not uniquely identify the transaction. -func (s londonSigner) Hash(tx *Transaction) common.Hash { - if tx.Type() != DynamicFeeTxType { - return s.eip2930Signer.Hash(tx) - } - return tx.inner.sigHash(s.chainId) -} - -type eip2930Signer struct{ EIP155Signer } - // NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions, // EIP-155 replay protected transactions, and legacy Homestead transactions. func NewEIP2930Signer(chainId *big.Int) Signer { - return eip2930Signer{NewEIP155Signer(chainId)} -} - -func (s eip2930Signer) ChainID() *big.Int { - return s.chainId -} - -func (s eip2930Signer) Equal(s2 Signer) bool { - x, ok := s2.(eip2930Signer) - return ok && x.chainId.Cmp(s.chainId) == 0 -} - -func (s eip2930Signer) Sender(tx *Transaction) (common.Address, error) { - V, R, S := tx.RawSignatureValues() - switch tx.Type() { - case LegacyTxType: - return s.EIP155Signer.Sender(tx) - case AccessListTxType: - // AL txs are defined to use 0 and 1 as their recovery - // id, add 27 to become equivalent to unprotected Homestead signatures. - V = new(big.Int).Add(V, big.NewInt(27)) - default: - return common.Address{}, ErrTxTypeNotSupported - } - if tx.ChainId().Cmp(s.chainId) != 0 { - return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId) - } - return recoverPlain(s.Hash(tx), R, S, V, true) -} - -func (s eip2930Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { - switch txdata := tx.inner.(type) { - case *LegacyTx: - return s.EIP155Signer.SignatureValues(tx, sig) - case *AccessListTx: - // Check that chain ID of tx matches the signer. We also accept ID zero here, - // because it indicates that the chain ID was not specified in the tx. - if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 { - return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId) - } - R, S, _ = decodeSignature(sig) - V = big.NewInt(int64(sig[64])) - default: - return nil, nil, nil, ErrTxTypeNotSupported - } - return R, S, V, nil -} - -// Hash returns the hash to be signed by the sender. -// It does not uniquely identify the transaction. -func (s eip2930Signer) Hash(tx *Transaction) common.Hash { - switch tx.Type() { - case LegacyTxType: - return s.EIP155Signer.Hash(tx) - case AccessListTxType: - return tx.inner.sigHash(s.chainId) - default: - // This _should_ not happen, but in case someone sends in a bad - // json struct via RPC, it's probably more prudent to return an - // empty hash instead of killing the node with a panic - //panic("Unsupported transaction type: %d", tx.typ) - return common.Hash{} - } + return newModernSigner(chainId, forks.Berlin) } // EIP155Signer implements Signer using the EIP-155 rules. This accepts transactions which // are replay-protected as well as unprotected homestead transactions. +// Deprecated: always use the Signer interface type type EIP155Signer struct { chainId, chainIdMul *big.Int } @@ -478,8 +376,9 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash { return tx.inner.sigHash(s.chainId) } -// HomesteadSigner implements Signer interface using the -// homestead rules. +// HomesteadSigner implements Signer using the homestead rules. The only valid reason to +// use this type is creating legacy transactions which are intentionally not +// replay-protected. type HomesteadSigner struct{ FrontierSigner } func (hs HomesteadSigner) ChainID() *big.Int { @@ -505,8 +404,8 @@ func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) { return recoverPlain(hs.Hash(tx), r, s, v, true) } -// FrontierSigner implements Signer interface using the -// frontier rules. +// FrontierSigner implements Signer using the frontier rules. +// Deprecated: always use the Signer interface type type FrontierSigner struct{} func (fs FrontierSigner) ChainID() *big.Int { diff --git a/params/forks/forks.go b/params/forks/forks.go index 2d44e13b04d8..02f6e5b6125d 100644 --- a/params/forks/forks.go +++ b/params/forks/forks.go @@ -24,8 +24,8 @@ const ( FrontierThawing Homestead DAO - TangerineWhistle - SpuriousDragon + TangerineWhistle // a.k.a. the EIP150 fork + SpuriousDragon // a.k.a. the EIP155 fork Byzantium Constantinople Petersburg