perf: Replace runsim with Go stdlib testing (#24045)
Co-authored-by: Alex | Interchain Labs <alex@interchainlabs.io>
This commit is contained in:
parent
b7c35f2568
commit
bc57ce3ba7
4
.github/workflows/sims.yml
vendored
4
.github/workflows/sims.yml
vendored
@ -63,9 +63,9 @@ jobs:
|
||||
with:
|
||||
go-version: "1.23"
|
||||
check-latest: true
|
||||
- name: test-sim-nondeterminism
|
||||
- name: test-sim-nondeterminism-streaming
|
||||
run: |
|
||||
make test-sim-nondeterminism
|
||||
make test-sim-nondeterminism-streaming
|
||||
|
||||
test-sim-multi-seed-short:
|
||||
runs-on: depot-ubuntu-22.04-16
|
||||
|
||||
@ -6,6 +6,7 @@ run:
|
||||
- e2e
|
||||
- ledger
|
||||
- test_ledger_mock
|
||||
- sims
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
|
||||
@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### Features
|
||||
|
||||
* (perf)[#24045](https://github.com/cosmos/cosmos-sdk/pull/24045) Sims: Replace runsim command with Go stdlib testing. CLI: `Commit` default true, `Lean`, `SimulateEveryOperation`, `PrintAllInvariants`, `DBBackend` params removed
|
||||
* (crypto/keyring) [#24040](https://github.com/cosmos/cosmos-sdk/pull/24040) Expose the db keyring used in the keystore.
|
||||
* (types) [#23919](https://github.com/cosmos/cosmos-sdk/pull/23919) Add MustValAddressFromBech32 function.
|
||||
* (all) [#23708](https://github.com/cosmos/cosmos-sdk/pull/23708) Add unordered transaction support.
|
||||
|
||||
68
Makefile
68
Makefile
@ -108,9 +108,6 @@ endif
|
||||
|
||||
all: tools build lint test vulncheck
|
||||
|
||||
# The below include contains the tools and runsim targets.
|
||||
include contrib/devtools/Makefile
|
||||
|
||||
###############################################################################
|
||||
### Build ###
|
||||
###############################################################################
|
||||
@ -267,8 +264,8 @@ endif
|
||||
|
||||
test-sim-nondeterminism:
|
||||
@echo "Running non-determinism test..."
|
||||
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -run TestAppStateDeterminism -Enabled=true \
|
||||
-NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h
|
||||
@cd ${CURRENT_DIR}/simapp && go test -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.
|
||||
#
|
||||
@ -281,41 +278,40 @@ 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 -run TestAppStateDeterminism -Enabled=true \
|
||||
-NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h -EnableStreaming=true
|
||||
@cd ${CURRENT_DIR}/simapp && go test -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 -run TestFullAppSimulation -Genesis=${HOME}/.simapp/config/genesis.json \
|
||||
-Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -SigverifyTx=false -v -timeout 24h
|
||||
@cd ${CURRENT_DIR}/simapp && go test -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: runsim
|
||||
test-sim-import-export:
|
||||
@echo "Running application import/export simulation. This may take several minutes..."
|
||||
@cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Jobs=4 -SimAppPkg=. -ExitOnFail 50 5 TestAppImportExport
|
||||
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout 20m -tags='sims' -run TestAppImportExport \
|
||||
-NumBlocks=50 -Period=5
|
||||
|
||||
test-sim-after-import: runsim
|
||||
test-sim-after-import:
|
||||
@echo "Running application simulation-after-import. This may take several minutes..."
|
||||
@cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Jobs=4 -SimAppPkg=. -ExitOnFail 50 5 TestAppSimulationAfterImport
|
||||
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout 30m -tags='sims' -run TestAppSimulationAfterImport \
|
||||
-NumBlocks=50 -Period=5
|
||||
|
||||
test-sim-custom-genesis-multi-seed: runsim
|
||||
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 && $(BINDIR)/runsim -Genesis=${HOME}/.simapp/config/genesis.json -SigverifyTx=false -SimAppPkg=. -ExitOnFail 400 5 TestFullAppSimulation
|
||||
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout 30m -tags='sims' -run TestFullAppSimulation -Genesis=${HOME}/.simapp/config/genesis.json \
|
||||
-NumBlocks=400 -Period=5
|
||||
|
||||
test-sim-multi-seed-long: runsim
|
||||
test-sim-multi-seed-long:
|
||||
@echo "Running long multi-seed application simulation. This may take awhile!"
|
||||
@cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Jobs=4 -SimAppPkg=. -ExitOnFail 500 50 TestFullAppSimulation
|
||||
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout=1h -tags='sims' -run TestFullAppSimulation \
|
||||
-NumBlocks=500 -Period=50
|
||||
|
||||
test-sim-multi-seed-short: runsim
|
||||
test-sim-multi-seed-short:
|
||||
@echo "Running short multi-seed application simulation. This may take awhile!"
|
||||
@cd ${CURRENT_DIR}/simapp && $(BINDIR)/runsim -Jobs=4 -SimAppPkg=. -ExitOnFail 50 10 TestFullAppSimulation
|
||||
|
||||
test-sim-benchmark-invariants:
|
||||
@echo "Running simulation invariant benchmarks..."
|
||||
cd ${CURRENT_DIR}/simapp && @go test -mod=readonly -benchmem -bench=BenchmarkInvariants -run=^$ \
|
||||
-Enabled=true -NumBlocks=1000 -BlockSize=200 \
|
||||
-Period=1 -Commit=true -Seed=57 -v -timeout 24h
|
||||
@cd ${CURRENT_DIR}/simapp && go test -mod=readonly -timeout 30m -tags='sims' -run TestFullAppSimulation \
|
||||
-NumBlocks=50 -Period=10
|
||||
|
||||
.PHONY: \
|
||||
test-sim-nondeterminism \
|
||||
@ -325,17 +321,23 @@ test-sim-import-export \
|
||||
test-sim-after-import \
|
||||
test-sim-custom-genesis-multi-seed \
|
||||
test-sim-multi-seed-short \
|
||||
test-sim-multi-seed-long \
|
||||
test-sim-benchmark-invariants
|
||||
test-sim-multi-seed-long
|
||||
|
||||
SIM_NUM_BLOCKS ?= 500
|
||||
SIM_BLOCK_SIZE ?= 200
|
||||
SIM_COMMIT ?= true
|
||||
|
||||
#? test-sim-fuzz: Run fuzz test for simapp
|
||||
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
|
||||
|
||||
#? 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 -run=^$$ $(.) -bench ^BenchmarkFullAppSimulation$$ \
|
||||
-Enabled=true -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -timeout 24h
|
||||
@cd ${CURRENT_DIR}/simapp && go test -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.
|
||||
#
|
||||
@ -349,12 +351,12 @@ test-sim-benchmark:
|
||||
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$$ \
|
||||
-Enabled=true -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -timeout 24h -EnableStreaming=true
|
||||
-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$$ \
|
||||
-Enabled=true -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out
|
||||
-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.
|
||||
#
|
||||
@ -368,9 +370,9 @@ test-sim-profile:
|
||||
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$$ \
|
||||
-Enabled=true -NumBlocks=$(SIM_NUM_BLOCKS) -BlockSize=$(SIM_BLOCK_SIZE) -Commit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out -EnableStreaming=true
|
||||
-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
|
||||
.PHONY: test-sim-profile test-sim-benchmark test-sim-fuzz
|
||||
|
||||
benchmark:
|
||||
@go test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION)
|
||||
|
||||
@ -1175,3 +1175,8 @@ func (app *BaseApp) Close() error {
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// GetBaseApp returns the pointer to itself.
|
||||
func (app *BaseApp) GetBaseApp() *BaseApp {
|
||||
return app
|
||||
}
|
||||
|
||||
@ -1,69 +0,0 @@
|
||||
###
|
||||
# Find OS and Go environment
|
||||
# GO contains the Go binary
|
||||
# FS contains the OS file separator
|
||||
###
|
||||
ifeq ($(OS),Windows_NT)
|
||||
GO := $(shell where go.exe 2> NUL)
|
||||
FS := "\\"
|
||||
else
|
||||
GO := $(shell command -v go 2> /dev/null)
|
||||
FS := "/"
|
||||
endif
|
||||
|
||||
ifeq ($(GO),)
|
||||
$(error could not find go. Is it in PATH? $(GO))
|
||||
endif
|
||||
|
||||
###############################################################################
|
||||
### Functions ###
|
||||
###############################################################################
|
||||
|
||||
go_get = $(if $(findstring Windows_NT,$(OS)),\
|
||||
IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS) ( mkdir $(GITHUBDIR)$(FS)$(1) ) else (cd .) &\
|
||||
IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS)$(2)$(FS) ( cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2) ) else (cd .) &\
|
||||
,\
|
||||
mkdir -p $(GITHUBDIR)$(FS)$(1) &&\
|
||||
(test ! -d $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2)) || true &&\
|
||||
)\
|
||||
cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && git fetch origin && git checkout -q $(3)
|
||||
|
||||
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
mkfile_dir := $(shell cd $(shell dirname $(mkfile_path)); pwd)
|
||||
|
||||
|
||||
###############################################################################
|
||||
### Tools ###
|
||||
###############################################################################
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
BIN ?= $(PREFIX)/bin
|
||||
UNAME_S ?= $(shell uname -s)
|
||||
UNAME_M ?= $(shell uname -m)
|
||||
|
||||
GOPATH ?= $(shell $(GO) env GOPATH)
|
||||
GITHUBDIR := $(GOPATH)$(FS)src$(FS)github.com
|
||||
|
||||
BUF_VERSION ?= 0.11.0
|
||||
|
||||
TOOLS_DESTDIR ?= $(GOPATH)/bin
|
||||
RUNSIM = $(TOOLS_DESTDIR)/runsim
|
||||
|
||||
tools: tools-stamp
|
||||
tools-stamp: runsim
|
||||
# Create dummy file to satisfy dependency and avoid
|
||||
# rebuilding when this Makefile target is hit twice
|
||||
# in a row.
|
||||
touch $@
|
||||
|
||||
# Install the runsim binary
|
||||
runsim: $(RUNSIM)
|
||||
$(RUNSIM):
|
||||
@echo "Installing runsim..."
|
||||
@go install github.com/cosmos/tools/cmd/runsim@v1.0.0
|
||||
|
||||
tools-clean:
|
||||
rm -f $(GOLANGCI_LINT) $(RUNSIM)
|
||||
rm -f tools-stamp
|
||||
|
||||
.PHONY: tools-clean runsim
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
LINT_TAGS="e2e,ledger,test_ledger_mock,system_test"
|
||||
LINT_TAGS="e2e,ledger,test_ledger_mock,system_test,sims"
|
||||
export LINT_TAGS
|
||||
|
||||
REPO_ROOT="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )/.." &> /dev/null && pwd )"
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
set -e -o pipefail
|
||||
|
||||
|
||||
LINT_TAGS="e2e,ledger,test_ledger_mock,system_test"
|
||||
LINT_TAGS="e2e,ledger,test_ledger_mock,system_test,sims"
|
||||
export LINT_TAGS
|
||||
|
||||
REPO_ROOT="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )/.." &> /dev/null && pwd )"
|
||||
|
||||
@ -1,38 +1,35 @@
|
||||
//go:build sims
|
||||
|
||||
package simapp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
"github.com/cosmos/cosmos-sdk/testutils/sims"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
simcli "github.com/cosmos/cosmos-sdk/x/simulation/client/cli"
|
||||
)
|
||||
|
||||
var FlagEnableBenchStreamingValue bool
|
||||
|
||||
// Get flags every time the simulator is run
|
||||
func init() {
|
||||
flag.BoolVar(&FlagEnableBenchStreamingValue, "EnableStreaming", false, "Enable streaming service")
|
||||
}
|
||||
|
||||
// Profile with:
|
||||
// /usr/local/go/bin/go test -benchmem -run=^$ cosmossdk.io/simapp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out
|
||||
func BenchmarkFullAppSimulation(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
config := simcli.NewConfigFromFlags()
|
||||
config.ChainID = SimAppChainID
|
||||
config.ChainID = sims.SimAppChainID
|
||||
|
||||
db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "goleveldb-app-sim", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue)
|
||||
db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "goleveldb-app-sim", "Simulation", simcli.FlagVerboseValue, true)
|
||||
if err != nil {
|
||||
b.Fatalf("simulation setup failed: %s", err.Error())
|
||||
}
|
||||
@ -47,86 +44,24 @@ func BenchmarkFullAppSimulation(b *testing.B) {
|
||||
}()
|
||||
|
||||
appOptions := viper.New()
|
||||
if FlagEnableStreamingValue {
|
||||
m := make(map[string]interface{})
|
||||
m["streaming.abci.keys"] = []string{"*"}
|
||||
m["streaming.abci.plugin"] = "abci_v1"
|
||||
m["streaming.abci.stop-node-on-err"] = true
|
||||
for key, value := range m {
|
||||
appOptions.SetDefault(key, value)
|
||||
}
|
||||
}
|
||||
appOptions.SetDefault(flags.FlagHome, DefaultNodeHome)
|
||||
appOptions.SetDefault(server.FlagInvCheckPeriod, simcli.FlagPeriodValue)
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, appOptions, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID))
|
||||
app := NewSimApp(logger, db, nil, true, appOptions, interBlockCacheOpt(), baseapp.SetChainID(sims.SimAppChainID))
|
||||
|
||||
// run randomized simulation
|
||||
_, simParams, simErr := simulation.SimulateFromSeed(
|
||||
simParams, simErr := simulation.SimulateFromSeedX(
|
||||
b,
|
||||
log.NewNopLogger(),
|
||||
os.Stdout,
|
||||
app.BaseApp,
|
||||
simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()),
|
||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simtestutil.SimulationOperations(app, app.AppCodec(), config),
|
||||
BlockedAddresses(),
|
||||
config,
|
||||
app.AppCodec(),
|
||||
)
|
||||
|
||||
// export state and simParams before the simulation error is checked
|
||||
if err = simtestutil.CheckExportSimulation(app, config, simParams); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
if simErr != nil {
|
||||
b.Fatal(simErr)
|
||||
}
|
||||
|
||||
if config.Commit {
|
||||
simtestutil.PrintStats(db)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInvariants(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
config := simcli.NewConfigFromFlags()
|
||||
config.ChainID = SimAppChainID
|
||||
|
||||
db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "leveldb-app-invariant-bench", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue)
|
||||
if err != nil {
|
||||
b.Fatalf("simulation setup failed: %s", err.Error())
|
||||
}
|
||||
|
||||
if skip {
|
||||
b.Skip("skipping benchmark application simulation")
|
||||
}
|
||||
|
||||
config.AllInvariants = false
|
||||
|
||||
defer func() {
|
||||
require.NoError(b, db.Close())
|
||||
require.NoError(b, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
appOptions := make(simtestutil.AppOptionsMap, 0)
|
||||
appOptions[flags.FlagHome] = DefaultNodeHome
|
||||
appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, appOptions, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID))
|
||||
|
||||
// run randomized simulation
|
||||
_, simParams, simErr := simulation.SimulateFromSeed(
|
||||
b,
|
||||
os.Stdout,
|
||||
app.BaseApp,
|
||||
simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()),
|
||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simtestutil.SimulationOperations(app, app.AppCodec(), config),
|
||||
simtypes.RandomAccounts,
|
||||
simtestutil.BuildSimulationOperations(app, app.AppCodec(), config, app.txConfig),
|
||||
BlockedAddresses(),
|
||||
config,
|
||||
app.AppCodec(),
|
||||
&simulation.DummyLogWriter{},
|
||||
)
|
||||
|
||||
// export state and simParams before the simulation error is checked
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
//go:build sims
|
||||
|
||||
package simapp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
abci "github.com/cometbft/cometbft/abci/types"
|
||||
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
|
||||
dbm "github.com/cosmos/cosmos-db"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
@ -22,9 +24,10 @@ import (
|
||||
"cosmossdk.io/x/feegrant"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
servertypes "github.com/cosmos/cosmos-sdk/server/types"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
"github.com/cosmos/cosmos-sdk/testutils/sims"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
@ -33,9 +36,6 @@ import (
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
// SimAppChainID hardcoded chainID for simulation
|
||||
const SimAppChainID = "simulation-app"
|
||||
|
||||
var FlagEnableStreamingValue bool
|
||||
|
||||
// Get flags every time the simulator is run
|
||||
@ -44,12 +44,6 @@ func init() {
|
||||
flag.BoolVar(&FlagEnableStreamingValue, "EnableStreaming", false, "Enable streaming service")
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@ -57,150 +51,188 @@ func interBlockCacheOpt() func(*baseapp.BaseApp) {
|
||||
}
|
||||
|
||||
func TestFullAppSimulation(t *testing.T) {
|
||||
config := simcli.NewConfigFromFlags()
|
||||
config.ChainID = SimAppChainID
|
||||
sims.Run(t, NewSimApp, setupStateFactory)
|
||||
}
|
||||
|
||||
db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "leveldb-app-sim", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue)
|
||||
if skip {
|
||||
t.Skip("skipping application simulation")
|
||||
}
|
||||
require.NoError(t, err, "simulation setup failed")
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, db.Close())
|
||||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
appOptions := make(simtestutil.AppOptionsMap, 0)
|
||||
appOptions[flags.FlagHome] = DefaultNodeHome
|
||||
appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, appOptions, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID))
|
||||
if !simcli.FlagSigverifyTxValue {
|
||||
app.SetNotSigverifyTx()
|
||||
}
|
||||
require.Equal(t, "SimApp", app.Name())
|
||||
|
||||
// run randomized simulation
|
||||
_, simParams, simErr := simulation.SimulateFromSeed(
|
||||
t,
|
||||
os.Stdout,
|
||||
app.BaseApp,
|
||||
simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()),
|
||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simtestutil.SimulationOperations(app, app.AppCodec(), config),
|
||||
BlockedAddresses(),
|
||||
config,
|
||||
app.AppCodec(),
|
||||
)
|
||||
|
||||
// export state and simParams before the simulation error is checked
|
||||
err = simtestutil.CheckExportSimulation(app, config, simParams)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, simErr)
|
||||
|
||||
if config.Commit {
|
||||
simtestutil.PrintStats(db)
|
||||
func setupStateFactory(app *SimApp) sims.SimStateFactory {
|
||||
return sims.SimStateFactory{
|
||||
Codec: app.AppCodec(),
|
||||
AppStateFn: simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()),
|
||||
BlockedAddr: BlockedAddresses(),
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
exportAllModules = []string{}
|
||||
exportWithValidatorSet = []string{}
|
||||
)
|
||||
|
||||
func TestAppImportExport(t *testing.T) {
|
||||
config := simcli.NewConfigFromFlags()
|
||||
config.ChainID = SimAppChainID
|
||||
sims.Run(t, NewSimApp, setupStateFactory, func(t *testing.T, ti sims.TestInstance[*SimApp]) {
|
||||
t.Helper()
|
||||
app := ti.App
|
||||
t.Log("exporting genesis...\n")
|
||||
exported, err := app.ExportAppStateAndValidators(false, exportWithValidatorSet, exportAllModules)
|
||||
require.NoError(t, err)
|
||||
|
||||
db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "leveldb-app-sim", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue)
|
||||
if skip {
|
||||
t.Skip("skipping application import/export simulation")
|
||||
}
|
||||
require.NoError(t, err, "simulation setup failed")
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, db.Close())
|
||||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
appOptions := make(simtestutil.AppOptionsMap, 0)
|
||||
appOptions[flags.FlagHome] = DefaultNodeHome
|
||||
appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, appOptions, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID))
|
||||
if !simcli.FlagSigverifyTxValue {
|
||||
app.SetNotSigverifyTx()
|
||||
}
|
||||
require.Equal(t, "SimApp", app.Name())
|
||||
|
||||
// Run randomized simulation
|
||||
_, simParams, simErr := simulation.SimulateFromSeed(
|
||||
t,
|
||||
os.Stdout,
|
||||
app.BaseApp,
|
||||
simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()),
|
||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simtestutil.SimulationOperations(app, app.AppCodec(), config),
|
||||
BlockedAddresses(),
|
||||
config,
|
||||
app.AppCodec(),
|
||||
)
|
||||
|
||||
// export state and simParams before the simulation error is checked
|
||||
err = simtestutil.CheckExportSimulation(app, config, simParams)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, simErr)
|
||||
|
||||
if config.Commit {
|
||||
simtestutil.PrintStats(db)
|
||||
}
|
||||
|
||||
fmt.Printf("exporting genesis...\n")
|
||||
|
||||
exported, err := app.ExportAppStateAndValidators(false, []string{}, []string{})
|
||||
require.NoError(t, err)
|
||||
|
||||
fmt.Printf("importing genesis...\n")
|
||||
|
||||
newDB, newDir, _, _, err := simtestutil.SetupSimulation(config, "leveldb-app-sim-2", "Simulation-2", simcli.FlagVerboseValue, simcli.FlagEnabledValue)
|
||||
require.NoError(t, err, "simulation setup failed")
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, newDB.Close())
|
||||
require.NoError(t, os.RemoveAll(newDir))
|
||||
}()
|
||||
|
||||
newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, appOptions, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID))
|
||||
require.Equal(t, "SimApp", newApp.Name())
|
||||
|
||||
var genesisState GenesisState
|
||||
err = json.Unmarshal(exported.AppState, &genesisState)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctxA := app.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()})
|
||||
ctxB := newApp.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()})
|
||||
_, err = newApp.ModuleManager.InitGenesis(ctxB, app.AppCodec(), genesisState)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "validator set is empty after InitGenesis") {
|
||||
logger.Info("Skipping simulation as all validators have been unbonded")
|
||||
logger.Info("err", err, "stacktrace", string(debug.Stack()))
|
||||
t.Log("importing genesis...\n")
|
||||
newTestInstance := sims.NewSimulationAppInstance(t, ti.Cfg, NewSimApp)
|
||||
newApp := newTestInstance.App
|
||||
var genesisState GenesisState
|
||||
require.NoError(t, json.Unmarshal(exported.AppState, &genesisState))
|
||||
ctxB := newApp.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()})
|
||||
_, err = newApp.ModuleManager.InitGenesis(ctxB, newApp.appCodec, genesisState)
|
||||
if IsEmptyValidatorSetErr(err) {
|
||||
t.Skip("Skipping simulation as all validators have been unbonded")
|
||||
return
|
||||
}
|
||||
}
|
||||
require.NoError(t, err)
|
||||
err = newApp.StoreConsensusParams(ctxB, exported.ConsensusParams)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, err)
|
||||
err = newApp.StoreConsensusParams(ctxB, exported.ConsensusParams)
|
||||
require.NoError(t, err)
|
||||
fmt.Printf("comparing stores...\n")
|
||||
t.Log("comparing stores...")
|
||||
// skip certain prefixes
|
||||
skipPrefixes := map[string][][]byte{
|
||||
stakingtypes.StoreKey: {
|
||||
stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey,
|
||||
stakingtypes.HistoricalInfoKey, stakingtypes.UnbondingIDKey, stakingtypes.UnbondingIndexKey,
|
||||
stakingtypes.UnbondingTypeKey,
|
||||
stakingtypes.ValidatorUpdatesKey, // todo (Alex): double check why there is a diff with test-sim-import-export
|
||||
},
|
||||
authzkeeper.StoreKey: {authzkeeper.GrantQueuePrefix},
|
||||
feegrant.StoreKey: {feegrant.FeeAllowanceQueueKeyPrefix},
|
||||
slashingtypes.StoreKey: {slashingtypes.ValidatorMissedBlockBitmapKeyPrefix},
|
||||
}
|
||||
AssertEqualStores(t, app, newApp, app.SimulationManager().StoreDecoders, skipPrefixes)
|
||||
})
|
||||
}
|
||||
|
||||
// skip certain prefixes
|
||||
skipPrefixes := map[string][][]byte{
|
||||
stakingtypes.StoreKey: {
|
||||
stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey,
|
||||
stakingtypes.HistoricalInfoKey, stakingtypes.UnbondingIDKey, stakingtypes.UnbondingIndexKey,
|
||||
stakingtypes.UnbondingTypeKey, stakingtypes.ValidatorUpdatesKey,
|
||||
},
|
||||
authzkeeper.StoreKey: {authzkeeper.GrantQueuePrefix},
|
||||
feegrant.StoreKey: {feegrant.FeeAllowanceQueueKeyPrefix},
|
||||
slashingtypes.StoreKey: {slashingtypes.ValidatorMissedBlockBitmapKeyPrefix},
|
||||
// Scenario:
|
||||
//
|
||||
// Start a fresh node and run n blocks, export state
|
||||
// set up a new node instance, Init chain from exported genesis
|
||||
// run new instance for n blocks
|
||||
func TestAppSimulationAfterImport(t *testing.T) {
|
||||
sims.Run(t, NewSimApp, setupStateFactory, func(t *testing.T, ti sims.TestInstance[*SimApp]) {
|
||||
t.Helper()
|
||||
app := ti.App
|
||||
t.Log("exporting genesis...\n")
|
||||
exported, err := app.ExportAppStateAndValidators(false, exportWithValidatorSet, exportAllModules)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Log("importing genesis...\n")
|
||||
newTestInstance := sims.NewSimulationAppInstance(t, ti.Cfg, NewSimApp)
|
||||
newApp := newTestInstance.App
|
||||
_, err = newApp.InitChain(&abci.RequestInitChain{
|
||||
AppStateBytes: exported.AppState,
|
||||
ChainId: sims.SimAppChainID,
|
||||
})
|
||||
if IsEmptyValidatorSetErr(err) {
|
||||
t.Skip("Skipping simulation as all validators have been unbonded")
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
newStateFactory := setupStateFactory(newApp)
|
||||
_, err = simulation.SimulateFromSeedX(
|
||||
t,
|
||||
newTestInstance.AppLogger,
|
||||
sims.WriteToDebugLog(newTestInstance.AppLogger),
|
||||
newApp.BaseApp,
|
||||
newStateFactory.AppStateFn,
|
||||
simtypes.RandomAccounts,
|
||||
simtestutil.BuildSimulationOperations(newApp, newApp.AppCodec(), newTestInstance.Cfg, newApp.TxConfig()),
|
||||
newStateFactory.BlockedAddr,
|
||||
newTestInstance.Cfg,
|
||||
newStateFactory.Codec,
|
||||
ti.ExecLogWriter,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func IsEmptyValidatorSetErr(err error) bool {
|
||||
return err != nil && strings.Contains(err.Error(), "validator set is empty after InitGenesis")
|
||||
}
|
||||
|
||||
func TestAppStateDeterminism(t *testing.T) {
|
||||
const numTimesToRunPerSeed = 3
|
||||
var seeds []int64
|
||||
if s := simcli.NewConfigFromFlags().Seed; s != simcli.DefaultSeedValue {
|
||||
// We will be overriding the random seed and just run a single simulation on the provided seed value
|
||||
for j := 0; j < numTimesToRunPerSeed; j++ { // multiple rounds
|
||||
seeds = append(seeds, s)
|
||||
}
|
||||
} else {
|
||||
// setup with 3 random seeds
|
||||
for i := 0; i < 3; i++ {
|
||||
seed := rand.Int63()
|
||||
for j := 0; j < numTimesToRunPerSeed; j++ { // multiple rounds
|
||||
seeds = append(seeds, seed)
|
||||
}
|
||||
}
|
||||
}
|
||||
// overwrite default app config
|
||||
interBlockCachingAppFactory := func(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp)) *SimApp {
|
||||
if FlagEnableStreamingValue {
|
||||
m := map[string]any{
|
||||
"streaming.abci.keys": []string{"*"},
|
||||
"streaming.abci.plugin": "abci_v1",
|
||||
"streaming.abci.stop-node-on-err": true,
|
||||
}
|
||||
others := appOpts
|
||||
appOpts = sims.AppOptionsFn(func(k string) any {
|
||||
if v, ok := m[k]; ok {
|
||||
return v
|
||||
}
|
||||
return others.Get(k)
|
||||
})
|
||||
}
|
||||
return NewSimApp(logger, db, nil, true, appOpts, append(baseAppOptions, interBlockCacheOpt())...)
|
||||
}
|
||||
var mx sync.Mutex
|
||||
appHashResults := make(map[int64][][]byte)
|
||||
appSimLogger := make(map[int64][]simulation.LogWriter)
|
||||
captureAndCheckHash := func(t *testing.T, ti sims.TestInstance[*SimApp]) {
|
||||
t.Helper()
|
||||
seed, appHash := ti.Cfg.Seed, ti.App.LastCommitID().Hash
|
||||
mx.Lock()
|
||||
otherHashes, execWriters := appHashResults[seed], appSimLogger[seed]
|
||||
if len(otherHashes) < numTimesToRunPerSeed-1 {
|
||||
appHashResults[seed], appSimLogger[seed] = append(otherHashes, appHash), append(execWriters, ti.ExecLogWriter)
|
||||
} else { // cleanup
|
||||
delete(appHashResults, seed)
|
||||
delete(appSimLogger, seed)
|
||||
}
|
||||
mx.Unlock()
|
||||
|
||||
var failNow bool
|
||||
// and check that all app hashes per seed are equal for each iteration
|
||||
for i := 0; i < len(otherHashes); i++ {
|
||||
if !assert.Equal(t, otherHashes[i], appHash) {
|
||||
execWriters[i].PrintLogs()
|
||||
failNow = true
|
||||
}
|
||||
}
|
||||
if failNow {
|
||||
ti.ExecLogWriter.PrintLogs()
|
||||
t.Fatalf("non-determinism in seed %d", seed)
|
||||
}
|
||||
}
|
||||
// run simulations
|
||||
sims.RunWithSeeds(t, interBlockCachingAppFactory, setupStateFactory, seeds, []byte{}, captureAndCheckHash)
|
||||
}
|
||||
|
||||
type ComparableStoreApp interface {
|
||||
LastBlockHeight() int64
|
||||
NewContextLegacy(isCheckTx bool, header cmtproto.Header) sdk.Context
|
||||
GetKey(storeKey string) *storetypes.KVStoreKey
|
||||
GetStoreKeys() []storetypes.StoreKey
|
||||
}
|
||||
|
||||
func AssertEqualStores(t *testing.T, app, newApp ComparableStoreApp, storeDecoders simtypes.StoreDecoderRegistry, skipPrefixes map[string][][]byte) {
|
||||
t.Helper()
|
||||
ctxA := app.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()})
|
||||
ctxB := newApp.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()})
|
||||
|
||||
storeKeys := app.GetStoreKeys()
|
||||
require.NotEmpty(t, storeKeys)
|
||||
@ -218,195 +250,30 @@ func TestAppImportExport(t *testing.T) {
|
||||
storeB := ctxB.KVStore(appKeyB)
|
||||
|
||||
failedKVAs, failedKVBs := simtestutil.DiffKVStores(storeA, storeB, skipPrefixes[keyName])
|
||||
require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare %s", keyName)
|
||||
require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare %s, key stores %s and %s", keyName, appKeyA, appKeyB)
|
||||
|
||||
fmt.Printf("compared %d different key/value pairs between %s and %s\n", len(failedKVAs), appKeyA, appKeyB)
|
||||
|
||||
require.Equal(t, 0, len(failedKVAs), simtestutil.GetSimulationLog(keyName, app.SimulationManager().StoreDecoders, failedKVAs, failedKVBs))
|
||||
t.Logf("compared %d different key/value pairs between %s and %s\n", len(failedKVAs), appKeyA, appKeyB)
|
||||
if !assert.Equal(t, 0, len(failedKVAs), simtestutil.GetSimulationLog(keyName, storeDecoders, failedKVAs, failedKVBs)) {
|
||||
for _, v := range failedKVAs {
|
||||
t.Logf("store mismatch: %q\n", v)
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppSimulationAfterImport(t *testing.T) {
|
||||
config := simcli.NewConfigFromFlags()
|
||||
config.ChainID = SimAppChainID
|
||||
|
||||
db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "leveldb-app-sim", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue)
|
||||
if skip {
|
||||
t.Skip("skipping application simulation after import")
|
||||
}
|
||||
require.NoError(t, err, "simulation setup failed")
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, db.Close())
|
||||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
appOptions := make(simtestutil.AppOptionsMap, 0)
|
||||
appOptions[flags.FlagHome] = DefaultNodeHome
|
||||
appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, appOptions, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID))
|
||||
if !simcli.FlagSigverifyTxValue {
|
||||
app.SetNotSigverifyTx()
|
||||
}
|
||||
require.Equal(t, "SimApp", app.Name())
|
||||
|
||||
// Run randomized simulation
|
||||
stopEarly, simParams, simErr := simulation.SimulateFromSeed(
|
||||
t,
|
||||
os.Stdout,
|
||||
app.BaseApp,
|
||||
simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()),
|
||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simtestutil.SimulationOperations(app, app.AppCodec(), config),
|
||||
BlockedAddresses(),
|
||||
config,
|
||||
app.AppCodec(),
|
||||
)
|
||||
|
||||
// export state and simParams before the simulation error is checked
|
||||
err = simtestutil.CheckExportSimulation(app, config, simParams)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, simErr)
|
||||
|
||||
if config.Commit {
|
||||
simtestutil.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{}, []string{})
|
||||
require.NoError(t, err)
|
||||
|
||||
fmt.Printf("importing genesis...\n")
|
||||
|
||||
newDB, newDir, _, _, err := simtestutil.SetupSimulation(config, "leveldb-app-sim-2", "Simulation-2", simcli.FlagVerboseValue, simcli.FlagEnabledValue)
|
||||
require.NoError(t, err, "simulation setup failed")
|
||||
|
||||
defer func() {
|
||||
require.NoError(t, newDB.Close())
|
||||
require.NoError(t, os.RemoveAll(newDir))
|
||||
}()
|
||||
|
||||
newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, appOptions, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID))
|
||||
require.Equal(t, "SimApp", newApp.Name())
|
||||
|
||||
_, err = newApp.InitChain(&abci.RequestInitChain{
|
||||
AppStateBytes: exported.AppState,
|
||||
ChainId: SimAppChainID,
|
||||
func FuzzFullAppSimulation(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, rawSeed []byte) {
|
||||
if len(rawSeed) < 8 {
|
||||
t.Skip()
|
||||
return
|
||||
}
|
||||
sims.RunWithSeeds(
|
||||
t,
|
||||
NewSimApp,
|
||||
setupStateFactory,
|
||||
[]int64{int64(binary.BigEndian.Uint64(rawSeed))},
|
||||
rawSeed[8:],
|
||||
)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, _, err = simulation.SimulateFromSeed(
|
||||
t,
|
||||
os.Stdout,
|
||||
newApp.BaseApp,
|
||||
simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()),
|
||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simtestutil.SimulationOperations(newApp, newApp.AppCodec(), config),
|
||||
BlockedAddresses(),
|
||||
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 !simcli.FlagEnabledValue {
|
||||
t.Skip("skipping application simulation")
|
||||
}
|
||||
|
||||
config := simcli.NewConfigFromFlags()
|
||||
config.InitialBlockHeight = 1
|
||||
config.ExportParamsPath = ""
|
||||
config.OnOperation = false
|
||||
config.AllInvariants = false
|
||||
config.ChainID = SimAppChainID
|
||||
|
||||
numSeeds := 3
|
||||
numTimesToRunPerSeed := 3 // This used to be set to 5, but we've temporarily reduced it to 3 for the sake of faster CI.
|
||||
appHashList := make([]json.RawMessage, numTimesToRunPerSeed)
|
||||
|
||||
// We will be overriding the random seed and just run a single simulation on the provided seed value
|
||||
if config.Seed != simcli.DefaultSeedValue {
|
||||
numSeeds = 1
|
||||
}
|
||||
|
||||
appOptions := viper.New()
|
||||
if FlagEnableStreamingValue {
|
||||
m := make(map[string]interface{})
|
||||
m["streaming.abci.keys"] = []string{"*"}
|
||||
m["streaming.abci.plugin"] = "abci_v1"
|
||||
m["streaming.abci.stop-node-on-err"] = true
|
||||
for key, value := range m {
|
||||
appOptions.SetDefault(key, value)
|
||||
}
|
||||
}
|
||||
appOptions.SetDefault(flags.FlagHome, DefaultNodeHome)
|
||||
appOptions.SetDefault(server.FlagInvCheckPeriod, simcli.FlagPeriodValue)
|
||||
if simcli.FlagVerboseValue {
|
||||
appOptions.SetDefault(flags.FlagLogLevel, "debug")
|
||||
}
|
||||
|
||||
for i := 0; i < numSeeds; i++ {
|
||||
if config.Seed == simcli.DefaultSeedValue {
|
||||
config.Seed = rand.Int63()
|
||||
}
|
||||
|
||||
fmt.Println("config.Seed: ", config.Seed)
|
||||
|
||||
for j := 0; j < numTimesToRunPerSeed; j++ {
|
||||
var logger log.Logger
|
||||
if simcli.FlagVerboseValue {
|
||||
logger = log.NewTestLogger(t)
|
||||
} else {
|
||||
logger = log.NewNopLogger()
|
||||
}
|
||||
|
||||
db := dbm.NewMemDB()
|
||||
app := NewSimApp(logger, db, nil, true, appOptions, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID))
|
||||
if !simcli.FlagSigverifyTxValue {
|
||||
app.SetNotSigverifyTx()
|
||||
}
|
||||
|
||||
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,
|
||||
simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()),
|
||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simtestutil.SimulationOperations(app, app.AppCodec(), config),
|
||||
BlockedAddresses(),
|
||||
config,
|
||||
app.AppCodec(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if config.Commit {
|
||||
simtestutil.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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,6 @@ import (
|
||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
servertypes "github.com/cosmos/cosmos-sdk/server/types"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/mock"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/network"
|
||||
@ -43,7 +42,6 @@ func setup(withGenesis bool, invCheckPeriod uint) (*SimApp, GenesisState) {
|
||||
|
||||
appOptions := make(simtestutil.AppOptionsMap, 0)
|
||||
appOptions[flags.FlagHome] = DefaultNodeHome
|
||||
appOptions[server.FlagInvCheckPeriod] = invCheckPeriod
|
||||
|
||||
app := NewSimApp(log.NewNopLogger(), db, nil, true, appOptions)
|
||||
if withGenesis {
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"cosmossdk.io/log"
|
||||
storetypes "cosmossdk.io/store/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/runtime"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@ -23,7 +24,7 @@ import (
|
||||
|
||||
// SetupSimulation creates the config, db (levelDB), temporary directory and logger for the simulation tests.
|
||||
// If `skip` is false it skips the current test. `skip` should be set using the `FlagEnabledValue` flag.
|
||||
// Returns error on an invalid db intantiation or temp dir creation.
|
||||
// Returns error on an invalid db instantiation or temp dir creation.
|
||||
func SetupSimulation(config simtypes.Config, dirPrefix, dbName string, verbose, skip bool) (dbm.DB, string, log.Logger, bool, error) {
|
||||
if !skip {
|
||||
return nil, "", nil, true, nil
|
||||
@ -31,7 +32,7 @@ func SetupSimulation(config simtypes.Config, dirPrefix, dbName string, verbose,
|
||||
|
||||
var logger log.Logger
|
||||
if verbose {
|
||||
logger = log.NewLogger(os.Stdout) // TODO(mr): enable selection of log destination.
|
||||
logger = log.NewLogger(os.Stdout)
|
||||
} else {
|
||||
logger = log.NewNopLogger()
|
||||
}
|
||||
@ -51,11 +52,18 @@ func SetupSimulation(config simtypes.Config, dirPrefix, dbName string, verbose,
|
||||
|
||||
// SimulationOperations retrieves the simulation params from the provided file path
|
||||
// and returns all the modules weighted operations
|
||||
// Deprecated: use BuildSimulationOperations with TxConfig
|
||||
func SimulationOperations(app runtime.AppI, cdc codec.JSONCodec, config simtypes.Config) []simtypes.WeightedOperation {
|
||||
return BuildSimulationOperations(app, cdc, config, moduletestutil.MakeTestTxConfig())
|
||||
}
|
||||
|
||||
// BuildSimulationOperations retrieves the simulation params from the provided file path
|
||||
// and returns all the modules weighted operations
|
||||
func BuildSimulationOperations(app runtime.AppI, cdc codec.JSONCodec, config simtypes.Config, txConfig client.TxConfig) []simtypes.WeightedOperation {
|
||||
simState := module.SimulationState{
|
||||
AppParams: make(simtypes.AppParams),
|
||||
Cdc: cdc,
|
||||
TxConfig: moduletestutil.MakeTestTxConfig(),
|
||||
TxConfig: txConfig,
|
||||
BondDenom: sdk.DefaultBondDenom,
|
||||
}
|
||||
|
||||
@ -71,8 +79,7 @@ func SimulationOperations(app runtime.AppI, cdc codec.JSONCodec, config simtypes
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:staticcheck // used for legacy testing
|
||||
simState.LegacyProposalContents = app.SimulationManager().GetProposalContents(simState)
|
||||
simState.LegacyProposalContents = app.SimulationManager().GetProposalContents(simState) //nolint:staticcheck // we're testing the old way here
|
||||
simState.ProposalMsgs = app.SimulationManager().GetProposalMsgs(simState)
|
||||
return app.SimulationManager().WeightedOperations(simState)
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
@ -35,7 +36,11 @@ const (
|
||||
|
||||
// AppStateFn returns the initial application state using a genesis or the simulation parameters.
|
||||
// It calls AppStateFnWithExtendedCb with nil rawStateCb.
|
||||
func AppStateFn(cdc codec.JSONCodec, simManager *module.SimulationManager, genesisState map[string]json.RawMessage) simtypes.AppStateFn {
|
||||
func AppStateFn(
|
||||
cdc codec.JSONCodec,
|
||||
simManager *module.SimulationManager,
|
||||
genesisState map[string]json.RawMessage,
|
||||
) simtypes.AppStateFn {
|
||||
return AppStateFnWithExtendedCb(cdc, simManager, genesisState, nil)
|
||||
}
|
||||
|
||||
@ -68,13 +73,9 @@ func AppStateFnWithExtendedCbs(
|
||||
accs []simtypes.Account,
|
||||
config simtypes.Config,
|
||||
) (appState json.RawMessage, simAccs []simtypes.Account, chainID string, genesisTimestamp time.Time) {
|
||||
if simcli.FlagGenesisTimeValue == 0 {
|
||||
genesisTimestamp = simtypes.RandTimestamp(r)
|
||||
} else {
|
||||
genesisTimestamp = time.Unix(simcli.FlagGenesisTimeValue, 0)
|
||||
}
|
||||
|
||||
genesisTimestamp = time.Unix(config.GenesisTime, 0)
|
||||
chainID = config.ChainID
|
||||
|
||||
switch {
|
||||
case config.ParamsFile != "" && config.GenesisFile != "":
|
||||
panic("cannot provide both a genesis file and a params file")
|
||||
@ -138,8 +139,7 @@ func AppStateFnWithExtendedCbs(
|
||||
}
|
||||
notBondedCoins := sdk.NewCoin(stakingState.Params.BondDenom, notBondedTokens)
|
||||
// edit bank state to make it have the not bonded pool tokens
|
||||
bankStateBz, ok := rawState[banktypes.ModuleName]
|
||||
// TODO(fdymylja/jonathan): should we panic in this case
|
||||
bankStateBz, ok := rawState[testutil.BankModuleName]
|
||||
if !ok {
|
||||
panic("bank genesis state is missing")
|
||||
}
|
||||
@ -166,7 +166,7 @@ func AppStateFnWithExtendedCbs(
|
||||
// change appState back
|
||||
for name, state := range map[string]proto.Message{
|
||||
stakingtypes.ModuleName: stakingState,
|
||||
banktypes.ModuleName: bankState,
|
||||
testutil.BankModuleName: bankState,
|
||||
} {
|
||||
if moduleStateCb != nil {
|
||||
moduleStateCb(name, state)
|
||||
@ -219,15 +219,6 @@ func AppStateRandomizedFn(
|
||||
numInitiallyBonded = numAccs
|
||||
}
|
||||
|
||||
fmt.Printf(
|
||||
`Selected randomly generated parameters for simulated genesis:
|
||||
{
|
||||
stake_per_account: "%d",
|
||||
initially_bonded_validators: "%d"
|
||||
}
|
||||
`, initialStake.Uint64(), numInitiallyBonded,
|
||||
)
|
||||
|
||||
simState := &module.SimulationState{
|
||||
AppParams: appParams,
|
||||
Cdc: cdc,
|
||||
@ -273,8 +264,8 @@ func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONCodec, genesisFile str
|
||||
}
|
||||
|
||||
var authGenesis authtypes.GenesisState
|
||||
if appState[authtypes.ModuleName] != nil {
|
||||
cdc.MustUnmarshalJSON(appState[authtypes.ModuleName], &authGenesis)
|
||||
if appState[testutil.AuthModuleName] != nil {
|
||||
cdc.MustUnmarshalJSON(appState[testutil.AuthModuleName], &authGenesis)
|
||||
}
|
||||
|
||||
newAccs := make([]simtypes.Account, len(authGenesis.Accounts))
|
||||
|
||||
235
testutils/sims/runner.go
Normal file
235
testutils/sims/runner.go
Normal file
@ -0,0 +1,235 @@
|
||||
package sims
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
dbm "github.com/cosmos/cosmos-db"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/runtime"
|
||||
servertypes "github.com/cosmos/cosmos-sdk/server/types"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation/client/cli"
|
||||
)
|
||||
|
||||
const SimAppChainID = "simulation-app"
|
||||
|
||||
// this list of seeds was imported from the original simulation runner: https://github.com/cosmos/tools/blob/v1.0.0/cmd/runsim/main.go#L32
|
||||
var defaultSeeds = []int64{
|
||||
1, 2, 4, 7,
|
||||
32, 123, 124, 582, 1893, 2989,
|
||||
3012, 4728, 37827, 981928, 87821, 891823782,
|
||||
989182, 89182391, 11, 22, 44, 77, 99, 2020,
|
||||
3232, 123123, 124124, 582582, 18931893,
|
||||
29892989, 30123012, 47284728, 7601778, 8090485,
|
||||
977367484, 491163361, 424254581, 673398983,
|
||||
}
|
||||
|
||||
type SimStateFactory struct {
|
||||
Codec codec.Codec
|
||||
AppStateFn simtypes.AppStateFn
|
||||
BlockedAddr map[string]bool
|
||||
}
|
||||
|
||||
// SimulationApp abstract app that is used by sims
|
||||
type SimulationApp interface {
|
||||
runtime.AppI
|
||||
SetNotSigverifyTx()
|
||||
GetBaseApp() *baseapp.BaseApp
|
||||
TxConfig() client.TxConfig
|
||||
}
|
||||
|
||||
// Run is a helper function that runs a simulation test with the given parameters.
|
||||
// It calls the RunWithSeeds function with the default seeds and parameters.
|
||||
//
|
||||
// This is the entrypoint to run simulation tests that used to run with the runsim binary.
|
||||
func Run[T SimulationApp](
|
||||
t *testing.T,
|
||||
appFactory func(
|
||||
logger log.Logger,
|
||||
db dbm.DB,
|
||||
traceStore io.Writer,
|
||||
loadLatest bool,
|
||||
appOpts servertypes.AppOptions,
|
||||
baseAppOptions ...func(*baseapp.BaseApp),
|
||||
) T,
|
||||
setupStateFactory func(app T) SimStateFactory,
|
||||
postRunActions ...func(t *testing.T, app TestInstance[T]),
|
||||
) {
|
||||
t.Helper()
|
||||
RunWithSeeds(t, appFactory, setupStateFactory, defaultSeeds, nil, postRunActions...)
|
||||
}
|
||||
|
||||
// RunWithSeeds is a helper function that runs a simulation test with the given parameters.
|
||||
// It iterates over the provided seeds and runs the simulation test for each seed in parallel.
|
||||
//
|
||||
// It sets up the environment, creates an instance of the simulation app,
|
||||
// calls the simulation.SimulateFromSeed function to run the simulation, and performs post-run actions for each seed.
|
||||
// The execution is deterministic and can be used for fuzz tests as well.
|
||||
//
|
||||
// The system under test is isolated for each run but unlike the old runsim command, there is no Process separation.
|
||||
// This means, global caches may be reused for example. This implementation build upon the vanialla Go stdlib test framework.
|
||||
func RunWithSeeds[T SimulationApp](
|
||||
t *testing.T,
|
||||
appFactory func(
|
||||
logger log.Logger,
|
||||
db dbm.DB,
|
||||
traceStore io.Writer,
|
||||
loadLatest bool,
|
||||
appOpts servertypes.AppOptions,
|
||||
baseAppOptions ...func(*baseapp.BaseApp),
|
||||
) T,
|
||||
setupStateFactory func(app T) SimStateFactory,
|
||||
seeds []int64,
|
||||
fuzzSeed []byte,
|
||||
postRunActions ...func(t *testing.T, app TestInstance[T]),
|
||||
) {
|
||||
t.Helper()
|
||||
cfg := cli.NewConfigFromFlags()
|
||||
cfg.ChainID = SimAppChainID
|
||||
if deprecatedParams := cli.GetDeprecatedFlagUsed(); len(deprecatedParams) != 0 {
|
||||
fmt.Printf("Warning: Deprecated flag are used: %s", strings.Join(deprecatedParams, ","))
|
||||
}
|
||||
for i := range seeds {
|
||||
seed := seeds[i]
|
||||
t.Run(fmt.Sprintf("seed: %d", seed), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// setup environment
|
||||
tCfg := cfg.With(t, seed, fuzzSeed)
|
||||
testInstance := NewSimulationAppInstance(t, tCfg, appFactory)
|
||||
var runLogger log.Logger
|
||||
if cli.FlagVerboseValue {
|
||||
runLogger = log.NewTestLogger(t)
|
||||
} else {
|
||||
runLogger = log.NewTestLoggerInfo(t)
|
||||
}
|
||||
runLogger = runLogger.With("seed", tCfg.Seed)
|
||||
|
||||
app := testInstance.App
|
||||
stateFactory := setupStateFactory(app)
|
||||
simParams, err := simulation.SimulateFromSeedX(
|
||||
t,
|
||||
runLogger,
|
||||
WriteToDebugLog(runLogger),
|
||||
app.GetBaseApp(),
|
||||
stateFactory.AppStateFn,
|
||||
simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1
|
||||
simtestutil.BuildSimulationOperations(app, stateFactory.Codec, tCfg, testInstance.App.TxConfig()),
|
||||
stateFactory.BlockedAddr,
|
||||
tCfg,
|
||||
stateFactory.Codec,
|
||||
testInstance.ExecLogWriter,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
err = simtestutil.CheckExportSimulation(app, tCfg, simParams)
|
||||
require.NoError(t, err)
|
||||
if tCfg.Commit {
|
||||
simtestutil.PrintStats(testInstance.DB)
|
||||
}
|
||||
for _, step := range postRunActions {
|
||||
step(t, testInstance)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestInstance is a generic type that represents an instance of a SimulationApp used for testing simulations.
|
||||
// It contains the following fields:
|
||||
// - App: The instance of the SimulationApp under test.
|
||||
// - DB: The LevelDB database for the simulation app.
|
||||
// - WorkDir: The temporary working directory for the simulation app.
|
||||
// - Cfg: The configuration flags for the simulator.
|
||||
// - AppLogger: The logger used for logging in the app during the simulation, with seed value attached.
|
||||
// - ExecLogWriter: Captures block and operation data coming from the simulation
|
||||
type TestInstance[T SimulationApp] struct {
|
||||
App T
|
||||
DB dbm.DB
|
||||
WorkDir string
|
||||
Cfg simtypes.Config
|
||||
AppLogger log.Logger
|
||||
ExecLogWriter simulation.LogWriter
|
||||
}
|
||||
|
||||
// NewSimulationAppInstance initializes and returns a TestInstance of a SimulationApp.
|
||||
// The function takes a testing.T instance, a simtypes.Config instance, and an appFactory function as parameters.
|
||||
// It creates a temporary working directory and a LevelDB database for the simulation app.
|
||||
// The function then initializes a logger based on the verbosity flag and sets the logger's seed to the test configuration's seed.
|
||||
// The database is closed and cleaned up on test completion.
|
||||
func NewSimulationAppInstance[T SimulationApp](
|
||||
t *testing.T,
|
||||
tCfg simtypes.Config,
|
||||
appFactory func(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp)) T,
|
||||
) TestInstance[T] {
|
||||
t.Helper()
|
||||
workDir := t.TempDir()
|
||||
dbDir := filepath.Join(workDir, "leveldb-app-sim")
|
||||
var logger log.Logger
|
||||
if cli.FlagVerboseValue {
|
||||
logger = log.NewTestLogger(t)
|
||||
} else {
|
||||
logger = log.NewTestLoggerError(t)
|
||||
}
|
||||
logger = logger.With("seed", tCfg.Seed)
|
||||
|
||||
db, err := dbm.NewDB("Simulation", dbm.BackendType(tCfg.DBBackend), dbDir)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, db.Close())
|
||||
})
|
||||
appOptions := make(simtestutil.AppOptionsMap)
|
||||
appOptions[flags.FlagHome] = workDir
|
||||
|
||||
app := appFactory(logger, db, nil, true, appOptions, baseapp.SetChainID(SimAppChainID))
|
||||
if !cli.FlagSigverifyTxValue {
|
||||
app.SetNotSigverifyTx()
|
||||
}
|
||||
return TestInstance[T]{
|
||||
App: app,
|
||||
DB: db,
|
||||
WorkDir: workDir,
|
||||
Cfg: tCfg,
|
||||
AppLogger: logger,
|
||||
ExecLogWriter: &simulation.StandardLogWriter{Seed: tCfg.Seed},
|
||||
}
|
||||
}
|
||||
|
||||
var _ io.Writer = writerFn(nil)
|
||||
|
||||
type writerFn func(p []byte) (n int, err error)
|
||||
|
||||
func (w writerFn) Write(p []byte) (n int, err error) {
|
||||
return w(p)
|
||||
}
|
||||
|
||||
// WriteToDebugLog is an adapter to io.Writer interface
|
||||
func WriteToDebugLog(logger log.Logger) io.Writer {
|
||||
return writerFn(func(p []byte) (n int, err error) {
|
||||
logger.Debug(string(p))
|
||||
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()
|
||||
}
|
||||
@ -31,22 +31,32 @@ func RandomAcc(r *rand.Rand, accs []Account) (Account, int) {
|
||||
return accs[idx], idx
|
||||
}
|
||||
|
||||
// RandomAccounts generates n random accounts
|
||||
// RandomAccounts deterministically generates n random accounts without duplicates.
|
||||
func RandomAccounts(r *rand.Rand, n int) []Account {
|
||||
accs := make([]Account, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
idx := make(map[string]struct{}, n)
|
||||
var i int
|
||||
for i < n {
|
||||
// don't need that much entropy for simulation
|
||||
privkeySeed := make([]byte, 15)
|
||||
r.Read(privkeySeed)
|
||||
|
||||
accs[i].PrivKey = secp256k1.GenPrivKeyFromSecret(privkeySeed)
|
||||
accs[i].PubKey = accs[i].PrivKey.PubKey()
|
||||
accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address())
|
||||
|
||||
accs[i].ConsKey = ed25519.GenPrivKeyFromSecret(privkeySeed)
|
||||
if _, err := r.Read(privkeySeed); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
privKey := secp256k1.GenPrivKeyFromSecret(privkeySeed)
|
||||
pubKey := privKey.PubKey()
|
||||
addr := sdk.AccAddress(pubKey.Address())
|
||||
if _, exists := idx[string(addr.Bytes())]; exists {
|
||||
continue
|
||||
}
|
||||
idx[string(addr.Bytes())] = struct{}{}
|
||||
accs[i] = Account{
|
||||
Address: addr,
|
||||
PrivKey: privKey,
|
||||
PubKey: pubKey,
|
||||
ConsKey: ed25519.GenPrivKeyFromSecret(privkeySeed),
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return accs
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package simulation
|
||||
|
||||
import "testing"
|
||||
|
||||
// Config contains the necessary configuration flags for the simulator
|
||||
type Config struct {
|
||||
GenesisFile string // custom simulation genesis file; cannot be used with params file
|
||||
@ -12,6 +14,7 @@ type Config struct {
|
||||
|
||||
Seed int64 // simulation random seed
|
||||
InitialBlockHeight int // initial block to start the simulation
|
||||
GenesisTime int64 // genesis time to start the simulation
|
||||
NumBlocks int // number of new blocks to simulate from the initial block height
|
||||
BlockSize int // operations per block
|
||||
ChainID string // chain-id used on the simulation
|
||||
@ -19,9 +22,28 @@ type Config struct {
|
||||
Lean bool // lean simulation log output
|
||||
Commit bool // have the simulation commit
|
||||
|
||||
OnOperation bool // run slow invariants every operation
|
||||
AllInvariants bool // print all failed invariants if a broken invariant is found
|
||||
|
||||
DBBackend string // custom db backend type
|
||||
BlockMaxGas int64 // custom max gas for block
|
||||
FuzzSeed []byte
|
||||
TB testing.TB
|
||||
FauxMerkle bool
|
||||
|
||||
// Deprecated: unused and will be removed
|
||||
OnOperation bool // run slow invariants every operation
|
||||
// Deprecated: unused and will be removed
|
||||
AllInvariants bool // print all failed invariants if a broken invariant is found
|
||||
}
|
||||
|
||||
func (c Config) shallowCopy() Config {
|
||||
return c
|
||||
}
|
||||
|
||||
// With sets the values of t, seed, and fuzzSeed in a copy of the Config and returns the copy.
|
||||
func (c Config) With(tb testing.TB, seed int64, fuzzSeed []byte) Config {
|
||||
tb.Helper()
|
||||
r := c.shallowCopy()
|
||||
r.TB = tb
|
||||
r.Seed = seed
|
||||
r.FuzzSeed = fuzzSeed
|
||||
return r
|
||||
}
|
||||
|
||||
@ -147,31 +147,8 @@ func RandSubsetCoins(r *rand.Rand, coins sdk.Coins) sdk.Coins {
|
||||
}
|
||||
|
||||
// DeriveRand derives a new Rand deterministically from another random source.
|
||||
// Unlike rand.New(rand.NewSource(seed)), the result is "more random"
|
||||
// depending on the source and state of r.
|
||||
//
|
||||
// NOTE: not crypto safe.
|
||||
func DeriveRand(r *rand.Rand) *rand.Rand {
|
||||
const num = 8 // TODO what's a good number? Too large is too slow.
|
||||
ms := multiSource(make([]rand.Source, num))
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
ms[i] = rand.NewSource(r.Int63())
|
||||
}
|
||||
|
||||
return rand.New(ms)
|
||||
}
|
||||
|
||||
type multiSource []rand.Source
|
||||
|
||||
func (ms multiSource) Int63() (r int64) {
|
||||
for _, source := range ms {
|
||||
r ^= source.Int63()
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (ms multiSource) Seed(_ int64) {
|
||||
panic("multiSource Seed should not be called")
|
||||
return rand.New(rand.NewSource(r.Int63()))
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package simulation
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
@ -18,7 +19,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
var initialProposalID = uint64(100000000000000)
|
||||
const unsetProposalID = 100000000000000
|
||||
|
||||
// Governance message types and routes
|
||||
var (
|
||||
@ -43,6 +44,26 @@ const (
|
||||
DefaultWeightMsgCancelProposal = 5
|
||||
)
|
||||
|
||||
// sharedState shared state between message invocations
|
||||
type sharedState struct {
|
||||
minProposalID atomic.Uint64
|
||||
}
|
||||
|
||||
// newSharedState constructor
|
||||
func newSharedState() *sharedState {
|
||||
r := &sharedState{}
|
||||
r.setMinProposalID(unsetProposalID)
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *sharedState) getMinProposalID() uint64 {
|
||||
return s.minProposalID.Load()
|
||||
}
|
||||
|
||||
func (s *sharedState) setMinProposalID(id uint64) {
|
||||
s.minProposalID.Store(id)
|
||||
}
|
||||
|
||||
// WeightedOperations returns all the operations from the module with their respective weights
|
||||
func WeightedOperations(
|
||||
appParams simtypes.AppParams,
|
||||
@ -119,19 +140,19 @@ func WeightedOperations(
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
state := newSharedState()
|
||||
wGovOps := simulation.WeightedOperations{
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgDeposit,
|
||||
SimulateMsgDeposit(txGen, ak, bk, k),
|
||||
simulateMsgDeposit(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgVote,
|
||||
SimulateMsgVote(txGen, ak, bk, k),
|
||||
simulateMsgVote(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgVoteWeighted,
|
||||
SimulateMsgVoteWeighted(txGen, ak, bk, k),
|
||||
simulateMsgVoteWeighted(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgCancelProposal,
|
||||
@ -205,7 +226,7 @@ func simulateMsgSubmitProposal(
|
||||
// column 3: 75% vote
|
||||
// column 4: 40% vote
|
||||
// column 5: 15% vote
|
||||
// column 6: noone votes
|
||||
// column 6: no one votes
|
||||
// All columns sum to 100 for simplicity, values chosen by @valardragon semi-arbitrarily,
|
||||
// feel free to change.
|
||||
numVotesTransitionMatrix, _ := simulation.CreateTransitionMatrix([][]int{
|
||||
@ -291,13 +312,13 @@ func simulateMsgSubmitProposal(
|
||||
whoVotes = whoVotes[:numVotes]
|
||||
params, _ := k.Params.Get(ctx)
|
||||
votingPeriod := params.VotingPeriod
|
||||
|
||||
s := newSharedState()
|
||||
fops := make([]simtypes.FutureOperation, numVotes+1)
|
||||
for i := 0; i < numVotes; i++ {
|
||||
whenVote := ctx.BlockHeader().Time.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second)
|
||||
fops[i] = simtypes.FutureOperation{
|
||||
BlockTime: whenVote,
|
||||
Op: operationSimulateMsgVote(txGen, ak, bk, k, accs[whoVotes[i]], int64(proposalID)),
|
||||
Op: operationSimulateMsgVote(txGen, ak, bk, k, accs[whoVotes[i]], int64(proposalID), s),
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,18 +327,29 @@ func simulateMsgSubmitProposal(
|
||||
}
|
||||
|
||||
// SimulateMsgDeposit generates a MsgDeposit with random values.
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgDeposit(
|
||||
txGen client.TxConfig,
|
||||
ak types.AccountKeeper,
|
||||
bk types.BankKeeper,
|
||||
k *keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgDeposit(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgDeposit(
|
||||
txGen client.TxConfig,
|
||||
ak types.AccountKeeper,
|
||||
bk types.BankKeeper,
|
||||
k *keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
simAccount, _ := simtypes.RandomAcc(r, accs)
|
||||
proposalID, ok := randomProposalID(r, k, ctx, v1.StatusDepositPeriod)
|
||||
proposalID, ok := randomProposalID(r, k, ctx, v1.StatusDepositPeriod, s)
|
||||
if !ok {
|
||||
return simtypes.NoOpMsg(types.ModuleName, TypeMsgDeposit, "unable to generate proposalID"), nil, nil
|
||||
}
|
||||
@ -366,13 +398,24 @@ func SimulateMsgDeposit(
|
||||
}
|
||||
|
||||
// SimulateMsgVote generates a MsgVote with random values.
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgVote(
|
||||
txGen client.TxConfig,
|
||||
ak types.AccountKeeper,
|
||||
bk types.BankKeeper,
|
||||
k *keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return operationSimulateMsgVote(txGen, ak, bk, k, simtypes.Account{}, -1)
|
||||
return simulateMsgVote(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgVote(
|
||||
txGen client.TxConfig,
|
||||
ak types.AccountKeeper,
|
||||
bk types.BankKeeper,
|
||||
k *keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return operationSimulateMsgVote(txGen, ak, bk, k, simtypes.Account{}, -1, s)
|
||||
}
|
||||
|
||||
func operationSimulateMsgVote(
|
||||
@ -382,6 +425,7 @@ func operationSimulateMsgVote(
|
||||
k *keeper.Keeper,
|
||||
simAccount simtypes.Account,
|
||||
proposalIDInt int64,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
@ -396,7 +440,7 @@ func operationSimulateMsgVote(
|
||||
switch {
|
||||
case proposalIDInt < 0:
|
||||
var ok bool
|
||||
proposalID, ok = randomProposalID(r, k, ctx, v1.StatusVotingPeriod)
|
||||
proposalID, ok = randomProposalID(r, k, ctx, v1.StatusVotingPeriod, s)
|
||||
if !ok {
|
||||
return simtypes.NoOpMsg(types.ModuleName, TypeMsgVote, "unable to generate proposalID"), nil, nil
|
||||
}
|
||||
@ -435,7 +479,17 @@ func SimulateMsgVoteWeighted(
|
||||
bk types.BankKeeper,
|
||||
k *keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return operationSimulateMsgVoteWeighted(txGen, ak, bk, k, simtypes.Account{}, -1)
|
||||
return simulateMsgVoteWeighted(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgVoteWeighted(
|
||||
txGen client.TxConfig,
|
||||
ak types.AccountKeeper,
|
||||
bk types.BankKeeper,
|
||||
k *keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return operationSimulateMsgVoteWeighted(txGen, ak, bk, k, simtypes.Account{}, -1, s)
|
||||
}
|
||||
|
||||
func operationSimulateMsgVoteWeighted(
|
||||
@ -445,6 +499,7 @@ func operationSimulateMsgVoteWeighted(
|
||||
k *keeper.Keeper,
|
||||
simAccount simtypes.Account,
|
||||
proposalIDInt int64,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
@ -459,7 +514,7 @@ func operationSimulateMsgVoteWeighted(
|
||||
switch {
|
||||
case proposalIDInt < 0:
|
||||
var ok bool
|
||||
proposalID, ok = randomProposalID(r, k, ctx, v1.StatusVotingPeriod)
|
||||
proposalID, ok = randomProposalID(r, k, ctx, v1.StatusVotingPeriod, s)
|
||||
if !ok {
|
||||
return simtypes.NoOpMsg(types.ModuleName, TypeMsgVoteWeighted, "unable to generate proposalID"), nil, nil
|
||||
}
|
||||
@ -626,20 +681,13 @@ func randomProposal(r *rand.Rand, k *keeper.Keeper, ctx sdk.Context) *v1.Proposa
|
||||
// (defined in gov GenesisState) and the latest proposal ID
|
||||
// that matches a given Status.
|
||||
// It does not provide a default ID.
|
||||
func randomProposalID(r *rand.Rand, k *keeper.Keeper, ctx sdk.Context, status v1.ProposalStatus) (proposalID uint64, found bool) {
|
||||
func randomProposalID(r *rand.Rand, k *keeper.Keeper, ctx sdk.Context, status v1.ProposalStatus, s *sharedState) (proposalID uint64, found bool) {
|
||||
proposalID, _ = k.ProposalID.Peek(ctx)
|
||||
|
||||
switch {
|
||||
case proposalID > initialProposalID:
|
||||
// select a random ID between [initialProposalID, proposalID]
|
||||
if initialProposalID := s.getMinProposalID(); initialProposalID == unsetProposalID {
|
||||
s.setMinProposalID(proposalID)
|
||||
} else if initialProposalID < proposalID {
|
||||
proposalID = uint64(simtypes.RandIntBetween(r, int(initialProposalID), int(proposalID)))
|
||||
|
||||
default:
|
||||
// This is called on the first call to this funcion
|
||||
// in order to update the global variable
|
||||
initialProposalID = proposalID
|
||||
}
|
||||
|
||||
proposal, err := k.Proposals.Get(ctx, proposalID)
|
||||
if err != nil || proposal.Status != status {
|
||||
return proposalID, false
|
||||
|
||||
@ -325,13 +325,13 @@ func TestSimulateMsgVote(t *testing.T) {
|
||||
proposal, err := v1.NewProposal([]sdk.Msg{contentMsg}, 1, submitTime, submitTime.Add(*depositPeriod), "", "text proposal", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"), false)
|
||||
require.NoError(t, err)
|
||||
|
||||
suite.GovKeeper.ActivateVotingPeriod(ctx, proposal)
|
||||
require.NoError(t, suite.GovKeeper.ActivateVotingPeriod(ctx, proposal))
|
||||
|
||||
app.FinalizeBlock(&abci.RequestFinalizeBlock{
|
||||
_, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{
|
||||
Height: app.LastBlockHeight() + 1,
|
||||
Hash: app.LastCommitID().Hash,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
// execute operation
|
||||
op := simulation.SimulateMsgVote(suite.TxConfig, suite.AccountKeeper, suite.BankKeeper, suite.GovKeeper)
|
||||
operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "")
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
@ -19,7 +20,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
var initialGroupID = uint64(100000000000000)
|
||||
const unsetGroupID = 100000000000000
|
||||
|
||||
// group message types
|
||||
var (
|
||||
@ -76,12 +77,32 @@ const (
|
||||
WeightMsgCreateGroupWithPolicy = 50
|
||||
)
|
||||
|
||||
// sharedState shared state between message invocations
|
||||
type sharedState struct {
|
||||
minGroupID atomic.Uint64
|
||||
}
|
||||
|
||||
// newSharedState constructor
|
||||
func newSharedState() *sharedState {
|
||||
r := &sharedState{}
|
||||
r.setMinGroupID(unsetGroupID)
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *sharedState) getMinGroupID() uint64 {
|
||||
return s.minGroupID.Load()
|
||||
}
|
||||
|
||||
func (s *sharedState) setMinGroupID(id uint64) {
|
||||
s.minGroupID.Store(id)
|
||||
}
|
||||
|
||||
// WeightedOperations returns all the operations from the module with their respective weights
|
||||
func WeightedOperations(
|
||||
registry cdctypes.InterfaceRegistry,
|
||||
appParams simtypes.AppParams, cdc codec.JSONCodec, txGen client.TxConfig,
|
||||
appParams simtypes.AppParams, _ codec.JSONCodec, txGen client.TxConfig,
|
||||
ak group.AccountKeeper, bk group.BankKeeper, k keeper.Keeper,
|
||||
appCdc cdctypes.AnyUnpacker,
|
||||
_ cdctypes.AnyUnpacker,
|
||||
) simulation.WeightedOperations {
|
||||
var (
|
||||
weightMsgCreateGroup int
|
||||
@ -145,12 +166,14 @@ func WeightedOperations(
|
||||
|
||||
pCdc := codec.NewProtoCodec(registry)
|
||||
|
||||
state := newSharedState()
|
||||
|
||||
// create two proposals for weightedOperations
|
||||
var createProposalOps simulation.WeightedOperations
|
||||
for i := 0; i < 2; i++ {
|
||||
createProposalOps = append(createProposalOps, simulation.NewWeightedOperation(
|
||||
weightMsgSubmitProposal,
|
||||
SimulateMsgSubmitProposal(pCdc, txGen, ak, bk, k),
|
||||
simulateMsgSubmitProposal(pCdc, txGen, ak, bk, k, state),
|
||||
))
|
||||
}
|
||||
|
||||
@ -161,7 +184,7 @@ func WeightedOperations(
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgCreateGroupPolicy,
|
||||
SimulateMsgCreateGroupPolicy(pCdc, txGen, ak, bk, k),
|
||||
simulateMsgCreateGroupPolicy(pCdc, txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgCreateGroupWithPolicy,
|
||||
@ -172,43 +195,43 @@ func WeightedOperations(
|
||||
wPostCreateProposalOps := simulation.WeightedOperations{
|
||||
simulation.NewWeightedOperation(
|
||||
WeightMsgWithdrawProposal,
|
||||
SimulateMsgWithdrawProposal(pCdc, txGen, ak, bk, k),
|
||||
simulateMsgWithdrawProposal(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgVote,
|
||||
SimulateMsgVote(pCdc, txGen, ak, bk, k),
|
||||
simulateMsgVote(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgExec,
|
||||
SimulateMsgExec(pCdc, txGen, ak, bk, k),
|
||||
simulateMsgExec(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgUpdateGroupMetadata,
|
||||
SimulateMsgUpdateGroupMetadata(pCdc, txGen, ak, bk, k),
|
||||
simulateMsgUpdateGroupMetadata(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgUpdateGroupAdmin,
|
||||
SimulateMsgUpdateGroupAdmin(pCdc, txGen, ak, bk, k),
|
||||
simulateMsgUpdateGroupAdmin(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgUpdateGroupMembers,
|
||||
SimulateMsgUpdateGroupMembers(pCdc, txGen, ak, bk, k),
|
||||
simulateMsgUpdateGroupMembers(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgUpdateGroupPolicyAdmin,
|
||||
SimulateMsgUpdateGroupPolicyAdmin(pCdc, txGen, ak, bk, k),
|
||||
simulateMsgUpdateGroupPolicyAdmin(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgUpdateGroupPolicyDecisionPolicy,
|
||||
SimulateMsgUpdateGroupPolicyDecisionPolicy(pCdc, txGen, ak, bk, k),
|
||||
simulateMsgUpdateGroupPolicyDecisionPolicy(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgUpdateGroupPolicyMetadata,
|
||||
SimulateMsgUpdateGroupPolicyMetadata(pCdc, txGen, ak, bk, k),
|
||||
simulateMsgUpdateGroupPolicyMetadata(txGen, ak, bk, k, state),
|
||||
),
|
||||
simulation.NewWeightedOperation(
|
||||
weightMsgLeaveGroup,
|
||||
SimulateMsgLeaveGroup(pCdc, txGen, k, ak, bk),
|
||||
simulateMsgLeaveGroup(txGen, k, ak, bk, state),
|
||||
),
|
||||
}
|
||||
|
||||
@ -216,8 +239,9 @@ func WeightedOperations(
|
||||
}
|
||||
|
||||
// SimulateMsgCreateGroup generates a MsgCreateGroup with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgCreateGroup(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
@ -263,8 +287,9 @@ func SimulateMsgCreateGroup(
|
||||
}
|
||||
|
||||
// SimulateMsgCreateGroupWithPolicy generates a MsgCreateGroupWithPolicy with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgCreateGroupWithPolicy(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
@ -297,7 +322,7 @@ func SimulateMsgCreateGroupWithPolicy(
|
||||
GroupPolicyMetadata: simtypes.RandStringOfLength(r, 10),
|
||||
GroupPolicyAsAdmin: r.Float32() < 0.5,
|
||||
}
|
||||
msg.SetDecisionPolicy(decisionPolicy)
|
||||
err = msg.SetDecisionPolicy(decisionPolicy)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to set decision policy"), nil, err
|
||||
}
|
||||
@ -327,17 +352,29 @@ func SimulateMsgCreateGroupWithPolicy(
|
||||
}
|
||||
|
||||
// SimulateMsgCreateGroupPolicy generates a NewMsgCreateGroupPolicy with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgCreateGroupPolicy(
|
||||
cdc *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgCreateGroupPolicy(cdc, txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgCreateGroupPolicy(
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts)
|
||||
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroupPolicy, ""), nil, err
|
||||
}
|
||||
@ -393,17 +430,29 @@ func SimulateMsgCreateGroupPolicy(
|
||||
}
|
||||
|
||||
// SimulateMsgSubmitProposal generates a NewMsgSubmitProposal with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgSubmitProposal(
|
||||
cdc *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgSubmitProposal(cdc, txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgSubmitProposal(
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
g, groupPolicy, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts)
|
||||
g, groupPolicy, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, ""), nil, err
|
||||
}
|
||||
@ -474,17 +523,28 @@ func SimulateMsgSubmitProposal(
|
||||
}
|
||||
|
||||
// SimulateMsgUpdateGroupAdmin generates a MsgUpdateGroupAdmin with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgUpdateGroupAdmin(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgUpdateGroupAdmin(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgUpdateGroupAdmin(
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts)
|
||||
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupAdmin, ""), nil, err
|
||||
}
|
||||
@ -539,17 +599,28 @@ func SimulateMsgUpdateGroupAdmin(
|
||||
}
|
||||
|
||||
// SimulateMsgUpdateGroupMetadata generates a MsgUpdateGroupMetadata with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgUpdateGroupMetadata(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgUpdateGroupMetadata(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgUpdateGroupMetadata(
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts)
|
||||
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMetadata, ""), nil, err
|
||||
}
|
||||
@ -595,17 +666,28 @@ func SimulateMsgUpdateGroupMetadata(
|
||||
}
|
||||
|
||||
// SimulateMsgUpdateGroupMembers generates a MsgUpdateGroupMembers with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgUpdateGroupMembers(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgUpdateGroupMembers(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgUpdateGroupMembers(
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts)
|
||||
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMembers, ""), nil, err
|
||||
}
|
||||
@ -678,17 +760,28 @@ func SimulateMsgUpdateGroupMembers(
|
||||
}
|
||||
|
||||
// SimulateMsgUpdateGroupPolicyAdmin generates a MsgUpdateGroupPolicyAdmin with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgUpdateGroupPolicyAdmin(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgUpdateGroupPolicyAdmin(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgUpdateGroupPolicyAdmin(
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts)
|
||||
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyAdmin, ""), nil, err
|
||||
}
|
||||
@ -742,18 +835,29 @@ func SimulateMsgUpdateGroupPolicyAdmin(
|
||||
}
|
||||
}
|
||||
|
||||
// // SimulateMsgUpdateGroupPolicyDecisionPolicy generates a NewMsgUpdateGroupPolicyDecisionPolicy with random values
|
||||
// SimulateMsgUpdateGroupPolicyDecisionPolicy generates a NewMsgUpdateGroupPolicyDecisionPolicy with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgUpdateGroupPolicyDecisionPolicy(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgUpdateGroupPolicyDecisionPolicy(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgUpdateGroupPolicyDecisionPolicy(
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts)
|
||||
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyDecisionPolicy, ""), nil, err
|
||||
}
|
||||
@ -806,18 +910,29 @@ func SimulateMsgUpdateGroupPolicyDecisionPolicy(
|
||||
}
|
||||
}
|
||||
|
||||
// // SimulateMsgUpdateGroupPolicyMetadata generates a MsgUpdateGroupPolicyMetadata with random values
|
||||
// SimulateMsgUpdateGroupPolicyMetadata generates a MsgUpdateGroupPolicyMetadata with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgUpdateGroupPolicyMetadata(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgUpdateGroupPolicyMetadata(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgUpdateGroupPolicyMetadata(
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts)
|
||||
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyMetadata, ""), nil, err
|
||||
}
|
||||
@ -863,17 +978,29 @@ func SimulateMsgUpdateGroupPolicyMetadata(
|
||||
}
|
||||
|
||||
// SimulateMsgWithdrawProposal generates a MsgWithdrawProposal with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgWithdrawProposal(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgWithdrawProposal(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
// simulateMsgWithdrawProposal generates a MsgWithdrawProposal with random values
|
||||
func simulateMsgWithdrawProposal(
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
g, groupPolicy, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts)
|
||||
g, groupPolicy, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, ""), nil, err
|
||||
}
|
||||
@ -958,7 +1085,6 @@ func SimulateMsgWithdrawProposal(
|
||||
}
|
||||
|
||||
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "group was modified") || strings.Contains(err.Error(), "group policy was modified") {
|
||||
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "no-op:group/group-policy was modified"), nil, nil
|
||||
@ -971,17 +1097,28 @@ func SimulateMsgWithdrawProposal(
|
||||
}
|
||||
|
||||
// SimulateMsgVote generates a MsgVote with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgVote(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgVote(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgVote(
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
g, groupPolicy, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts)
|
||||
g, groupPolicy, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, ""), nil, err
|
||||
}
|
||||
@ -1066,7 +1203,6 @@ func SimulateMsgVote(
|
||||
}
|
||||
|
||||
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "group was modified") || strings.Contains(err.Error(), "group policy was modified") {
|
||||
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "no-op:group/group-policy was modified"), nil, nil
|
||||
@ -1078,18 +1214,29 @@ func SimulateMsgVote(
|
||||
}
|
||||
}
|
||||
|
||||
// // SimulateMsgExec generates a MsgExec with random values
|
||||
// SimulateMsgExec generates a MsgExec with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgExec(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgExec(txGen, ak, bk, k, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgExec(
|
||||
txGen client.TxConfig,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
k keeper.Keeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts)
|
||||
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, ""), nil, err
|
||||
}
|
||||
@ -1159,17 +1306,28 @@ func SimulateMsgExec(
|
||||
}
|
||||
|
||||
// SimulateMsgLeaveGroup generates a MsgLeaveGroup with random values
|
||||
// Deprecated: this is an internal method and will be removed
|
||||
func SimulateMsgLeaveGroup(
|
||||
cdc *codec.ProtoCodec,
|
||||
_ *codec.ProtoCodec,
|
||||
txGen client.TxConfig,
|
||||
k keeper.Keeper,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
) simtypes.Operation {
|
||||
return simulateMsgLeaveGroup(txGen, k, ak, bk, newSharedState())
|
||||
}
|
||||
|
||||
func simulateMsgLeaveGroup(
|
||||
txGen client.TxConfig,
|
||||
k keeper.Keeper,
|
||||
ak group.AccountKeeper,
|
||||
bk group.BankKeeper,
|
||||
s *sharedState,
|
||||
) simtypes.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
|
||||
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
groupInfo, policyInfo, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts)
|
||||
groupInfo, policyInfo, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgLeaveGroup, ""), nil, err
|
||||
}
|
||||
@ -1223,20 +1381,14 @@ func SimulateMsgLeaveGroup(
|
||||
}
|
||||
|
||||
func randomGroup(r *rand.Rand, k keeper.Keeper, ak group.AccountKeeper,
|
||||
ctx sdk.Context, accounts []simtypes.Account,
|
||||
ctx sdk.Context, accounts []simtypes.Account, s *sharedState,
|
||||
) (groupInfo *group.GroupInfo, acc simtypes.Account, account sdk.AccountI, err error) {
|
||||
groupID := k.GetGroupSequence(ctx)
|
||||
|
||||
switch {
|
||||
case groupID > initialGroupID:
|
||||
// select a random ID between (initialGroupID, groupID]
|
||||
// if there is at least one group information, then the groupID at this time must be greater than or equal to 1
|
||||
if initialGroupID := s.getMinGroupID(); initialGroupID == unsetGroupID {
|
||||
s.setMinGroupID(groupID)
|
||||
} else if initialGroupID < groupID {
|
||||
groupID = uint64(simtypes.RandIntBetween(r, int(initialGroupID+1), int(groupID+1)))
|
||||
|
||||
default:
|
||||
// This is called on the first call to this function
|
||||
// in order to update the global variable
|
||||
initialGroupID = groupID
|
||||
}
|
||||
|
||||
// when groupID is 0, it proves that SimulateMsgCreateGroup has never been called. that is, no group exists in the chain
|
||||
@ -1267,9 +1419,9 @@ func randomGroup(r *rand.Rand, k keeper.Keeper, ak group.AccountKeeper,
|
||||
}
|
||||
|
||||
func randomGroupPolicy(r *rand.Rand, k keeper.Keeper, ak group.AccountKeeper,
|
||||
ctx sdk.Context, accounts []simtypes.Account,
|
||||
ctx sdk.Context, accounts []simtypes.Account, s *sharedState,
|
||||
) (groupInfo *group.GroupInfo, groupPolicyInfo *group.GroupPolicyInfo, acc simtypes.Account, account sdk.AccountI, err error) {
|
||||
groupInfo, _, _, err = randomGroup(r, k, ak, ctx, accounts)
|
||||
groupInfo, _, _, err = randomGroup(r, k, ak, ctx, accounts, s)
|
||||
if err != nil {
|
||||
return nil, nil, simtypes.Account{}, nil, err
|
||||
}
|
||||
@ -1352,12 +1504,8 @@ func genGroupMembers(r *rand.Rand, accounts []simtypes.Account) []group.MemberRe
|
||||
}
|
||||
}
|
||||
|
||||
max := 5
|
||||
if len(accounts) < max {
|
||||
max = len(accounts)
|
||||
}
|
||||
|
||||
membersLen := simtypes.RandIntBetween(r, 1, max)
|
||||
maxMembers := min(len(accounts), 5)
|
||||
membersLen := simtypes.RandIntBetween(r, 1, maxMembers)
|
||||
members := make([]group.MemberRequest, membersLen)
|
||||
|
||||
for i := 0; i < membersLen; i++ {
|
||||
|
||||
@ -782,7 +782,7 @@ func (suite *SimTestSuite) TestSimulateLeaveGroup() {
|
||||
})
|
||||
|
||||
// execute operation
|
||||
op := simulation.SimulateMsgLeaveGroup(codec.NewProtoCodec(suite.interfaceRegistry), suite.txConfig, suite.groupKeeper, suite.accountKeeper, suite.bankKeeper)
|
||||
op := simulation.SimulateMsgLeaveGroup(nil, suite.txConfig, suite.groupKeeper, suite.accountKeeper, suite.bankKeeper)
|
||||
operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "")
|
||||
suite.Require().NoError(err)
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
)
|
||||
@ -22,15 +23,21 @@ var (
|
||||
FlagBlockSizeValue int
|
||||
FlagLeanValue bool
|
||||
FlagCommitValue bool
|
||||
FlagOnOperationValue bool // TODO: Remove in favor of binary search for invariant violation
|
||||
FlagAllInvariantsValue bool
|
||||
FlagDBBackendValue string
|
||||
|
||||
FlagEnabledValue bool
|
||||
FlagVerboseValue bool
|
||||
FlagPeriodValue uint
|
||||
FlagGenesisTimeValue int64
|
||||
FlagSigverifyTxValue bool
|
||||
FlagFauxMerkle bool
|
||||
|
||||
// Deprecated: This flag is unused and will be removed in a future release.
|
||||
FlagPeriodValue uint
|
||||
// Deprecated: This flag is unused and will be removed in a future release.
|
||||
FlagEnabledValue bool
|
||||
// Deprecated: This flag is unused and will be removed in a future release.
|
||||
FlagOnOperationValue bool
|
||||
// Deprecated: This flag is unused and will be removed in a future release.
|
||||
FlagAllInvariantsValue bool
|
||||
)
|
||||
|
||||
// GetSimulatorFlags gets the values of all the available simulation flags
|
||||
@ -47,17 +54,19 @@ func GetSimulatorFlags() {
|
||||
flag.IntVar(&FlagNumBlocksValue, "NumBlocks", 500, "number of new blocks to simulate from the initial block height")
|
||||
flag.IntVar(&FlagBlockSizeValue, "BlockSize", 200, "operations per block")
|
||||
flag.BoolVar(&FlagLeanValue, "Lean", false, "lean simulation log output")
|
||||
flag.BoolVar(&FlagCommitValue, "Commit", false, "have the simulation commit")
|
||||
flag.BoolVar(&FlagOnOperationValue, "SimulateEveryOperation", false, "run slow invariants every operation")
|
||||
flag.BoolVar(&FlagAllInvariantsValue, "PrintAllInvariants", false, "print all invariants if a broken invariant is found")
|
||||
flag.StringVar(&FlagDBBackendValue, "DBBackend", "goleveldb", "custom db backend type")
|
||||
flag.BoolVar(&FlagCommitValue, "Commit", true, "have the simulation commit")
|
||||
flag.StringVar(&FlagDBBackendValue, "DBBackend", "memdb", "custom db backend type: goleveldb, pebbledb, memdb")
|
||||
|
||||
// simulation flags
|
||||
flag.BoolVar(&FlagEnabledValue, "Enabled", false, "enable the simulation")
|
||||
flag.BoolVar(&FlagVerboseValue, "Verbose", false, "verbose log output")
|
||||
flag.UintVar(&FlagPeriodValue, "Period", 0, "run slow invariants only once every period assertions")
|
||||
flag.Int64Var(&FlagGenesisTimeValue, "GenesisTime", 0, "override genesis UNIX time instead of using a random UNIX time")
|
||||
flag.Int64Var(&FlagGenesisTimeValue, "GenesisTime", time.Now().Unix(), "use current time as genesis UNIX time for default")
|
||||
flag.BoolVar(&FlagSigverifyTxValue, "SigverifyTx", true, "whether to sigverify check for transaction ")
|
||||
flag.BoolVar(&FlagFauxMerkle, "FauxMerkle", false, "use faux merkle instead of iavl")
|
||||
|
||||
flag.UintVar(&FlagPeriodValue, "Period", 0, "This parameter is unused and will be removed")
|
||||
flag.BoolVar(&FlagEnabledValue, "Enabled", false, "This parameter is unused and will be removed")
|
||||
flag.BoolVar(&FlagOnOperationValue, "SimulateEveryOperation", false, "This parameter is unused and will be removed")
|
||||
flag.BoolVar(&FlagAllInvariantsValue, "PrintAllInvariants", false, "This parameter is unused and will be removed")
|
||||
}
|
||||
|
||||
// NewConfigFromFlags creates a simulation from the retrieved values of the flags.
|
||||
@ -71,12 +80,28 @@ func NewConfigFromFlags() simulation.Config {
|
||||
ExportStatsPath: FlagExportStatsPathValue,
|
||||
Seed: FlagSeedValue,
|
||||
InitialBlockHeight: FlagInitialBlockHeightValue,
|
||||
GenesisTime: FlagGenesisTimeValue,
|
||||
NumBlocks: FlagNumBlocksValue,
|
||||
BlockSize: FlagBlockSizeValue,
|
||||
Lean: FlagLeanValue,
|
||||
Commit: FlagCommitValue,
|
||||
OnOperation: FlagOnOperationValue,
|
||||
AllInvariants: FlagAllInvariantsValue,
|
||||
DBBackend: FlagDBBackendValue,
|
||||
}
|
||||
}
|
||||
|
||||
// GetDeprecatedFlagUsed return list of deprecated flag names that are being used.
|
||||
// This function is for internal usage only and may be removed with the deprecated fields.
|
||||
func GetDeprecatedFlagUsed() []string {
|
||||
var usedFlags []string
|
||||
for _, flagName := range []string{
|
||||
"Enabled",
|
||||
"SimulateEveryOperation",
|
||||
"PrintAllInvariants",
|
||||
"Period",
|
||||
} {
|
||||
if flag.Lookup(flagName) != nil {
|
||||
usedFlags = append(usedFlags, flagName)
|
||||
}
|
||||
}
|
||||
return usedFlags
|
||||
}
|
||||
|
||||
@ -46,70 +46,65 @@ others state execution outcome.
|
||||
|
||||
# Usage
|
||||
|
||||
Switch to `simapp/` directory:
|
||||
|
||||
$ cd simapp/
|
||||
|
||||
To execute a completely pseudo-random simulation:
|
||||
|
||||
$ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \
|
||||
$ go test -mod=readonly . \
|
||||
-tags='sims' \
|
||||
-run=TestFullAppSimulation \
|
||||
-Enabled=true \
|
||||
-NumBlocks=100 \
|
||||
-BlockSize=200 \
|
||||
-Commit=true \
|
||||
-Seed=99 \
|
||||
-Period=5 \
|
||||
-v -timeout 24h
|
||||
|
||||
To execute simulation from a genesis file:
|
||||
|
||||
$ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \
|
||||
$ go test -mod=readonly . \
|
||||
-tags='sims' \
|
||||
-run=TestFullAppSimulation \
|
||||
-Enabled=true \
|
||||
-NumBlocks=100 \
|
||||
-BlockSize=200 \
|
||||
-Commit=true \
|
||||
-Seed=99 \
|
||||
-Period=5 \
|
||||
-Genesis=/path/to/genesis.json \
|
||||
-v -timeout 24h
|
||||
|
||||
To execute simulation from a simulation params file:
|
||||
|
||||
$ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \
|
||||
$ go test -mod=readonly . \
|
||||
-tags='sims' \
|
||||
-run=TestFullAppSimulation \
|
||||
-Enabled=true \
|
||||
-NumBlocks=100 \
|
||||
-BlockSize=200 \
|
||||
-Commit=true \
|
||||
-Seed=99 \
|
||||
-Period=5 \
|
||||
-Params=/path/to/params.json \
|
||||
-v -timeout 24h
|
||||
|
||||
To export the simulation params to a file at a given block height:
|
||||
|
||||
$ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \
|
||||
$ go test -mod=readonly . \
|
||||
-tags='sims' \
|
||||
-run=TestFullAppSimulation \
|
||||
-Enabled=true \
|
||||
-NumBlocks=100 \
|
||||
-BlockSize=200 \
|
||||
-Commit=true \
|
||||
-Seed=99 \
|
||||
-Period=5 \
|
||||
-ExportParamsPath=/path/to/params.json \
|
||||
-ExportParamsHeight=50 \
|
||||
-v -timeout 24h
|
||||
-v -timeout 24h
|
||||
|
||||
To export the simulation app state (i.e genesis) to a file:
|
||||
|
||||
$ go test -mod=readonly github.com/cosmos/cosmos-sdk/simapp \
|
||||
$ go test -mod=readonly . \
|
||||
-tags='sims' \
|
||||
-run=TestFullAppSimulation \
|
||||
-Enabled=true \
|
||||
-NumBlocks=100 \
|
||||
-BlockSize=200 \
|
||||
-Commit=true \
|
||||
-Seed=99 \
|
||||
-Period=5 \
|
||||
-ExportStatePath=/path/to/genesis.json \
|
||||
v -timeout 24h
|
||||
-v -timeout 24h
|
||||
|
||||
# Params
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -24,7 +25,11 @@ func NewLogWriter(testingmode bool) LogWriter {
|
||||
|
||||
// log writter
|
||||
type StandardLogWriter struct {
|
||||
Seed int64
|
||||
|
||||
OpEntries []OperationEntry `json:"op_entries" yaml:"op_entries"`
|
||||
wMtx sync.Mutex
|
||||
written bool
|
||||
}
|
||||
|
||||
// add an entry to the log writter
|
||||
@ -34,7 +39,12 @@ func (lw *StandardLogWriter) AddEntry(opEntry OperationEntry) {
|
||||
|
||||
// PrintLogs - print the logs to a simulation file
|
||||
func (lw *StandardLogWriter) PrintLogs() {
|
||||
f := createLogFile()
|
||||
lw.wMtx.Lock()
|
||||
defer lw.wMtx.Unlock()
|
||||
if lw.written { // print once only
|
||||
return
|
||||
}
|
||||
f := createLogFile(lw.Seed)
|
||||
defer f.Close()
|
||||
|
||||
for i := 0; i < len(lw.OpEntries); i++ {
|
||||
@ -44,12 +54,16 @@ func (lw *StandardLogWriter) PrintLogs() {
|
||||
panic("Failed to write logs to file")
|
||||
}
|
||||
}
|
||||
lw.written = true
|
||||
}
|
||||
|
||||
func createLogFile() *os.File {
|
||||
func createLogFile(seed int64) *os.File {
|
||||
var f *os.File
|
||||
|
||||
fileName := fmt.Sprintf("%d.log", time.Now().UnixMilli())
|
||||
var prefix string
|
||||
if seed != 0 {
|
||||
prefix = fmt.Sprintf("seed_%10d", seed)
|
||||
}
|
||||
fileName := fmt.Sprintf("%s--%d.log", prefix, time.Now().UnixNano())
|
||||
folderPath := path.Join(os.ExpandEnv("$HOME"), ".simapp", "simulations")
|
||||
filePath := path.Join(folderPath, fileName)
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@ package simulation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
|
||||
@ -197,12 +196,5 @@ func randomConsensusParams(r *rand.Rand, appState json.RawMessage, cdc codec.JSO
|
||||
MaxAgeDuration: stakingGenesisState.Params.UnbondingTime,
|
||||
},
|
||||
}
|
||||
|
||||
bz, err := json.MarshalIndent(&consensusParams, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("Selected randomly generated consensus parameters:\n%s\n", bz)
|
||||
|
||||
return consensusParams
|
||||
}
|
||||
|
||||
@ -1,18 +1,21 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
abci "github.com/cometbft/cometbft/abci/types"
|
||||
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
|
||||
|
||||
"cosmossdk.io/core/header"
|
||||
"cosmossdk.io/log"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@ -65,14 +68,37 @@ func SimulateFromSeed(
|
||||
config simulation.Config,
|
||||
cdc codec.JSONCodec,
|
||||
) (stopEarly bool, exportedParams Params, err error) {
|
||||
tb.Helper()
|
||||
mode, _, _ := getTestingMode(tb)
|
||||
expParams, err := SimulateFromSeedX(tb, log.NewTestLogger(tb), w, app, appStateFn, randAccFn, ops, blockedAddrs, config, cdc, NewLogWriter(mode))
|
||||
return false, expParams, err
|
||||
}
|
||||
|
||||
// SimulateFromSeedX tests an application by running the provided
|
||||
// operations, testing the provided invariants, but using the provided config.Seed.
|
||||
func SimulateFromSeedX(
|
||||
tb testing.TB,
|
||||
logger log.Logger,
|
||||
w io.Writer,
|
||||
app *baseapp.BaseApp,
|
||||
appStateFn simulation.AppStateFn,
|
||||
randAccFn simulation.RandomAccountFn,
|
||||
ops WeightedOperations,
|
||||
blockedAddrs map[string]bool,
|
||||
config simulation.Config,
|
||||
cdc codec.JSONCodec,
|
||||
logWriter LogWriter,
|
||||
) (exportedParams Params, err error) {
|
||||
tb.Helper()
|
||||
// in case we have to end early, don't os.Exit so that we can run cleanup code.
|
||||
testingMode, _, b := getTestingMode(tb)
|
||||
|
||||
r := rand.New(rand.NewSource(config.Seed))
|
||||
r := rand.New(newByteSource(config.FuzzSeed, config.Seed))
|
||||
params := RandomParams(r)
|
||||
|
||||
fmt.Fprintf(w, "Starting SimulateFromSeed with randomness created with seed %d\n", int(config.Seed))
|
||||
fmt.Fprintf(w, "Randomized simulation params: \n%s\n", mustMarshalJSONIndent(params))
|
||||
startTime := time.Now()
|
||||
logger.Info("Starting SimulateFromSeed with randomness", "time", startTime)
|
||||
logger.Debug("Randomized simulation setup", "params", mustMarshalJSONIndent(params))
|
||||
|
||||
timeDiff := maxTimePerBlock - minTimePerBlock
|
||||
accs := randAccFn(r, params.NumKeys())
|
||||
@ -84,16 +110,11 @@ func SimulateFromSeed(
|
||||
// At least 2 accounts must be added here, otherwise when executing SimulateMsgSend
|
||||
// two accounts will be selected to meet the conditions from != to and it will fall into an infinite loop.
|
||||
if len(accs) <= 1 {
|
||||
return true, params, fmt.Errorf("at least two genesis accounts are required")
|
||||
return params, fmt.Errorf("at least two genesis accounts are required")
|
||||
}
|
||||
|
||||
config.ChainID = chainID
|
||||
|
||||
fmt.Printf(
|
||||
"Starting the simulation from time %v (unixtime %v)\n",
|
||||
blockTime.UTC().Format(time.UnixDate), blockTime.Unix(),
|
||||
)
|
||||
|
||||
// remove module account address if they exist in accs
|
||||
var tmpAccs []simulation.Account
|
||||
|
||||
@ -107,7 +128,7 @@ func SimulateFromSeed(
|
||||
nextValidators := validators
|
||||
if len(nextValidators) == 0 {
|
||||
tb.Skip("skipping: empty validator set in genesis")
|
||||
return true, params, nil
|
||||
return params, nil
|
||||
}
|
||||
|
||||
var (
|
||||
@ -120,17 +141,6 @@ func SimulateFromSeed(
|
||||
opCount = 0
|
||||
)
|
||||
|
||||
// Setup code to catch SIGTERM's
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
|
||||
|
||||
go func() {
|
||||
receivedSignal := <-c
|
||||
fmt.Fprintf(w, "\nExiting early due to %s, on block %d, operation %d\n", receivedSignal, blockHeight, opCount)
|
||||
err = fmt.Errorf("exited due to %s", receivedSignal)
|
||||
stopEarly = true
|
||||
}()
|
||||
|
||||
finalizeBlockReq := RandomRequestFinalizeBlock(
|
||||
r,
|
||||
params,
|
||||
@ -145,11 +155,10 @@ func SimulateFromSeed(
|
||||
|
||||
// These are operations which have been queued by previous operations
|
||||
operationQueue := NewOperationQueue()
|
||||
logWriter := NewLogWriter(testingMode)
|
||||
|
||||
blockSimulator := createBlockSimulator(
|
||||
testingMode,
|
||||
tb,
|
||||
testingMode,
|
||||
w,
|
||||
params,
|
||||
eventStats.Tally,
|
||||
@ -166,7 +175,7 @@ func SimulateFromSeed(
|
||||
// recover logs in case of panic
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
_, _ = fmt.Fprintf(w, "simulation halted due to panic on block %d\n", blockHeight)
|
||||
logger.Error("simulation halted due to panic", "height", blockHeight)
|
||||
logWriter.PrintLogs()
|
||||
panic(r)
|
||||
}
|
||||
@ -178,7 +187,7 @@ func SimulateFromSeed(
|
||||
exportedParams = params
|
||||
}
|
||||
|
||||
for blockHeight < int64(config.NumBlocks+config.InitialBlockHeight) && !stopEarly {
|
||||
for blockHeight < int64(config.NumBlocks+config.InitialBlockHeight) {
|
||||
pastTimes = append(pastTimes, blockTime)
|
||||
pastVoteInfos = append(pastVoteInfos, finalizeBlockReq.DecidedLastCommit.Votes)
|
||||
|
||||
@ -187,7 +196,7 @@ func SimulateFromSeed(
|
||||
|
||||
res, err := app.FinalizeBlock(finalizeBlockReq)
|
||||
if err != nil {
|
||||
return true, params, err
|
||||
return params, fmt.Errorf("block finalization failed at height %d: %w", blockHeight, err)
|
||||
}
|
||||
|
||||
ctx := app.NewContextLegacy(false, cmtproto.Header{
|
||||
@ -195,17 +204,21 @@ func SimulateFromSeed(
|
||||
Time: blockTime,
|
||||
ProposerAddress: proposerAddress,
|
||||
ChainID: config.ChainID,
|
||||
}).WithHeaderInfo(header.Info{
|
||||
Height: blockHeight,
|
||||
Time: blockTime,
|
||||
ChainID: config.ChainID,
|
||||
})
|
||||
|
||||
// run queued operations; ignores block size if block size is too small
|
||||
numQueuedOpsRan, futureOps := runQueuedOperations(
|
||||
operationQueue, int(blockHeight), tb, r, app, ctx, accs, logWriter,
|
||||
tb, operationQueue, blockTime, int(blockHeight), r, app, ctx, accs, logWriter,
|
||||
eventStats.Tally, config.Lean, config.ChainID,
|
||||
)
|
||||
|
||||
numQueuedTimeOpsRan, timeFutureOps := runQueuedTimeOperations(
|
||||
numQueuedTimeOpsRan, timeFutureOps := runQueuedTimeOperations(tb,
|
||||
timeOperationQueue, int(blockHeight), blockTime,
|
||||
tb, r, app, ctx, accs, logWriter, eventStats.Tally,
|
||||
r, app, ctx, accs, logWriter, eventStats.Tally,
|
||||
config.Lean, config.ChainID,
|
||||
)
|
||||
|
||||
@ -223,19 +236,20 @@ func SimulateFromSeed(
|
||||
|
||||
blockHeight++
|
||||
|
||||
logWriter.AddEntry(EndBlockEntry(blockHeight))
|
||||
|
||||
blockTime = blockTime.Add(time.Duration(minTimePerBlock) * time.Second)
|
||||
blockTime = blockTime.Add(time.Duration(int64(r.Intn(int(timeDiff)))) * time.Second)
|
||||
proposerAddress = validators.randomProposer(r)
|
||||
|
||||
logWriter.AddEntry(EndBlockEntry(blockHeight))
|
||||
|
||||
if config.Commit {
|
||||
app.Commit()
|
||||
if _, err := app.Commit(); err != nil {
|
||||
return params, fmt.Errorf("commit failed at height %d: %w", blockHeight, err)
|
||||
}
|
||||
}
|
||||
|
||||
if proposerAddress == nil {
|
||||
fmt.Fprintf(w, "\nSimulation stopped early as all validators have been unbonded; nobody left to propose a block!\n")
|
||||
stopEarly = true
|
||||
logger.Info("Simulation stopped early as all validators have been unbonded; nobody left to propose a block", "height", blockHeight)
|
||||
break
|
||||
}
|
||||
|
||||
@ -249,7 +263,7 @@ func SimulateFromSeed(
|
||||
nextValidators = updateValidators(tb, r, params, validators, res.ValidatorUpdates, eventStats.Tally)
|
||||
if len(nextValidators) == 0 {
|
||||
tb.Skip("skipping: empty validator set")
|
||||
return true, params, nil
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// update the exported params
|
||||
@ -258,22 +272,8 @@ func SimulateFromSeed(
|
||||
}
|
||||
}
|
||||
|
||||
if stopEarly {
|
||||
if config.ExportStatsPath != "" {
|
||||
fmt.Println("Exporting simulation statistics...")
|
||||
eventStats.ExportJSON(config.ExportStatsPath)
|
||||
} else {
|
||||
eventStats.Print(w)
|
||||
}
|
||||
|
||||
return true, exportedParams, err
|
||||
}
|
||||
|
||||
fmt.Fprintf(
|
||||
w,
|
||||
"\nSimulation complete; Final height (blocks): %d, final time (seconds): %v, operations ran: %d\n",
|
||||
blockHeight, blockTime, opCount,
|
||||
)
|
||||
logger.Info("Simulation complete", "height", blockHeight, "block-time", blockTime, "opsCount", opCount,
|
||||
"run-time", time.Since(startTime), "app-hash", hex.EncodeToString(app.LastCommitID().Hash))
|
||||
|
||||
if config.ExportStatsPath != "" {
|
||||
fmt.Println("Exporting simulation statistics...")
|
||||
@ -281,8 +281,7 @@ func SimulateFromSeed(
|
||||
} else {
|
||||
eventStats.Print(w)
|
||||
}
|
||||
|
||||
return false, exportedParams, nil
|
||||
return exportedParams, err
|
||||
}
|
||||
|
||||
type blockSimFn func(
|
||||
@ -294,12 +293,13 @@ type blockSimFn func(
|
||||
) (opCount int)
|
||||
|
||||
// Returns a function to simulate blocks. Written like this to avoid constant
|
||||
// parameters being passed everytime, to minimize memory overhead.
|
||||
func createBlockSimulator(testingMode bool, tb testing.TB, w io.Writer, params Params,
|
||||
// parameters being passed every time, to minimize memory overhead.
|
||||
func createBlockSimulator(tb testing.TB, printProgress bool, w io.Writer, params Params,
|
||||
event func(route, op, evResult string), ops WeightedOperations,
|
||||
operationQueue OperationQueue, timeOperationQueue []simulation.FutureOperation,
|
||||
logWriter LogWriter, config simulation.Config,
|
||||
) blockSimFn {
|
||||
tb.Helper()
|
||||
lastBlockSizeState := 0 // state for [4 * uniform distribution]
|
||||
blocksize := 0
|
||||
selectOp := ops.getSelectOpFn()
|
||||
@ -325,7 +325,7 @@ func createBlockSimulator(testingMode bool, tb testing.TB, w io.Writer, params P
|
||||
for i := 0; i < blocksize; i++ {
|
||||
opAndRz = append(opAndRz, opAndR{
|
||||
op: selectOp(r),
|
||||
rand: simulation.DeriveRand(r),
|
||||
rand: r,
|
||||
})
|
||||
}
|
||||
|
||||
@ -350,8 +350,8 @@ Comment: %s`,
|
||||
|
||||
queueOperations(operationQueue, timeOperationQueue, futureOps)
|
||||
|
||||
if testingMode && opCount%50 == 0 {
|
||||
fmt.Fprintf(w, "\rSimulating... block %d/%d, operation %d/%d. ",
|
||||
if printProgress && opCount%50 == 0 {
|
||||
_, _ = fmt.Fprintf(w, "\rSimulating... block %d/%d, operation %d/%d. ",
|
||||
header.Height, config.NumBlocks, opCount, blocksize)
|
||||
}
|
||||
|
||||
@ -362,11 +362,21 @@ Comment: %s`,
|
||||
}
|
||||
}
|
||||
|
||||
func runQueuedOperations(queueOps map[int][]simulation.Operation,
|
||||
height int, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp,
|
||||
ctx sdk.Context, accounts []simulation.Account, logWriter LogWriter,
|
||||
event func(route, op, evResult string), lean bool, chainID string,
|
||||
func runQueuedOperations(
|
||||
tb testing.TB,
|
||||
queueOps map[int][]simulation.Operation,
|
||||
blockTime time.Time,
|
||||
height int,
|
||||
r *rand.Rand,
|
||||
app *baseapp.BaseApp,
|
||||
ctx sdk.Context,
|
||||
accounts []simulation.Account,
|
||||
logWriter LogWriter,
|
||||
event func(route, op, evResult string),
|
||||
lean bool,
|
||||
chainID string,
|
||||
) (numOpsRan int, allFutureOps []simulation.FutureOperation) {
|
||||
tb.Helper()
|
||||
queuedOp, ok := queueOps[height]
|
||||
if !ok {
|
||||
return 0, nil
|
||||
@ -398,12 +408,13 @@ func runQueuedOperations(queueOps map[int][]simulation.Operation,
|
||||
return numOpsRan, allFutureOps
|
||||
}
|
||||
|
||||
func runQueuedTimeOperations(queueOps []simulation.FutureOperation,
|
||||
height int, currentTime time.Time, tb testing.TB, r *rand.Rand,
|
||||
func runQueuedTimeOperations(tb testing.TB, queueOps []simulation.FutureOperation,
|
||||
height int, currentTime time.Time, r *rand.Rand,
|
||||
app *baseapp.BaseApp, ctx sdk.Context, accounts []simulation.Account,
|
||||
logWriter LogWriter, event func(route, op, evResult string),
|
||||
lean bool, chainID string,
|
||||
) (numOpsRan int, allFutureOps []simulation.FutureOperation) {
|
||||
tb.Helper()
|
||||
// Keep all future operations
|
||||
allFutureOps = make([]simulation.FutureOperation, 0)
|
||||
|
||||
@ -432,3 +443,41 @@ func runQueuedTimeOperations(queueOps []simulation.FutureOperation,
|
||||
|
||||
return numOpsRan, allFutureOps
|
||||
}
|
||||
|
||||
const (
|
||||
rngMax = 1 << 63
|
||||
rngMask = rngMax - 1
|
||||
)
|
||||
|
||||
// byteSource offers deterministic pseudo-random numbers for math.Rand with fuzzer support.
|
||||
// The 'seed' data is read in big endian to uint64. When exhausted,
|
||||
// it falls back to a standard random number generator initialized with a specific 'seed' value.
|
||||
type byteSource struct {
|
||||
seed *bytes.Reader
|
||||
fallback *rand.Rand
|
||||
}
|
||||
|
||||
// newByteSource creates a new byteSource with a specified byte slice and seed. This gives a fixed sequence of pseudo-random numbers.
|
||||
// Initially, it utilizes the byte slice. Once that's exhausted, it continues generating numbers using the provided seed.
|
||||
func newByteSource(fuzzSeed []byte, seed int64) *byteSource {
|
||||
return &byteSource{
|
||||
seed: bytes.NewReader(fuzzSeed),
|
||||
fallback: rand.New(rand.NewSource(seed)),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *byteSource) Uint64() uint64 {
|
||||
if s.seed.Len() < 8 {
|
||||
return s.fallback.Uint64()
|
||||
}
|
||||
var b [8]byte
|
||||
if _, err := s.seed.Read(b[:]); err != nil && err != io.EOF {
|
||||
panic(err) // Should not happen.
|
||||
}
|
||||
return binary.BigEndian.Uint64(b[:])
|
||||
}
|
||||
|
||||
func (s *byteSource) Int63() int64 {
|
||||
return int64(s.Uint64() & rngMask)
|
||||
}
|
||||
func (s *byteSource) Seed(seed int64) {}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user