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