From ba9634e5d6bdc5e87fe53110588e54270682b380 Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Thu, 1 May 2025 15:54:19 +0530 Subject: [PATCH] Revert "core/types: cleanup tx signer logic (#31434)" This reverts commit 80b8d7a13c20254a9cfb9f7cbca1ab00aa6a3b50. --- cmd/evm/internal/t8ntool/tx_iterator.go | 2 +- core/types/transaction_signing.go | 315 ++++++++++++++++-------- params/forks/forks.go | 4 +- 3 files changed, 211 insertions(+), 110 deletions(-) diff --git a/cmd/evm/internal/t8ntool/tx_iterator.go b/cmd/evm/internal/t8ntool/tx_iterator.go index 047626c56b..d4ebb4b399 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.HomesteadSigner{}, tx.key) + signed, err = types.SignTx(tx.tx, types.FrontierSigner{}, 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 831d1bc3ce..bd217df572 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -20,13 +20,11 @@ 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") @@ -180,101 +178,7 @@ type Signer interface { Equal(Signer) bool } -// 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 newModernSigner(chainID *big.Int, fork forks.Fork) Signer { - if chainID == nil || chainID.Sign() <= 0 { - panic(fmt.Sprintf("invalid chainID %v", chainID)) - } - s := &modernSigner{ - chainID: chainID, - txtypes: make(map[byte]struct{}, 4), - } - // 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 *modernSigner) ChainID() *big.Int { - return s.chainID -} - -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) -} - -func (s *modernSigner) Hash(tx *Transaction) common.Hash { - return tx.inner.sigHash(s.chainID) -} - -func (s *modernSigner) supportsType(txtype byte) bool { - _, ok := s.txtypes[txtype] - return ok -} - -func (s *modernSigner) Sender(tx *Transaction) (common.Address, error) { - tt := tx.Type() - if !s.supportsType(tt) { - return common.Address{}, ErrTxTypeNotSupported - } - 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)) - return recoverPlain(s.Hash(tx), R, S, V, true) -} - -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 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 -} +type pragueSigner struct{ cancunSigner } // NewPragueSigner returns a signer that accepts // - EIP-7702 set code transactions @@ -284,9 +188,56 @@ func (s *modernSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *bi // - EIP-155 replay protected transactions, and // - legacy Homestead transactions. func NewPragueSigner(chainId *big.Int) Signer { - return newModernSigner(chainId, forks.Prague) + signer, _ := NewCancunSigner(chainId).(cancunSigner) + return pragueSigner{signer} } +func (s pragueSigner) Sender(tx *Transaction) (common.Address, error) { + if tx.Type() != SetCodeTxType { + return s.cancunSigner.Sender(tx) + } + 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) + } + return recoverPlain(s.Hash(tx), R, S, V, true) +} + +func (s pragueSigner) Equal(s2 Signer) bool { + x, ok := s2.(pragueSigner) + return ok && x.chainId.Cmp(s.chainId) == 0 +} + +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 +} + +// 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) +} + +type cancunSigner struct{ londonSigner } + // NewCancunSigner returns a signer that accepts // - EIP-4844 blob transactions // - EIP-1559 dynamic fee transactions @@ -294,27 +245,178 @@ func NewPragueSigner(chainId *big.Int) Signer { // - EIP-155 replay protected transactions, and // - legacy Homestead transactions. func NewCancunSigner(chainId *big.Int) Signer { - return newModernSigner(chainId, forks.Cancun) + return cancunSigner{londonSigner{eip2930Signer{NewEIP155Signer(chainId)}}} } +func (s cancunSigner) Sender(tx *Transaction) (common.Address, error) { + if tx.Type() != BlobTxType { + return s.londonSigner.Sender(tx) + } + V, R, S := tx.RawSignatureValues() + // Blob 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 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) + } + // 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 +} + +// 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) +} + +type londonSigner struct{ eip2930Signer } + // NewLondonSigner returns a signer that accepts // - EIP-1559 dynamic fee transactions // - EIP-2930 access list transactions, // - EIP-155 replay protected transactions, and // - legacy Homestead transactions. func NewLondonSigner(chainId *big.Int) Signer { - return newModernSigner(chainId, forks.London) + return londonSigner{eip2930Signer{NewEIP155Signer(chainId)}} } +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 newModernSigner(chainId, forks.Berlin) + 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{} + } } // 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 } @@ -376,9 +478,8 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash { return tx.inner.sigHash(s.chainId) } -// 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. +// HomesteadSigner implements Signer interface using the +// homestead rules. type HomesteadSigner struct{ FrontierSigner } func (hs HomesteadSigner) ChainID() *big.Int { @@ -404,8 +505,8 @@ func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) { return recoverPlain(hs.Hash(tx), r, s, v, true) } -// FrontierSigner implements Signer using the frontier rules. -// Deprecated: always use the Signer interface type +// FrontierSigner implements Signer interface using the +// frontier rules. type FrontierSigner struct{} func (fs FrontierSigner) ChainID() *big.Int { diff --git a/params/forks/forks.go b/params/forks/forks.go index 02f6e5b612..2d44e13b04 100644 --- a/params/forks/forks.go +++ b/params/forks/forks.go @@ -24,8 +24,8 @@ const ( FrontierThawing Homestead DAO - TangerineWhistle // a.k.a. the EIP150 fork - SpuriousDragon // a.k.a. the EIP155 fork + TangerineWhistle + SpuriousDragon Byzantium Constantinople Petersburg