29d3abcf09
* Reuse cosmos-sdk client library to create keyring Extracted from https://github.com/evmos/ethermint/pull/1168 Cleanup cmd code for easier to migration to cosmos-sdk 0.46 * Update cosmos-sdk v0.46 prepare for implementing cosmos-sdk feemarket and tx prioritization changelog refactor cmd use sdkmath fix lint fix unit tests fix unit test genesis fix unit tests fix unit test env setup fix unit tests fix unit tests register PrivKey impl fix extension options fix lint fix unit tests make HandlerOption.Validate private gofumpt fix msg response decoding fix sim test bump cosmos-sdk version fix sim test sdk 46 fix unit test fix unit tests update ibc-go
372 lines
12 KiB
Go
372 lines
12 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"
|
|
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()
|
|
}
|
|
|
|
// 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 := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
|
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)
|
|
}
|
|
}
|
|
|
|
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 := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
|
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 := NewEthermintApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
|
require.Equal(t, appName, newApp.Name())
|
|
|
|
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 := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
|
require.Equal(t, appName, app.Name())
|
|
|
|
// 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 := NewEthermintApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
|
require.Equal(t, appName, newApp.Name())
|
|
|
|
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 := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, interBlockCacheOpt())
|
|
|
|
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,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|