github.com/Finschia/finschia-sdk@v0.48.1/simapp/test_helpers.go (about) 1 package simapp 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "math/rand" 9 "os" 10 "path/filepath" 11 "sort" 12 "strconv" 13 "testing" 14 "time" 15 16 "github.com/stretchr/testify/require" 17 abci "github.com/tendermint/tendermint/abci/types" 18 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 19 dbm "github.com/tendermint/tm-db" 20 21 ocabci "github.com/Finschia/ostracon/abci/types" 22 "github.com/Finschia/ostracon/libs/log" 23 octypes "github.com/Finschia/ostracon/types" 24 25 bam "github.com/Finschia/finschia-sdk/baseapp" 26 "github.com/Finschia/finschia-sdk/client" 27 "github.com/Finschia/finschia-sdk/crypto/keys/ed25519" 28 cryptotypes "github.com/Finschia/finschia-sdk/crypto/types" 29 "github.com/Finschia/finschia-sdk/simapp/helpers" 30 "github.com/Finschia/finschia-sdk/snapshots" 31 sdk "github.com/Finschia/finschia-sdk/types" 32 "github.com/Finschia/finschia-sdk/types/errors" 33 authtypes "github.com/Finschia/finschia-sdk/x/auth/types" 34 banktypes "github.com/Finschia/finschia-sdk/x/bank/types" 35 minttypes "github.com/Finschia/finschia-sdk/x/mint/types" 36 ) 37 38 // DefaultConsensusParams defines the default Tendermint consensus params used in 39 // SimApp testing. 40 var DefaultConsensusParams = &abci.ConsensusParams{ 41 Block: &abci.BlockParams{ 42 MaxBytes: 200000, 43 MaxGas: 2000000, 44 }, 45 Evidence: &tmproto.EvidenceParams{ 46 MaxAgeNumBlocks: 302400, 47 MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration 48 MaxBytes: 10000, 49 }, 50 Validator: &tmproto.ValidatorParams{ 51 PubKeyTypes: []string{ 52 octypes.ABCIPubKeyTypeEd25519, 53 }, 54 }, 55 } 56 57 func setup(withGenesis bool, invCheckPeriod uint) (*SimApp, GenesisState) { 58 randDir, _ := os.MkdirTemp(DefaultNodeHome, "") 59 snapshotDir := filepath.Join(randDir, "data", "snapshots") 60 snapshotDB := dbm.NewMemDB() 61 62 snapshotStore, _ := snapshots.NewStore(snapshotDB, snapshotDir) 63 baseAppOpts := []func(*bam.BaseApp){bam.SetSnapshotStore(snapshotStore), bam.SetSnapshotKeepRecent(2)} 64 65 db := dbm.NewMemDB() 66 encCdc := MakeTestEncodingConfig() 67 app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, invCheckPeriod, encCdc, EmptyAppOptions{}, baseAppOpts...) 68 if withGenesis { 69 return app, NewDefaultGenesisState(encCdc.Marshaler) 70 } 71 return app, GenesisState{} 72 } 73 74 // Setup initializes a new SimApp. A Nop logger is set in SimApp. 75 func Setup(isCheckTx bool) *SimApp { 76 app, genesisState := setup(!isCheckTx, 5) 77 if !isCheckTx { 78 // init chain must be called to stop deliverState from being nil 79 stateBytes, err := json.MarshalIndent(genesisState, "", " ") 80 if err != nil { 81 panic(err) 82 } 83 84 // Initialize the chain 85 app.InitChain( 86 abci.RequestInitChain{ 87 Validators: []abci.ValidatorUpdate{}, 88 ConsensusParams: DefaultConsensusParams, 89 AppStateBytes: stateBytes, 90 }, 91 ) 92 } 93 94 return app 95 } 96 97 // SetupWithGenesisValSet initializes a new SimApp with a validator set and genesis accounts 98 // that also act as delegators. For simplicity, each validator is bonded with a delegation 99 // of one consensus engine unit (10^6) in the default token of the simapp from first genesis 100 // account. A Nop logger is set in SimApp. 101 //func SetupWithGenesisValSet(t *testing.T, valSet *octypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { 102 // app, genesisState := setup(true, 5) 103 // // set genesis accounts 104 // authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) 105 // genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) 106 // 107 // validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) 108 // delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) 109 // 110 // bondAmt := sdk.NewInt(1000000) 111 // 112 // for _, val := range valSet.Validators { 113 // pk, err := cryptocodec.FromOcPubKeyInterface(val.PubKey) 114 // require.NoError(t, err) 115 // pkAny, err := codectypes.NewAnyWithValue(pk) 116 // require.NoError(t, err) 117 // validator := stakingtypes.Validator{ 118 // OperatorAddress: sdk.ValAddress(val.Address).String(), 119 // ConsensusPubkey: pkAny, 120 // Jailed: false, 121 // Status: stakingtypes.Bonded, 122 // Tokens: bondAmt, 123 // DelegatorShares: sdk.OneDec(), 124 // Description: stakingtypes.Description{}, 125 // UnbondingHeight: int64(0), 126 // UnbondingTime: time.Unix(0, 0).UTC(), 127 // Commission: stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), 128 // MinSelfDelegation: sdk.ZeroInt(), 129 // } 130 // validators = append(validators, validator) 131 // delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec())) 132 // 133 // } 134 // // set validators and delegations 135 // stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) 136 // genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis) 137 // 138 // totalSupply := sdk.NewCoins() 139 // for _, b := range balances { 140 // // add genesis acc tokens and delegated tokens to total supply 141 // totalSupply = totalSupply.Add(b.Coins.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt))...) 142 // } 143 // 144 // // add bonded amount to bonded pool module account 145 // balances = append(balances, banktypes.Balance{ 146 // Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), 147 // Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)}, 148 // }) 149 // 150 // // update total supply 151 // bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}) 152 // genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) 153 // 154 // stateBytes, err := json.MarshalIndent(genesisState, "", " ") 155 // require.NoError(t, err) 156 // 157 // // init chain will set the validator set and initialize the genesis accounts 158 // app.InitChain( 159 // abci.RequestInitChain{ 160 // Validators: []abci.ValidatorUpdate{}, 161 // ConsensusParams: DefaultConsensusParams, 162 // AppStateBytes: stateBytes, 163 // }, 164 // ) 165 // 166 // // commit genesis changes 167 // app.Commit() 168 // app.BeginBlock(ocabci.RequestBeginBlock{Header: tmproto.Header{ 169 // Height: app.LastBlockHeight() + 1, 170 // AppHash: app.LastCommitID().Hash, 171 // ValidatorsHash: valSet.Hash(), 172 // NextValidatorsHash: valSet.Hash(), 173 // }}) 174 // 175 // return app 176 //} 177 178 // SetupWithGenesisAccounts initializes a new SimApp with the provided genesis 179 // accounts and possible balances. 180 func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { 181 app, genesisState := setup(true, 0) 182 authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) 183 genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) 184 185 totalSupply := sdk.NewCoins() 186 for _, b := range balances { 187 totalSupply = totalSupply.Add(b.Coins...) 188 } 189 190 bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}) 191 genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) 192 193 stateBytes, err := json.MarshalIndent(genesisState, "", " ") 194 if err != nil { 195 panic(err) 196 } 197 198 app.InitChain( 199 abci.RequestInitChain{ 200 Validators: []abci.ValidatorUpdate{}, 201 ConsensusParams: DefaultConsensusParams, 202 AppStateBytes: stateBytes, 203 }, 204 ) 205 206 app.Commit() 207 app.BeginBlock(ocabci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1}}) 208 209 return app 210 } 211 212 type GenerateAccountStrategy func(int) []sdk.AccAddress 213 214 // createRandomAccounts is a strategy used by addTestAddrs() in order to generated addresses in random order. 215 func createRandomAccounts(accNum int) []sdk.AccAddress { 216 testAddrs := make([]sdk.AccAddress, accNum) 217 for i := 0; i < accNum; i++ { 218 pk := ed25519.GenPrivKey().PubKey() 219 testAddrs[i] = sdk.AccAddress(pk.Address()) 220 } 221 222 return testAddrs 223 } 224 225 // createIncrementalAccounts is a strategy used by addTestAddrs() in order to generated addresses in ascending order. 226 func createIncrementalAccounts(accNum int) []sdk.AccAddress { 227 var addresses []sdk.AccAddress 228 var buffer bytes.Buffer 229 230 // start at 100 so we can make up to 999 test addresses with valid test addresses 231 for i := 100; i < (accNum + 100); i++ { 232 numString := strconv.Itoa(i) 233 buffer.WriteString("A58856F0FD53BF058B4909A21AEC019107BA6") // base address string 234 235 buffer.WriteString(numString) // adding on final two digits to make addresses unique 236 res, _ := sdk.AccAddressFromHex(buffer.String()) 237 bech := res.String() 238 addr, _ := TestAddr(buffer.String(), bech) 239 240 addresses = append(addresses, addr) 241 buffer.Reset() 242 } 243 244 return addresses 245 } 246 247 // AddTestAddrsFromPubKeys adds the addresses into the SimApp providing only the public keys. 248 func AddTestAddrsFromPubKeys(app *SimApp, ctx sdk.Context, pubKeys []cryptotypes.PubKey, accAmt sdk.Int) { 249 initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) 250 251 // fill all the addresses with some coins, set the loose pool tokens simultaneously 252 for _, pk := range pubKeys { 253 initAccountWithCoins(app, ctx, sdk.AccAddress(pk.Address()), initCoins) 254 } 255 } 256 257 // SortAddresses - Sorts Addresses 258 func SortAddresses(addrs []sdk.AccAddress) { 259 byteAddrs := make([][]byte, len(addrs)) 260 261 for i, addr := range addrs { 262 byteAddrs[i] = addr.Bytes() 263 } 264 265 SortByteArrays(byteAddrs) 266 267 for i, byteAddr := range byteAddrs { 268 addrs[i] = sdk.AccAddress(byteAddr) 269 } 270 } 271 272 // implement `Interface` in sort package. 273 type sortByteArrays [][]byte 274 275 func (b sortByteArrays) Len() int { 276 return len(b) 277 } 278 279 func (b sortByteArrays) Less(i, j int) bool { 280 // bytes package already implements Comparable for []byte. 281 switch bytes.Compare(b[i], b[j]) { 282 case -1: 283 return true 284 case 0, 1: 285 return false 286 default: 287 panic("not fail-able with `bytes.Comparable` bounded [-1, 1].") 288 } 289 } 290 291 func (b sortByteArrays) Swap(i, j int) { 292 b[j], b[i] = b[i], b[j] 293 } 294 295 // SortByteArrays - sorts the provided byte array 296 func SortByteArrays(src [][]byte) [][]byte { 297 sorted := sortByteArrays(src) 298 sort.Sort(sorted) 299 return sorted 300 } 301 302 // AddTestAddrs constructs and returns accNum amount of accounts with an 303 // initial balance of accAmt in random order 304 func AddTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int) []sdk.AccAddress { 305 return addTestAddrs(app, ctx, accNum, accAmt, createRandomAccounts) 306 } 307 308 // AddTestAddrs constructs and returns accNum amount of accounts with an 309 // initial balance of accAmt in random order 310 func AddTestAddrsIncremental(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int) []sdk.AccAddress { 311 return addTestAddrs(app, ctx, accNum, accAmt, createIncrementalAccounts) 312 } 313 314 func addTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int, strategy GenerateAccountStrategy) []sdk.AccAddress { 315 testAddrs := strategy(accNum) 316 317 initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) 318 319 for _, addr := range testAddrs { 320 initAccountWithCoins(app, ctx, addr, initCoins) 321 } 322 323 SortAddresses(testAddrs) 324 return testAddrs 325 } 326 327 func initAccountWithCoins(app *SimApp, ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins) { 328 err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, coins) 329 if err != nil { 330 panic(err) 331 } 332 333 err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, coins) 334 if err != nil { 335 panic(err) 336 } 337 } 338 339 // ConvertAddrsToValAddrs converts the provided addresses to ValAddress. 340 func ConvertAddrsToValAddrs(addrs []sdk.AccAddress) []sdk.ValAddress { 341 valAddrs := make([]sdk.ValAddress, len(addrs)) 342 343 for i, addr := range addrs { 344 valAddrs[i] = sdk.ValAddress(addr) 345 } 346 347 return valAddrs 348 } 349 350 func TestAddr(addr string, bech string) (sdk.AccAddress, error) { 351 res, err := sdk.AccAddressFromHex(addr) 352 if err != nil { 353 return nil, err 354 } 355 bechexpected := res.String() 356 if bech != bechexpected { 357 return nil, fmt.Errorf("bech encoding doesn't match reference") 358 } 359 360 bechres, err := sdk.AccAddressFromBech32(bech) 361 if err != nil { 362 return nil, err 363 } 364 if !bytes.Equal(bechres, res) { 365 return nil, err 366 } 367 368 return res, nil 369 } 370 371 // CheckBalance checks the balance of an account. 372 func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, balances sdk.Coins) { 373 ctxCheck := app.BaseApp.NewContext(true, tmproto.Header{}) 374 require.True(t, balances.IsEqual(app.BankKeeper.GetAllBalances(ctxCheck, addr))) 375 } 376 377 // SignCheckDeliver checks a generated signed transaction and simulates a 378 // block commitment with the given transaction. A test assertion is made using 379 // the parameter 'expPass' against the result. A corresponding result is 380 // returned. 381 func SignCheckDeliver( 382 t *testing.T, txCfg client.TxConfig, app *bam.BaseApp, header tmproto.Header, msgs []sdk.Msg, 383 chainID string, accNums, accSeqs []uint64, expSimPass, expPass bool, priv ...cryptotypes.PrivKey, 384 ) (sdk.GasInfo, *sdk.Result, error) { 385 tx, err := helpers.GenSignedMockTx( 386 rand.New(rand.NewSource(time.Now().UnixNano())), 387 txCfg, 388 msgs, 389 sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, 390 helpers.DefaultGenTxGas, 391 chainID, 392 accNums, 393 accSeqs, 394 priv..., 395 ) 396 require.NoError(t, err) 397 txBytes, err := txCfg.TxEncoder()(tx) 398 require.Nil(t, err) 399 400 // Must simulate now as CheckTx doesn't run Msgs anymore 401 _, res, err := app.Simulate(txBytes) 402 403 if expSimPass { 404 require.NoError(t, err) 405 require.NotNil(t, res) 406 } else { 407 require.Error(t, err) 408 require.Nil(t, res) 409 } 410 411 // Simulate a sending a transaction and committing a block and recheck 412 app.BeginBlock(ocabci.RequestBeginBlock{Header: header}) 413 gInfo, res, err := app.Deliver(txCfg.TxEncoder(), tx) 414 415 if expPass { 416 require.NoError(t, err) 417 require.NotNil(t, res) 418 } else { 419 require.Error(t, err) 420 require.Nil(t, res) 421 } 422 423 app.EndBlock(abci.RequestEndBlock{}) 424 app.Commit() 425 426 app.BeginRecheckTx(ocabci.RequestBeginRecheckTx{Header: header}) 427 app.EndRecheckTx(ocabci.RequestEndRecheckTx{}) 428 429 return gInfo, res, err 430 } 431 432 // SignAndDeliver signs and delivers a transaction. No simulation occurs as the 433 // ibc testing package causes checkState and deliverState to diverge in block time. 434 func SignAndDeliver( 435 t *testing.T, txCfg client.TxConfig, app *bam.BaseApp, header tmproto.Header, msgs []sdk.Msg, 436 chainID string, accNums, accSeqs []uint64, expSimPass, expPass bool, priv ...cryptotypes.PrivKey, 437 ) (sdk.GasInfo, *sdk.Result, error) { 438 tx, err := helpers.GenTx( 439 txCfg, 440 msgs, 441 sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, 442 2*helpers.DefaultGenTxGas, 443 chainID, 444 accNums, 445 accSeqs, 446 priv..., 447 ) 448 require.NoError(t, err) 449 450 // Simulate a sending a transaction and committing a block 451 app.BeginBlock(ocabci.RequestBeginBlock{Header: header}) 452 gInfo, res, err := app.Deliver(txCfg.TxEncoder(), tx) 453 454 if expPass { 455 require.NoError(t, err) 456 require.NotNil(t, res) 457 } else { 458 require.Error(t, err) 459 require.Nil(t, res) 460 } 461 462 app.EndBlock(abci.RequestEndBlock{}) 463 app.Commit() 464 465 return gInfo, res, err 466 } 467 468 // GenSequenceOfTxs generates a set of signed transactions of messages, such 469 // that they differ only by having the sequence numbers incremented between 470 // every transaction. 471 func GenSequenceOfTxs(txGen client.TxConfig, msgs []sdk.Msg, accNums []uint64, initSeqNums []uint64, numToGenerate int, priv ...cryptotypes.PrivKey) ([]sdk.Tx, error) { 472 txs := make([]sdk.Tx, numToGenerate) 473 var err error 474 for i := 0; i < numToGenerate; i++ { 475 txs[i], err = helpers.GenSignedMockTx( 476 rand.New(rand.NewSource(time.Now().UnixNano())), 477 txGen, 478 msgs, 479 sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, 480 helpers.DefaultGenTxGas, 481 "", 482 accNums, 483 initSeqNums, 484 priv..., 485 ) 486 if err != nil { 487 break 488 } 489 incrementAllSequenceNumbers(initSeqNums) 490 } 491 492 return txs, err 493 } 494 495 func incrementAllSequenceNumbers(initSeqNums []uint64) { 496 for i := 0; i < len(initSeqNums); i++ { 497 initSeqNums[i]++ 498 } 499 } 500 501 // CreateTestPubKeys returns a total of numPubKeys public keys in ascending order. 502 func CreateTestPubKeys(numPubKeys int) []cryptotypes.PubKey { 503 var publicKeys []cryptotypes.PubKey 504 var buffer bytes.Buffer 505 506 // start at 10 to avoid changing 1 to 01, 2 to 02, etc 507 for i := 100; i < (numPubKeys + 100); i++ { 508 numString := strconv.Itoa(i) 509 buffer.WriteString("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AF") // base pubkey string 510 buffer.WriteString(numString) // adding on final two digits to make pubkeys unique 511 publicKeys = append(publicKeys, NewPubKeyFromHex(buffer.String())) 512 buffer.Reset() 513 } 514 515 return publicKeys 516 } 517 518 // NewPubKeyFromHex returns a PubKey from a hex string. 519 func NewPubKeyFromHex(pk string) (res cryptotypes.PubKey) { 520 pkBytes, err := hex.DecodeString(pk) 521 if err != nil { 522 panic(err) 523 } 524 if len(pkBytes) != ed25519.PubKeySize { 525 panic(errors.Wrap(errors.ErrInvalidPubKey, "invalid pubkey size")) 526 } 527 return &ed25519.PubKey{Key: pkBytes} 528 } 529 530 // EmptyAppOptions is a stub implementing AppOptions 531 type EmptyAppOptions struct{} 532 533 // Get implements AppOptions 534 func (ao EmptyAppOptions) Get(o string) interface{} { 535 return nil 536 } 537 538 // FundAccount is a utility function that funds an account by minting and 539 // sending the coins to the address. This should be used for testing purposes 540 // only! 541 // 542 // TODO: Instead of using the mint module account, which has the 543 // permission of minting, create a "faucet" account. (@fdymylja) 544 func FundAccount(app *SimApp, ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins) error { 545 if err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts); err != nil { 546 return err 547 } 548 549 return app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, amounts) 550 } 551 552 // FundModuleAccount is a utility function that funds a module account by 553 // minting and sending the coins to the address. This should be used for testing 554 // purposes only! 555 // 556 // TODO: Instead of using the mint module account, which has the 557 // permission of minting, create a "faucet" account. (@fdymylja) 558 func FundModuleAccount(app *SimApp, ctx sdk.Context, recipientMod string, amounts sdk.Coins) error { 559 if err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts); err != nil { 560 return err 561 } 562 563 return app.BankKeeper.SendCoinsFromModuleToModule(ctx, minttypes.ModuleName, recipientMod, amounts) 564 }