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  }