Skip to content

Commit

Permalink
test: add simulation test app-import-export
Browse files Browse the repository at this point in the history
  • Loading branch information
Lockwarr committed Feb 15, 2023
1 parent b435c00 commit c70d933
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 14 deletions.
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ LEDGER_ENABLED ?= true
VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//')
COMMIT := $(shell git log -1 --format='%H')
NOLUS_BINARY=nolusd
BINDIR ?= $(GOPATH)/bin
FUZZ_NUM_SEEDS ?= 2
FUZZ_NUM_RUNS_PER_SEED ?= 3
FUZZ_NUM_BLOCKS ?= 100
Expand Down Expand Up @@ -152,6 +153,12 @@ test-fuzz:
-NumBlocks=$(FUZZ_NUM_BLOCKS) -BlockSize=$(FUZZ_BLOCK_SIZE) -Commit=true -Period=0 -v \
-NumSeeds=$(FUZZ_NUM_SEEDS) -NumTimesToRunPerSeed=$(FUZZ_NUM_RUNS_PER_SEED) -timeout 24h

test-sim-import-export:
go get github.com/cosmos/tools/cmd/runsim
go install github.com/cosmos/tools/cmd/runsim
@echo "Running application import/export simulation. This may take several minutes..."
@$(BINDIR)/runsim -Jobs=4 -SimAppPkg=./app -ExitOnFail 10 5 TestAppImportExport

test-unit-cosmos:
./scripts/test/run-test-unit-cosmos.sh >&2

Expand Down
14 changes: 14 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app

