fix: Web3 RPC handlers panic (#702)
* Problem: Some Web3 RPC Handlers could panic Closes: #701 Solution: - return error rather than panic when decoding invalid tx * add validation rules * changelog
This commit is contained in:
parent
23a3362475
commit
bc1d81c5e8
@ -53,6 +53,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
|||||||
* (rpc) [tharsis#661](https://github.com/tharsis/ethermint/pull/661) Fix OOM bug when creating too many filters using JSON-RPC.
|
* (rpc) [tharsis#661](https://github.com/tharsis/ethermint/pull/661) Fix OOM bug when creating too many filters using JSON-RPC.
|
||||||
* (evm) [tharsis#660](https://github.com/tharsis/ethermint/pull/660) Fix `nil` pointer panic in `ApplyNativeMessage`.
|
* (evm) [tharsis#660](https://github.com/tharsis/ethermint/pull/660) Fix `nil` pointer panic in `ApplyNativeMessage`.
|
||||||
* (evm, test) [tharsis#649](https://github.com/tharsis/ethermint/pull/649) Test DynamicFeeTx.
|
* (evm, test) [tharsis#649](https://github.com/tharsis/ethermint/pull/649) Test DynamicFeeTx.
|
||||||
|
* (evm) [tharsis#702](https://github.com/tharsis/ethermint/pull/702) Fix panic in web3 RPC handlers
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
|
||||||
|
@ -68,7 +68,10 @@ func (g txConfig) TxDecoder() sdk.TxDecoder {
|
|||||||
err := tx.UnmarshalBinary(txBytes)
|
err := tx.UnmarshalBinary(txBytes)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
msg := &evmtypes.MsgEthereumTx{}
|
msg := &evmtypes.MsgEthereumTx{}
|
||||||
msg.FromEthereumTx(tx)
|
err := msg.FromEthereumTx(tx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/tharsis/ethermint/types"
|
"github.com/tharsis/ethermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newAccessListTx(tx *ethtypes.Transaction) *AccessListTx {
|
func newAccessListTx(tx *ethtypes.Transaction) (*AccessListTx, error) {
|
||||||
txData := &AccessListTx{
|
txData := &AccessListTx{
|
||||||
Nonce: tx.Nonce(),
|
Nonce: tx.Nonce(),
|
||||||
Data: tx.Data(),
|
Data: tx.Data(),
|
||||||
@ -23,12 +23,18 @@ func newAccessListTx(tx *ethtypes.Transaction) *AccessListTx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tx.Value() != nil {
|
if tx.Value() != nil {
|
||||||
amountInt := sdk.NewIntFromBigInt(tx.Value())
|
amountInt, err := SafeNewIntFromBigInt(tx.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
txData.Amount = &amountInt
|
txData.Amount = &amountInt
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.GasPrice() != nil {
|
if tx.GasPrice() != nil {
|
||||||
gasPriceInt := sdk.NewIntFromBigInt(tx.GasPrice())
|
gasPriceInt, err := SafeNewIntFromBigInt(tx.GasPrice())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
txData.GasPrice = &gasPriceInt
|
txData.GasPrice = &gasPriceInt
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +44,7 @@ func newAccessListTx(tx *ethtypes.Transaction) *AccessListTx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
txData.SetSignatureValues(tx.ChainId(), v, r, s)
|
txData.SetSignatureValues(tx.ChainId(), v, r, s)
|
||||||
return txData
|
return txData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxType returns the tx type
|
// TxType returns the tx type
|
||||||
@ -177,6 +183,9 @@ func (tx AccessListTx) Validate() error {
|
|||||||
if gasPrice == nil {
|
if gasPrice == nil {
|
||||||
return sdkerrors.Wrap(ErrInvalidGasPrice, "cannot be nil")
|
return sdkerrors.Wrap(ErrInvalidGasPrice, "cannot be nil")
|
||||||
}
|
}
|
||||||
|
if !IsValidInt256(gasPrice) {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidGasPrice, "out of bound")
|
||||||
|
}
|
||||||
|
|
||||||
if gasPrice.Sign() == -1 {
|
if gasPrice.Sign() == -1 {
|
||||||
return sdkerrors.Wrapf(ErrInvalidGasPrice, "gas price cannot be negative %s", gasPrice)
|
return sdkerrors.Wrapf(ErrInvalidGasPrice, "gas price cannot be negative %s", gasPrice)
|
||||||
@ -187,6 +196,13 @@ func (tx AccessListTx) Validate() error {
|
|||||||
if amount != nil && amount.Sign() == -1 {
|
if amount != nil && amount.Sign() == -1 {
|
||||||
return sdkerrors.Wrapf(ErrInvalidAmount, "amount cannot be negative %s", amount)
|
return sdkerrors.Wrapf(ErrInvalidAmount, "amount cannot be negative %s", amount)
|
||||||
}
|
}
|
||||||
|
if !IsValidInt256(amount) {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidAmount, "out of bound")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsValidInt256(tx.Fee()) {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidGasFee, "out of bound")
|
||||||
|
}
|
||||||
|
|
||||||
if tx.To != "" {
|
if tx.To != "" {
|
||||||
if err := types.ValidateAddress(tx.To); err != nil {
|
if err := types.ValidateAddress(tx.To); err != nil {
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/tharsis/ethermint/types"
|
"github.com/tharsis/ethermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newDynamicFeeTx(tx *ethtypes.Transaction) *DynamicFeeTx {
|
func newDynamicFeeTx(tx *ethtypes.Transaction) (*DynamicFeeTx, error) {
|
||||||
txData := &DynamicFeeTx{
|
txData := &DynamicFeeTx{
|
||||||
Nonce: tx.Nonce(),
|
Nonce: tx.Nonce(),
|
||||||
Data: tx.Data(),
|
Data: tx.Data(),
|
||||||
@ -25,17 +25,26 @@ func newDynamicFeeTx(tx *ethtypes.Transaction) *DynamicFeeTx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tx.Value() != nil {
|
if tx.Value() != nil {
|
||||||
amountInt := sdk.NewIntFromBigInt(tx.Value())
|
amountInt, err := SafeNewIntFromBigInt(tx.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
txData.Amount = &amountInt
|
txData.Amount = &amountInt
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.GasFeeCap() != nil {
|
if tx.GasFeeCap() != nil {
|
||||||
gasFeeCapInt := sdk.NewIntFromBigInt(tx.GasFeeCap())
|
gasFeeCapInt, err := SafeNewIntFromBigInt(tx.GasFeeCap())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
txData.GasFeeCap = &gasFeeCapInt
|
txData.GasFeeCap = &gasFeeCapInt
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.GasTipCap() != nil {
|
if tx.GasTipCap() != nil {
|
||||||
gasTipCapInt := sdk.NewIntFromBigInt(tx.GasTipCap())
|
gasTipCapInt, err := SafeNewIntFromBigInt(tx.GasTipCap())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
txData.GasTipCap = &gasTipCapInt
|
txData.GasTipCap = &gasTipCapInt
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +54,7 @@ func newDynamicFeeTx(tx *ethtypes.Transaction) *DynamicFeeTx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
txData.SetSignatureValues(tx.ChainId(), v, r, s)
|
txData.SetSignatureValues(tx.ChainId(), v, r, s)
|
||||||
return txData
|
return txData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxType returns the tx type
|
// TxType returns the tx type
|
||||||
@ -201,6 +210,14 @@ func (tx DynamicFeeTx) Validate() error {
|
|||||||
return sdkerrors.Wrapf(ErrInvalidGasCap, "gas fee cap cannot be negative %s", tx.GasFeeCap)
|
return sdkerrors.Wrapf(ErrInvalidGasCap, "gas fee cap cannot be negative %s", tx.GasFeeCap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !IsValidInt256(tx.GetGasTipCap()) {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidGasCap, "out of bound")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsValidInt256(tx.GetGasFeeCap()) {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidGasCap, "out of bound")
|
||||||
|
}
|
||||||
|
|
||||||
if tx.GasFeeCap.LT(*tx.GasTipCap) {
|
if tx.GasFeeCap.LT(*tx.GasTipCap) {
|
||||||
return sdkerrors.Wrapf(
|
return sdkerrors.Wrapf(
|
||||||
ErrInvalidGasCap, "max priority fee per gas higher than max fee per gas (%s > %s)",
|
ErrInvalidGasCap, "max priority fee per gas higher than max fee per gas (%s > %s)",
|
||||||
@ -208,11 +225,18 @@ func (tx DynamicFeeTx) Validate() error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !IsValidInt256(tx.Fee()) {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidGasFee, "out of bound")
|
||||||
|
}
|
||||||
|
|
||||||
amount := tx.GetValue()
|
amount := tx.GetValue()
|
||||||
// Amount can be 0
|
// Amount can be 0
|
||||||
if amount != nil && amount.Sign() == -1 {
|
if amount != nil && amount.Sign() == -1 {
|
||||||
return sdkerrors.Wrapf(ErrInvalidAmount, "amount cannot be negative %s", amount)
|
return sdkerrors.Wrapf(ErrInvalidAmount, "amount cannot be negative %s", amount)
|
||||||
}
|
}
|
||||||
|
if !IsValidInt256(amount) {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidAmount, "out of bound")
|
||||||
|
}
|
||||||
|
|
||||||
if tx.To != "" {
|
if tx.To != "" {
|
||||||
if err := types.ValidateAddress(tx.To); err != nil {
|
if err := types.ValidateAddress(tx.To); err != nil {
|
||||||
|
@ -60,7 +60,8 @@ func (suite *TxDataTestSuite) TestNewDynamicFeeTx() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
tx := newDynamicFeeTx(tc.tx)
|
tx, err := newDynamicFeeTx(tc.tx)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
suite.Require().NotEmpty(tx)
|
suite.Require().NotEmpty(tx)
|
||||||
suite.Require().Equal(uint8(2), tx.TxType())
|
suite.Require().Equal(uint8(2), tx.TxType())
|
||||||
|
@ -25,6 +25,7 @@ const (
|
|||||||
codeErrCallDisabled
|
codeErrCallDisabled
|
||||||
codeErrInvalidAmount
|
codeErrInvalidAmount
|
||||||
codeErrInvalidGasPrice
|
codeErrInvalidGasPrice
|
||||||
|
codeErrInvalidGasFee
|
||||||
codeErrVMExecution
|
codeErrVMExecution
|
||||||
codeErrInvalidRefund
|
codeErrInvalidRefund
|
||||||
codeErrInconsistentGas
|
codeErrInconsistentGas
|
||||||
@ -72,6 +73,9 @@ var (
|
|||||||
// ErrInvalidGasPrice returns an error if an invalid gas price is provided to the tx.
|
// ErrInvalidGasPrice returns an error if an invalid gas price is provided to the tx.
|
||||||
ErrInvalidGasPrice = sdkerrors.Register(ModuleName, codeErrInvalidGasPrice, "invalid gas price")
|
ErrInvalidGasPrice = sdkerrors.Register(ModuleName, codeErrInvalidGasPrice, "invalid gas price")
|
||||||
|
|
||||||
|
// ErrInvalidGasFee returns an error if the tx gas fee is out of bound.
|
||||||
|
ErrInvalidGasFee = sdkerrors.Register(ModuleName, codeErrInvalidGasFee, "invalid gas fee")
|
||||||
|
|
||||||
// ErrVMExecution returns an error resulting from an error in EVM execution.
|
// ErrVMExecution returns an error resulting from an error in EVM execution.
|
||||||
ErrVMExecution = sdkerrors.Register(ModuleName, codeErrVMExecution, "evm transaction execution failed")
|
ErrVMExecution = sdkerrors.Register(ModuleName, codeErrVMExecution, "evm transaction execution failed")
|
||||||
|
|
||||||
|
@ -3,14 +3,13 @@ package types
|
|||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/tharsis/ethermint/types"
|
"github.com/tharsis/ethermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newLegacyTx(tx *ethtypes.Transaction) *LegacyTx {
|
func newLegacyTx(tx *ethtypes.Transaction) (*LegacyTx, error) {
|
||||||
txData := &LegacyTx{
|
txData := &LegacyTx{
|
||||||
Nonce: tx.Nonce(),
|
Nonce: tx.Nonce(),
|
||||||
Data: tx.Data(),
|
Data: tx.Data(),
|
||||||
@ -23,17 +22,23 @@ func newLegacyTx(tx *ethtypes.Transaction) *LegacyTx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tx.Value() != nil {
|
if tx.Value() != nil {
|
||||||
amountInt := sdk.NewIntFromBigInt(tx.Value())
|
amountInt, err := SafeNewIntFromBigInt(tx.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
txData.Amount = &amountInt
|
txData.Amount = &amountInt
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.GasPrice() != nil {
|
if tx.GasPrice() != nil {
|
||||||
gasPriceInt := sdk.NewIntFromBigInt(tx.GasPrice())
|
gasPriceInt, err := SafeNewIntFromBigInt(tx.GasPrice())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
txData.GasPrice = &gasPriceInt
|
txData.GasPrice = &gasPriceInt
|
||||||
}
|
}
|
||||||
|
|
||||||
txData.SetSignatureValues(tx.ChainId(), v, r, s)
|
txData.SetSignatureValues(tx.ChainId(), v, r, s)
|
||||||
return txData
|
return txData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxType returns the tx type
|
// TxType returns the tx type
|
||||||
@ -161,12 +166,21 @@ func (tx LegacyTx) Validate() error {
|
|||||||
if gasPrice.Sign() == -1 {
|
if gasPrice.Sign() == -1 {
|
||||||
return sdkerrors.Wrapf(ErrInvalidGasPrice, "gas price cannot be negative %s", gasPrice)
|
return sdkerrors.Wrapf(ErrInvalidGasPrice, "gas price cannot be negative %s", gasPrice)
|
||||||
}
|
}
|
||||||
|
if !IsValidInt256(gasPrice) {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidGasPrice, "out of bound")
|
||||||
|
}
|
||||||
|
if !IsValidInt256(tx.Fee()) {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidGasFee, "out of bound")
|
||||||
|
}
|
||||||
|
|
||||||
amount := tx.GetValue()
|
amount := tx.GetValue()
|
||||||
// Amount can be 0
|
// Amount can be 0
|
||||||
if amount != nil && amount.Sign() == -1 {
|
if amount != nil && amount.Sign() == -1 {
|
||||||
return sdkerrors.Wrapf(ErrInvalidAmount, "amount cannot be negative %s", amount)
|
return sdkerrors.Wrapf(ErrInvalidAmount, "amount cannot be negative %s", amount)
|
||||||
}
|
}
|
||||||
|
if !IsValidInt256(amount) {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidAmount, "out of bound")
|
||||||
|
}
|
||||||
|
|
||||||
if tx.To != "" {
|
if tx.To != "" {
|
||||||
if err := types.ValidateAddress(tx.To); err != nil {
|
if err := types.ValidateAddress(tx.To); err != nil {
|
||||||
|
@ -29,7 +29,8 @@ func (suite *TxDataTestSuite) TestNewLegacyTx() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
tx := newLegacyTx(tc.tx)
|
tx, err := newLegacyTx(tc.tx)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
suite.Require().NotEmpty(tc.tx)
|
suite.Require().NotEmpty(tc.tx)
|
||||||
suite.Require().Equal(uint8(0), tx.TxType())
|
suite.Require().Equal(uint8(0), tx.TxType())
|
||||||
|
@ -130,17 +130,21 @@ func newMsgEthereumTx(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fromEthereumTx populates the message fields from the given ethereum transaction
|
// fromEthereumTx populates the message fields from the given ethereum transaction
|
||||||
func (msg *MsgEthereumTx) FromEthereumTx(tx *ethtypes.Transaction) {
|
func (msg *MsgEthereumTx) FromEthereumTx(tx *ethtypes.Transaction) error {
|
||||||
txData := NewTxDataFromTx(tx)
|
txData, err := NewTxDataFromTx(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
anyTxData, err := PackTxData(txData)
|
anyTxData, err := PackTxData(txData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Data = anyTxData
|
msg.Data = anyTxData
|
||||||
msg.Size_ = float64(tx.Size())
|
msg.Size_ = float64(tx.Size())
|
||||||
msg.Hash = tx.Hash().Hex()
|
msg.Hash = tx.Hash().Hex()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route returns the route value of an MsgEthereumTx.
|
// Route returns the route value of an MsgEthereumTx.
|
||||||
@ -225,8 +229,7 @@ func (msg *MsgEthereumTx) Sign(ethSigner ethtypes.Signer, keyringSigner keyring.
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.FromEthereumTx(tx)
|
return msg.FromEthereumTx(tx)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGas implements the GasTx interface. It returns the GasLimit of the transaction.
|
// GetGas implements the GasTx interface. It returns the GasLimit of the transaction.
|
||||||
|
@ -8,10 +8,12 @@ import (
|
|||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/tharsis/ethermint/crypto/ethsecp256k1"
|
||||||
"github.com/tharsis/ethermint/tests"
|
"github.com/tharsis/ethermint/tests"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const invalidFromAddress = "0x0000"
|
const invalidFromAddress = "0x0000"
|
||||||
@ -59,6 +61,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
|
|||||||
hundredInt := sdk.NewInt(100)
|
hundredInt := sdk.NewInt(100)
|
||||||
zeroInt := sdk.ZeroInt()
|
zeroInt := sdk.ZeroInt()
|
||||||
minusOneInt := sdk.NewInt(-1)
|
minusOneInt := sdk.NewInt(-1)
|
||||||
|
exp_2_255 := sdk.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(2), big.NewInt(255), nil))
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
msg string
|
msg string
|
||||||
@ -80,6 +83,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
|
|||||||
{msg: "negative gas price - Legacy Tx", to: suite.to.Hex(), amount: &hundredInt, gasPrice: &minusOneInt, expectPass: false},
|
{msg: "negative gas price - Legacy Tx", to: suite.to.Hex(), amount: &hundredInt, gasPrice: &minusOneInt, expectPass: false},
|
||||||
{msg: "zero gas price - Legacy Tx", to: suite.to.Hex(), amount: &hundredInt, gasPrice: &zeroInt, expectPass: true},
|
{msg: "zero gas price - Legacy Tx", to: suite.to.Hex(), amount: &hundredInt, gasPrice: &zeroInt, expectPass: true},
|
||||||
{msg: "invalid from address - Legacy Tx", to: suite.to.Hex(), amount: &hundredInt, gasPrice: &zeroInt, from: invalidFromAddress, expectPass: false},
|
{msg: "invalid from address - Legacy Tx", to: suite.to.Hex(), amount: &hundredInt, gasPrice: &zeroInt, from: invalidFromAddress, expectPass: false},
|
||||||
|
{msg: "out of bound gas fee - Legacy Tx", to: suite.to.Hex(), amount: &hundredInt, gasPrice: &exp_2_255, expectPass: false},
|
||||||
{msg: "nil amount - AccessListTx", to: suite.to.Hex(), amount: nil, gasPrice: &hundredInt, accessList: &types.AccessList{}, chainID: &hundredInt, expectPass: true},
|
{msg: "nil amount - AccessListTx", to: suite.to.Hex(), amount: nil, gasPrice: &hundredInt, accessList: &types.AccessList{}, chainID: &hundredInt, expectPass: true},
|
||||||
{msg: "negative amount - AccessListTx", to: suite.to.Hex(), amount: &minusOneInt, gasPrice: &hundredInt, accessList: &types.AccessList{}, chainID: nil, expectPass: false},
|
{msg: "negative amount - AccessListTx", to: suite.to.Hex(), amount: &minusOneInt, gasPrice: &hundredInt, accessList: &types.AccessList{}, chainID: nil, expectPass: false},
|
||||||
{msg: "nil gas price - AccessListTx", to: suite.to.Hex(), amount: &hundredInt, gasPrice: nil, accessList: &types.AccessList{}, chainID: &hundredInt, expectPass: false},
|
{msg: "nil gas price - AccessListTx", to: suite.to.Hex(), amount: &hundredInt, gasPrice: nil, accessList: &types.AccessList{}, chainID: &hundredInt, expectPass: false},
|
||||||
@ -183,3 +187,80 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_Sign() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *MsgsTestSuite) TestFromEthereumTx() {
|
||||||
|
privkey, _ := ethsecp256k1.GenerateKey()
|
||||||
|
ethPriv, err := privkey.ToECDSA()
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
// 10^80 is more than 256 bits
|
||||||
|
exp_10_80 := new(big.Int).Mul(big.NewInt(1), new(big.Int).Exp(big.NewInt(10), big.NewInt(80), nil))
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
msg string
|
||||||
|
expectPass bool
|
||||||
|
buildTx func() *ethtypes.Transaction
|
||||||
|
}{
|
||||||
|
{"success, normal tx", true, func() *ethtypes.Transaction {
|
||||||
|
tx := ethtypes.NewTransaction(
|
||||||
|
0,
|
||||||
|
common.BigToAddress(big.NewInt(1)),
|
||||||
|
big.NewInt(10),
|
||||||
|
21000, big.NewInt(0),
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
tx, err := ethtypes.SignTx(tx, types.NewEIP2930Signer(suite.chainID), ethPriv)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
return tx
|
||||||
|
}},
|
||||||
|
{"fail, value bigger than 256bits", false, func() *ethtypes.Transaction {
|
||||||
|
tx := ethtypes.NewTransaction(
|
||||||
|
0,
|
||||||
|
common.BigToAddress(big.NewInt(1)),
|
||||||
|
exp_10_80,
|
||||||
|
21000, big.NewInt(0),
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
tx, err := ethtypes.SignTx(tx, types.NewEIP2930Signer(suite.chainID), ethPriv)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
return tx
|
||||||
|
}},
|
||||||
|
{"fail, gas price bigger than 256bits", false, func() *ethtypes.Transaction {
|
||||||
|
tx := ethtypes.NewTransaction(
|
||||||
|
0,
|
||||||
|
common.BigToAddress(big.NewInt(1)),
|
||||||
|
big.NewInt(10),
|
||||||
|
21000, exp_10_80,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
tx, err := ethtypes.SignTx(tx, types.NewEIP2930Signer(suite.chainID), ethPriv)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
return tx
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
ethTx := tc.buildTx()
|
||||||
|
tx := &MsgEthereumTx{}
|
||||||
|
err := tx.FromEthereumTx(ethTx)
|
||||||
|
if tc.expectPass {
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
// round-trip test
|
||||||
|
suite.assertEthTxEqual(tx.AsTransaction(), ethTx)
|
||||||
|
} else {
|
||||||
|
suite.Require().Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MsgsTestSuite) assertEthTxEqual(tx1 *ethtypes.Transaction, tx2 *ethtypes.Transaction) {
|
||||||
|
suite.Require().Equal(tx1.Hash(), tx2.Hash())
|
||||||
|
suite.Require().Equal(tx1.Size(), tx2.Size())
|
||||||
|
|
||||||
|
bin1, err := tx1.MarshalBinary()
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
bin2, err := tx2.MarshalBinary()
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
suite.Require().Equal(bin1, bin2)
|
||||||
|
}
|
||||||
|
@ -40,18 +40,22 @@ type TxData interface {
|
|||||||
Cost() *big.Int
|
Cost() *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTxDataFromTx(tx *ethtypes.Transaction) TxData {
|
func NewTxDataFromTx(tx *ethtypes.Transaction) (TxData, error) {
|
||||||
var txData TxData
|
var txData TxData
|
||||||
|
var err error
|
||||||
switch tx.Type() {
|
switch tx.Type() {
|
||||||
case ethtypes.DynamicFeeTxType:
|
case ethtypes.DynamicFeeTxType:
|
||||||
txData = newDynamicFeeTx(tx)
|
txData, err = newDynamicFeeTx(tx)
|
||||||
case ethtypes.AccessListTxType:
|
case ethtypes.AccessListTxType:
|
||||||
txData = newAccessListTx(tx)
|
txData, err = newAccessListTx(tx)
|
||||||
default:
|
default:
|
||||||
txData = newLegacyTx(tx)
|
txData, err = newLegacyTx(tx)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return txData
|
return txData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeriveChainID derives the chain id from the given v parameter.
|
// DeriveChainID derives the chain id from the given v parameter.
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
|
|
||||||
@ -11,6 +13,8 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maxBitLen = 256
|
||||||
|
|
||||||
var EmptyCodeHash = crypto.Keccak256(nil)
|
var EmptyCodeHash = crypto.Keccak256(nil)
|
||||||
|
|
||||||
// DecodeTxResponse decodes an protobuf-encoded byte slice into TxResponse
|
// DecodeTxResponse decodes an protobuf-encoded byte slice into TxResponse
|
||||||
@ -86,3 +90,16 @@ func BinSearch(lo, hi uint64, executable func(uint64) (bool, *MsgEthereumTxRespo
|
|||||||
}
|
}
|
||||||
return hi, nil
|
return hi, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SafeNewIntFromBigInt constructs Int from big.Int, return error if more than 256bits
|
||||||
|
func SafeNewIntFromBigInt(i *big.Int) (sdk.Int, error) {
|
||||||
|
if !IsValidInt256(i) {
|
||||||
|
return sdk.NewInt(0), errors.New("SafeNewIntFromBigInt() out of bound") // nolint
|
||||||
|
}
|
||||||
|
return sdk.NewIntFromBigInt(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidInt256 check the bound of 256 bit number
|
||||||
|
func IsValidInt256(i *big.Int) bool {
|
||||||
|
return i == nil || i.BitLen() <= maxBitLen
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user