github.com/Finschia/finschia-sdk@v0.48.1/simapp/state.go (about) 1 package simapp 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "math/rand" 8 "os" 9 "time" 10 11 ostjson "github.com/Finschia/ostracon/libs/json" 12 octypes "github.com/Finschia/ostracon/types" 13 14 "github.com/Finschia/finschia-sdk/codec" 15 "github.com/Finschia/finschia-sdk/crypto/keys/secp256k1" 16 simappparams "github.com/Finschia/finschia-sdk/simapp/params" 17 sdk "github.com/Finschia/finschia-sdk/types" 18 "github.com/Finschia/finschia-sdk/types/module" 19 simtypes "github.com/Finschia/finschia-sdk/types/simulation" 20 authtypes "github.com/Finschia/finschia-sdk/x/auth/types" 21 banktypes "github.com/Finschia/finschia-sdk/x/bank/types" 22 stakingtypes "github.com/Finschia/finschia-sdk/x/staking/types" 23 ) 24 25 // AppStateFn returns the initial application state using a genesis or the simulation parameters. 26 // It panics if the user provides files for both of them. 27 // If a file is not given for the genesis or the sim params, it creates a randomized one. 28 func AppStateFn(cdc codec.JSONCodec, simManager *module.SimulationManager) simtypes.AppStateFn { 29 return func(r *rand.Rand, accs []simtypes.Account, config simtypes.Config, 30 ) (appState json.RawMessage, simAccs []simtypes.Account, chainID string, genesisTimestamp time.Time) { 31 if FlagGenesisTimeValue == 0 { 32 genesisTimestamp = simtypes.RandTimestamp(r) 33 } else { 34 genesisTimestamp = time.Unix(FlagGenesisTimeValue, 0) 35 } 36 37 chainID = config.ChainID 38 switch { 39 case config.ParamsFile != "" && config.GenesisFile != "": 40 panic("cannot provide both a genesis file and a params file") 41 42 case config.GenesisFile != "": 43 // override the default chain-id from simapp to set it later to the config 44 genesisDoc, accounts := AppStateFromGenesisFileFn(r, cdc, config.GenesisFile) 45 46 if FlagGenesisTimeValue == 0 { 47 // use genesis timestamp if no custom timestamp is provided (i.e no random timestamp) 48 genesisTimestamp = genesisDoc.GenesisTime 49 } 50 51 appState = genesisDoc.AppState 52 chainID = genesisDoc.ChainID 53 simAccs = accounts 54 55 case config.ParamsFile != "": 56 appParams := make(simtypes.AppParams) 57 bz, err := os.ReadFile(config.ParamsFile) 58 if err != nil { 59 panic(err) 60 } 61 62 err = json.Unmarshal(bz, &appParams) 63 if err != nil { 64 panic(err) 65 } 66 appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) 67 68 default: 69 appParams := make(simtypes.AppParams) 70 appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams) 71 } 72 73 rawState := make(map[string]json.RawMessage) 74 err := json.Unmarshal(appState, &rawState) 75 if err != nil { 76 panic(err) 77 } 78 79 stakingStateBz, ok := rawState[stakingtypes.ModuleName] 80 if !ok { 81 panic("staking genesis state is missing") 82 } 83 84 stakingState := new(stakingtypes.GenesisState) 85 err = cdc.UnmarshalJSON(stakingStateBz, stakingState) 86 if err != nil { 87 panic(err) 88 } 89 // compute not bonded balance 90 notBondedTokens := sdk.ZeroInt() 91 for _, val := range stakingState.Validators { 92 if val.Status != stakingtypes.Unbonded { 93 continue 94 } 95 notBondedTokens = notBondedTokens.Add(val.GetTokens()) 96 } 97 notBondedCoins := sdk.NewCoin(stakingState.Params.BondDenom, notBondedTokens) 98 // edit bank state to make it have the not bonded pool tokens 99 bankStateBz, ok := rawState[banktypes.ModuleName] 100 // TODO(fdymylja/jonathan): should we panic in this case 101 if !ok { 102 panic("bank genesis state is missing") 103 } 104 bankState := new(banktypes.GenesisState) 105 err = cdc.UnmarshalJSON(bankStateBz, bankState) 106 if err != nil { 107 panic(err) 108 } 109 110 bankState.Balances = append(bankState.Balances, banktypes.Balance{ 111 Address: authtypes.NewModuleAddress(stakingtypes.NotBondedPoolName).String(), 112 Coins: sdk.NewCoins(notBondedCoins), 113 }) 114 115 // change appState back 116 rawState[stakingtypes.ModuleName] = cdc.MustMarshalJSON(stakingState) 117 rawState[banktypes.ModuleName] = cdc.MustMarshalJSON(bankState) 118 119 // replace appstate 120 appState, err = json.Marshal(rawState) 121 if err != nil { 122 panic(err) 123 } 124 return appState, simAccs, chainID, genesisTimestamp 125 } 126 } 127 128 // AppStateRandomizedFn creates calls each module's GenesisState generator function 129 // and creates the simulation params 130 func AppStateRandomizedFn( 131 simManager *module.SimulationManager, r *rand.Rand, cdc codec.JSONCodec, 132 accs []simtypes.Account, genesisTimestamp time.Time, appParams simtypes.AppParams, 133 ) (json.RawMessage, []simtypes.Account) { 134 numAccs := int64(len(accs)) 135 genesisState := NewDefaultGenesisState(cdc) 136 137 // generate a random amount of initial stake coins and a random initial 138 // number of bonded accounts 139 var initialStake, numInitiallyBonded int64 140 appParams.GetOrGenerate( 141 cdc, simappparams.StakePerAccount, &initialStake, r, 142 func(r *rand.Rand) { initialStake = r.Int63n(1e12) }, 143 ) 144 appParams.GetOrGenerate( 145 cdc, simappparams.InitiallyBondedValidators, &numInitiallyBonded, r, 146 func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(300)) }, 147 ) 148 149 if numInitiallyBonded > numAccs { 150 numInitiallyBonded = numAccs 151 } 152 153 fmt.Printf( 154 `Selected randomly generated parameters for simulated genesis: 155 { 156 stake_per_account: "%d", 157 initially_bonded_validators: "%d" 158 } 159 `, initialStake, numInitiallyBonded, 160 ) 161 162 simState := &module.SimulationState{ 163 AppParams: appParams, 164 Cdc: cdc, 165 Rand: r, 166 GenState: genesisState, 167 Accounts: accs, 168 InitialStake: initialStake, 169 NumBonded: numInitiallyBonded, 170 GenTimestamp: genesisTimestamp, 171 } 172 173 simManager.GenerateGenesisStates(simState) 174 175 appState, err := json.Marshal(genesisState) 176 if err != nil { 177 panic(err) 178 } 179 180 return appState, accs 181 } 182 183 // AppStateFromGenesisFileFn util function to generate the genesis AppState 184 // from a genesis.json file. 185 func AppStateFromGenesisFileFn(r io.Reader, cdc codec.JSONCodec, genesisFile string) (octypes.GenesisDoc, []simtypes.Account) { 186 bytes, err := os.ReadFile(genesisFile) 187 if err != nil { 188 panic(err) 189 } 190 191 var genesis octypes.GenesisDoc 192 // NOTE: Tendermint uses a custom JSON decoder for GenesisDoc 193 err = ostjson.Unmarshal(bytes, &genesis) 194 if err != nil { 195 panic(err) 196 } 197 198 var appState GenesisState 199 err = json.Unmarshal(genesis.AppState, &appState) 200 if err != nil { 201 panic(err) 202 } 203 204 var authGenesis authtypes.GenesisState 205 if appState[authtypes.ModuleName] != nil { 206 cdc.MustUnmarshalJSON(appState[authtypes.ModuleName], &authGenesis) 207 } 208 209 newAccs := make([]simtypes.Account, len(authGenesis.Accounts)) 210 for i, acc := range authGenesis.Accounts { 211 // Pick a random private key, since we don't know the actual key 212 // This should be fine as it's only used for mock Tendermint validators 213 // and these keys are never actually used to sign by mock Tendermint. 214 privkeySeed := make([]byte, 15) 215 if _, err := r.Read(privkeySeed); err != nil { 216 panic(err) 217 } 218 219 privKey := secp256k1.GenPrivKeyFromSecret(privkeySeed) 220 221 a, ok := acc.GetCachedValue().(authtypes.AccountI) 222 if !ok { 223 panic("expected account") 224 } 225 226 // create simulator accounts 227 simAcc := simtypes.Account{PrivKey: privKey, PubKey: privKey.PubKey(), Address: a.GetAddress()} 228 newAccs[i] = simAcc 229 } 230 231 return genesis, newAccs 232 }