diff --git a/crypto/crypto.go b/crypto/crypto.go index 4f8d2a1629..061ca03c06 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -30,6 +30,7 @@ var ( ErrInvalidBLSSignature = errors.New("invalid BLS Signature") errHashOfInvalidLength = errors.New("message hash of invalid length") errInvalidSignature = errors.New("invalid signature") + errInvalidChainID = errors.New("invalid chain id for signer") ) type KeyType string diff --git a/crypto/txsigner.go b/crypto/txsigner.go index 55aed47392..2d43bab9ee 100644 --- a/crypto/txsigner.go +++ b/crypto/txsigner.go @@ -7,208 +7,106 @@ import ( "math/big" "github.com/0xPolygon/polygon-edge/chain" - "github.com/0xPolygon/polygon-edge/helper/keccak" "github.com/0xPolygon/polygon-edge/types" + "github.com/umbracle/fastrlp" ) -// Magic numbers from Ethereum, used in v calculation +// Magic numbers, taken from the Ethereum, used in the calculation of the V value +// Only matters in pre-EIP-2930 (pre-Berlin) transactions var ( - big27 = big.NewInt(27) - big35 = big.NewInt(35) + big27 = big.NewInt(27) // pre-EIP-155 + big35 = big.NewInt(35) // EIP-155 ) -// TxSigner is a utility interface used to recover data from a transaction +// RLP encoding helper +var arenaPool fastrlp.ArenaPool + +// TxSigner is a utility interface used to work with transaction signatures type TxSigner interface { // Hash returns the hash of the transaction - Hash(tx *types.Transaction) types.Hash + Hash(*types.Transaction) types.Hash // Sender returns the sender of the transaction - Sender(tx *types.Transaction) (types.Address, error) + Sender(*types.Transaction) (types.Address, error) - // SignTx signs a transaction - SignTx(tx *types.Transaction, priv *ecdsa.PrivateKey) (*types.Transaction, error) + // SingTx takes the original transaction as input and returns its signed version + SignTx(*types.Transaction, *ecdsa.PrivateKey) (*types.Transaction, error) } -// NewSigner creates a new signer object (EIP155 or FrontierSigner) +// NewSigner creates a new signer based on currently supported forks func NewSigner(forks chain.ForksInTime, chainID uint64) TxSigner { - var signer TxSigner + if forks.London { + return NewLondonSigner(chainID) + } + + if forks.Berlin { + return NewBerlinSigner(chainID) + } if forks.EIP155 { - signer = NewEIP155Signer(chainID, forks.Homestead) - } else { - signer = NewFrontierSigner(forks.Homestead) + return NewEIP155Signer(chainID) } - // London signer requires a fallback signer that is defined above. - // This is the reason why the london signer check is separated. - if forks.London || forks.Berlin { - return NewLondonOrBerlinSigner(chainID, forks.Homestead, signer) + if forks.Homestead { + return NewHomesteadSigner() } - return signer + return NewFrontierSigner() } -// encodeSignature generates a signature value based on the R, S and V value -func encodeSignature(R, S, V *big.Int, isHomestead bool) ([]byte, error) { - if !ValidateSignatureValues(V, R, S, isHomestead) { - return nil, fmt.Errorf("invalid txn signature") +// encodeSignature generates a signature based on the R, S and parity values +// +// The signature encoding format is as follows: +// (32-bytes R, 32-bytes S, 1-byte parity) +// +// Note: although the signature value V, based on different standards, is calculated and encoded in different ways, +// the encodeSignature function expects parity of Y coordinate as third input and that is what will be encoded +func encodeSignature(r, s, parity *big.Int, isHomestead bool) ([]byte, error) { + if !ValidateSignatureValues(parity, r, s, isHomestead) { + return nil, errors.New("signature encoding failed, because transaction signature is invalid") } - sig := make([]byte, 65) - copy(sig[32-len(R.Bytes()):32], R.Bytes()) - copy(sig[64-len(S.Bytes()):64], S.Bytes()) - sig[64] = byte(V.Int64()) // here is safe to convert it since ValidateSignatureValues will validate the v value + signature := make([]byte, 65) - return sig, nil + copy(signature[32-len(r.Bytes()):32], r.Bytes()) + copy(signature[64-len(s.Bytes()):64], s.Bytes()) + signature[64] = byte(parity.Int64()) + + return signature, nil } -// recoverAddress recovers the sender address from a transaction hash and signature parameters. -// It takes the transaction hash, r, s, v values of the signature, -// and a flag indicating if the transaction is in the Homestead format. -// It returns the recovered address and an error if any. -func recoverAddress(txHash types.Hash, r, s, v *big.Int, isHomestead bool) (types.Address, error) { - sig, err := encodeSignature(r, s, v, isHomestead) +// recoverAddress recovers the sender address from the transaction hash and signature R, S and parity values +func recoverAddress(txHash types.Hash, r, s, parity *big.Int, isHomestead bool) (types.Address, error) { + signature, err := encodeSignature(r, s, parity, isHomestead) if err != nil { return types.ZeroAddress, err } - pub, err := Ecrecover(txHash.Bytes(), sig) + publicKey, err := Ecrecover(txHash.Bytes(), signature) if err != nil { return types.ZeroAddress, err } - if len(pub) == 0 || pub[0] != 4 { + if len(publicKey) == 0 || publicKey[0] != 4 { return types.ZeroAddress, errors.New("invalid public key") } - buf := Keccak256(pub[1:])[12:] - - return types.BytesToAddress(buf), nil -} + // First byte of the publicKey indicates that it is serialized in uncompressed form + // (it has the value 0x04), so we ommit that + hash := Keccak256(publicKey[1:]) -// calcTxHash calculates the transaction hash (keccak256 hash of the RLP value) -// LegacyTx: -// keccak256(RLP(nonce, gasPrice, gas, to, value, input, chainId, 0, 0)) -// AccessListsTx: -// keccak256(RLP(type, chainId, nonce, gasPrice, gas, to, value, input, accessList)) -// DynamicFeeTx: -// keccak256(RLP(type, chainId, nonce, gasTipCap, gasFeeCap, gas, to, value, input, accessList)) -func calcTxHash(tx *types.Transaction, chainID uint64) types.Hash { - var hash []byte + address := hash[12:] - switch tx.Type() { - case types.AccessListTxType: - a := signerPool.Get() - v := a.NewArray() - - v.Set(a.NewUint(chainID)) - v.Set(a.NewUint(tx.Nonce())) - v.Set(a.NewBigInt(tx.GasPrice())) - v.Set(a.NewUint(tx.Gas())) - - if tx.To() == nil { - v.Set(a.NewNull()) - } else { - v.Set(a.NewCopyBytes((*(tx.To())).Bytes())) - } - - v.Set(a.NewBigInt(tx.Value())) - v.Set(a.NewCopyBytes(tx.Input())) - - // add accessList - accessListVV := a.NewArray() - - if tx.AccessList() != nil { - for _, accessTuple := range tx.AccessList() { - accessTupleVV := a.NewArray() - accessTupleVV.Set(a.NewCopyBytes(accessTuple.Address.Bytes())) - - storageKeysVV := a.NewArray() - for _, storageKey := range accessTuple.StorageKeys { - storageKeysVV.Set(a.NewCopyBytes(storageKey.Bytes())) - } - - accessTupleVV.Set(storageKeysVV) - accessListVV.Set(accessTupleVV) - } - } - - v.Set(accessListVV) - - hash = keccak.PrefixedKeccak256Rlp([]byte{byte(tx.Type())}, nil, v) - - signerPool.Put(a) - - return types.BytesToHash(hash) - - case types.DynamicFeeTxType, types.LegacyTxType, types.StateTxType: - a := signerPool.Get() - isDynamicFeeTx := tx.Type() == types.DynamicFeeTxType - - v := a.NewArray() - - if isDynamicFeeTx { - v.Set(a.NewUint(chainID)) - } - - v.Set(a.NewUint(tx.Nonce())) - - if isDynamicFeeTx { - v.Set(a.NewBigInt(tx.GasTipCap())) - v.Set(a.NewBigInt(tx.GasFeeCap())) - } else { - v.Set(a.NewBigInt(tx.GasPrice())) - } - - v.Set(a.NewUint(tx.Gas())) - - if tx.To() == nil { - v.Set(a.NewNull()) - } else { - v.Set(a.NewCopyBytes((*(tx.To())).Bytes())) - } - - v.Set(a.NewBigInt(tx.Value())) - - v.Set(a.NewCopyBytes(tx.Input())) - - if isDynamicFeeTx { - // Convert TxAccessList to RLP format and add it to the vv array. - accessListVV := a.NewArray() - - if tx.AccessList() != nil { - for _, accessTuple := range tx.AccessList() { - accessTupleVV := a.NewArray() - accessTupleVV.Set(a.NewCopyBytes(accessTuple.Address.Bytes())) - - storageKeysVV := a.NewArray() - for _, storageKey := range accessTuple.StorageKeys { - storageKeysVV.Set(a.NewCopyBytes(storageKey.Bytes())) - } - - accessTupleVV.Set(storageKeysVV) - accessListVV.Set(accessTupleVV) - } - } - - v.Set(accessListVV) - } else { - // EIP155 - if chainID != 0 { - v.Set(a.NewUint(chainID)) - v.Set(a.NewUint(0)) - v.Set(a.NewUint(0)) - } - } + return types.BytesToAddress(address), nil +} - if isDynamicFeeTx { - hash = keccak.PrefixedKeccak256Rlp([]byte{byte(tx.Type())}, nil, v) - } else { - hash = keccak.Keccak256Rlp(nil, v) - } +// validateTxChainID checks if the transaction chain ID matches the expected chain ID +func validateTxChainID(tx *types.Transaction, chainID uint64) error { + txChainID := tx.ChainID() - signerPool.Put(a) + if txChainID == nil || txChainID.Uint64() != chainID { + return fmt.Errorf("%w: have %d want %d", errInvalidChainID, tx.ChainID(), chainID) } - return types.BytesToHash(hash) + return nil } diff --git a/crypto/txsigner_berlin.go b/crypto/txsigner_berlin.go new file mode 100644 index 0000000000..21f9777e2e --- /dev/null +++ b/crypto/txsigner_berlin.go @@ -0,0 +1,154 @@ +package crypto + +import ( + "crypto/ecdsa" + "errors" + "math/big" + + "github.com/0xPolygon/polygon-edge/helper/keccak" + "github.com/0xPolygon/polygon-edge/types" + "github.com/umbracle/fastrlp" +) + +// BerlinSigner may be used for signing legacy (pre-EIP-155 and EIP-155) and EIP-2930 transactions +type BerlinSigner struct { + *EIP155Signer +} + +// NewBerlinSigner returns new BerlinSinger object (constructor) +// +// BerlinSigner accepts the following types of transactions: +// - EIP-2930 access list transactions, +// - EIP-155 replay protected transactions, and +// - pre-EIP-155 legacy transactions +func NewBerlinSigner(chainID uint64) *BerlinSigner { + return &BerlinSigner{EIP155Signer: NewEIP155Signer(chainID)} +} + +// Hash returns the keccak256 hash of the transaction +// +// The EIP-2930 transaction hash preimage is as follows: +// (0x01 || RLP(chainId, nonce, gasPrice, gas, to, value, input, accessList) +// +// Specification: https://eips.ethereum.org/EIPS/eip-2930#specification +func (signer *BerlinSigner) Hash(tx *types.Transaction) types.Hash { + if tx.Type() != types.AccessListTxType { + return signer.EIP155Signer.Hash(tx) + } + + RLP := arenaPool.Get() + defer arenaPool.Put(RLP) + + // RLP(-, -, -, -, -, -, -, -) + hashPreimage := RLP.NewArray() + + // RLP(chainId, -, -, -, -, -, -, -) + hashPreimage.Set(RLP.NewUint(signer.chainID)) + + // RLP(chainId, nonce, -, -, -, -, -, -) + hashPreimage.Set(RLP.NewUint(tx.Nonce())) + + // RLP(chainId, nonce, gasPrice, -, -, -, -, -) + hashPreimage.Set(RLP.NewBigInt(tx.GasPrice())) + + // RLP(chainId, nonce, gasPrice, gas, -, -, -, -) + hashPreimage.Set(RLP.NewUint(tx.Gas())) + + // Checking whether the transaction is a smart contract deployment + if tx.To() == nil { + // RLP(chainId, nonce, gasPrice, gas, to, -, -, -) + hashPreimage.Set(RLP.NewNull()) + } else { + // RLP(chainId, nonce, gasPrice, gas, to, -, -, -) + hashPreimage.Set(RLP.NewCopyBytes((*(tx.To())).Bytes())) + } + + // RLP(chainId, nonce, gasPrice, gas, to, value, -, -) + hashPreimage.Set(RLP.NewBigInt(tx.Value())) + + // RLP(chainId, nonce, gasPrice, gas, to, value, input, -) + hashPreimage.Set(RLP.NewCopyBytes(tx.Input())) + + // Serialization format of the access list: + // [[{20-bytes address}, [{32-bytes key}, ...]], ...] where `...` denotes zero or more items + var rlpAccessList *fastrlp.Value + + accessList := tx.AccessList() + if accessList != nil { + rlpAccessList = accessList.MarshallRLPWith(RLP) + } else { + rlpAccessList = RLP.NewArray() + } + + // RLP(chainId, nonce, gasPrice, gas, to, value, input, accessList) + hashPreimage.Set(rlpAccessList) + + // keccak256(0x01 || RLP(chainId, nonce, gasPrice, gas, to, value, input, accessList) + hash := keccak.PrefixedKeccak256Rlp([]byte{byte(tx.Type())}, nil, hashPreimage) + + return types.BytesToHash(hash) +} + +// Sender returns the sender of the transaction +func (signer *BerlinSigner) Sender(tx *types.Transaction) (types.Address, error) { + if tx.Type() == types.DynamicFeeTxType { + return types.ZeroAddress, types.ErrTxTypeNotSupported + } + + if tx.Type() != types.AccessListTxType { + return signer.EIP155Signer.Sender(tx) + } + + v, r, s := tx.RawSignatureValues() + + // Checking one of the values is enough since they are inseparable + if v == nil { + return types.Address{}, errors.New("failed to recover sender, because signature is unknown") + } + + if err := validateTxChainID(tx, signer.chainID); err != nil { + return types.ZeroAddress, err + } + + return recoverAddress(signer.Hash(tx), r, s, v, true) +} + +// SingTx takes the original transaction as input and returns its signed version +func (signer *BerlinSigner) SignTx(tx *types.Transaction, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) { + if tx.Type() == types.DynamicFeeTxType { + return nil, types.ErrTxTypeNotSupported + } + + if tx.Type() != types.AccessListTxType { + return signer.EIP155Signer.SignTx(tx, privateKey) + } + + tx = tx.Copy() + + h := signer.Hash(tx) + + sig, err := Sign(privateKey, h[:]) + if err != nil { + return nil, err + } + + r := new(big.Int).SetBytes(sig[:32]) + s := new(big.Int).SetBytes(sig[32:64]) + + if s.Cmp(secp256k1NHalf) > 0 { + return nil, errors.New("SignTx method: S must be inclusively lower than secp256k1n/2") + } + + v := new(big.Int).SetBytes(signer.calculateV(sig[64])) + + tx.SetSignatureValues(v, r, s) + + return tx, nil +} + +// Private method calculateV returns the V value for the EIP-2930 transactions +// +// V represents the parity of the Y coordinate +func (signer *BerlinSigner) calculateV(parity byte) []byte { + return big.NewInt(int64(parity)).Bytes() +} diff --git a/crypto/txsigner_eip155.go b/crypto/txsigner_eip155.go index 5e330cb977..275dee04c9 100644 --- a/crypto/txsigner_eip155.go +++ b/crypto/txsigner_eip155.go @@ -2,90 +2,163 @@ package crypto import ( "crypto/ecdsa" + "errors" "math/big" "math/bits" + "github.com/0xPolygon/polygon-edge/helper/keccak" "github.com/0xPolygon/polygon-edge/types" ) +// EIP155Signer may be used for signing legacy (pre-EIP-155 and EIP-155) transactions type EIP155Signer struct { - chainID uint64 - isHomestead bool + *HomesteadSigner + chainID uint64 } -// NewEIP155Signer returns a new EIP155Signer object -func NewEIP155Signer(chainID uint64, isHomestead bool) *EIP155Signer { +// NewEIP155Signer returns new EIP155Signer object (constructor) +// +// EIP155Signer accepts the following types of transactions: +// - EIP-155 replay protected transactions, and +// - pre-EIP-155 legacy transactions +func NewEIP155Signer(chainID uint64) *EIP155Signer { return &EIP155Signer{ - chainID: chainID, - isHomestead: isHomestead, + chainID: chainID, + HomesteadSigner: NewHomesteadSigner(), } } -// Hash is a wrapper function that calls calcTxHash with the EIP155Signer's chainID -func (e *EIP155Signer) Hash(tx *types.Transaction) types.Hash { - return calcTxHash(tx, e.chainID) +// Hash returns the keccak256 hash of the transaction +// +// The EIP-155 transaction hash preimage is as follows: +// RLP(nonce, gasPrice, gas, to, value, input, chainId, 0, 0) +// +// Specification: https://eips.ethereum.org/EIPS/eip-155#specification +func (signer *EIP155Signer) Hash(tx *types.Transaction) types.Hash { + RLP := arenaPool.Get() + defer arenaPool.Put(RLP) + + // RLP(-, -, -, -, -, -, -, -, -) + hashPreimage := RLP.NewArray() + + // RLP(nonce, -, -, -, -, -, -, -, -) + hashPreimage.Set(RLP.NewUint(tx.Nonce())) + + // RLP(nonce, gasPrice, -, -, -, -, -, -, -) + hashPreimage.Set(RLP.NewBigInt(tx.GasPrice())) + + // RLP(nonce, gasPrice, gas, -, -, -, -, -, -) + hashPreimage.Set(RLP.NewUint(tx.Gas())) + + // Checking whether the transaction is a smart contract deployment + if tx.To() == nil { + // RLP(nonce, gasPrice, gas, to, -, -, -, -, -) + hashPreimage.Set(RLP.NewNull()) + } else { + // RLP(nonce, gasPrice, gas, to, -, -, -, -, -) + hashPreimage.Set(RLP.NewCopyBytes((*(tx.To())).Bytes())) + } + + // RLP(nonce, gasPrice, gas, to, value, -, -, -, -) + hashPreimage.Set(RLP.NewBigInt(tx.Value())) + + // RLP(nonce, gasPrice, gas, to, value, input, -, -, -) + hashPreimage.Set(RLP.NewCopyBytes(tx.Input())) + + // RLP(nonce, gasPrice, gas, to, value, input, chainId, -, -) + hashPreimage.Set(RLP.NewUint(signer.chainID)) + + // RLP(nonce, gasPrice, gas, to, value, input, chainId, 0, -) + hashPreimage.Set(RLP.NewUint(0)) + + // RLP(nonce, gasPrice, gas, to, value, input, chainId, 0, 0) + hashPreimage.Set(RLP.NewUint(0)) + + // keccak256(RLP(nonce, gasPrice, gas, to, value, input)) + hash := keccak.Keccak256Rlp(nil, hashPreimage) + + return types.BytesToHash(hash) } -// Sender returns the transaction sender -func (e *EIP155Signer) Sender(tx *types.Transaction) (types.Address, error) { - protected := true +// Sender returns the sender of the transaction +func (signer *EIP155Signer) Sender(tx *types.Transaction) (types.Address, error) { + if tx.Type() != types.LegacyTxType && tx.Type() != types.StateTxType { + return types.ZeroAddress, types.ErrTxTypeNotSupported + } - // Check if v value conforms to an earlier standard (before EIP155) - bigV := big.NewInt(0) + protected := true v, r, s := tx.RawSignatureValues() - if v != nil { - bigV.SetBytes(v.Bytes()) + + // Checking one of the values is enought since they are inseparable + if v == nil { + return types.Address{}, errors.New("failed to recover sender, because signature is unknown") } - if vv := bigV.Uint64(); bits.Len(uint(vv)) <= 8 { + bigV := big.NewInt(0).SetBytes(v.Bytes()) + + if vv := v.Uint64(); bits.Len(uint(vv)) <= 8 { protected = vv != 27 && vv != 28 } if !protected { - return (&FrontierSigner{}).Sender(tx) + return signer.HomesteadSigner.Sender(tx) + } + + if err := validateTxChainID(tx, signer.chainID); err != nil { + return types.ZeroAddress, err } - // Reverse the V calculation to find the original V in the range [0, 1] - // v = CHAIN_ID * 2 + 35 + {0, 1} - mulOperand := big.NewInt(0).Mul(big.NewInt(int64(e.chainID)), big.NewInt(2)) + // Reverse the V calculation to find the parity of the Y coordinate + // v = CHAIN_ID * 2 + 35 + {0, 1} -> {0, 1} = v - 35 - CHAIN_ID * 2 + mulOperand := big.NewInt(0).Mul(big.NewInt(int64(signer.chainID)), big.NewInt(2)) bigV.Sub(bigV, mulOperand) bigV.Sub(bigV, big35) - return recoverAddress(e.Hash(tx), r, s, bigV, e.isHomestead) + return recoverAddress(signer.Hash(tx), r, s, bigV, true) } -// SignTx signs the transaction using the passed in private key -func (e *EIP155Signer) SignTx( - tx *types.Transaction, - privateKey *ecdsa.PrivateKey, -) (*types.Transaction, error) { +// SingTx takes the original transaction as input and returns its signed version +func (signer *EIP155Signer) SignTx(tx *types.Transaction, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) { + if tx.Type() != types.LegacyTxType && tx.Type() != types.StateTxType { + return nil, types.ErrTxTypeNotSupported + } + tx = tx.Copy() - h := e.Hash(tx) + hash := signer.Hash(tx) - sig, err := Sign(privateKey, h[:]) + signature, err := Sign(privateKey, hash[:]) if err != nil { return nil, err } - r := new(big.Int).SetBytes(sig[:32]) - s := new(big.Int).SetBytes(sig[32:64]) - v := new(big.Int).SetBytes(e.calculateV(sig[64])) + r := new(big.Int).SetBytes(signature[:32]) + s := new(big.Int).SetBytes(signature[32:64]) + + if s.Cmp(secp256k1NHalf) > 0 { + return nil, errors.New("SignTx method: S must be inclusively lower than secp256k1n/2") + } + + v := new(big.Int).SetBytes(signer.calculateV(signature[64])) tx.SetSignatureValues(v, r, s) return tx, nil } -// calculateV returns the V value for transaction signatures. Based on EIP155 -func (e *EIP155Signer) calculateV(parity byte) []byte { - reference := big.NewInt(int64(parity)) - reference.Add(reference, big35) +// Private method calculateV returns the V value for the EIP-155 transactions +// +// V is calculated by the formula: {0, 1} + CHAIN_ID * 2 + 35 where {0, 1} denotes the parity of the Y coordinate +func (signer *EIP155Signer) calculateV(parity byte) []byte { + // a = {0, 1} + 35 + a := big.NewInt(int64(parity)) + a.Add(a, big35) - mulOperand := big.NewInt(0).Mul(big.NewInt(int64(e.chainID)), big.NewInt(2)) + // b = CHAIN_ID * 2 + b := big.NewInt(0).Mul(big.NewInt(int64(signer.chainID)), big.NewInt(2)) - reference.Add(reference, mulOperand) + a.Add(a, b) - return reference.Bytes() + return a.Bytes() } diff --git a/crypto/txsigner_eip155_test.go b/crypto/txsigner_eip155_test.go index ce6c0752a1..5ddf2d204c 100644 --- a/crypto/txsigner_eip155_test.go +++ b/crypto/txsigner_eip155_test.go @@ -78,7 +78,6 @@ func TestEIP155Signer_Sender(t *testing.T) { signer := NewEIP155Signer( testCase.chainID.Uint64(), - testCase.isHomestead, ) signedTx, signErr := signer.SignTx(txn, key) @@ -112,7 +111,7 @@ func TestEIP155Signer_ChainIDMismatch(t *testing.T) { GasPrice: big.NewInt(0), }) - signer := NewEIP155Signer(chainIDTop, true) + signer := NewEIP155Signer(chainIDTop) signedTx, signErr := signer.SignTx(txn, key) if signErr != nil { @@ -120,7 +119,7 @@ func TestEIP155Signer_ChainIDMismatch(t *testing.T) { } for _, chainIDBottom := range chainIDS { - signerBottom := NewEIP155Signer(chainIDBottom, true) + signerBottom := NewEIP155Signer(chainIDBottom) recoveredSender, recoverErr := signerBottom.Sender(signedTx) if chainIDTop == chainIDBottom { diff --git a/crypto/txsigner_frontier.go b/crypto/txsigner_frontier.go index 5365e54562..26158576d2 100644 --- a/crypto/txsigner_frontier.go +++ b/crypto/txsigner_frontier.go @@ -2,73 +2,137 @@ package crypto import ( "crypto/ecdsa" + "errors" "math/big" - "github.com/umbracle/fastrlp" - + "github.com/0xPolygon/polygon-edge/helper/keccak" "github.com/0xPolygon/polygon-edge/types" ) -var signerPool fastrlp.ArenaPool - -// FrontierSigner implements tx signer interface +// FrontierSigner may be used for pre-EIP-155 transactions type FrontierSigner struct { - isHomestead bool } -// NewFrontierSigner is the constructor of FrontierSigner -func NewFrontierSigner(isHomestead bool) *FrontierSigner { - return &FrontierSigner{ - isHomestead: isHomestead, +// NewFrontierSigner returns new FrontierSigner object (constructor) +// +// FrontierSigner accepts the following types of transactions: +// - pre-EIP-155 transactions +func NewFrontierSigner() *FrontierSigner { + return &FrontierSigner{} +} + +// Hash returns the keccak256 hash of the transaction +// +// The pre-EIP-155 transaction hash preimage is as follows: +// RLP(nonce, gasPrice, gas, to, value, input) +// +// Specification: https://eips.ethereum.org/EIPS/eip-155#specification +func (signer *FrontierSigner) Hash(tx *types.Transaction) types.Hash { + RLP := arenaPool.Get() + defer arenaPool.Put(RLP) + + // RLP(-, -, -, -, -, -) + hashPreimage := RLP.NewArray() + + // RLP(nonce, -, -, -, -, -) + hashPreimage.Set(RLP.NewUint(tx.Nonce())) + + // RLP(nonce, gasPrice, -, -, -, -) + hashPreimage.Set(RLP.NewBigInt(tx.GasPrice())) + + // RLP(nonce, gasPrice, gas, -, -, -) + hashPreimage.Set(RLP.NewUint(tx.Gas())) + + // Checking whether the transaction is a smart contract deployment + if tx.To() == nil { + // RLP(nonce, gasPrice, gas, to, -, -) + hashPreimage.Set(RLP.NewNull()) + } else { + // RLP(nonce, gasPrice, gas, to, -, -) + hashPreimage.Set(RLP.NewCopyBytes((*(tx.To())).Bytes())) } + + // RLP(nonce, gasPrice, gas, to, value, -) + hashPreimage.Set(RLP.NewBigInt(tx.Value())) + + // RLP(nonce, gasPrice, gas, to, value, input) + hashPreimage.Set(RLP.NewCopyBytes(tx.Input())) + + // keccak256(RLP(nonce, gasPrice, gas, to, value, input)) + hash := keccak.Keccak256Rlp(nil, hashPreimage) + + return types.BytesToHash(hash) } -// Hash is a wrapper function for the calcTxHash, with chainID 0 -func (f *FrontierSigner) Hash(tx *types.Transaction) types.Hash { - return calcTxHash(tx, 0) +// Sender returns the sender of the transaction +func (signer *FrontierSigner) Sender(tx *types.Transaction) (types.Address, error) { + return signer.sender(tx, false) } -// Sender decodes the signature and returns the sender of the transaction -func (f *FrontierSigner) Sender(tx *types.Transaction) (types.Address, error) { - refV := big.NewInt(0) +// sender returns the sender of the transaction +func (signer *FrontierSigner) sender(tx *types.Transaction, isHomestead bool) (types.Address, error) { + if tx.Type() != types.LegacyTxType && tx.Type() != types.StateTxType { + return types.ZeroAddress, types.ErrTxTypeNotSupported + } v, r, s := tx.RawSignatureValues() - if v != nil { - refV.SetBytes(v.Bytes()) + + // Checking one of the values is enought since they are inseparable + if v == nil { + return types.Address{}, errors.New("failed to recover sender, because signature is unknown") } - refV.Sub(refV, big27) + // Reverse the V calculation to find the parity of the Y coordinate + // v = {0, 1} + 27 -> {0, 1} = v - 27 + parity := big.NewInt(0).Sub(v, big27) + + return recoverAddress(signer.Hash(tx), r, s, parity, isHomestead) +} - return recoverAddress(f.Hash(tx), r, s, refV, f.isHomestead) +// SingTx takes the original transaction as input and returns its signed version +func (signer *FrontierSigner) SignTx(tx *types.Transaction, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) { + return signer.signTx(tx, privateKey, nil) } -// SignTx signs the transaction using the passed in private key -func (f *FrontierSigner) SignTx( - tx *types.Transaction, - privateKey *ecdsa.PrivateKey, -) (*types.Transaction, error) { +// SingTx takes the original transaction as input and returns its signed version +func (signer *FrontierSigner) signTx(tx *types.Transaction, privateKey *ecdsa.PrivateKey, + validateFn func(v, r, s *big.Int) error) (*types.Transaction, error) { + if tx.Type() != types.LegacyTxType && tx.Type() != types.StateTxType { + return nil, types.ErrTxTypeNotSupported + } + tx = tx.Copy() - h := f.Hash(tx) + hash := signer.Hash(tx) - sig, err := Sign(privateKey, h[:]) + signature, err := Sign(privateKey, hash[:]) if err != nil { return nil, err } - r := new(big.Int).SetBytes(sig[:32]) - s := new(big.Int).SetBytes(sig[32:64]) - v := new(big.Int).SetBytes(f.calculateV(sig[64])) + r := new(big.Int).SetBytes(signature[:32]) + s := new(big.Int).SetBytes(signature[32:64]) + v := new(big.Int).SetBytes(signer.calculateV(signature[64])) + + if validateFn != nil { + if err := validateFn(v, r, s); err != nil { + return nil, err + } + } tx.SetSignatureValues(v, r, s) return tx, nil } -// calculateV returns the V value for transactions pre EIP155 -func (f *FrontierSigner) calculateV(parity byte) []byte { - reference := big.NewInt(int64(parity)) - reference.Add(reference, big27) +// Private method calculateV returns the V value for the pre-EIP-155 transactions +// +// V is calculated by the formula: {0, 1} + 27 where {0, 1} denotes the parity of the Y coordinate +func (signer *FrontierSigner) calculateV(parity byte) []byte { + result := big.NewInt(0) + + // result = {0, 1} + 27 + result.Add(big.NewInt(int64(parity)), big27) - return reference.Bytes() + return result.Bytes() } diff --git a/crypto/txsigner_homestead.go b/crypto/txsigner_homestead.go new file mode 100644 index 0000000000..d4ff10a0ac --- /dev/null +++ b/crypto/txsigner_homestead.go @@ -0,0 +1,55 @@ +package crypto + +import ( + "crypto/ecdsa" + "errors" + "math/big" + + "github.com/0xPolygon/polygon-edge/types" +) + +// HomesteadSigner may be used for signing pre-EIP155 transactions +type HomesteadSigner struct { + *FrontierSigner +} + +// NewHomesteadSigner returns new FrontierSigner object (constructor) +// +// HomesteadSigner accepts the following types of transactions: +// - pre-EIP-155 transactions +func NewHomesteadSigner() *HomesteadSigner { + return &HomesteadSigner{ + FrontierSigner: NewFrontierSigner(), + } +} + +// Hash returns the keccak256 hash of the transaction +// +// The pre-EIP-155 transaction hash preimage is as follows: +// RLP(nonce, gasPrice, gas, to, value, input) +// +// Specification: https://eips.ethereum.org/EIPS/eip-155#specification +// +// Note: Since the hash is calculated in the same way as with FrontierSigner, this is just a wrapper method +func (signer *HomesteadSigner) Hash(tx *types.Transaction) types.Hash { + return signer.FrontierSigner.Hash(tx) +} + +// Sender returns the sender of the transaction +func (signer *HomesteadSigner) Sender(tx *types.Transaction) (types.Address, error) { + return signer.sender(tx, true) +} + +// SingTx takes the original transaction as input and returns its signed version +func (signer *HomesteadSigner) SignTx(tx *types.Transaction, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) { + return signer.signTx(tx, privateKey, func(v, r, s *big.Int) error { + // Homestead hard-fork introduced the rule that the S value + // must be inclusively lower than the half of the secp256k1 curve order + // Specification: https://eips.ethereum.org/EIPS/eip-2#specification (2) + if s.Cmp(secp256k1NHalf) > 0 { + return errors.New("SignTx method: S must be inclusively lower than secp256k1n/2") + } + + return nil + }) +} diff --git a/crypto/txsigner_london.go b/crypto/txsigner_london.go new file mode 100644 index 0000000000..0c2d140a19 --- /dev/null +++ b/crypto/txsigner_london.go @@ -0,0 +1,145 @@ +package crypto + +import ( + "crypto/ecdsa" + "errors" + "math/big" + + "github.com/0xPolygon/polygon-edge/helper/keccak" + "github.com/0xPolygon/polygon-edge/types" + "github.com/umbracle/fastrlp" +) + +// LondonSigner may be used for signing legacy (pre-EIP-155 and EIP-155), EIP-2930 and EIP-1559 transactions +type LondonSigner struct { + *BerlinSigner +} + +// NewLondonSigner returns new LondonSinger object (constructor) +// +// LondonSigner accepts the following types of transactions: +// - EIP-1559 dynamic fee transactions +// - EIP-2930 access list transactions, +// - EIP-155 replay protected transactions, and +// - pre-EIP-155 legacy transactions +func NewLondonSigner(chainID uint64) *LondonSigner { + return &LondonSigner{ + BerlinSigner: NewBerlinSigner(chainID), + } +} + +// Hash returns the keccak256 hash of the transaction +// +// The EIP-1559 transaction hash preimage is as follows: +// (0x02 || RLP(chainId, nonce, gasTipCap, gasFeeCap, gas, to, value, input, accessList) +// +// Specification: https://eips.ethereum.org/EIPS/eip-1559#specification +func (signer *LondonSigner) Hash(tx *types.Transaction) types.Hash { + if tx.Type() != types.DynamicFeeTxType { + return signer.BerlinSigner.Hash(tx) + } + + RLP := arenaPool.Get() + defer arenaPool.Put(RLP) + + // RLP(-, -, -, -, -, -, -, -, -) + hashPreimage := RLP.NewArray() + + // RLP(chainId, -, -, -, -, -, -, -, -) + hashPreimage.Set(RLP.NewUint(signer.chainID)) + + // RLP(chainId, nonce, -, -, -, -, -, -, -) + hashPreimage.Set(RLP.NewUint(tx.Nonce())) + + // RLP(chainId, nonce, gasTipCap, -, -, -, -, -, -) + hashPreimage.Set(RLP.NewBigInt(tx.GasTipCap())) + + // RLP(chainId, nonce, gasTipCap, gasFeeCap, -, -, -, -, -) + hashPreimage.Set(RLP.NewBigInt(tx.GasFeeCap())) + + // RLP(chainId, nonce, gasTipCap, gasFeeCap, gas, -, -, -, -) + hashPreimage.Set(RLP.NewUint(tx.Gas())) + + // Checking whether the transaction is a smart contract deployment + if tx.To() == nil { + // RLP(chainId, nonce, gasTipCap, gasFeeCap, gas, to, -, -, -) + hashPreimage.Set(RLP.NewNull()) + } else { + // RLP(chainId, nonce, gasTipCap, gasFeeCap, gas, to, -, -, -) + hashPreimage.Set(RLP.NewCopyBytes((*(tx.To())).Bytes())) + } + + // RLP(chainId, nonce, gasTipCap, gasFeeCap, gas, to, value, -, -) + hashPreimage.Set(RLP.NewBigInt(tx.Value())) + + // RLP(chainId, nonce, gasTipCap, gasFeeCap, gas, to, value, input, -) + hashPreimage.Set(RLP.NewCopyBytes(tx.Input())) + + // Serialization format of the access list: + // [[{20-bytes address}, [{32-bytes key}, ...]], ...] where `...` denotes zero or more items + var rlpAccessList *fastrlp.Value + + accessList := tx.AccessList() + if accessList != nil { + rlpAccessList = accessList.MarshallRLPWith(RLP) + } else { + rlpAccessList = RLP.NewArray() + } + + // RLP(chainId, nonce, gasTipCap, gasFeeCap, gas, to, value, input,accessList) + hashPreimage.Set(rlpAccessList) + + // keccak256(0x02 || RLP(chainId, nonce, gasTipCap, gasFeeCap, gas, to, value, input,accessList) + hash := keccak.PrefixedKeccak256Rlp([]byte{byte(tx.Type())}, nil, hashPreimage) + + return types.BytesToHash(hash) +} + +// Sender returns the sender of the transaction +func (signer *LondonSigner) Sender(tx *types.Transaction) (types.Address, error) { + if tx.Type() != types.DynamicFeeTxType { + return signer.BerlinSigner.Sender(tx) + } + + v, r, s := tx.RawSignatureValues() + + // Checking one of the values is enought since they are inseparable + if v == nil { + return types.Address{}, errors.New("failed to recover sender, because signature is unknown") + } + + if err := validateTxChainID(tx, signer.chainID); err != nil { + return types.ZeroAddress, err + } + + return recoverAddress(signer.Hash(tx), r, s, v, true) +} + +// SingTx takes the original transaction as input and returns its signed version +func (signer *LondonSigner) SignTx(tx *types.Transaction, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) { + if tx.Type() != types.DynamicFeeTxType { + return signer.BerlinSigner.SignTx(tx, privateKey) + } + + tx = tx.Copy() + + h := signer.Hash(tx) + + sig, err := Sign(privateKey, h[:]) + if err != nil { + return nil, err + } + + r := new(big.Int).SetBytes(sig[:32]) + s := new(big.Int).SetBytes(sig[32:64]) + + if s.Cmp(secp256k1NHalf) > 0 { + return nil, errors.New("SignTx method: S must be inclusively lower than secp256k1n/2") + } + + v := new(big.Int).SetBytes(signer.calculateV(sig[64])) + + tx.SetSignatureValues(v, r, s) + + return tx, nil +} diff --git a/crypto/txsigner_london_berlin.go b/crypto/txsigner_london_berlin.go deleted file mode 100644 index a8030d334d..0000000000 --- a/crypto/txsigner_london_berlin.go +++ /dev/null @@ -1,72 +0,0 @@ -package crypto - -import ( - "crypto/ecdsa" - "math/big" - - "github.com/0xPolygon/polygon-edge/types" -) - -// LondonOrBerlinSigner implements signer for london and berlin hard forks -type LondonOrBerlinSigner struct { - chainID uint64 - isHomestead bool - fallbackSigner TxSigner -} - -// NewLondonOrBerlinSigner returns new LondonOrBerlinSigner object that accepts -// - EIP-1559 dynamic fee transactions -// - EIP-2930 access list transactions, -// - EIP-155 replay protected transactions, and -// - legacy Homestead transactions. -func NewLondonOrBerlinSigner(chainID uint64, isHomestead bool, fallbackSigner TxSigner) *LondonOrBerlinSigner { - return &LondonOrBerlinSigner{ - chainID: chainID, - isHomestead: isHomestead, - fallbackSigner: fallbackSigner, - } -} - -// Hash is a wrapper function that calls calcTxHash with the LondonSigner's fields -func (e *LondonOrBerlinSigner) Hash(tx *types.Transaction) types.Hash { - return calcTxHash(tx, e.chainID) -} - -// Sender returns the transaction sender -func (e *LondonOrBerlinSigner) Sender(tx *types.Transaction) (types.Address, error) { - if tx.Type() != types.DynamicFeeTxType && tx.Type() != types.AccessListTxType { - return e.fallbackSigner.Sender(tx) - } - - v, r, s := tx.RawSignatureValues() - - return recoverAddress(e.Hash(tx), r, s, v, e.isHomestead) -} - -// SignTx signs the transaction using the passed in private key -func (e *LondonOrBerlinSigner) SignTx(tx *types.Transaction, pk *ecdsa.PrivateKey) (*types.Transaction, error) { - if tx.Type() != types.DynamicFeeTxType && tx.Type() != types.AccessListTxType { - return e.fallbackSigner.SignTx(tx, pk) - } - - tx = tx.Copy() - - h := e.Hash(tx) - - sig, err := Sign(pk, h[:]) - if err != nil { - return nil, err - } - - r := new(big.Int).SetBytes(sig[:32]) - s := new(big.Int).SetBytes(sig[32:64]) - v := new(big.Int).SetBytes(e.calculateV(sig[64])) - tx.SetSignatureValues(v, r, s) - - return tx, nil -} - -// calculateV returns the V value for transaction signatures. Based on EIP155 -func (e *LondonOrBerlinSigner) calculateV(parity byte) []byte { - return big.NewInt(int64(parity)).Bytes() -} diff --git a/crypto/txsigner_london_berlin_test.go b/crypto/txsigner_london_test.go similarity index 93% rename from crypto/txsigner_london_berlin_test.go rename to crypto/txsigner_london_test.go index 617cc351ee..fee95c9d64 100644 --- a/crypto/txsigner_london_berlin_test.go +++ b/crypto/txsigner_london_test.go @@ -78,6 +78,7 @@ func TestLondonSignerSender(t *testing.T) { To: &recipient, Value: big.NewInt(1), GasPrice: big.NewInt(5), + ChainID: tc.chainID, }) case types.LegacyTxType: txn = types.NewTx(&types.LegacyTx{ @@ -93,13 +94,14 @@ func TestLondonSignerSender(t *testing.T) { }) case types.DynamicFeeTxType: txn = types.NewTx(&types.DynamicFeeTx{ - To: &recipient, - Value: big.NewInt(1), + To: &recipient, + Value: big.NewInt(1), + ChainID: tc.chainID, }) } chainID := tc.chainID.Uint64() - signer := NewLondonOrBerlinSigner(chainID, true, NewEIP155Signer(chainID, true)) + signer := NewLondonSigner(chainID) signedTx, err := signer.SignTx(txn, key) require.NoError(t, err, "unable to sign transaction") @@ -115,7 +117,8 @@ func TestLondonSignerSender(t *testing.T) { func Test_LondonSigner_Sender(t *testing.T) { t.Parallel() - signer := NewLondonOrBerlinSigner(100, true, NewEIP155Signer(100, true)) + signer := NewLondonSigner(100) + to := types.StringToAddress("0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF") r, ok := big.NewInt(0).SetString("102623819621514684481463796449525884981685455700611671612296611353030973716382", 10) @@ -140,6 +143,7 @@ func Test_LondonSigner_Sender(t *testing.T) { V: big.NewInt(0), R: r, S: s, + ChainID: big.NewInt(100), }), sender: types.StringToAddress("0x85dA99c8a7C2C95964c8EfD687E95E632Fc533D6"), }, diff --git a/e2e/txpool_test.go b/e2e/txpool_test.go index cbf48e3d08..9f9f0c994f 100644 --- a/e2e/txpool_test.go +++ b/e2e/txpool_test.go @@ -25,8 +25,9 @@ import ( ) var ( - oneEth = framework.EthToWei(1) - signer = crypto.NewSigner(chain.AllForksEnabled.At(0), 100) + oneEth = framework.EthToWei(1) + defaultChainID = uint64(100) + signer = crypto.NewSigner(chain.AllForksEnabled.At(0), defaultChainID) ) type generateTxReqParams struct { @@ -50,16 +51,15 @@ func generateTx(params generateTxReqParams) *types.Transaction { To: ¶ms.toAddress, Gas: 1000000, Value: params.value, - V: big.NewInt(27), // it is necessary to encode in rlp }) unsignedTx.SetGasPrice(params.gasPrice) } else { unsignedTx = types.NewTx(&types.DynamicFeeTx{ - Nonce: params.nonce, - To: ¶ms.toAddress, - Gas: 1000000, - Value: params.value, - V: big.NewInt(27), // it is necessary to encode in rlp + Nonce: params.nonce, + To: ¶ms.toAddress, + Gas: 1000000, + Value: params.value, + ChainID: new(big.Int).SetUint64(defaultChainID), }) unsignedTx.SetGasFeeCap(params.gasFeeCap) unsignedTx.SetGasTipCap(params.gasTipCap) @@ -265,6 +265,7 @@ func TestTxPool_RecoverableError(t *testing.T) { Gas: 22000, To: &receiverAddress, Value: oneEth, + ChainID: new(big.Int).SetUint64(defaultChainID), V: big.NewInt(27), }), } diff --git a/gasprice/gasprice_test.go b/gasprice/gasprice_test.go index a148f2ccad..207f8439c8 100644 --- a/gasprice/gasprice_test.go +++ b/gasprice/gasprice_test.go @@ -90,6 +90,7 @@ func TestGasHelper_MaxPriorityFeePerGas(t *testing.T) { To: &types.ZeroAddress, GasTipCap: ethgo.Gwei(uint64(rand.Intn(200))), GasFeeCap: ethgo.Gwei(uint64(rand.Intn(200) + 200)), + ChainID: big.NewInt(backend.Config().ChainID), }) tx, err := signer.SignTx(tx, senderKey) @@ -224,6 +225,7 @@ func createTestTxs(t *testing.T, backend *backendMock, numOfTxsPerBlock, txCap i To: &types.ZeroAddress, GasTipCap: ethgo.Gwei(uint64(rand.Intn(txCap))), GasFeeCap: ethgo.Gwei(uint64(rand.Intn(txCap) + txCap)), + ChainID: big.NewInt(backend.Config().ChainID), }) tx, err := signer.SignTx(tx, senderKey) diff --git a/helper/tests/testing.go b/helper/tests/testing.go index 96a6ac49be..6349b7dadf 100644 --- a/helper/tests/testing.go +++ b/helper/tests/testing.go @@ -236,7 +236,7 @@ type GenerateTxReqParams struct { } func generateTx(params GenerateTxReqParams) (*types.Transaction, error) { - signer := crypto.NewEIP155Signer(100, true) + signer := crypto.NewEIP155Signer(100) signedTx, signErr := signer.SignTx(types.NewTx(&types.LegacyTx{ Nonce: params.Nonce, diff --git a/jsonrpc/testsuite/block-with-txn-full.json b/jsonrpc/testsuite/block-with-txn-full.json index 6f2a00489b..171bfebee5 100644 --- a/jsonrpc/testsuite/block-with-txn-full.json +++ b/jsonrpc/testsuite/block-with-txn-full.json @@ -33,7 +33,8 @@ "blockHash": "0x7935c790bdff1ec20912f28c9f7722eed41837c478a1f9ce0dc49f5d4a2d7c88", "blockNumber": "0x14", "transactionIndex": "0x0", - "type": "0x0" + "type": "0x0", + "chainId": "0x7fffffffffffffef" }, { "nonce": "0x0", @@ -50,7 +51,8 @@ "blockHash": "0x7935c790bdff1ec20912f28c9f7722eed41837c478a1f9ce0dc49f5d4a2d7c88", "blockNumber": "0x14", "transactionIndex": "0x1", - "type": "0x0" + "type": "0x0", + "chainId": "0x7fffffffffffffef" } ], "uncles": [], diff --git a/server/server.go b/server/server.go index b4495f4181..5e9e9b9ff6 100644 --- a/server/server.go +++ b/server/server.go @@ -283,15 +283,7 @@ func NewServer(config *Config) (*Server, error) { // compute the genesis root state config.Chain.Genesis.StateRoot = genesisRoot - // Use the london signer with eip-155 as a fallback one - var signer crypto.TxSigner = crypto.NewLondonOrBerlinSigner( - uint64(m.config.Chain.Params.ChainID), - config.Chain.Params.Forks.IsActive(chain.Homestead, 0), - crypto.NewEIP155Signer( - uint64(m.config.Chain.Params.ChainID), - config.Chain.Params.Forks.IsActive(chain.Homestead, 0), - ), - ) + signer := crypto.NewSigner(config.Chain.Params.Forks.At(0), uint64(m.config.Chain.Params.ChainID)) // create storage instance for blockchain var db storage.Storage diff --git a/txpool/txpool.go b/txpool/txpool.go index 3fcba4a526..e1f2e5a187 100644 --- a/txpool/txpool.go +++ b/txpool/txpool.go @@ -572,7 +572,7 @@ func (p *TxPool) validateTx(tx *types.Transaction) error { if signerErr != nil { metrics.IncrCounter([]string{txPoolMetrics, "invalid_signature_txs"}, 1) - return ErrExtractSignature + return fmt.Errorf("%w. %w", ErrExtractSignature, signerErr) } // If the from field is set, check that diff --git a/txpool/txpool_test.go b/txpool/txpool_test.go index 72fc1af1f4..b55c45760e 100644 --- a/txpool/txpool_test.go +++ b/txpool/txpool_test.go @@ -121,7 +121,7 @@ type result struct { func TestAddTxErrors(t *testing.T) { t.Parallel() - poolSigner := crypto.NewEIP155Signer(100, true) + poolSigner := crypto.NewLondonSigner(100) // Generate a private key and address defaultKey, defaultAddr := tests.GenerateKeyAndAddr(t) @@ -138,7 +138,10 @@ func TestAddTxErrors(t *testing.T) { } signTx := func(transaction *types.Transaction) *types.Transaction { + transaction.SetChainID(big.NewInt(100)) + signedTx, signErr := poolSigner.SignTx(transaction, defaultKey) + if signErr != nil { t.Fatalf("Unable to sign transaction, %v", signErr) } @@ -170,7 +173,6 @@ func TestAddTxErrors(t *testing.T) { pool.forks.RemoveFork(chain.London) tx := newTx(defaultAddr, 0, 1, types.DynamicFeeTxType) - err := pool.addTx(local, signTx(tx)) assert.ErrorContains(t, @@ -218,6 +220,8 @@ func TestAddTxErrors(t *testing.T) { tx := newTx(defaultAddr, 0, 1, types.LegacyTxType) + tx.SetChainID(big.NewInt(0)) + assert.ErrorIs(t, pool.addTx(local, tx), ErrExtractSignature, @@ -589,7 +593,7 @@ func TestAddGossipTx(t *testing.T) { t.Parallel() key, sender := tests.GenerateKeyAndAddr(t) - signer := crypto.NewEIP155Signer(100, true) + signer := crypto.NewEIP155Signer(100) tx := newTx(types.ZeroAddress, 1, 1, types.LegacyTxType) t.Run("node is a validator", func(t *testing.T) { @@ -612,6 +616,7 @@ func TestAddGossipTx(t *testing.T) { Value: signedTx.MarshalRLP(), }, } + pool.addGossipTx(protoTx, "") assert.Equal(t, uint64(1), pool.accounts.get(sender).enqueued.length()) @@ -620,6 +625,8 @@ func TestAddGossipTx(t *testing.T) { t.Run("node is a non validator", func(t *testing.T) { t.Parallel() + tx.SetChainID(big.NewInt(0)) + pool, err := newTestPool() assert.NoError(t, err) pool.SetSigner(signer) @@ -639,6 +646,7 @@ func TestAddGossipTx(t *testing.T) { Value: signedTx.MarshalRLP(), }, } + pool.addGossipTx(protoTx, "") assert.Equal(t, uint64(0), pool.accounts.get(sender).enqueued.length()) @@ -2140,7 +2148,7 @@ func Test_updateAccountSkipsCounts(t *testing.T) { func Test_TxPool_validateTx(t *testing.T) { t.Parallel() - signer := crypto.NewEIP155Signer(100, true) + signer := crypto.NewLondonSigner(100) // Generate a private key and address defaultKey, defaultAddr := tests.GenerateKeyAndAddr(t) @@ -2160,7 +2168,10 @@ func Test_TxPool_validateTx(t *testing.T) { } signTx := func(transaction *types.Transaction) *types.Transaction { + transaction.SetChainID(big.NewInt(100)) + signedTx, signErr := signer.SignTx(transaction, defaultKey) + if signErr != nil { t.Fatalf("Unable to sign transaction, %v", signErr) } @@ -2377,7 +2388,7 @@ func (e *eoa) signTx(t *testing.T, tx *types.Transaction, signer crypto.TxSigner return signedTx } -var signerEIP155 = crypto.NewEIP155Signer(100, true) +var signerLondon = crypto.NewLondonSigner(100) func TestResetAccounts_Promoted(t *testing.T) { t.Parallel() @@ -2397,30 +2408,30 @@ func TestResetAccounts_Promoted(t *testing.T) { allTxs := map[types.Address][]*types.Transaction{ addr1: { - eoa1.signTx(t, newTx(addr1, 0, 1, types.LegacyTxType), signerEIP155), // will be pruned - eoa1.signTx(t, newTx(addr1, 1, 1, types.LegacyTxType), signerEIP155), // will be pruned - eoa1.signTx(t, newTx(addr1, 2, 1, types.LegacyTxType), signerEIP155), // will be pruned - eoa1.signTx(t, newTx(addr1, 3, 1, types.LegacyTxType), signerEIP155), // will be pruned + eoa1.signTx(t, newTx(addr1, 0, 1, types.LegacyTxType), signerLondon), // will be pruned + eoa1.signTx(t, newTx(addr1, 1, 1, types.LegacyTxType), signerLondon), // will be pruned + eoa1.signTx(t, newTx(addr1, 2, 1, types.LegacyTxType), signerLondon), // will be pruned + eoa1.signTx(t, newTx(addr1, 3, 1, types.LegacyTxType), signerLondon), // will be pruned }, addr2: { - eoa2.signTx(t, newTx(addr2, 0, 1, types.LegacyTxType), signerEIP155), // will be pruned - eoa2.signTx(t, newTx(addr2, 1, 1, types.LegacyTxType), signerEIP155), // will be pruned + eoa2.signTx(t, newTx(addr2, 0, 1, types.LegacyTxType), signerLondon), // will be pruned + eoa2.signTx(t, newTx(addr2, 1, 1, types.LegacyTxType), signerLondon), // will be pruned }, addr3: { - eoa3.signTx(t, newTx(addr3, 0, 1, types.LegacyTxType), signerEIP155), // will be pruned - eoa3.signTx(t, newTx(addr3, 1, 1, types.LegacyTxType), signerEIP155), // will be pruned - eoa3.signTx(t, newTx(addr3, 2, 1, types.LegacyTxType), signerEIP155), // will be pruned + eoa3.signTx(t, newTx(addr3, 0, 1, types.LegacyTxType), signerLondon), // will be pruned + eoa3.signTx(t, newTx(addr3, 1, 1, types.LegacyTxType), signerLondon), // will be pruned + eoa3.signTx(t, newTx(addr3, 2, 1, types.LegacyTxType), signerLondon), // will be pruned }, addr4: { // all txs will be pruned - eoa4.signTx(t, newTx(addr4, 0, 1, types.LegacyTxType), signerEIP155), // will be pruned - eoa4.signTx(t, newTx(addr4, 1, 1, types.LegacyTxType), signerEIP155), // will be pruned - eoa4.signTx(t, newTx(addr4, 2, 1, types.LegacyTxType), signerEIP155), // will be pruned - eoa4.signTx(t, newTx(addr4, 3, 1, types.LegacyTxType), signerEIP155), // will be pruned - eoa4.signTx(t, newTx(addr4, 4, 1, types.LegacyTxType), signerEIP155), // will be pruned + eoa4.signTx(t, newTx(addr4, 0, 1, types.LegacyTxType), signerLondon), // will be pruned + eoa4.signTx(t, newTx(addr4, 1, 1, types.LegacyTxType), signerLondon), // will be pruned + eoa4.signTx(t, newTx(addr4, 2, 1, types.LegacyTxType), signerLondon), // will be pruned + eoa4.signTx(t, newTx(addr4, 3, 1, types.LegacyTxType), signerLondon), // will be pruned + eoa4.signTx(t, newTx(addr4, 4, 1, types.LegacyTxType), signerLondon), // will be pruned }, } @@ -2443,7 +2454,7 @@ func TestResetAccounts_Promoted(t *testing.T) { pool, err := newTestPool() assert.NoError(t, err) - pool.SetSigner(signerEIP155) + pool.SetSigner(signerLondon) pool.Start() defer pool.Close() @@ -2535,22 +2546,22 @@ func TestResetAccounts_Enqueued(t *testing.T) { allTxs := map[types.Address][]*types.Transaction{ addr1: { - eoa1.signTx(t, newTx(addr1, 3, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 4, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 5, 1, types.LegacyTxType), signerEIP155), + eoa1.signTx(t, newTx(addr1, 3, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 4, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 5, 1, types.LegacyTxType), signerLondon), }, addr2: { - eoa2.signTx(t, newTx(addr2, 2, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 3, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 4, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 5, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 6, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 7, 1, types.LegacyTxType), signerEIP155), + eoa2.signTx(t, newTx(addr2, 2, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 3, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 4, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 5, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 6, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 7, 1, types.LegacyTxType), signerLondon), }, addr3: { - eoa3.signTx(t, newTx(addr3, 7, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 8, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 9, 1, types.LegacyTxType), signerEIP155), + eoa3.signTx(t, newTx(addr3, 7, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 8, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 9, 1, types.LegacyTxType), signerLondon), }, } newNonces := map[types.Address]uint64{ @@ -2578,7 +2589,7 @@ func TestResetAccounts_Enqueued(t *testing.T) { pool, err := newTestPool() assert.NoError(t, err) - pool.SetSigner(signerEIP155) + pool.SetSigner(signerLondon) pool.Start() defer pool.Close() @@ -3149,40 +3160,40 @@ func TestGetTxs(t *testing.T) { name: "get promoted txs", allTxs: map[types.Address][]*types.Transaction{ addr1: { - eoa1.signTx(t, newTx(addr1, 0, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 1, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 2, 1, types.LegacyTxType), signerEIP155), + eoa1.signTx(t, newTx(addr1, 0, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 1, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 2, 1, types.LegacyTxType), signerLondon), }, addr2: { - eoa2.signTx(t, newTx(addr2, 0, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 1, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 2, 1, types.LegacyTxType), signerEIP155), + eoa2.signTx(t, newTx(addr2, 0, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 1, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 2, 1, types.LegacyTxType), signerLondon), }, addr3: { - eoa3.signTx(t, newTx(addr3, 0, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 1, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 2, 1, types.LegacyTxType), signerEIP155), + eoa3.signTx(t, newTx(addr3, 0, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 1, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 2, 1, types.LegacyTxType), signerLondon), }, }, expectedPromoted: map[types.Address][]*types.Transaction{ addr1: { - eoa1.signTx(t, newTx(addr1, 0, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 1, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 2, 1, types.LegacyTxType), signerEIP155), + eoa1.signTx(t, newTx(addr1, 0, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 1, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 2, 1, types.LegacyTxType), signerLondon), }, addr2: { - eoa2.signTx(t, newTx(addr2, 0, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 1, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 2, 1, types.LegacyTxType), signerEIP155), + eoa2.signTx(t, newTx(addr2, 0, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 1, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 2, 1, types.LegacyTxType), signerLondon), }, addr3: { - eoa3.signTx(t, newTx(addr3, 0, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 1, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 2, 1, types.LegacyTxType), signerEIP155), + eoa3.signTx(t, newTx(addr3, 0, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 1, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 2, 1, types.LegacyTxType), signerLondon), }, }, }, @@ -3190,73 +3201,73 @@ func TestGetTxs(t *testing.T) { name: "get all txs", allTxs: map[types.Address][]*types.Transaction{ addr1: { - eoa1.signTx(t, newTx(addr1, 0, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 1, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 2, 1, types.LegacyTxType), signerEIP155), + eoa1.signTx(t, newTx(addr1, 0, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 1, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 2, 1, types.LegacyTxType), signerLondon), // enqueued - eoa1.signTx(t, newTx(addr1, 10, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 11, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 12, 1, types.LegacyTxType), signerEIP155), + eoa1.signTx(t, newTx(addr1, 10, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 11, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 12, 1, types.LegacyTxType), signerLondon), }, addr2: { - eoa2.signTx(t, newTx(addr2, 0, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 1, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 2, 1, types.LegacyTxType), signerEIP155), + eoa2.signTx(t, newTx(addr2, 0, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 1, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 2, 1, types.LegacyTxType), signerLondon), // enqueued - eoa2.signTx(t, newTx(addr2, 10, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 11, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 12, 1, types.LegacyTxType), signerEIP155), + eoa2.signTx(t, newTx(addr2, 10, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 11, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 12, 1, types.LegacyTxType), signerLondon), }, addr3: { - eoa3.signTx(t, newTx(addr3, 0, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 1, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 2, 1, types.LegacyTxType), signerEIP155), + eoa3.signTx(t, newTx(addr3, 0, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 1, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 2, 1, types.LegacyTxType), signerLondon), // enqueued - eoa3.signTx(t, newTx(addr3, 10, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 11, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 12, 1, types.LegacyTxType), signerEIP155), + eoa3.signTx(t, newTx(addr3, 10, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 11, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 12, 1, types.LegacyTxType), signerLondon), }, }, expectedPromoted: map[types.Address][]*types.Transaction{ addr1: { - eoa1.signTx(t, newTx(addr1, 0, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 1, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 2, 1, types.LegacyTxType), signerEIP155), + eoa1.signTx(t, newTx(addr1, 0, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 1, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 2, 1, types.LegacyTxType), signerLondon), }, addr2: { - eoa2.signTx(t, newTx(addr2, 0, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 1, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 2, 1, types.LegacyTxType), signerEIP155), + eoa2.signTx(t, newTx(addr2, 0, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 1, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 2, 1, types.LegacyTxType), signerLondon), }, addr3: { - eoa3.signTx(t, newTx(addr3, 0, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 1, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 2, 1, types.LegacyTxType), signerEIP155), + eoa3.signTx(t, newTx(addr3, 0, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 1, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 2, 1, types.LegacyTxType), signerLondon), }, }, expectedEnqueued: map[types.Address][]*types.Transaction{ addr1: { - eoa1.signTx(t, newTx(addr1, 10, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 11, 1, types.LegacyTxType), signerEIP155), - eoa1.signTx(t, newTx(addr1, 12, 1, types.LegacyTxType), signerEIP155), + eoa1.signTx(t, newTx(addr1, 10, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 11, 1, types.LegacyTxType), signerLondon), + eoa1.signTx(t, newTx(addr1, 12, 1, types.LegacyTxType), signerLondon), }, addr2: { - eoa2.signTx(t, newTx(addr2, 10, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 11, 1, types.LegacyTxType), signerEIP155), - eoa2.signTx(t, newTx(addr2, 12, 1, types.LegacyTxType), signerEIP155), + eoa2.signTx(t, newTx(addr2, 10, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 11, 1, types.LegacyTxType), signerLondon), + eoa2.signTx(t, newTx(addr2, 12, 1, types.LegacyTxType), signerLondon), }, addr3: { - eoa3.signTx(t, newTx(addr3, 10, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 11, 1, types.LegacyTxType), signerEIP155), - eoa3.signTx(t, newTx(addr3, 12, 1, types.LegacyTxType), signerEIP155), + eoa3.signTx(t, newTx(addr3, 10, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 11, 1, types.LegacyTxType), signerLondon), + eoa3.signTx(t, newTx(addr3, 12, 1, types.LegacyTxType), signerLondon), }, }, }, @@ -3282,7 +3293,7 @@ func TestGetTxs(t *testing.T) { pool, err := newTestPool() assert.NoError(t, err) - pool.SetSigner(signerEIP155) + pool.SetSigner(signerLondon) pool.Start() defer pool.Close() @@ -3533,7 +3544,7 @@ func TestAddTxsInOrder(t *testing.T) { pool, err := newTestPool() require.NoError(t, err) - signer := crypto.NewEIP155Signer(100, true) + signer := crypto.NewEIP155Signer(100) pool.SetSigner(signer) pool.Start() @@ -3636,7 +3647,7 @@ func TestAddTx_TxReplacement(t *testing.T) { secondAccountNonce = 2 ) - poolSigner := crypto.NewEIP155Signer(100, true) + poolSigner := crypto.NewLondonSigner(100) firstKey, firstKeyAddr := tests.GenerateKeyAndAddr(t) secondKey, secondKeyAddr := tests.GenerateKeyAndAddr(t) @@ -3650,6 +3661,7 @@ func TestAddTx_TxReplacement(t *testing.T) { tx.SetGasPrice(nil) tx.SetGasTipCap(new(big.Int).SetUint64(gasTipCap)) tx.SetGasFeeCap(new(big.Int).SetUint64(gasFeeCap)) + tx.SetChainID(big.NewInt(100)) singedTx, err := poolSigner.SignTx(tx, key) require.NoError(t, err) @@ -3677,6 +3689,7 @@ func TestAddTx_TxReplacement(t *testing.T) { } pool, err := newTestPool() + pool.chainID = big.NewInt(100) require.NoError(t, err) pool.baseFee = 100 @@ -3749,7 +3762,7 @@ func getDefaultEnabledForks() *chain.Forks { func BenchmarkAddTxTime(b *testing.B) { b.Run("benchmark add one tx", func(b *testing.B) { - signer := crypto.NewEIP155Signer(100, true) + signer := crypto.NewEIP155Signer(100) key, err := crypto.GenerateECDSAKey() if err != nil { @@ -3777,7 +3790,7 @@ func BenchmarkAddTxTime(b *testing.B) { }) b.Run("benchmark fill account", func(b *testing.B) { - signer := crypto.NewEIP155Signer(100, true) + signer := crypto.NewEIP155Signer(100) key, err := crypto.GenerateECDSAKey() if err != nil { diff --git a/types/access_list_tx.go b/types/access_list_tx.go index 2a3b5a01f6..1f55558e92 100644 --- a/types/access_list_tx.go +++ b/types/access_list_tx.go @@ -45,7 +45,7 @@ func (al TxAccessList) Copy() TxAccessList { return newAccessList } -func (al TxAccessList) unmarshallRLPFrom(p *fastrlp.Parser, accessListVV []*fastrlp.Value) error { +func (al TxAccessList) UnmarshallRLPFrom(p *fastrlp.Parser, accessListVV []*fastrlp.Value) error { for i, accessTupleVV := range accessListVV { accessTupleElems, err := accessTupleVV.GetElems() if err != nil { @@ -85,7 +85,7 @@ func (al TxAccessList) unmarshallRLPFrom(p *fastrlp.Parser, accessListVV []*fast return nil } -func (al TxAccessList) marshallRLPWith(arena *fastrlp.Arena) *fastrlp.Value { +func (al TxAccessList) MarshallRLPWith(arena *fastrlp.Arena) *fastrlp.Value { accessListVV := arena.NewArray() for _, accessTuple := range al { @@ -290,7 +290,7 @@ func (tx *AccessListTxn) unmarshalRLPFrom(p *fastrlp.Parser, v *fastrlp.Value) e txAccessList = make(TxAccessList, len(accessListVV)) } - if err = txAccessList.unmarshallRLPFrom(p, accessListVV); err != nil { + if err = txAccessList.UnmarshallRLPFrom(p, accessListVV); err != nil { return err } @@ -341,7 +341,7 @@ func (tx *AccessListTxn) marshalRLPWith(arena *fastrlp.Arena) *fastrlp.Value { vv.Set(arena.NewCopyBytes(tx.input())) // Convert TxAccessList to RLP format and add it to the vv array. - vv.Set(tx.accessList().marshallRLPWith(arena)) + vv.Set(tx.accessList().MarshallRLPWith(arena)) v, r, s := tx.rawSignatureValues() vv.Set(arena.NewBigInt(v)) diff --git a/types/dynamic_fee_tx.go b/types/dynamic_fee_tx.go index bb055947f1..c671100ce7 100644 --- a/types/dynamic_fee_tx.go +++ b/types/dynamic_fee_tx.go @@ -189,7 +189,7 @@ func (tx *DynamicFeeTx) unmarshalRLPFrom(p *fastrlp.Parser, v *fastrlp.Value) er txAccessList = make(TxAccessList, len(accessListVV)) } - if err = txAccessList.unmarshallRLPFrom(p, accessListVV); err != nil { + if err = txAccessList.UnmarshallRLPFrom(p, accessListVV); err != nil { return err } @@ -243,7 +243,7 @@ func (tx *DynamicFeeTx) marshalRLPWith(arena *fastrlp.Arena) *fastrlp.Value { vv.Set(arena.NewCopyBytes(tx.input())) // Convert TxAccessList to RLP format and add it to the vv array. - vv.Set(tx.accessList().marshallRLPWith(arena)) + vv.Set(tx.accessList().MarshallRLPWith(arena)) // signature values v, r, s := tx.rawSignatureValues() diff --git a/types/legacy_tx.go b/types/legacy_tx.go index 23af43bd10..30b728dbdc 100644 --- a/types/legacy_tx.go +++ b/types/legacy_tx.go @@ -20,7 +20,7 @@ type LegacyTx struct { } func (tx *LegacyTx) transactionType() TxType { return LegacyTxType } -func (tx *LegacyTx) chainID() *big.Int { return nil } +func (tx *LegacyTx) chainID() *big.Int { return deriveChainID(tx.V) } func (tx *LegacyTx) input() []byte { return tx.Input } func (tx *LegacyTx) gas() uint64 { return tx.Gas } func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice } @@ -265,3 +265,19 @@ func (tx *LegacyTx) copy() TxData { //nolint:dupl return cpy } + +// deriveChainID derives the chain id from the given v parameter +func deriveChainID(v *big.Int) *big.Int { + if v != nil && v.BitLen() <= 64 { + v := v.Uint64() + if v == 27 || v == 28 { + return new(big.Int) + } + + return new(big.Int).SetUint64((v - 35) / 2) + } + + v = new(big.Int).Sub(v, big.NewInt(35)) + + return v.Div(v, big.NewInt(2)) +} diff --git a/types/state_tx.go b/types/state_tx.go index 52c86e3f3b..b98eb834ca 100644 --- a/types/state_tx.go +++ b/types/state_tx.go @@ -20,7 +20,7 @@ type StateTx struct { } func (tx *StateTx) transactionType() TxType { return StateTxType } -func (tx *StateTx) chainID() *big.Int { return nil } +func (tx *StateTx) chainID() *big.Int { return deriveChainID(tx.V) } func (tx *StateTx) input() []byte { return tx.Input } func (tx *StateTx) gas() uint64 { return tx.Gas } func (tx *StateTx) gasPrice() *big.Int { return tx.GasPrice }