b1cd16e5bf
* Problem: feemarket's query cli has redundant height parameter Soluton: - remove the positional height parameter, since there's a flag already. Update CHANGELOG.md * Apply feemarket to native cosmos tx - add tx extension option for user to input tip price - apply feemarket's base fee to native tx comments and cleanup fallback to default sdk logic when london hardfork not enabled integration test cleanup feemarket query cli commands Update CHANGELOG.md update unit tests disable feemarket in simulation tests for now fix lint Update app/simulation_test.go fix python lint fix lint Update x/evm/types/extension_option.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> address review suggestions * fix unit tests * fix integration test * improve unit test coverage * fix go lint * refactor * fix integration test * fix simulation tests * fix go linter Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
404 lines
13 KiB
Go
404 lines
13 KiB
Go
package app
|
|
|
|
// TODO: COsmos SDK fix for the simulator issue for custom keys
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/rand"
|
|
"os"
|
|
"runtime/debug"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
"github.com/cosmos/cosmos-sdk/simapp"
|
|
"github.com/cosmos/cosmos-sdk/simapp/params"
|
|
"github.com/cosmos/cosmos-sdk/store"
|
|
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
|
|
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
|
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
|
|
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
|
|
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
|
|
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
|
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
|
|
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
|
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
|
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
ibctransfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types"
|
|
ibchost "github.com/cosmos/ibc-go/v5/modules/core/24-host"
|
|
"github.com/evmos/ethermint/app/ante"
|
|
evmenc "github.com/evmos/ethermint/encoding"
|
|
abci "github.com/tendermint/tendermint/abci/types"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
|
dbm "github.com/tendermint/tm-db"
|
|
)
|
|
|
|
// MakeEncodingConfig creates the EncodingConfig
|
|
func MakeEncodingConfig() params.EncodingConfig {
|
|
return evmenc.MakeConfig(ModuleBasics)
|
|
}
|
|
|
|
func init() {
|
|
simapp.GetSimulatorFlags()
|
|
}
|
|
|
|
const SimAppChainID = "simulation_777-1"
|
|
|
|
type storeKeysPrefixes struct {
|
|
A storetypes.StoreKey
|
|
B storetypes.StoreKey
|
|
Prefixes [][]byte
|
|
}
|
|
|
|
// fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of
|
|
// an IAVLStore for faster simulation speed.
|
|
func fauxMerkleModeOpt(bapp *baseapp.BaseApp) {
|
|
bapp.SetFauxMerkleMode()
|
|
}
|
|
|
|
// NewSimApp disable feemarket on native tx, otherwise the cosmos-sdk simulation tests will fail.
|
|
func NewSimApp(logger log.Logger, db dbm.DB) (*EthermintApp, error) {
|
|
encodingConfig := MakeEncodingConfig()
|
|
app := NewEthermintApp(logger, db, nil, false, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, encodingConfig, simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
|
// disable feemarket on native tx
|
|
anteHandler, err := ante.NewAnteHandler(ante.HandlerOptions{
|
|
AccountKeeper: app.AccountKeeper,
|
|
BankKeeper: app.BankKeeper,
|
|
SignModeHandler: encodingConfig.TxConfig.SignModeHandler(),
|
|
FeegrantKeeper: app.FeeGrantKeeper,
|
|
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
|
|
IBCKeeper: app.IBCKeeper,
|
|
EvmKeeper: app.EvmKeeper,
|
|
FeeMarketKeeper: app.FeeMarketKeeper,
|
|
MaxTxGasWanted: 0,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
app.SetAnteHandler(anteHandler)
|
|
if err := app.LoadLatestVersion(); err != nil {
|
|
return nil, err
|
|
}
|
|
return app, nil
|
|
}
|
|
|
|
// interBlockCacheOpt returns a BaseApp option function that sets the persistent
|
|
// inter-block write-through cache.
|
|
func interBlockCacheOpt() func(*baseapp.BaseApp) {
|
|
return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager())
|
|
}
|
|
|
|
func TestFullAppSimulation(t *testing.T) {
|
|
config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation")
|
|
if skip {
|
|
t.Skip("skipping application simulation")
|
|
}
|
|
require.NoError(t, err, "simulation setup failed")
|
|
|
|
config.ChainID = SimAppChainID
|
|
|
|
defer func() {
|
|
require.NoError(t, db.Close())
|
|
require.NoError(t, os.RemoveAll(dir))
|
|
}()
|
|
|
|
app, err := NewSimApp(logger, db)
|
|
require.Equal(t, appName, app.Name())
|
|
require.NoError(t, err)
|
|
|
|
// run randomized simulation
|
|
_, simParams, simErr := simulation.SimulateFromSeed(
|
|
t,
|
|
os.Stdout,
|
|
app.BaseApp,
|
|
StateFn(app.AppCodec(), app.SimulationManager()),
|
|
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
|
simapp.SimulationOperations(app, app.AppCodec(), config),
|
|
app.ModuleAccountAddrs(),
|
|
config,
|
|
app.AppCodec(),
|
|
)
|
|
|
|
// export state and simParams before the simulation error is checked
|
|
err = simapp.CheckExportSimulation(app, config, simParams)
|
|
require.NoError(t, err)
|
|
require.NoError(t, simErr)
|
|
|
|
if config.Commit {
|
|
simapp.PrintStats(db)
|
|
}
|
|
}
|
|
|
|
func TestAppImportExport(t *testing.T) {
|
|
config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation")
|
|
if skip {
|
|
t.Skip("skipping application import/export simulation")
|
|
}
|
|
require.NoError(t, err, "simulation setup failed")
|
|
|
|
config.ChainID = SimAppChainID
|
|
|
|
defer func() {
|
|
require.NoError(t, db.Close())
|
|
require.NoError(t, os.RemoveAll(dir))
|
|
}()
|
|
app, err := NewSimApp(logger, db)
|
|
require.NoError(t, err)
|
|
require.Equal(t, appName, app.Name())
|
|
|
|
// Run randomized simulation
|
|
_, simParams, simErr := simulation.SimulateFromSeed(
|
|
t,
|
|
os.Stdout,
|
|
app.BaseApp,
|
|
StateFn(app.AppCodec(), app.SimulationManager()),
|
|
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
|
simapp.SimulationOperations(app, app.AppCodec(), config),
|
|
app.ModuleAccountAddrs(),
|
|
config,
|
|
app.AppCodec(),
|
|
)
|
|
|
|
// export state and simParams before the simulation error is checked
|
|
err = simapp.CheckExportSimulation(app, config, simParams)
|
|
require.NoError(t, err)
|
|
require.NoError(t, simErr)
|
|
|
|
if config.Commit {
|
|
simapp.PrintStats(db)
|
|
}
|
|
|
|
fmt.Printf("exporting genesis...\n")
|
|
|
|
exported, err := app.ExportAppStateAndValidators(false, []string{})
|
|
require.NoError(t, err)
|
|
|
|
fmt.Printf("importing genesis...\n")
|
|
|
|
//nolint: dogsled
|
|
_, newDB, newDir, _, _, err := simapp.SetupSimulation("leveldb-app-sim-2", "Simulation-2")
|
|
require.NoError(t, err, "simulation setup failed")
|
|
|
|
defer func() {
|
|
require.NoError(t, newDB.Close())
|
|
require.NoError(t, os.RemoveAll(newDir))
|
|
}()
|
|
|
|
newApp, err := NewSimApp(log.NewNopLogger(), newDB)
|
|
require.Equal(t, appName, newApp.Name())
|
|
require.NoError(t, err)
|
|
|
|
var genesisState simapp.GenesisState
|
|
err = json.Unmarshal(exported.AppState, &genesisState)
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
err := fmt.Sprintf("%v", r)
|
|
if !strings.Contains(err, "validator set is empty after InitGenesis") {
|
|
panic(r)
|
|
}
|
|
logger.Info("Skipping simulation as all validators have been unbonded")
|
|
logger.Info("err", err, "stacktrace", string(debug.Stack()))
|
|
}
|
|
}()
|
|
|
|
ctxA := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight(), ChainID: SimAppChainID})
|
|
ctxB := newApp.NewContext(true, tmproto.Header{Height: app.LastBlockHeight(), ChainID: SimAppChainID})
|
|
newApp.mm.InitGenesis(ctxB, app.AppCodec(), genesisState)
|
|
newApp.StoreConsensusParams(ctxB, exported.ConsensusParams)
|
|
|
|
fmt.Printf("comparing stores...\n")
|
|
|
|
storeKeysPrefixes := []storeKeysPrefixes{
|
|
{app.keys[authtypes.StoreKey], newApp.keys[authtypes.StoreKey], [][]byte{}},
|
|
{
|
|
app.keys[stakingtypes.StoreKey], newApp.keys[stakingtypes.StoreKey],
|
|
[][]byte{
|
|
stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey,
|
|
stakingtypes.HistoricalInfoKey,
|
|
},
|
|
}, // ordering may change but it doesn't matter
|
|
{app.keys[slashingtypes.StoreKey], newApp.keys[slashingtypes.StoreKey], [][]byte{}},
|
|
{app.keys[minttypes.StoreKey], newApp.keys[minttypes.StoreKey], [][]byte{}},
|
|
{app.keys[distrtypes.StoreKey], newApp.keys[distrtypes.StoreKey], [][]byte{}},
|
|
{app.keys[banktypes.StoreKey], newApp.keys[banktypes.StoreKey], [][]byte{banktypes.BalancesPrefix}},
|
|
{app.keys[paramtypes.StoreKey], newApp.keys[paramtypes.StoreKey], [][]byte{}},
|
|
{app.keys[govtypes.StoreKey], newApp.keys[govtypes.StoreKey], [][]byte{}},
|
|
{app.keys[evidencetypes.StoreKey], newApp.keys[evidencetypes.StoreKey], [][]byte{}},
|
|
{app.keys[capabilitytypes.StoreKey], newApp.keys[capabilitytypes.StoreKey], [][]byte{}},
|
|
{app.keys[authzkeeper.StoreKey], newApp.keys[authzkeeper.StoreKey], [][]byte{authzkeeper.GrantKey, authzkeeper.GrantQueuePrefix}},
|
|
{app.keys[ibchost.StoreKey], newApp.keys[ibchost.StoreKey], [][]byte{}},
|
|
{app.keys[ibctransfertypes.StoreKey], newApp.keys[ibctransfertypes.StoreKey], [][]byte{}},
|
|
}
|
|
|
|
for _, skp := range storeKeysPrefixes {
|
|
storeA := ctxA.KVStore(skp.A)
|
|
storeB := ctxB.KVStore(skp.B)
|
|
|
|
failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, skp.Prefixes)
|
|
require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare")
|
|
|
|
fmt.Printf("compared %d different key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B)
|
|
require.Equal(t, len(failedKVAs), 0, simapp.GetSimulationLog(skp.A.Name(), app.SimulationManager().StoreDecoders, failedKVAs, failedKVBs))
|
|
}
|
|
}
|
|
|
|
func TestAppSimulationAfterImport(t *testing.T) {
|
|
config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation")
|
|
if skip {
|
|
t.Skip("skipping application simulation after import")
|
|
}
|
|
require.NoError(t, err, "simulation setup failed")
|
|
|
|
config.ChainID = SimAppChainID
|
|
|
|
defer func() {
|
|
require.NoError(t, db.Close())
|
|
require.NoError(t, os.RemoveAll(dir))
|
|
}()
|
|
|
|
app, err := NewSimApp(logger, db)
|
|
require.Equal(t, appName, app.Name())
|
|
require.NoError(t, err)
|
|
|
|
// Run randomized simulation
|
|
stopEarly, simParams, simErr := simulation.SimulateFromSeed(
|
|
t,
|
|
os.Stdout,
|
|
app.BaseApp,
|
|
StateFn(app.AppCodec(), app.SimulationManager()),
|
|
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
|
simapp.SimulationOperations(app, app.AppCodec(), config),
|
|
app.ModuleAccountAddrs(),
|
|
config,
|
|
app.AppCodec(),
|
|
)
|
|
|
|
// export state and simParams before the simulation error is checked
|
|
err = simapp.CheckExportSimulation(app, config, simParams)
|
|
require.NoError(t, err)
|
|
require.NoError(t, simErr)
|
|
|
|
if config.Commit {
|
|
simapp.PrintStats(db)
|
|
}
|
|
|
|
if stopEarly {
|
|
fmt.Println("can't export or import a zero-validator genesis, exiting test...")
|
|
return
|
|
}
|
|
|
|
fmt.Printf("exporting genesis...\n")
|
|
|
|
exported, err := app.ExportAppStateAndValidators(true, []string{})
|
|
require.NoError(t, err)
|
|
|
|
fmt.Printf("importing genesis...\n")
|
|
|
|
_, newDB, newDir, _, _, err := simapp.SetupSimulation("leveldb-app-sim-2", "Simulation-2")
|
|
require.NoError(t, err, "simulation setup failed")
|
|
|
|
defer func() {
|
|
require.NoError(t, newDB.Close())
|
|
require.NoError(t, os.RemoveAll(newDir))
|
|
}()
|
|
|
|
newApp, err := NewSimApp(log.NewNopLogger(), newDB)
|
|
require.Equal(t, appName, newApp.Name())
|
|
require.NoError(t, err)
|
|
|
|
newApp.InitChain(abci.RequestInitChain{
|
|
ChainId: SimAppChainID,
|
|
AppStateBytes: exported.AppState,
|
|
})
|
|
|
|
_, _, err = simulation.SimulateFromSeed(
|
|
t,
|
|
os.Stdout,
|
|
newApp.BaseApp,
|
|
StateFn(app.AppCodec(), app.SimulationManager()),
|
|
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
|
simapp.SimulationOperations(newApp, newApp.AppCodec(), config),
|
|
app.ModuleAccountAddrs(),
|
|
config,
|
|
app.AppCodec(),
|
|
)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// TODO: Make another test for the fuzzer itself, which just has noOp txs
|
|
// and doesn't depend on the application.
|
|
func TestAppStateDeterminism(t *testing.T) {
|
|
if !simapp.FlagEnabledValue {
|
|
t.Skip("skipping application simulation")
|
|
}
|
|
|
|
config := simapp.NewConfigFromFlags()
|
|
config.InitialBlockHeight = 1
|
|
config.ExportParamsPath = ""
|
|
config.OnOperation = false
|
|
config.AllInvariants = false
|
|
config.ChainID = SimAppChainID
|
|
|
|
numSeeds := 3
|
|
numTimesToRunPerSeed := 5
|
|
appHashList := make([]json.RawMessage, numTimesToRunPerSeed)
|
|
|
|
for i := 0; i < numSeeds; i++ {
|
|
config.Seed = rand.Int63()
|
|
|
|
for j := 0; j < numTimesToRunPerSeed; j++ {
|
|
var logger log.Logger
|
|
if simapp.FlagVerboseValue {
|
|
logger = log.TestingLogger()
|
|
} else {
|
|
logger = log.NewNopLogger()
|
|
}
|
|
|
|
db := dbm.NewMemDB()
|
|
app, err := NewSimApp(logger, db)
|
|
require.NoError(t, err)
|
|
|
|
fmt.Printf(
|
|
"running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n",
|
|
config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed,
|
|
)
|
|
|
|
_, _, err = simulation.SimulateFromSeed(
|
|
t,
|
|
os.Stdout,
|
|
app.BaseApp,
|
|
StateFn(app.AppCodec(), app.SimulationManager()),
|
|
RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
|
simapp.SimulationOperations(app, app.AppCodec(), config),
|
|
app.ModuleAccountAddrs(),
|
|
config,
|
|
app.AppCodec(),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
if config.Commit {
|
|
simapp.PrintStats(db)
|
|
}
|
|
|
|
appHash := app.LastCommitID().Hash
|
|
appHashList[j] = appHash
|
|
|
|
if j != 0 {
|
|
require.Equal(
|
|
t, string(appHashList[0]), string(appHashList[j]),
|
|
"non-determinism in seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|