From 7827aee97b2cde997fef64a7f56157f5bcf47d56 Mon Sep 17 00:00:00 2001 From: Alexander Peters Date: Tue, 25 Mar 2025 18:45:55 +0100 Subject: [PATCH] fix(sims): Minor fixes (#24095) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Alex | Interchain Labs --- Makefile | 26 +++++++++++++------------- baseapp/test_helpers.go | 6 ++++++ simapp/sim_test.go | 15 ++++++++++++++- testutils/sims/runner.go | 20 ++++++-------------- x/simulation/simulate.go | 5 +++-- x/slashing/simulation/operations.go | 14 ++++++++------ x/staking/simulation/operations.go | 3 +++ 7 files changed, 53 insertions(+), 36 deletions(-) diff --git a/Makefile b/Makefile index dfa63fad0e..cba68c98f6 100644 --- a/Makefile +++ b/Makefile @@ -264,7 +264,7 @@ endif test-sim-nondeterminism: @echo "Running non-determinism test..." - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout=30m -tags='sims' -run TestAppStateDeterminism \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -timeout=30m -tags='sims' -run TestAppStateDeterminism \ -NumBlocks=100 -BlockSize=200 -Period=0 # Requires an exported plugin. See store/streaming/README.md for documentation. @@ -278,39 +278,39 @@ test-sim-nondeterminism: # make test-sim-nondeterminism-streaming test-sim-nondeterminism-streaming: @echo "Running non-determinism-streaming test..." - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout=30m -tags='sims' -run TestAppStateDeterminism \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -timeout=30m -tags='sims' -run TestAppStateDeterminism \ -NumBlocks=100 -BlockSize=200 -Period=0 -EnableStreaming=true test-sim-custom-genesis-fast: @echo "Running custom genesis simulation..." @echo "By default, ${HOME}/.simapp/config/genesis.json will be used." - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout=30m -tags='sims' -run TestFullAppSimulation -Genesis=${HOME}/.simapp/config/genesis.json \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -timeout=30m -tags='sims' -run TestFullAppSimulation -Genesis=${HOME}/.simapp/config/genesis.json \ -NumBlocks=100 -BlockSize=200 -Seed=99 -Period=5 -SigverifyTx=false test-sim-import-export: @echo "Running application import/export simulation. This may take several minutes..." - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout 20m -tags='sims' -run TestAppImportExport \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -timeout 20m -tags='sims' -run TestAppImportExport \ -NumBlocks=50 -Period=5 test-sim-after-import: @echo "Running application simulation-after-import. This may take several minutes..." - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout 30m -tags='sims' -run TestAppSimulationAfterImport \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -timeout 30m -tags='sims' -run TestAppSimulationAfterImport \ -NumBlocks=50 -Period=5 test-sim-custom-genesis-multi-seed: @echo "Running multi-seed custom genesis simulation..." @echo "By default, ${HOME}/.simapp/config/genesis.json will be used." - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout 30m -tags='sims' -run TestFullAppSimulation -Genesis=${HOME}/.simapp/config/genesis.json \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -timeout 30m -tags='sims' -run TestFullAppSimulation -Genesis=${HOME}/.simapp/config/genesis.json \ -NumBlocks=400 -Period=5 test-sim-multi-seed-long: @echo "Running long multi-seed application simulation. This may take awhile!" - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout=1h -tags='sims' -run TestFullAppSimulation \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -timeout=1h -tags='sims' -run TestFullAppSimulation \ -NumBlocks=500 -Period=50 test-sim-multi-seed-short: @echo "Running short multi-seed application simulation. This may take awhile!" - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout 30m -tags='sims' -run TestFullAppSimulation \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -timeout 30m -tags='sims' -run TestFullAppSimulation \ -NumBlocks=50 -Period=10 .PHONY: \ @@ -331,12 +331,12 @@ SIM_COMMIT ?= true test-sim-fuzz: @echo "Running application fuzz for numBlocks=2, blockSize=20. This may take awhile!" #ld flags are a quick fix to make it work on current osx - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -json -tags='sims' -ldflags="-extldflags=-Wl,-ld_classic" -timeout=60m -fuzztime=60m -run=^$$ -fuzz=FuzzFullAppSimulation -GenesisTime=1714720615 -NumBlocks=2 -BlockSize=20 + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -json -tags='sims' -ldflags="-extldflags=-Wl,-ld_classic" -timeout=60m -fuzztime=60m -run=^$$ -fuzz=FuzzFullAppSimulation -GenesisTime=1714720615 -NumBlocks=2 -BlockSize=20 #? test-sim-benchmark: Run benchmark test for simapp test-sim-benchmark: @echo "Running application benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -tags='sims' -run=^$$ $(.) -bench ^BenchmarkFullAppSimulation$$ \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -tags='sims' -run=^$$ $(.) -bench ^BenchmarkFullAppSimulation$$ \ -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -Seed=57 -timeout 30m # Requires an exported plugin. See store/streaming/README.md for documentation. @@ -350,12 +350,12 @@ test-sim-benchmark: # make test-sim-benchmark-streaming test-sim-benchmark-streaming: @echo "Running application benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -run=^$$ $(.) -bench ^BenchmarkFullAppSimulation$$ \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -run=^$$ $(.) -bench ^BenchmarkFullAppSimulation$$ \ -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -timeout 24h -EnableStreaming=true test-sim-profile: @echo "Running application benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -benchmem -run=^$$ $(.) -bench ^BenchmarkFullAppSimulation$$ \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -benchmem -run=^$$ $(.) -bench ^BenchmarkFullAppSimulation$$ \ -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out # Requires an exported plugin. See store/streaming/README.md for documentation. @@ -369,7 +369,7 @@ test-sim-profile: # make test-sim-profile-streaming test-sim-profile-streaming: @echo "Running application benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" - @cd ${CURRENT_DIR}/simapp && go test -mod=readonly -benchmem -run=^$$ $(.) -bench ^BenchmarkFullAppSimulation$$ \ + @cd ${CURRENT_DIR}/simapp && go test -failfast -mod=readonly -benchmem -run=^$$ $(.) -bench ^BenchmarkFullAppSimulation$$ \ -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out -EnableStreaming=true .PHONY: test-sim-profile test-sim-benchmark test-sim-fuzz diff --git a/baseapp/test_helpers.go b/baseapp/test_helpers.go index a8f7853f22..f6735dcee1 100644 --- a/baseapp/test_helpers.go +++ b/baseapp/test_helpers.go @@ -51,6 +51,12 @@ func (app *BaseApp) SimTxFinalizeBlock(txEncoder sdk.TxEncoder, tx sdk.Tx) (sdk. return gasInfo, result, err } +// SimWriteState is an entrypoint for simulations only. They are not executed during the normal ABCI finalize +// block step but later. Therefor an extra call to the root multi-store (app.cms) is required to write the changes. +func (app *BaseApp) SimWriteState() { + app.finalizeBlockState.ms.Write() +} + // NewContextLegacy returns a new sdk.Context with the provided header func (app *BaseApp) NewContextLegacy(isCheckTx bool, header cmtproto.Header) sdk.Context { if isCheckTx { diff --git a/simapp/sim_test.go b/simapp/sim_test.go index 3a08fb16e7..6ea173b5cc 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -180,7 +180,7 @@ func TestAppStateDeterminism(t *testing.T) { "streaming.abci.stop-node-on-err": true, } others := appOpts - appOpts = sims.AppOptionsFn(func(k string) any { + appOpts = appOptionsFn(func(k string) any { if v, ok := m[k]; ok { return v } @@ -262,6 +262,19 @@ func AssertEqualStores(t *testing.T, app, newApp ComparableStoreApp, storeDecode } } +// appOptionsFn is an adapter to the single method AppOptions interface +type appOptionsFn func(string) any + +func (f appOptionsFn) Get(k string) any { + return f(k) +} + +// 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 FuzzFullAppSimulation(f *testing.F) { f.Fuzz(func(t *testing.T, rawSeed []byte) { if len(rawSeed) < 8 { diff --git a/testutils/sims/runner.go b/testutils/sims/runner.go index b8bf9101f4..afc7484a6a 100644 --- a/testutils/sims/runner.go +++ b/testutils/sims/runner.go @@ -3,6 +3,7 @@ package sims import ( "fmt" "io" + "os" "path/filepath" "strings" "testing" @@ -49,6 +50,7 @@ type SimulationApp interface { SetNotSigverifyTx() GetBaseApp() *baseapp.BaseApp TxConfig() client.TxConfig + Close() error } // Run is a helper function that runs a simulation test with the given parameters. @@ -141,6 +143,7 @@ func RunWithSeeds[T SimulationApp]( for _, step := range postRunActions { step(t, testInstance) } + require.NoError(t, app.Close()) }) } } @@ -174,6 +177,8 @@ func NewSimulationAppInstance[T SimulationApp]( ) TestInstance[T] { t.Helper() workDir := t.TempDir() + require.NoError(t, os.Mkdir(filepath.Join(workDir, "data"), 0o755)) + dbDir := filepath.Join(workDir, "leveldb-app-sim") var logger log.Logger if cli.FlagVerboseValue { @@ -186,7 +191,7 @@ func NewSimulationAppInstance[T SimulationApp]( db, err := dbm.NewDB("Simulation", dbm.BackendType(tCfg.DBBackend), dbDir) require.NoError(t, err) t.Cleanup(func() { - require.NoError(t, db.Close()) + _ = db.Close() // ensure db is closed }) appOptions := make(simtestutil.AppOptionsMap) appOptions[flags.FlagHome] = workDir @@ -220,16 +225,3 @@ func WriteToDebugLog(logger log.Logger) io.Writer { return len(p), nil }) } - -// AppOptionsFn is an adapter to the single method AppOptions interface -type AppOptionsFn func(string) any - -func (f AppOptionsFn) Get(k string) any { - return f(k) -} - -// FauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of -// an IAVLStore for faster simulation speed. -func FauxMerkleModeOpt(bapp *baseapp.BaseApp) { - bapp.SetFauxMerkleMode() -} diff --git a/x/simulation/simulate.go b/x/simulation/simulate.go index 44133bdca7..92df42f529 100644 --- a/x/simulation/simulate.go +++ b/x/simulation/simulate.go @@ -243,6 +243,7 @@ func SimulateFromSeedX( proposerAddress = validators.randomProposer(r) if config.Commit { + app.SimWriteState() if _, err := app.Commit(); err != nil { return params, fmt.Errorf("commit failed at height %d: %w", blockHeight, err) } @@ -342,10 +343,10 @@ func createBlockSimulator(tb testing.TB, printProgress bool, w io.Writer, params if err != nil { logWriter.PrintLogs() - tb.Fatalf(`error on block %d/%d, operation (%d/%d) from x/%s: + tb.Fatalf(`error on block %d/%d, operation (%d/%d) from x/%s for msg %q: %v Comment: %s`, - header.Height, config.NumBlocks, opCount, blocksize, opMsg.Route, err, opMsg.Comment) + header.Height, config.NumBlocks, opCount, blocksize, opMsg.Route, opMsg.Name, err, opMsg.Comment) } queueOperations(operationQueue, timeOperationQueue, futureOps) diff --git a/x/slashing/simulation/operations.go b/x/slashing/simulation/operations.go index 280e06f7ee..65fc2ca9d3 100644 --- a/x/slashing/simulation/operations.go +++ b/x/slashing/simulation/operations.go @@ -90,20 +90,20 @@ func SimulateMsgUnjail( consAddr, err := validator.GetConsAddr() if err != nil { - return simtypes.NoOpMsg(types.ModuleName, msgType, "unable to get validator consensus key"), nil, err + return simtypes.NoOpMsg(types.ModuleName, msgType, "unable to get validator consensus key"), nil, nil } info, err := k.GetValidatorSigningInfo(ctx, consAddr) if err != nil { - return simtypes.NoOpMsg(types.ModuleName, msgType, "unable to find validator signing info"), nil, err // skip + return simtypes.NoOpMsg(types.ModuleName, msgType, "unable to find validator signing info"), nil, nil } selfDel, err := sk.Delegation(ctx, simAccount.Address, bz) if err != nil { - return simtypes.NoOpMsg(types.ModuleName, msgType, "unable to get self delegation"), nil, err + return simtypes.NoOpMsg(types.ModuleName, msgType, "unable to get self delegation"), nil, nil } if selfDel == nil { - return simtypes.NoOpMsg(types.ModuleName, msgType, "self delegation is nil"), nil, nil // skip + return simtypes.NoOpMsg(types.ModuleName, msgType, "self delegation is nil"), nil, nil } account := ak.GetAccount(ctx, sdk.AccAddress(bz)) @@ -111,7 +111,7 @@ func SimulateMsgUnjail( fees, err := simtypes.RandomFees(r, ctx, spendable) if err != nil { - return simtypes.NoOpMsg(types.ModuleName, msgType, "unable to generate fees"), nil, err + return simtypes.NoOpMsg(types.ModuleName, msgType, "unable to generate fees"), nil, nil } msg := types.NewMsgUnjail(validator.GetOperator()) @@ -139,6 +139,7 @@ func SimulateMsgUnjail( // - self delegation too low if info.Tombstoned || ctx.BlockHeader().Time.Before(info.JailedUntil) || + selfDel.GetShares().IsNil() || validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) { if res != nil && err == nil { if info.Tombstoned { @@ -147,7 +148,8 @@ func SimulateMsgUnjail( if ctx.BlockHeader().Time.Before(info.JailedUntil) { return simtypes.NewOperationMsg(msg, true, ""), nil, errors.New("validator unjailed while validator still in jail period") } - if validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) { + if selfDel.GetShares().IsNil() || + validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) { return simtypes.NewOperationMsg(msg, true, ""), nil, errors.New("validator unjailed even though self-delegation too low") } } diff --git a/x/staking/simulation/operations.go b/x/staking/simulation/operations.go index 0776de5dbc..7da52f4d54 100644 --- a/x/staking/simulation/operations.go +++ b/x/staking/simulation/operations.go @@ -649,6 +649,9 @@ func SimulateMsgBeginRedelegate( if redAmt.IsZero() { return simtypes.NoOpMsg(types.ModuleName, msgType, "amount is zero"), nil, nil } + if totalBond.Sub(redAmt).IsZero() { + return simtypes.NoOpMsg(types.ModuleName, msgType, "can not redelegate all"), nil, nil + } // check if the shares truncate to zero shares, err := srcVal.SharesFromTokens(redAmt)