e1560849dd
* Set priority for eth transactions Set the tx priority to the lowest priority in the messages. fix unit tests code cleanup and spec update spec fix go lint add priority integration test add python linter job add access list tx type fix gas limit remove ledger tag, so no need to replace hid dependency fix earlier check ibc-go v5.0.0-beta1 * fix pruned node integration test * Update x/feemarket/spec/09_antehandlers.md Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
291 lines
9.0 KiB
Go
291 lines
9.0 KiB
Go
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), ðtypes.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), ðtypes.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), ðtypes.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), ðtypes.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.NewTestGenesisState(newapp.AppCodec())
|
|
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)
|
|
|
|
// A valid msg should have empty `From`
|
|
msgEthereumTx.From = ""
|
|
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
|
|
}
|