diff --git a/sims.mk b/sims.mk index 5ce445832b..65301e0e9e 100644 --- a/sims.mk +++ b/sims.mk @@ -6,6 +6,8 @@ BINDIR ?= $(GOPATH)/bin SIMAPP = ./tests/simulation + +# Run sim is a cosmos tool which helps us to run multiple simulations in parallel. runsim: $(BINDIR)/runsim $(BINDIR)/runsim: @echo "Installing runsim..." @@ -15,13 +17,14 @@ $(BINDIR)/runsim: test-sim-nondeterminism: @echo "Running non-determinism test..." @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ - -NumBlocks=10 -BlockSize=20 -Commit=true -Period=0 -v -timeout 24h + -NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h test-sim-fullappsimulation: @echo "Running TestFullAppSimulation." @go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation -Enabled=true \ - -NumBlocks=10 -BlockSize=20 -Commit=true -Period=0 -v -timeout 24h + -NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h + test-sim-multi-seed-long: runsim @echo "Running long multi-seed application simulation. This may take awhile!" @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 500 50 TestFullAppSimulation diff --git a/tests/simulation/sim/sim_state.go b/tests/simulation/sim/sim_state.go index 12df57dc5c..8b4d73a0c6 100644 --- a/tests/simulation/sim/sim_state.go +++ b/tests/simulation/sim/sim_state.go @@ -107,9 +107,9 @@ func AppStateFn(cdc codec.Codec, simManager *module.SimulationManager, genesisSt notBondedTokens = notBondedTokens.Add(val.GetTokens()) } 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 if !ok { panic("bank genesis state is missing") } @@ -134,6 +134,7 @@ func AppStateFn(cdc codec.Codec, simManager *module.SimulationManager, genesisSt }) } + // Set the bond denom in the EVM genesis state evmStateBz, ok := rawState[evmtypes.ModuleName] if !ok { panic("evm genesis state is missing") @@ -207,8 +208,6 @@ func AppStateRandomizedFn( GenTimestamp: genesisTimestamp, } - fmt.Println("Generating genesis states...") - simManager.GenerateGenesisStates(simState) appState, err := json.Marshal(genesisState) diff --git a/tests/simulation/sim_test.go b/tests/simulation/sim_test.go index 934f57b82f..4efb6a1646 100644 --- a/tests/simulation/sim_test.go +++ b/tests/simulation/sim_test.go @@ -39,14 +39,16 @@ func init() { const ( SimAppChainID = "simulation_777-1" SimBlockMaxGas = 815000000 - TestAppChainID = "zetachain_777-1" + SimDBBackend = "goleveldb" + SimDBName = "simulation" ) -// NewSimApp disable feemarket on native tx, otherwise the cosmos-sdk simulation tests will fail. func NewSimApp(logger log.Logger, db dbm.DB, appOptions servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp)) (*zetaapp.App, error) { encCdc := zetaapp.MakeEncodingConfig() - app := zetaapp.New( + + // Set load latest version to false as we manually set it later. + zetaApp := zetaapp.New( logger, db, nil, @@ -58,17 +60,20 @@ func NewSimApp(logger log.Logger, db dbm.DB, appOptions servertypes.AppOptions, appOptions, baseAppOptions..., ) + + // Set power reduction to 1 to make sure all bonded validators are added to the validator set sdk.DefaultPowerReduction = sdk.OneInt() - // disable feemarket on native tx + + // use zeta antehandler options := ante.HandlerOptions{ - AccountKeeper: app.AccountKeeper, - BankKeeper: app.BankKeeper, - EvmKeeper: app.EvmKeeper, - FeeMarketKeeper: app.FeeMarketKeeper, + AccountKeeper: zetaApp.AccountKeeper, + BankKeeper: zetaApp.BankKeeper, + EvmKeeper: zetaApp.EvmKeeper, + FeeMarketKeeper: zetaApp.FeeMarketKeeper, SignModeHandler: encCdc.TxConfig.SignModeHandler(), SigGasConsumer: evmante.DefaultSigVerificationGasConsumer, MaxTxGasWanted: 0, - ObserverKeeper: app.ObserverKeeper, + ObserverKeeper: zetaApp.ObserverKeeper, } anteHandler, err := ante.NewAnteHandler(options) @@ -76,11 +81,11 @@ func NewSimApp(logger log.Logger, db dbm.DB, appOptions servertypes.AppOptions, panic(err) } - app.SetAnteHandler(anteHandler) - if err := app.LoadLatestVersion(); err != nil { + zetaApp.SetAnteHandler(anteHandler) + if err := zetaApp.LoadLatestVersion(); err != nil { return nil, err } - return app, nil + return zetaApp, nil } // interBlockCacheOpt returns a BaseApp option function that sets the persistent @@ -89,20 +94,24 @@ func interBlockCacheOpt() func(*baseapp.BaseApp) { return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager()) } -// TODO: Make another test for the fuzzer itself, which just has noOp txs -// and doesn't depend on the application. +// TestAppStateDeterminism runs a full application simulation , and produces multiple blocks as per the config +// It checks the determinism of the application by comparing the apphash at the end of each run to other runs +// The following test certifies that , for the same set of operations ( irrespective of what the operations are ) , +// we would reach the same final state func TestAppStateDeterminism(t *testing.T) { if !simutils.FlagEnabledValue { t.Skip("skipping application simulation") } config := simutils.NewConfigFromFlags() + config.InitialBlockHeight = 1 config.ExportParamsPath = "" config.OnOperation = false config.AllInvariants = false config.ChainID = SimAppChainID - config.DBBackend = "goleveldb" + config.DBBackend = SimDBBackend + config.BlockMaxGas = SimBlockMaxGas numSeeds := 3 numTimesToRunPerSeed := 5 @@ -112,59 +121,51 @@ func TestAppStateDeterminism(t *testing.T) { numSeeds = 1 } + // For the same seed, the app hash produced at the end of each run should be the same appHashList := make([]json.RawMessage, numTimesToRunPerSeed) + appOptions := make(cosmossimutils.AppOptionsMap, 0) appOptions[server.FlagInvCheckPeriod] = simutils.FlagPeriodValue + fmt.Println("Running tests for numSeeds: ", numSeeds, " numTimesToRunPerSeed: ", numTimesToRunPerSeed) + for i := 0; i < numSeeds; i++ { if config.Seed == cosmossimcli.DefaultSeedValue { config.Seed = rand.Int63() } - fmt.Println("config.Seed: ", config.Seed) - + //dbPrefix := fmt.Sprintf("%s-%d", SimDBBackend, i) + // For the same seed, the app hash produced at the end of each run should be the same for j := 0; j < numTimesToRunPerSeed; j++ { - var logger log.Logger - if simutils.FlagVerboseValue { - logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) - } else { - logger = log.NewNopLogger() - } - - db, dir, logger, skip, err := cosmossimutils.SetupSimulation(config, "level-db", "Simulation", simutils.FlagVerboseValue, simutils.FlagEnabledValue) - if skip { - t.Skip("skipping application simulation") - } + db, dir, logger, _, err := cosmossimutils.SetupSimulation(config, SimDBBackend, SimDBName, simutils.FlagVerboseValue, simutils.FlagEnabledValue) require.NoError(t, err) appOptions[flags.FlagHome] = dir - app, err := NewSimApp(logger, db, appOptions, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID)) + simApp, err := NewSimApp(logger, db, appOptions, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID)) fmt.Printf( "running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed, ) - blockedAddresses := app.ModuleAccountAddrs() + blockedAddresses := simApp.ModuleAccountAddrs() _, _, err = simulation.SimulateFromSeed( t, os.Stdout, - app.BaseApp, - simutils.AppStateFn(app.AppCodec(), app.SimulationManager(), app.ModuleBasics.DefaultGenesis(app.AppCodec())), - cosmossim.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 - cosmossimutils.SimulationOperations(app, app.AppCodec(), config), + simApp.BaseApp, + simutils.AppStateFn(simApp.AppCodec(), simApp.SimulationManager(), simApp.ModuleBasics.DefaultGenesis(simApp.AppCodec())), + cosmossim.RandomAccounts, + cosmossimutils.SimulationOperations(simApp, simApp.AppCodec(), config), blockedAddresses, config, - app.AppCodec(), + simApp.AppCodec(), ) require.NoError(t, err) - if config.Commit { - simutils.PrintStats(db) - } + simutils.PrintStats(db) - appHash := app.LastCommitID().Hash + appHash := simApp.LastCommitID().Hash appHashList[j] = appHash if j != 0 { @@ -177,14 +178,17 @@ func TestAppStateDeterminism(t *testing.T) { } } +// TestFullAppSimulation runs a full app simulation with the provided configuration. +// At the end of the run it tries to export the genesis state to make sure the export works. func TestFullAppSimulation(t *testing.T) { + config := simutils.NewConfigFromFlags() + config.ChainID = SimAppChainID config.BlockMaxGas = SimBlockMaxGas - config.DBBackend = "goleveldb" - //config.ExportStatePath = "/Users/tanmay/.zetacored/simulation_state_export.json" + config.DBBackend = SimDBBackend - db, dir, logger, skip, err := cosmossimutils.SetupSimulation(config, "level-db", "Simulation", simutils.FlagVerboseValue, simutils.FlagEnabledValue) + db, dir, logger, skip, err := cosmossimutils.SetupSimulation(config, SimDBBackend, SimDBName, simutils.FlagVerboseValue, simutils.FlagEnabledValue) if skip { t.Skip("skipping application simulation") } @@ -196,29 +200,32 @@ func TestFullAppSimulation(t *testing.T) { }() appOptions := make(cosmossimutils.AppOptionsMap, 0) appOptions[server.FlagInvCheckPeriod] = simutils.FlagPeriodValue + appOptions[flags.FlagHome] = dir - app, err := NewSimApp(logger, db, appOptions, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID)) + simApp, err := NewSimApp(logger, db, appOptions, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID)) require.NoError(t, err) - blockedAddresses := app.ModuleAccountAddrs() + blockedAddresses := simApp.ModuleAccountAddrs() _, _, simerr := simulation.SimulateFromSeed( t, os.Stdout, - app.BaseApp, - simutils.AppStateFn(app.AppCodec(), app.SimulationManager(), app.ModuleBasics.DefaultGenesis(app.AppCodec())), + simApp.BaseApp, + simutils.AppStateFn(simApp.AppCodec(), simApp.SimulationManager(), simApp.ModuleBasics.DefaultGenesis(simApp.AppCodec())), cosmossim.RandomAccounts, - cosmossimutils.SimulationOperations(app, app.AppCodec(), config), + cosmossimutils.SimulationOperations(simApp, simApp.AppCodec(), config), blockedAddresses, config, - app.AppCodec(), + simApp.AppCodec(), ) require.NoError(t, simerr) // check export works as expected - _, err = app.ExportAppStateAndValidators(false, nil, nil) + exported, err := simApp.ExportAppStateAndValidators(false, nil, nil) require.NoError(t, err) - - if config.Commit { - cosmossimutils.PrintStats(db) + if config.ExportStatePath != "" { + err := os.WriteFile(config.ExportStatePath, exported.AppState, 0o600) + require.NoError(t, err) } + + cosmossimutils.PrintStats(db) } diff --git a/testutil/simapp/default_constants.go b/testutil/simapp/default_constants.go deleted file mode 100644 index c4398ad7bb..0000000000 --- a/testutil/simapp/default_constants.go +++ /dev/null @@ -1,25 +0,0 @@ -package simapp - -import ( - "time" - - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmtypes "github.com/cometbft/cometbft/types" -) - -var defaultConsensusParams = &tmproto.ConsensusParams{ - Block: &tmproto.BlockParams{ - MaxBytes: 200000, - MaxGas: 2000000, - }, - Evidence: &tmproto.EvidenceParams{ - MaxAgeNumBlocks: 302400, - MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration - MaxBytes: 10000, - }, - Validator: &tmproto.ValidatorParams{ - PubKeyTypes: []string{ - tmtypes.ABCIPubKeyTypeEd25519, - }, - }, -} diff --git a/testutil/simapp/simapp.go b/testutil/simapp/simapp.go deleted file mode 100644 index b42b0b5f1a..0000000000 --- a/testutil/simapp/simapp.go +++ /dev/null @@ -1,219 +0,0 @@ -package simapp - -import ( - "encoding/json" - "testing" - "time" - - tmdb "github.com/cometbft/cometbft-db" - abci "github.com/cometbft/cometbft/abci/types" - "github.com/cometbft/cometbft/libs/log" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmtypes "github.com/cometbft/cometbft/types" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/stretchr/testify/require" - - "github.com/zeta-chain/node/app" - "github.com/zeta-chain/node/cmd/zetacored/config" - types2 "github.com/zeta-chain/node/x/emissions/types" -) - -func Setup(isCheckTx bool) *app.App { - app, genesisState := setup(!isCheckTx, 5) - if !isCheckTx { - // init chain must be called to stop deliverState from being nil - stateBytes, err := json.MarshalIndent(genesisState, "", " ") - if err != nil { - panic(err) - } - - // Initialize the chain - app.InitChain( - abci.RequestInitChain{ - ChainId: "simnet_101-1", - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: defaultConsensusParams, - AppStateBytes: stateBytes, - }, - ) - } - - return app -} - -func setup(withGenesis bool, invCheckPeriod uint) (*app.App, app.GenesisState) { - db := tmdb.NewMemDB() - encCdc := app.MakeEncodingConfig() - a := app.New( - log.NewNopLogger(), - db, - nil, - true, - map[int64]bool{}, - app.DefaultNodeHome, - invCheckPeriod, - encCdc, - simtestutil.EmptyAppOptions{}, - ) - if withGenesis { - return a, app.NewDefaultGenesisState(encCdc.Codec) - } - return a, app.GenesisState{} -} - -func SetupWithGenesisValSet( - t *testing.T, - valSet *tmtypes.ValidatorSet, - genDelAccs []authtypes.GenesisAccount, - bondAmt sdk.Int, - emissionParams types2.Params, - genDelBalances []banktypes.Balance, - genBalances []banktypes.Balance, -) *app.App { - app, genesisState := setup(true, 5) - // set genesis accounts - authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genDelAccs) - genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) - - validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) - delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) - // Make all members of valSet as validators - // Make all members of delSet as delegators to each of the validators - for _, val := range valSet.Validators { - pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) - require.NoError(t, err) - pkAny, err := codectypes.NewAnyWithValue(pk) - require.NoError(t, err) - validator := stakingtypes.Validator{ - OperatorAddress: sdk.ValAddress(val.Address).String(), - ConsensusPubkey: pkAny, - Jailed: false, - Status: stakingtypes.Bonded, - Tokens: bondAmt, - DelegatorShares: sdk.OneDec(), - Description: stakingtypes.Description{}, - UnbondingHeight: int64(0), - UnbondingTime: time.Unix(0, 0).UTC(), - Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), - MinSelfDelegation: sdk.ZeroInt(), - } - validators = append(validators, validator) - delegations = append( - delegations, - stakingtypes.NewDelegation(genDelAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec()), - ) - } - - emissionsGenesis := types2.DefaultGenesis() - emissionsGenesis.Params = emissionParams - genesisState[types2.ModuleName] = app.AppCodec().MustMarshalJSON(emissionsGenesis) - // set validators and delegations - params := stakingtypes.DefaultParams() - params.BondDenom = config.BaseDenom - stakingGenesis := stakingtypes.NewGenesisState(params, validators, delegations) - genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis) - - totalSupply := sdk.NewCoins() - // genDelBalances contains additional balances for delegators - // Add Bond amount and additional coins for these addresses - - for _, b := range genDelBalances { - // add genesis acc tokens and delegated tokens to total supply - totalSupply = totalSupply.Add(b.Coins.Add(sdk.NewCoin(config.BaseDenom, bondAmt))...) - } - // add balances for non delegator accounts - // Add only external balances - for _, b := range genBalances { - // add genesis acc tokens and delegated tokens to total supply - totalSupply = totalSupply.Add(b.Coins...) - } - - totalBalances := []banktypes.Balance{} - // Add extra balance to account for delegator bonded pool - totalBalances = append(append(append(totalBalances, genBalances...), genDelBalances...), banktypes.Balance{ - Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), - Coins: sdk.Coins{sdk.NewCoin(config.BaseDenom, bondAmt)}, - }) - - // update total supply - - bankGenesis := banktypes.NewGenesisState( - banktypes.DefaultGenesisState().Params, - totalBalances, - totalSupply, - []banktypes.Metadata{}, - []banktypes.SendEnabled{}, - ) - genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) - - stateBytes, err := json.MarshalIndent(genesisState, "", " ") - require.NoError(t, err) - - // init chain will set the validator set and initialize the genesis accounts - app.InitChain( - abci.RequestInitChain{ - ChainId: "simnet_101-1", - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: defaultConsensusParams, - AppStateBytes: stateBytes, - }, - ) - - // commit genesis changes - app.Commit() - - app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ - Height: app.LastBlockHeight() + 1, - AppHash: app.LastCommitID().Hash, - ValidatorsHash: valSet.Hash(), - NextValidatorsHash: valSet.Hash(), - ChainID: "simnet_101-1", - }}) - - return app -} - -func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *app.App { - app, genesisState := setup(true, 0) - authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) - genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) - - totalSupply := sdk.NewCoins() - for _, b := range balances { - totalSupply = totalSupply.Add(b.Coins...) - } - - bankGenesis := banktypes.NewGenesisState( - banktypes.DefaultGenesisState().Params, - balances, - totalSupply, - []banktypes.Metadata{}, - []banktypes.SendEnabled{}, - ) - genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) - - stateBytes, err := json.MarshalIndent(genesisState, "", " ") - if err != nil { - panic(err) - } - - app.InitChain( - abci.RequestInitChain{ - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: defaultConsensusParams, - AppStateBytes: stateBytes, - }, - ) - - app.Commit() - - app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1}}) - - return app -}