github.com/Finschia/finschia-sdk@v0.48.1/simapp/sim_test.go (about) 1 package simapp 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "math/rand" 7 "os" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 abci "github.com/tendermint/tendermint/abci/types" 12 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 13 dbm "github.com/tendermint/tm-db" 14 15 "github.com/Finschia/ostracon/libs/log" 16 17 "github.com/Finschia/finschia-sdk/baseapp" 18 "github.com/Finschia/finschia-sdk/simapp/helpers" 19 "github.com/Finschia/finschia-sdk/store" 20 "github.com/Finschia/finschia-sdk/store/cache" 21 sdk "github.com/Finschia/finschia-sdk/types" 22 simtypes "github.com/Finschia/finschia-sdk/types/simulation" 23 authtypes "github.com/Finschia/finschia-sdk/x/auth/types" 24 authzkeeper "github.com/Finschia/finschia-sdk/x/authz/keeper" 25 banktypes "github.com/Finschia/finschia-sdk/x/bank/types" 26 capabilitytypes "github.com/Finschia/finschia-sdk/x/capability/types" 27 distrtypes "github.com/Finschia/finschia-sdk/x/distribution/types" 28 evidencetypes "github.com/Finschia/finschia-sdk/x/evidence/types" 29 govtypes "github.com/Finschia/finschia-sdk/x/gov/types" 30 minttypes "github.com/Finschia/finschia-sdk/x/mint/types" 31 paramtypes "github.com/Finschia/finschia-sdk/x/params/types" 32 "github.com/Finschia/finschia-sdk/x/simulation" 33 slashingtypes "github.com/Finschia/finschia-sdk/x/slashing/types" 34 stakingtypes "github.com/Finschia/finschia-sdk/x/staking/types" 35 ) 36 37 // Get flags every time the simulator is run 38 func init() { 39 GetSimulatorFlags() 40 } 41 42 type StoreKeysPrefixes struct { 43 A sdk.StoreKey 44 B sdk.StoreKey 45 Prefixes [][]byte 46 } 47 48 // fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of 49 // an IAVLStore for faster simulation speed. 50 func fauxMerkleModeOpt(bapp *baseapp.BaseApp) { 51 bapp.SetFauxMerkleMode() 52 } 53 54 // interBlockCacheOpt returns a BaseApp option function that sets the persistent 55 // inter-block write-through cache. 56 func interBlockCacheOpt() func(*baseapp.BaseApp) { 57 return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager( 58 cache.DefaultCommitKVStoreCacheSize, cache.NopMetricsProvider())) 59 } 60 61 func TestFullAppSimulation(t *testing.T) { 62 config, db, dir, logger, skip, err := SetupSimulation("leveldb-app-sim", "Simulation") 63 if skip { 64 t.Skip("skipping application simulation") 65 } 66 require.NoError(t, err, "simulation setup failed") 67 68 defer func() { 69 db.Close() 70 require.NoError(t, os.RemoveAll(dir)) 71 }() 72 73 app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) 74 require.Equal(t, "SimApp", app.Name()) 75 76 // run randomized simulation 77 _, simParams, simErr := simulation.SimulateFromSeed( 78 t, 79 os.Stdout, 80 app.BaseApp, 81 AppStateFn(app.AppCodec(), app.SimulationManager()), 82 simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 83 SimulationOperations(app, app.AppCodec(), config), 84 app.ModuleAccountAddrs(), 85 config, 86 app.AppCodec(), 87 ) 88 89 // export state and simParams before the simulation error is checked 90 err = CheckExportSimulation(app, config, simParams) 91 require.NoError(t, err) 92 require.NoError(t, simErr) 93 94 if config.Commit { 95 PrintStats(db) 96 } 97 } 98 99 func TestAppImportExport(t *testing.T) { 100 config, db, dir, logger, skip, err := SetupSimulation("leveldb-app-sim", "Simulation") 101 if skip { 102 t.Skip("skipping application import/export simulation") 103 } 104 require.NoError(t, err, "simulation setup failed") 105 106 defer func() { 107 db.Close() 108 require.NoError(t, os.RemoveAll(dir)) 109 }() 110 111 app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) 112 require.Equal(t, "SimApp", app.Name()) 113 114 // Run randomized simulation 115 _, simParams, simErr := simulation.SimulateFromSeed( 116 t, 117 os.Stdout, 118 app.BaseApp, 119 AppStateFn(app.AppCodec(), app.SimulationManager()), 120 simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 121 SimulationOperations(app, app.AppCodec(), config), 122 app.ModuleAccountAddrs(), 123 config, 124 app.AppCodec(), 125 ) 126 127 // export state and simParams before the simulation error is checked 128 err = CheckExportSimulation(app, config, simParams) 129 require.NoError(t, err) 130 require.NoError(t, simErr) 131 132 if config.Commit { 133 PrintStats(db) 134 } 135 136 fmt.Printf("exporting genesis...\n") 137 138 exported, err := app.ExportAppStateAndValidators(false, []string{}) 139 require.NoError(t, err) 140 141 fmt.Printf("importing genesis...\n") 142 143 _, newDB, newDir, _, _, err := SetupSimulation("leveldb-app-sim-2", "Simulation-2") 144 require.NoError(t, err, "simulation setup failed") 145 146 defer func() { 147 newDB.Close() 148 require.NoError(t, os.RemoveAll(newDir)) 149 }() 150 151 newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) 152 require.Equal(t, "SimApp", newApp.Name()) 153 154 var genesisState GenesisState 155 err = json.Unmarshal(exported.AppState, &genesisState) 156 require.NoError(t, err) 157 158 ctxA := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) 159 ctxB := newApp.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) 160 newApp.mm.InitGenesis(ctxB, app.AppCodec(), genesisState) 161 newApp.StoreConsensusParams(ctxB, exported.ConsensusParams) 162 163 fmt.Printf("comparing stores...\n") 164 165 storeKeysPrefixes := []StoreKeysPrefixes{ 166 {app.keys[authtypes.StoreKey], newApp.keys[authtypes.StoreKey], [][]byte{}}, 167 { 168 app.keys[stakingtypes.StoreKey], newApp.keys[stakingtypes.StoreKey], 169 [][]byte{ 170 stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey, 171 stakingtypes.HistoricalInfoKey, 172 }, 173 }, // ordering may change but it doesn't matter 174 {app.keys[slashingtypes.StoreKey], newApp.keys[slashingtypes.StoreKey], [][]byte{}}, 175 {app.keys[minttypes.StoreKey], newApp.keys[minttypes.StoreKey], [][]byte{}}, 176 {app.keys[distrtypes.StoreKey], newApp.keys[distrtypes.StoreKey], [][]byte{}}, 177 {app.keys[banktypes.StoreKey], newApp.keys[banktypes.StoreKey], [][]byte{banktypes.BalancesPrefix}}, 178 {app.keys[paramtypes.StoreKey], newApp.keys[paramtypes.StoreKey], [][]byte{}}, 179 {app.keys[govtypes.StoreKey], newApp.keys[govtypes.StoreKey], [][]byte{}}, 180 {app.keys[evidencetypes.StoreKey], newApp.keys[evidencetypes.StoreKey], [][]byte{}}, 181 {app.keys[capabilitytypes.StoreKey], newApp.keys[capabilitytypes.StoreKey], [][]byte{}}, 182 {app.keys[authzkeeper.StoreKey], newApp.keys[authzkeeper.StoreKey], [][]byte{}}, 183 } 184 185 for _, skp := range storeKeysPrefixes { 186 storeA := ctxA.KVStore(skp.A) 187 storeB := ctxB.KVStore(skp.B) 188 189 failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, skp.Prefixes) 190 require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare") 191 192 fmt.Printf("compared %d different key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B) 193 require.Equal(t, len(failedKVAs), 0, GetSimulationLog(skp.A.Name(), app.SimulationManager().StoreDecoders, failedKVAs, failedKVBs)) 194 } 195 } 196 197 func TestAppSimulationAfterImport(t *testing.T) { 198 config, db, dir, logger, skip, err := SetupSimulation("leveldb-app-sim", "Simulation") 199 if skip { 200 t.Skip("skipping application simulation after import") 201 } 202 require.NoError(t, err, "simulation setup failed") 203 204 defer func() { 205 db.Close() 206 require.NoError(t, os.RemoveAll(dir)) 207 }() 208 209 app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) 210 require.Equal(t, "SimApp", app.Name()) 211 212 // Run randomized simulation 213 stopEarly, simParams, simErr := simulation.SimulateFromSeed( 214 t, 215 os.Stdout, 216 app.BaseApp, 217 AppStateFn(app.AppCodec(), app.SimulationManager()), 218 simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 219 SimulationOperations(app, app.AppCodec(), config), 220 app.ModuleAccountAddrs(), 221 config, 222 app.AppCodec(), 223 ) 224 225 // export state and simParams before the simulation error is checked 226 err = CheckExportSimulation(app, config, simParams) 227 require.NoError(t, err) 228 require.NoError(t, simErr) 229 230 if config.Commit { 231 PrintStats(db) 232 } 233 234 if stopEarly { 235 fmt.Println("can't export or import a zero-validator genesis, exiting test...") 236 return 237 } 238 239 fmt.Printf("exporting genesis...\n") 240 241 exported, err := app.ExportAppStateAndValidators(true, []string{}) 242 require.NoError(t, err) 243 244 fmt.Printf("importing genesis...\n") 245 246 _, newDB, newDir, _, _, err := SetupSimulation("leveldb-app-sim-2", "Simulation-2") 247 require.NoError(t, err, "simulation setup failed") 248 249 defer func() { 250 newDB.Close() 251 require.NoError(t, os.RemoveAll(newDir)) 252 }() 253 254 newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, MakeTestEncodingConfig(), EmptyAppOptions{}, fauxMerkleModeOpt) 255 require.Equal(t, "SimApp", newApp.Name()) 256 257 newApp.InitChain(abci.RequestInitChain{ 258 AppStateBytes: exported.AppState, 259 }) 260 261 _, _, err = simulation.SimulateFromSeed( 262 t, 263 os.Stdout, 264 newApp.BaseApp, 265 AppStateFn(app.AppCodec(), app.SimulationManager()), 266 simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 267 SimulationOperations(newApp, newApp.AppCodec(), config), 268 app.ModuleAccountAddrs(), 269 config, 270 app.AppCodec(), 271 ) 272 require.NoError(t, err) 273 } 274 275 // TODO: Make another test for the fuzzer itself, which just has noOp txs 276 // and doesn't depend on the application. 277 func TestAppStateDeterminism(t *testing.T) { 278 if !FlagEnabledValue { 279 t.Skip("skipping application simulation") 280 } 281 282 config := NewConfigFromFlags() 283 config.InitialBlockHeight = 1 284 config.ExportParamsPath = "" 285 config.OnOperation = false 286 config.AllInvariants = false 287 config.ChainID = helpers.SimAppChainID 288 289 numSeeds := 3 290 numTimesToRunPerSeed := 5 291 appHashList := make([]json.RawMessage, numTimesToRunPerSeed) 292 293 for i := 0; i < numSeeds; i++ { 294 config.Seed = rand.Int63() 295 296 for j := 0; j < numTimesToRunPerSeed; j++ { 297 var logger log.Logger 298 if FlagVerboseValue { 299 logger = log.TestingLogger() 300 } else { 301 logger = log.NewNopLogger() 302 } 303 304 db := dbm.NewMemDB() 305 app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, 306 MakeTestEncodingConfig(), EmptyAppOptions{}, interBlockCacheOpt()) 307 308 fmt.Printf( 309 "running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n", 310 config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed, 311 ) 312 313 _, _, err := simulation.SimulateFromSeed( 314 t, 315 os.Stdout, 316 app.BaseApp, 317 AppStateFn(app.AppCodec(), app.SimulationManager()), 318 simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 319 SimulationOperations(app, app.AppCodec(), config), 320 app.ModuleAccountAddrs(), 321 config, 322 app.AppCodec(), 323 ) 324 require.NoError(t, err) 325 326 if config.Commit { 327 PrintStats(db) 328 } 329 330 appHash := app.LastCommitID().Hash 331 appHashList[j] = appHash 332 333 if j != 0 { 334 require.Equal( 335 t, string(appHashList[0]), string(appHashList[j]), 336 "non-determinism in seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed, 337 ) 338 } 339 } 340 } 341 }