import (
"fmt"
"io"
"net/http"
"os"
Expand Down Expand Up @@ -102,6 +103,7 @@ import (

"github.com/CosmWasm/wasmd/x/wasm"
wasmclient "github.com/CosmWasm/wasmd/x/wasm/client"
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"

"github.com/neutron-org/neutron/x/contractmanager"
Expand Down Expand Up @@ -770,6 +772,18 @@ func New(
app.SetBeginBlocker(app.BeginBlocker)
app.SetInitChainer(app.InitChainer)

// must be before Loading version
// requires the snapshot store to be created and registered as a BaseAppOption
// see cmd/wasmd/root.go: 206 - 214 approx
if manager := app.SnapshotManager(); manager != nil {
err := manager.RegisterExtensions(
wasmkeeper.NewWasmSnapshotter(app.CommitMultiStore(), &app.WasmKeeper),
)
if err != nil {
panic(fmt.Errorf("failed to register snapshot extension: %s", err))
}
}

if loadLatest {
if err := app.LoadLatestVersion(); err != nil {
tmos.Exit(err.Error())
Expand Down
220 changes: 206 additions & 14 deletions app/sim_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app_test
package app

import (
"encoding/json"
Expand All @@ -9,26 +9,44 @@ import (
"os"
"path/filepath"
"testing"
"time"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/simapp/helpers"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/kv"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
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"
paramstypes "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/v3/modules/apps/transfer/types"
ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host"
"github.com/stretchr/testify/require"
"github.com/tendermint/spm/cosmoscmd"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmdb "github.com/tendermint/tm-db"

"github.com/CosmWasm/wasmd/x/wasm"
wasmsim "github.com/CosmWasm/wasmd/x/wasm/simulation"
"github.com/Nolus-Protocol/nolus-core/app"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"

"github.com/Nolus-Protocol/nolus-core/app/params"
minttypes "github.com/Nolus-Protocol/nolus-core/x/mint/types"
taxtypes "github.com/Nolus-Protocol/nolus-core/x/tax/types"

feetypes "github.com/neutron-org/neutron/x/feerefunder/types"
)

var (
Expand All @@ -42,6 +60,18 @@ func init() {
flag.IntVar(&NumTimesToRunPerSeed, "NumTimesToRunPerSeed", 5, "number of time to run the simulation per seed")
}

type StoreKeysPrefixes struct {
A sdk.StoreKey
B sdk.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()
}

func interBlockCacheOpt() func(*baseapp.BaseApp) {
return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager())
}
Expand Down Expand Up @@ -91,13 +121,13 @@ func TestAppStateDeterminism(t *testing.T) {
}

db := tmdb.NewMemDB()
newApp := app.New(logger, db, nil, true, map[int64]bool{}, app.DefaultNodeHome, simapp.FlagPeriodValue, cosmoscmd.MakeEncodingConfig(app.ModuleBasics), simapp.EmptyAppOptions{}, interBlockCacheOpt())
newApp := New(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, cosmoscmd.MakeEncodingConfig(ModuleBasics), simapp.EmptyAppOptions{}, interBlockCacheOpt())
params.SetAddressPrefixes()
ctx := newApp.(*app.App).BaseApp.NewUncachedContext(true, tmproto.Header{})
newApp.(*app.App).TaxKeeper.SetParams(ctx, taxtypes.DefaultParams())
newApp.(*app.App).MintKeeper.SetParams(ctx, minttypes.DefaultParams())
newApp.(*app.App).AccountKeeper.SetParams(ctx, authtypes.DefaultParams())
newApp.(*app.App).BankKeeper.SetParams(ctx, banktypes.DefaultParams())
ctx := newApp.(*App).BaseApp.NewUncachedContext(true, tmproto.Header{})
newApp.(*App).TaxKeeper.SetParams(ctx, taxtypes.DefaultParams())
newApp.(*App).MintKeeper.SetParams(ctx, minttypes.DefaultParams())
newApp.(*App).AccountKeeper.SetParams(ctx, authtypes.DefaultParams())
newApp.(*App).BankKeeper.SetParams(ctx, banktypes.DefaultParams())

fmt.Printf(
"running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n",
Expand All @@ -107,21 +137,21 @@ func TestAppStateDeterminism(t *testing.T) {
_, _, err := simulation.SimulateFromSeed(
t,
os.Stdout,
newApp.(*app.App).BaseApp,
simapp.AppStateFn(newApp.(*app.App).AppCodec(), newApp.(*app.App).SimulationManager()),
newApp.(*App).BaseApp,
simapp.AppStateFn(newApp.(*App).AppCodec(), newApp.(*App).SimulationManager()),
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
simapp.SimulationOperations(newApp.(*app.App), newApp.(*app.App).AppCodec(), config),
newApp.(*app.App).BlockedAddrs(),
simapp.SimulationOperations(newApp.(*App), newApp.(*App).AppCodec(), config),
newApp.(*App).BlockedAddrs(),
config,
newApp.(*app.App).AppCodec(),
newApp.(*App).AppCodec(),
)
require.NoError(t, err)

if config.Commit {
simapp.PrintStats(db)
}

appHash := newApp.(*app.App).LastCommitID().Hash
appHash := newApp.(*App).LastCommitID().Hash
appHashList[j] = appHash

if j != 0 {
Expand All @@ -133,3 +163,165 @@ func TestAppStateDeterminism(t *testing.T) {
}
}
}

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")

defer func() {
db.Close()
require.NoError(t, os.RemoveAll(dir))
}()

encConf := cosmoscmd.MakeEncodingConfig(ModuleBasics)
nolusApp := New(logger, db, nil, true, map[int64]bool{}, dir, simapp.FlagPeriodValue, encConf, simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
require.Equal(t, Name, nolusApp.(*App).Name())

// Run randomized simulation
_, simParams, simErr := simulation.SimulateFromSeed(
t,
os.Stdout,
nolusApp.(*App).BaseApp,
AppStateFn(nolusApp.(*App).AppCodec(), nolusApp.(*App).SimulationManager()),
simtypes.RandomAccounts,
simapp.SimulationOperations(nolusApp.(*App), nolusApp.(*App).AppCodec(), config),
nolusApp.(*App).ModuleAccountAddrs(),
config,
nolusApp.(*App).AppCodec(),
)

// export state and simParams before the simulation error is checked
err = simapp.CheckExportSimulation(nolusApp.(*App), config, simParams)
require.NoError(t, err)
require.NoError(t, simErr)

if config.Commit {
simapp.PrintStats(db)
}

t.Log("exporting genesis...")

exported, err := nolusApp.ExportAppStateAndValidators(false, []string{})
require.NoError(t, err)

t.Log("importing genesis...")

_, newDB, newDir, _, _, err := SetupSimulation("leveldb-app-sim-2", "Simulation-2")
require.NoError(t, err, "simulation setup failed")

defer func() {
newDB.Close()
require.NoError(t, os.RemoveAll(newDir))
}()
newNolusApp := New(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, cosmoscmd.MakeEncodingConfig(ModuleBasics), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
require.Equal(t, Name, newNolusApp.(*App).Name())
// newApp := NewWasmApp(logger, newDB, nil, true, map[int64]bool{}, newDir, simapp.FlagPeriodValue, encConf, wasm.EnableAllProposals, EmptyBaseAppOptions{}, nil, fauxMerkleModeOpt)

var genesisState GenesisState
err = json.Unmarshal(exported.AppState, &genesisState)
require.NoError(t, err)

ctxA := nolusApp.(*App).NewContext(true, tmproto.Header{Height: nolusApp.(*App).LastBlockHeight()})
ctxB := newNolusApp.(*App).NewContext(true, tmproto.Header{Height: nolusApp.(*App).LastBlockHeight()})
newNolusApp.(*App).mm.InitGenesis(ctxB, nolusApp.(*App).AppCodec(), genesisState)
newNolusApp.(*App).StoreConsensusParams(ctxB, exported.ConsensusParams)

t.Log("comparing stores...")

storeKeysPrefixes := []StoreKeysPrefixes{
{nolusApp.(*App).keys[authtypes.StoreKey], newNolusApp.(*App).keys[authtypes.StoreKey], [][]byte{}},
{
nolusApp.(*App).keys[stakingtypes.StoreKey], newNolusApp.(*App).keys[stakingtypes.StoreKey],
[][]byte{
stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey,
stakingtypes.HistoricalInfoKey, stakingtypes.UnbondingIdKey, stakingtypes.UnbondingIndexKey, stakingtypes.UnbondingTypeKey, stakingtypes.ValidatorUpdatesKey,
},
},
{nolusApp.(*App).keys[slashingtypes.StoreKey], newNolusApp.(*App).keys[slashingtypes.StoreKey], [][]byte{}},
{nolusApp.(*App).keys[minttypes.StoreKey], newNolusApp.(*App).keys[minttypes.StoreKey], [][]byte{}},
{nolusApp.(*App).keys[distrtypes.StoreKey], newNolusApp.(*App).keys[distrtypes.StoreKey], [][]byte{}},
{nolusApp.(*App).keys[banktypes.StoreKey], newNolusApp.(*App).keys[banktypes.StoreKey], [][]byte{banktypes.BalancesPrefix}},
{nolusApp.(*App).keys[paramstypes.StoreKey], newNolusApp.(*App).keys[paramstypes.StoreKey], [][]byte{}},
{nolusApp.(*App).keys[govtypes.StoreKey], newNolusApp.(*App).keys[govtypes.StoreKey], [][]byte{}},
{nolusApp.(*App).keys[evidencetypes.StoreKey], newNolusApp.(*App).keys[evidencetypes.StoreKey], [][]byte{}},
{nolusApp.(*App).keys[capabilitytypes.StoreKey], newNolusApp.(*App).keys[capabilitytypes.StoreKey], [][]byte{}},
{nolusApp.(*App).keys[ibchost.StoreKey], newNolusApp.(*App).keys[ibchost.StoreKey], [][]byte{}},
{nolusApp.(*App).keys[ibctransfertypes.StoreKey], newNolusApp.(*App).keys[ibctransfertypes.StoreKey], [][]byte{}},
{nolusApp.(*App).keys[feetypes.StoreKey], newNolusApp.(*App).keys[feetypes.StoreKey], [][]byte{}},
// {nolusApp.(*App).keys[wasm.StoreKey], newNolusApp.(*App).keys[wasm.StoreKey], [][]byte{}},
}

// delete persistent tx counter value
ctxA.KVStore(nolusApp.(*App).keys[wasm.StoreKey]).Delete(wasmtypes.TXCounterPrefix)

// diff both stores
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")

t.Logf("compared %d different key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B)
require.Len(t, failedKVAs, 0, GetSimulationLog(skp.A.Name(), nolusApp.(*App).SimulationManager().StoreDecoders, failedKVAs, failedKVBs))
}
}

// SetupSimulation wraps simapp.SetupSimulation in order to create any export directory if they do not exist yet
func SetupSimulation(dirPrefix, dbName string) (simtypes.Config, tmdb.DB, string, log.Logger, bool, error) {
config, db, dir, logger, skip, err := simapp.SetupSimulation(dirPrefix, dbName)
if err != nil {
return simtypes.Config{}, nil, "", nil, false, err
}

paths := []string{config.ExportParamsPath, config.ExportStatePath, config.ExportStatsPath}
for _, path := range paths {
if len(path) == 0 {
continue
}

path = filepath.Dir(path)
if _, err := os.Stat(path); os.IsNotExist(err) {
if err := os.MkdirAll(path, os.ModePerm); err != nil {
panic(err)
}
}
}

return config, db, dir, logger, skip, err
}

// GetSimulationLog unmarshals the KVPair's Value to the corresponding type based on the
// each's module store key and the prefix bytes of the KVPair's key.
func GetSimulationLog(storeName string, sdr sdk.StoreDecoderRegistry, kvAs, kvBs []kv.Pair) (log string) {
for i := 0; i < len(kvAs); i++ {
if len(kvAs[i].Value) == 0 && len(kvBs[i].Value) == 0 {
// skip if the value doesn't have any bytes
continue
}

decoder, ok := sdr[storeName]
if ok {
log += decoder(kvAs[i], kvBs[i])
} else {
log += fmt.Sprintf("store A %q => %q\nstore B %q => %q\n", kvAs[i].Key, kvAs[i].Value, kvBs[i].Key, kvBs[i].Value)
}
}

return log
}

// AppStateFn returns the initial application state using a genesis or the simulation parameters.
// It panics if the user provides files for both of them.
// If a file is not given for the genesis or the sim params, it creates a randomized one.
func AppStateFn(codec codec.Codec, manager *module.SimulationManager) simtypes.AppStateFn {
// quick hack to setup app state genesis with our app modules
simapp.ModuleBasics = ModuleBasics
if simapp.FlagGenesisTimeValue == 0 { // always set to have a block time
simapp.FlagGenesisTimeValue = time.Now().Unix()
}
return simapp.AppStateFn(codec, manager)
}

0 comments on commit c70d933

Please sign in to comment.