package simapp import ( "encoding/json" "fmt" "io" "io/ioutil" "math/rand" "time" tmjson "github.com/tendermint/tendermint/libs/json" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" simapparams "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) // AppStateFn returns the initial application state using a genesis or the simulation parameters. // It panics if the user provides files for both of them. // If a file is not given for the genesis or the sim params, it creates a randomized one. func AppStateFn(cdc codec.JSONMarshaler, simManager *module.SimulationManager) simtypes.AppStateFn { return func(r *rand.Rand, accs []simtypes.Account, config simtypes.Config, ) (appState json.RawMessage, simAccs []simtypes.Account, chainID string, genesisTimestamp time.Time) { if FlagGenesisTimeValue == 0 { genesisTimestamp = simtypes.RandTimestamp(r) } else { genesisTimestamp = time.Unix(FlagGenesisTimeValue, 0) } chainID = config.ChainID switch { case config.ParamsFile != "" && config.GenesisFile != "": panic("cannot provide both a genesis file and a params file") case config.GenesisFile != "": // override the default chain-id from simapp to set it later to the config genesisDoc, accounts := AppStateFromGenesisFileFn(r, cdc, config.GenesisFile) if FlagGenesisTimeValue == 0 { // use genesis timestamp if no custom timestamp is provided (i.e no random timestamp) genesisTimestamp = genesisDoc.GenesisTime } appState = genesisDoc.AppState chainID = genesisDoc.ChainID simAccs = accounts case config.ParamsFile != "": appParams := make(simtypes.AppParams) bz, err := ioutil.ReadFile(config.ParamsFile) if err != nil { panic(err) } err = json.Unmarshal(bz, &appParams) if err != nil { panic(err) } appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) default: appParams := make(simtypes.AppParams) appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) } return appState, simAccs, chainID, genesisTimestamp } } // AppStateRandomizedFn creates calls each module's GenesisState generator function // and creates the simulation params func AppStateRandomizedFn( simManager *module.SimulationManager, r *rand.Rand, cdc codec.JSONMarshaler, accs []simtypes.Account, genesisTimestamp time.Time, appParams simtypes.AppParams, ) (json.RawMessage, []simtypes.Account) { numAccs := int64(len(accs)) genesisState := NewDefaultGenesisState() // generate a random amount of initial stake coins and a random initial // number of bonded accounts var initialStake, numInitiallyBonded int64 appParams.GetOrGenerate( cdc, simapparams.StakePerAccount, &initialStake, r, func(r *rand.Rand) { initialStake = r.Int63n(1e12) }, ) appParams.GetOrGenerate( cdc, simapparams.InitiallyBondedValidators, &numInitiallyBonded, r, func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(300)) }, ) if numInitiallyBonded > numAccs { numInitiallyBonded = numAccs } fmt.Printf( `Selected randomly generated parameters for simulated genesis: { stake_per_account: "%d", initially_bonded_validators: "%d" } `, initialStake, numInitiallyBonded, ) simState := &module.SimulationState{ AppParams: appParams, Cdc: cdc, Rand: r, GenState: genesisState, Accounts: accs, InitialStake: initialStake, NumBonded: numInitiallyBonded, GenTimestamp: genesisTimestamp, } simManager.GenerateGenesisStates(simState) appState, err := json.Marshal(genesisState) if err != nil { panic(err) } return appState, accs } // AppStateFromGenesisFileFn util function to generate the genesis AppState // from a genesis.json file. func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONMarshaler, genesisFile string) (tmtypes.GenesisDoc, []simtypes.Account) { bytes, err := ioutil.ReadFile(genesisFile) if err != nil { panic(err) } var genesis tmtypes.GenesisDoc // NOTE: Tendermint uses a custom JSON decoder for GenesisDoc err = tmjson.Unmarshal(bytes, &genesis) if err != nil { panic(err) } var appState GenesisState err = json.Unmarshal(genesis.AppState, &appState) if err != nil { panic(err) } var authGenesis authtypes.GenesisState if appState[authtypes.ModuleName] != nil { cdc.MustUnmarshalJSON(appState[authtypes.ModuleName], &authGenesis) } newAccs := make([]simtypes.Account, len(authGenesis.Accounts)) for i, acc := range authGenesis.Accounts { // Pick a random private key, since we don't know the actual key // This should be fine as it's only used for mock Tendermint validators // and these keys are never actually used to sign by mock Tendermint. privkeySeed := make([]byte, 15) if _, err := r.Read(privkeySeed); err != nil { panic(err) } privKey := secp256k1.GenPrivKeyFromSecret(privkeySeed) a, ok := acc.GetCachedValue().(authtypes.AccountI) if !ok { panic("expected account") } // create simulator accounts simAcc := simtypes.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: a.GetAddress()} newAccs[i] = simAcc } return genesis, newAccs }