impr(evm): add check msg.Validate check to prevent 0 gas txs (#1174)

* impr(evm): add check msg.Validate check to prevent 0 gas txs

* add changelog

* impr(evm): add integration tests for zero gas txs

* go mod tidy and proto swagger

* fix comment

* move changelog to state machine breaking

* add ReadHeaderTimeout`

* revert ReadHeaderTimeout config change

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
Daniel Burckhardt 2022-07-18 22:21:04 +02:00 committed by GitHub
parent b74b37f5f0
commit 969794c5b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 2233 additions and 315 deletions

View File

@ -42,6 +42,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (deps) [\#1159](https://github.com/evmos/ethermint/pull/1159) Bump Geth version to `v1.10.19`.
* (deps) [#1167](https://github.com/evmos/ethermint/pull/1167) Upgrade ibc-go to v4.
* (evm) [\#1174](https://github.com/evmos/ethermint/pull/1174) Don't allow eth txs with 0 in mempool.
### Improvements

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ import (
func SetupContract(b *testing.B) (*KeeperTestSuite, common.Address) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
amt := sdk.Coins{ethermint.NewPhotonCoinInt64(1000000000000000000)}
err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, amt)
@ -33,7 +33,7 @@ func SetupContract(b *testing.B) (*KeeperTestSuite, common.Address) {
func SetupTestMessageCall(b *testing.B) (*KeeperTestSuite, common.Address) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
amt := sdk.Coins{ethermint.NewPhotonCoinInt64(1000000000000000000)}
err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, amt)

View File

@ -0,0 +1,290 @@
package keeper_test
import (
"encoding/json"
"math/big"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/cosmos/cosmos-sdk/baseapp"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/evmos/ethermint/app"
"github.com/evmos/ethermint/crypto/ethsecp256k1"
"github.com/evmos/ethermint/encoding"
"github.com/evmos/ethermint/tests"
"github.com/evmos/ethermint/testutil"
"github.com/evmos/ethermint/x/feemarket/types"
"github.com/cosmos/cosmos-sdk/simapp"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"
)
var _ = Describe("Feemarket", func() {
var (
privKey *ethsecp256k1.PrivKey
)
Describe("Performing EVM transactions", func() {
type txParams struct {
gasLimit uint64
gasPrice *big.Int
gasFeeCap *big.Int
gasTipCap *big.Int
accesses *ethtypes.AccessList
}
type getprices func() txParams
Context("with MinGasPrices (feemarket param) < BaseFee (feemarket)", func() {
var (
baseFee int64
minGasPrices int64
)
BeforeEach(func() {
baseFee = 10_000_000_000
minGasPrices = baseFee - 5_000_000_000
// Note that the tests run the same transactions with `gasLimit =
// 100_000`. With the fee calculation `Fee = (baseFee + tip) * gasLimit`,
// a `minGasPrices = 5_000_000_000` results in `minGlobalFee =
// 500_000_000_000_000`
privKey, _ = setupTestWithContext("1", sdk.NewDec(minGasPrices), sdk.NewInt(baseFee))
})
Context("during CheckTx", func() {
DescribeTable("should accept transactions with gas Limit > 0",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasLimit, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := checkEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{100000, big.NewInt(baseFee), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{100000, nil, big.NewInt(baseFee), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
DescribeTable("should not accept transactions with gas Limit > 0",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasLimit, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := checkEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(false), "transaction should have succeeded", res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{0, big.NewInt(baseFee), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{0, nil, big.NewInt(baseFee), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
})
Context("during DeliverTx", func() {
DescribeTable("should accept transactions with gas Limit > 0",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasLimit, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := deliverEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{100000, big.NewInt(baseFee), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{100000, nil, big.NewInt(baseFee), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
DescribeTable("should not accept transactions with gas Limit > 0",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasLimit, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := checkEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(false), "transaction should have succeeded", res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{0, big.NewInt(baseFee), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{0, nil, big.NewInt(baseFee), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
})
})
})
})
// setupTestWithContext sets up a test chain with an example Cosmos send msg,
// given a local (validator config) and a gloabl (feemarket param) minGasPrice
func setupTestWithContext(valMinGasPrice string, minGasPrice sdk.Dec, baseFee sdk.Int) (*ethsecp256k1.PrivKey, banktypes.MsgSend) {
privKey, msg := setupTest(valMinGasPrice + s.denom)
params := types.DefaultParams()
params.MinGasPrice = minGasPrice
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
s.app.FeeMarketKeeper.SetBaseFee(s.ctx, baseFee.BigInt())
s.Commit()
return privKey, msg
}
func setupTest(localMinGasPrices string) (*ethsecp256k1.PrivKey, banktypes.MsgSend) {
setupChain(localMinGasPrices)
privKey, address := generateKey()
amount, ok := sdk.NewIntFromString("10000000000000000000")
s.Require().True(ok)
initBalance := sdk.Coins{sdk.Coin{
Denom: s.denom,
Amount: amount,
}}
testutil.FundAccount(s.app.BankKeeper, s.ctx, address, initBalance)
msg := banktypes.MsgSend{
FromAddress: address.String(),
ToAddress: address.String(),
Amount: sdk.Coins{sdk.Coin{
Denom: s.denom,
Amount: sdk.NewInt(10000),
}},
}
s.Commit()
return privKey, msg
}
func setupChain(localMinGasPricesStr string) {
// Initialize the app, so we can use SetMinGasPrices to set the
// validator-specific min-gas-prices setting
db := dbm.NewMemDB()
newapp := app.NewEthermintApp(
log.NewNopLogger(),
db,
nil,
true,
map[int64]bool{},
app.DefaultNodeHome,
5,
encoding.MakeConfig(app.ModuleBasics),
simapp.EmptyAppOptions{},
baseapp.SetMinGasPrices(localMinGasPricesStr),
)
genesisState := app.NewDefaultGenesisState()
genesisState[types.ModuleName] = newapp.AppCodec().MustMarshalJSON(types.DefaultGenesisState())
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
s.Require().NoError(err)
// Initialize the chain
newapp.InitChain(
abci.RequestInitChain{
ChainId: "ethermint_9000-1",
Validators: []abci.ValidatorUpdate{},
AppStateBytes: stateBytes,
ConsensusParams: app.DefaultConsensusParams,
},
)
s.app = newapp
s.SetupApp(false)
}
func generateKey() (*ethsecp256k1.PrivKey, sdk.AccAddress) {
address, priv := tests.NewAddrKey()
return priv.(*ethsecp256k1.PrivKey), sdk.AccAddress(address.Bytes())
}
func getNonce(addressBytes []byte) uint64 {
return s.app.EvmKeeper.GetNonce(
s.ctx,
common.BytesToAddress(addressBytes),
)
}
func buildEthTx(
priv *ethsecp256k1.PrivKey,
to *common.Address,
gasLimit uint64,
gasPrice *big.Int,
gasFeeCap *big.Int,
gasTipCap *big.Int,
accesses *ethtypes.AccessList,
) *evmtypes.MsgEthereumTx {
chainID := s.app.EvmKeeper.ChainID()
from := common.BytesToAddress(priv.PubKey().Address().Bytes())
nonce := getNonce(from.Bytes())
data := make([]byte, 0)
msgEthereumTx := evmtypes.NewTx(
chainID,
nonce,
to,
nil,
gasLimit,
gasPrice,
gasFeeCap,
gasTipCap,
data,
accesses,
)
msgEthereumTx.From = from.String()
return msgEthereumTx
}
func prepareEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereumTx) []byte {
encodingConfig := encoding.MakeConfig(app.ModuleBasics)
option, err := codectypes.NewAnyWithValue(&evmtypes.ExtensionOptionsEthereumTx{})
s.Require().NoError(err)
txBuilder := encodingConfig.TxConfig.NewTxBuilder()
builder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder)
s.Require().True(ok)
builder.SetExtensionOptions(option)
err = msgEthereumTx.Sign(s.ethSigner, tests.NewSigner(priv))
s.Require().NoError(err)
err = txBuilder.SetMsgs(msgEthereumTx)
s.Require().NoError(err)
txData, err := evmtypes.UnpackTxData(msgEthereumTx.Data)
s.Require().NoError(err)
evmDenom := s.app.EvmKeeper.GetParams(s.ctx).EvmDenom
fees := sdk.Coins{{Denom: evmDenom, Amount: sdk.NewIntFromBigInt(txData.Fee())}}
builder.SetFeeAmount(fees)
builder.SetGasLimit(msgEthereumTx.GetGas())
// bz are bytes to be broadcasted over the network
bz, err := encodingConfig.TxConfig.TxEncoder()(txBuilder.GetTx())
s.Require().NoError(err)
return bz
}
func checkEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereumTx) abci.ResponseCheckTx {
bz := prepareEthTx(priv, msgEthereumTx)
req := abci.RequestCheckTx{Tx: bz}
res := s.app.BaseApp.CheckTx(req)
return res
}
func deliverEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereumTx) abci.ResponseDeliverTx {
bz := prepareEthTx(priv, msgEthereumTx)
req := abci.RequestDeliverTx{Tx: bz}
res := s.app.BaseApp.DeliverTx(req)
return res
}

View File

@ -8,6 +8,9 @@ import (
"testing"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
@ -31,6 +34,7 @@ import (
ethermint "github.com/evmos/ethermint/types"
"github.com/evmos/ethermint/x/evm/statedb"
"github.com/evmos/ethermint/x/evm/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@ -66,12 +70,31 @@ type KeeperTestSuite struct {
enableFeemarket bool
enableLondonHF bool
mintFeeCollector bool
denom string
}
/// DoSetupTest setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`.
func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
checkTx := false
var s *KeeperTestSuite
func TestKeeperTestSuite(t *testing.T) {
s = new(KeeperTestSuite)
s.enableFeemarket = false
s.enableLondonHF = true
suite.Run(t, s)
// Run Ginkgo integration tests
RegisterFailHandler(Fail)
RunSpecs(t, "Keeper Suite")
}
func (suite *KeeperTestSuite) SetupTest() {
checkTx := false
suite.app = app.Setup(checkTx, nil)
suite.SetupApp(checkTx)
}
/// SetupApp setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`.
func (suite *KeeperTestSuite) SetupApp(checkTx bool) {
t := suite.T()
// account key, use a constant account to keep unit test deterministic.
ecdsaPriv, err := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
require.NoError(t, err)
@ -186,10 +209,7 @@ func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) {
suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig)
suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
suite.appCodec = encodingConfig.Marshaler
}
func (suite *KeeperTestSuite) SetupTest() {
suite.DoSetupTest(suite.T())
suite.denom = evmtypes.DefaultEVMDenom
}
func (suite *KeeperTestSuite) EvmDenom() string {
@ -413,10 +433,3 @@ func (suite *KeeperTestSuite) TestBaseFee() {
suite.enableFeemarket = false
suite.enableLondonHF = true
}
func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, &KeeperTestSuite{
enableFeemarket: false,
enableLondonHF: true,
})
}

View File

@ -143,7 +143,7 @@ func newNativeMessage(
func BenchmarkApplyTransaction(b *testing.B) {
suite := KeeperTestSuite{enableLondonHF: true}
suite.DoSetupTest(b)
suite.SetupTest()
ethSigner := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
@ -170,7 +170,7 @@ func BenchmarkApplyTransaction(b *testing.B) {
func BenchmarkApplyTransactionWithLegacyTx(b *testing.B) {
suite := KeeperTestSuite{enableLondonHF: true}
suite.DoSetupTest(b)
suite.SetupTest()
ethSigner := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
@ -197,7 +197,7 @@ func BenchmarkApplyTransactionWithLegacyTx(b *testing.B) {
func BenchmarkApplyTransactionWithDynamicFeeTx(b *testing.B) {
suite := KeeperTestSuite{enableFeemarket: true, enableLondonHF: true}
suite.DoSetupTest(b)
suite.SetupTest()
ethSigner := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
@ -224,7 +224,7 @@ func BenchmarkApplyTransactionWithDynamicFeeTx(b *testing.B) {
func BenchmarkApplyMessage(b *testing.B) {
suite := KeeperTestSuite{enableLondonHF: true}
suite.DoSetupTest(b)
suite.SetupTest()
params := suite.app.EvmKeeper.GetParams(suite.ctx)
ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID())
@ -259,7 +259,7 @@ func BenchmarkApplyMessage(b *testing.B) {
func BenchmarkApplyMessageWithLegacyTx(b *testing.B) {
suite := KeeperTestSuite{enableLondonHF: true}
suite.DoSetupTest(b)
suite.SetupTest()
params := suite.app.EvmKeeper.GetParams(suite.ctx)
ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID())
@ -294,7 +294,7 @@ func BenchmarkApplyMessageWithLegacyTx(b *testing.B) {
func BenchmarkApplyMessageWithDynamicFeeTx(b *testing.B) {
suite := KeeperTestSuite{enableFeemarket: true, enableLondonHF: true}
suite.DoSetupTest(b)
suite.SetupTest()
params := suite.app.EvmKeeper.GetParams(suite.ctx)
ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID())

View File

@ -15,7 +15,7 @@ import (
func BenchmarkCreateAccountNew(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
vmdb := suite.StateDB()
b.ResetTimer()
@ -31,7 +31,7 @@ func BenchmarkCreateAccountNew(b *testing.B) {
func BenchmarkCreateAccountExisting(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
vmdb := suite.StateDB()
b.ResetTimer()
@ -44,7 +44,7 @@ func BenchmarkCreateAccountExisting(b *testing.B) {
func BenchmarkAddBalance(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
vmdb := suite.StateDB()
amt := big.NewInt(10)
@ -59,7 +59,7 @@ func BenchmarkAddBalance(b *testing.B) {
func BenchmarkSetCode(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
vmdb := suite.StateDB()
hash := crypto.Keccak256Hash([]byte("code")).Bytes()
@ -74,7 +74,7 @@ func BenchmarkSetCode(b *testing.B) {
func BenchmarkSetState(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
vmdb := suite.StateDB()
hash := crypto.Keccak256Hash([]byte("topic")).Bytes()
@ -89,7 +89,7 @@ func BenchmarkSetState(b *testing.B) {
func BenchmarkAddLog(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
vmdb := suite.StateDB()
topic := crypto.Keccak256Hash([]byte("topic"))
@ -116,7 +116,7 @@ func BenchmarkAddLog(b *testing.B) {
func BenchmarkSnapshot(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
vmdb := suite.StateDB()
b.ResetTimer()
@ -136,7 +136,7 @@ func BenchmarkSnapshot(b *testing.B) {
func BenchmarkSubBalance(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
vmdb := suite.StateDB()
amt := big.NewInt(10)
@ -151,7 +151,7 @@ func BenchmarkSubBalance(b *testing.B) {
func BenchmarkSetNonce(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
vmdb := suite.StateDB()
b.ResetTimer()
@ -164,7 +164,7 @@ func BenchmarkSetNonce(b *testing.B) {
func BenchmarkAddRefund(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
vmdb := suite.StateDB()
b.ResetTimer()
@ -177,7 +177,7 @@ func BenchmarkAddRefund(b *testing.B) {
func BenchmarkSuicide(b *testing.B) {
suite := KeeperTestSuite{}
suite.DoSetupTest(b)
suite.SetupTest()
vmdb := suite.StateDB()
b.ResetTimer()

View File

@ -33,6 +33,7 @@ const (
codeErrInvalidBaseFee
codeErrGasOverflow
codeErrInvalidAccount
codeErrInvalidGasLimit
)
var ErrPostTxProcessing = errors.New("failed to execute post processing")
@ -97,6 +98,9 @@ var (
// ErrInvalidAccount returns an error if the account is not an EVM compatible account
ErrInvalidAccount = sdkerrors.Register(ModuleName, codeErrInvalidAccount, "account type is not a valid ethereum account")
// ErrInvalidGasLimit returns an error if gas limit value is invalid
ErrInvalidGasLimit = sdkerrors.Register(ModuleName, codeErrInvalidGasLimit, "invalid gas limit")
)
// NewExecErrorWithReason unpacks the revert return bytes and returns a wrapped error

View File

@ -171,6 +171,11 @@ func (msg MsgEthereumTx) ValidateBasic() error {
return sdkerrors.Wrap(err, "failed to unpack tx data")
}
// prevent txs with 0 gas to fill up the mempool
if txData.GetGas() == 0 {
return sdkerrors.Wrap(ErrInvalidGasLimit, "gas limit must not be zero")
}
return txData.Validate()
}

View File

@ -119,6 +119,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg string
to string
amount *big.Int
gasLimit uint64
gasPrice *big.Int
gasFeeCap *big.Int
gasTipCap *big.Int
@ -131,6 +132,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "pass with recipient - Legacy Tx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: hundredInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -140,6 +142,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "pass with recipient - AccessList Tx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: zeroInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -151,6 +154,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "pass with recipient - DynamicFee Tx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: zeroInt,
gasFeeCap: hundredInt,
gasTipCap: zeroInt,
@ -162,6 +166,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "pass contract - Legacy Tx",
to: "",
amount: hundredInt,
gasLimit: 1000,
gasPrice: hundredInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -171,6 +176,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "invalid recipient",
to: invalidFromAddress,
amount: minusOneInt,
gasLimit: 1000,
gasPrice: hundredInt,
expectPass: false,
},
@ -178,6 +184,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "nil amount - Legacy Tx",
to: suite.to.Hex(),
amount: nil,
gasLimit: 1000,
gasPrice: hundredInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -187,6 +194,17 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "negative amount - Legacy Tx",
to: suite.to.Hex(),
amount: minusOneInt,
gasLimit: 1000,
gasPrice: hundredInt,
gasFeeCap: nil,
gasTipCap: nil,
expectPass: false,
},
{
msg: "zero gas limit - Legacy Tx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 0,
gasPrice: hundredInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -196,6 +214,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "nil gas price - Legacy Tx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: nil,
gasFeeCap: nil,
gasTipCap: nil,
@ -205,6 +224,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "negative gas price - Legacy Tx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: minusOneInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -214,6 +234,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "zero gas price - Legacy Tx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: zeroInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -223,6 +244,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "invalid from address - Legacy Tx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: zeroInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -233,6 +255,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "out of bound gas fee - Legacy Tx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: exp_2_255,
gasFeeCap: nil,
gasTipCap: nil,
@ -242,6 +265,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "nil amount - AccessListTx",
to: suite.to.Hex(),
amount: nil,
gasLimit: 1000,
gasPrice: hundredInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -253,6 +277,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "negative amount - AccessListTx",
to: suite.to.Hex(),
amount: minusOneInt,
gasLimit: 1000,
gasPrice: hundredInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -260,10 +285,23 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
chainID: nil,
expectPass: false,
},
{
msg: "zero gas limit - AccessListTx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 0,
gasPrice: zeroInt,
gasFeeCap: nil,
gasTipCap: nil,
accessList: &ethtypes.AccessList{},
chainID: hundredInt,
expectPass: false,
},
{
msg: "nil gas price - AccessListTx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: nil,
gasFeeCap: nil,
gasTipCap: nil,
@ -275,6 +313,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "negative gas price - AccessListTx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: minusOneInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -286,6 +325,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "zero gas price - AccessListTx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: zeroInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -297,6 +337,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "invalid from address - AccessListTx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: zeroInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -309,6 +350,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "chain ID not set on AccessListTx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: zeroInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -320,6 +362,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
msg: "nil tx.Data - AccessList Tx",
to: suite.to.Hex(),
amount: hundredInt,
gasLimit: 1000,
gasPrice: zeroInt,
gasFeeCap: nil,
gasTipCap: nil,
@ -332,7 +375,7 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
for i, tc := range testCases {
to := common.HexToAddress(tc.from)
tx := types.NewTx(tc.chainID, 1, &to, tc.amount, 1000, tc.gasPrice, tc.gasFeeCap, tc.gasTipCap, nil, tc.accessList)
tx := types.NewTx(tc.chainID, 1, &to, tc.amount, tc.gasLimit, tc.gasPrice, tc.gasFeeCap, tc.gasTipCap, nil, tc.accessList)
tx.From = tc.from
// apply nil assignment here to test ValidateBasic function instead of NewTx

View File

@ -230,7 +230,6 @@ var _ = Describe("Feemarket", func() {
// Note that max priority fee per gas can't be higher than the max fee per gas (gasFeeCap), i.e. 30_000_000_000)
return txParams{nil, big.NewInt(minGasPrices - 10_000_000_000), big.NewInt(30_000_000_000), &ethtypes.AccessList{}}
}),
Entry("dynamic tx with GasFeeCap > MinGasPrices, EffectivePrice < MinGasPrices", func() txParams {
return txParams{nil, big.NewInt(minGasPrices + 10_000_000_000), big.NewInt(0), &ethtypes.AccessList{}}
}),
@ -297,143 +296,144 @@ var _ = Describe("Feemarket", func() {
}),
)
})
})
Context("with MinGasPrices (feemarket param) < BaseFee (feemarket)", func() {
var (
baseFee int64
minGasPrices int64
Context("with MinGasPrices (feemarket param) < BaseFee (feemarket)", func() {
var (
baseFee int64
minGasPrices int64
)
BeforeEach(func() {
baseFee = 10_000_000_000
minGasPrices = baseFee - 5_000_000_000
// Note that the tests run the same transactions with `gasLimit =
// 100_000`. With the fee calculation `Fee = (baseFee + tip) * gasLimit`,
// a `minGasPrices = 5_000_000_000` results in `minGlobalFee =
// 500_000_000_000_000`
privKey, _ = setupTestWithContext("1", sdk.NewDec(minGasPrices), sdk.NewInt(baseFee))
})
Context("during CheckTx", func() {
DescribeTable("should reject transactions with gasPrice < MinGasPrices",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := checkEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
Expect(
strings.Contains(res.GetLog(),
"provided fee < minimum global fee"),
).To(BeTrue(), res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(minGasPrices - 1_000_000_000), nil, nil, nil}
}),
Entry("dynamic tx with GasFeeCap < MinGasPrices, no gasTipCap", func() txParams {
return txParams{nil, big.NewInt(minGasPrices - 1_000_000_000), big.NewInt(0), &ethtypes.AccessList{}}
}),
Entry("dynamic tx with GasFeeCap < MinGasPrices, max gasTipCap", func() txParams {
return txParams{nil, big.NewInt(minGasPrices - 1_000_000_000), big.NewInt(minGasPrices - 1_000_000_000), &ethtypes.AccessList{}}
}),
)
BeforeEach(func() {
baseFee = 10_000_000_000
minGasPrices = baseFee - 5_000_000_000
DescribeTable("should reject transactions with MinGasPrices < tx gasPrice < EffectivePrice",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := checkEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
Expect(
strings.Contains(res.GetLog(),
"insufficient fee"),
).To(BeTrue(), res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(baseFee - 1_000_000_000), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{nil, big.NewInt(baseFee - 1_000_000_000), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
// Note that the tests run the same transactions with `gasLimit =
// 100_000`. With the fee calculation `Fee = (baseFee + tip) * gasLimit`,
// a `minGasPrices = 5_000_000_000` results in `minGlobalFee =
// 500_000_000_000_000`
privKey, _ = setupTestWithContext("1", sdk.NewDec(minGasPrices), sdk.NewInt(baseFee))
})
DescribeTable("should accept transactions with gasPrice >= EffectivePrice",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := checkEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(baseFee), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{nil, big.NewInt(baseFee), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
})
Context("during CheckTx", func() {
DescribeTable("should reject transactions with gasPrice < MinGasPrices",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := checkEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
Expect(
strings.Contains(res.GetLog(),
"provided fee < minimum global fee"),
).To(BeTrue(), res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(minGasPrices - 1_000_000_000), nil, nil, nil}
}),
Entry("dynamic tx with GasFeeCap < MinGasPrices, no gasTipCap", func() txParams {
return txParams{nil, big.NewInt(minGasPrices - 1_000_000_000), big.NewInt(0), &ethtypes.AccessList{}}
}),
Entry("dynamic tx with GasFeeCap < MinGasPrices, max gasTipCap", func() txParams {
return txParams{nil, big.NewInt(minGasPrices - 1_000_000_000), big.NewInt(minGasPrices - 1_000_000_000), &ethtypes.AccessList{}}
}),
)
Context("during DeliverTx", func() {
DescribeTable("should reject transactions with gasPrice < MinGasPrices",
func(malleate getprices) {
DescribeTable("should reject transactions with MinGasPrices < tx gasPrice < EffectivePrice",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := checkEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
Expect(
strings.Contains(res.GetLog(),
"insufficient fee"),
).To(BeTrue(), res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(baseFee - 1_000_000_000), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{nil, big.NewInt(baseFee - 1_000_000_000), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := deliverEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
Expect(
strings.Contains(res.GetLog(),
"provided fee < minimum global fee"),
).To(BeTrue(), res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(minGasPrices - 1_000_000_000), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{nil, big.NewInt(minGasPrices - 1_000_000_000), nil, &ethtypes.AccessList{}}
}),
)
DescribeTable("should accept transactions with gasPrice >= EffectivePrice",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := checkEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(baseFee), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{nil, big.NewInt(baseFee), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
})
DescribeTable("should reject transactions with MinGasPrices < gasPrice < EffectivePrice",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := deliverEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
Expect(
strings.Contains(res.GetLog(),
"insufficient fee"),
).To(BeTrue(), res.GetLog())
},
// Note that the baseFee is not 10_000_000_000 anymore but updates to 8_750_000_000 because of the s.Commit
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(baseFee - 2_000_000_000), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{nil, big.NewInt(baseFee - 2_000_000_000), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
Context("during DeliverTx", func() {
DescribeTable("should reject transactions with gasPrice < MinGasPrices",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := deliverEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
Expect(
strings.Contains(res.GetLog(),
"provided fee < minimum global fee"),
).To(BeTrue(), res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(minGasPrices - 1_000_000_000), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{nil, big.NewInt(minGasPrices - 1_000_000_000), nil, &ethtypes.AccessList{}}
}),
)
DescribeTable("should reject transactions with MinGasPrices < gasPrice < EffectivePrice",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := deliverEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
Expect(
strings.Contains(res.GetLog(),
"insufficient fee"),
).To(BeTrue(), res.GetLog())
},
// Note that the baseFee is not 10_000_000_000 anymore but updates to 8_750_000_000 because of the s.Commit
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(baseFee - 2_000_000_000), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{nil, big.NewInt(baseFee - 2_000_000_000), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
DescribeTable("should accept transactions with gasPrice >= EffectivePrice",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := deliverEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(baseFee), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{nil, big.NewInt(baseFee), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
})
DescribeTable("should accept transactions with gasPrice >= EffectivePrice",
func(malleate getprices) {
p := malleate()
to := tests.GenerateAddress()
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
res := deliverEthTx(privKey, msgEthereumTx)
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
},
Entry("legacy tx", func() txParams {
return txParams{big.NewInt(baseFee), nil, nil, nil}
}),
Entry("dynamic tx", func() txParams {
return txParams{nil, big.NewInt(baseFee), big.NewInt(0), &ethtypes.AccessList{}}
}),
)
})
})
})