diff --git a/PENDING.md b/PENDING.md index ab0a42d45..543010eb1 100644 --- a/PENDING.md +++ b/PENDING.md @@ -44,6 +44,7 @@ BREAKING CHANGES * [types] [\#2119](https://github.com/cosmos/cosmos-sdk/issues/2119) Parsed error messages and ABCI log errors to make them more human readable. * [simulation] Rename TestAndRunTx to Operation [#2153](https://github.com/cosmos/cosmos-sdk/pull/2153) * [simulation] Remove log and testing.TB from Operation and Invariants, in favor of using errors \#2282 + * [simulation] Remove usage of keys and addrs in the types, in favor of simulation.Account \#2384 * [tools] Removed gocyclo [#2211](https://github.com/cosmos/cosmos-sdk/issues/2211) * [baseapp] Remove `SetTxDecoder` in favor of requiring the decoder be set in baseapp initialization. [#1441](https://github.com/cosmos/cosmos-sdk/issues/1441) * [baseapp] [\#1921](https://github.com/cosmos/cosmos-sdk/issues/1921) Add minimumFees field to BaseApp. diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 078eece11..6097b5a08 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" @@ -42,14 +41,14 @@ func init() { flag.BoolVar(&commit, "SimulationCommit", false, "Have the simulation commit") } -func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage { +func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { var genesisAccounts []GenesisAccount // Randomly generate some genesis accounts for _, acc := range accs { coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(100)}} genesisAccounts = append(genesisAccounts, GenesisAccount{ - Address: acc, + Address: acc.Address, Coins: coins, }) } @@ -61,10 +60,10 @@ func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json // XXX Try different numbers of initially bonded validators numInitiallyBonded := int64(50) for i := 0; i < int(numInitiallyBonded); i++ { - validator := stake.NewValidator(sdk.ValAddress(accs[i]), keys[i].PubKey(), stake.Description{}) + validator := stake.NewValidator(sdk.ValAddress(accs[i].Address), accs[i].PubKey, stake.Description{}) validator.Tokens = sdk.NewDec(100) validator.DelegatorShares = sdk.NewDec(100) - delegation := stake.Delegation{accs[i], sdk.ValAddress(accs[i]), sdk.NewDec(100), 0} + delegation := stake.Delegation{accs[i].Address, sdk.ValAddress(accs[i].Address), sdk.NewDec(100), 0} validators = append(validators, validator) delegations = append(delegations, delegation) } diff --git a/x/bank/simulation/msgs.go b/x/bank/simulation/msgs.go index cefa2a460..0b8ec3026 100644 --- a/x/bank/simulation/msgs.go +++ b/x/bank/simulation/msgs.go @@ -18,19 +18,18 @@ import ( // SimulateSingleInputMsgSend tests and runs a single msg send, with one input and one output, where both // accounts already exist. func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation { - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { - fromKey := simulation.RandomKey(r, keys) - fromAddr := sdk.AccAddress(fromKey.PubKey().Address()) - toKey := simulation.RandomKey(r, keys) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { + fromAcc := simulation.RandomAcc(r, accs) + toAcc := simulation.RandomAcc(r, accs) // Disallow sending money to yourself for { - if !fromKey.Equals(toKey) { + if !fromAcc.PubKey.Equals(toAcc.PubKey) { break } - toKey = simulation.RandomKey(r, keys) + toAcc = simulation.RandomAcc(r, accs) } - toAddr := sdk.AccAddress(toKey.PubKey().Address()) - initFromCoins := mapper.GetAccount(ctx, fromAddr).GetCoins() + toAddr := toAcc.Address + initFromCoins := mapper.GetAccount(ctx, fromAcc.Address).GetCoins() if len(initFromCoins) == 0 { return "skipping, no coins at all", nil, nil @@ -43,7 +42,7 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation } action = fmt.Sprintf("%s is sending %s %s to %s", - fromAddr.String(), + fromAcc.Address.String(), amt.String(), initFromCoins[denomIndex].Denom, toAddr.String(), @@ -51,10 +50,10 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation coins := sdk.Coins{{initFromCoins[denomIndex].Denom, amt}} var msg = bank.MsgSend{ - Inputs: []bank.Input{bank.NewInput(fromAddr, coins)}, + Inputs: []bank.Input{bank.NewInput(fromAcc.Address, coins)}, Outputs: []bank.Output{bank.NewOutput(toAddr, coins)}, } - goErr = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromKey}) + goErr = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}) if goErr != nil { return "", nil, goErr } diff --git a/x/bank/simulation/sim_test.go b/x/bank/simulation/sim_test.go index 811535507..819289845 100644 --- a/x/bank/simulation/sim_test.go +++ b/x/bank/simulation/sim_test.go @@ -5,8 +5,6 @@ import ( "math/rand" "testing" - "github.com/tendermint/tendermint/crypto" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" @@ -26,8 +24,8 @@ func TestBankWithRandomMessages(t *testing.T) { panic(err) } - appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage { - mock.RandomSetGenesis(r, mapp, accs, []string{"stake"}) + appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage { + simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"}) return json.RawMessage("{}") } diff --git a/x/gov/simulation/msgs.go b/x/gov/simulation/msgs.go index f46fe995e..d5ff1881e 100644 --- a/x/gov/simulation/msgs.go +++ b/x/gov/simulation/msgs.go @@ -6,8 +6,6 @@ import ( "math/rand" "time" - "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" @@ -45,9 +43,9 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe }) statePercentageArray := []float64{1, .9, .75, .4, .15, 0} curNumVotesState := 1 - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { // 1) submit proposal now - sender := simulation.RandomKey(r, keys) + sender := simulation.RandomAcc(r, accs) msg, err := simulationCreateMsgSubmitProposal(r, sender) if err != nil { return "", nil, err @@ -61,16 +59,16 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe // 2) Schedule operations for votes // 2.1) first pick a number of people to vote. curNumVotesState = numVotesTransitionMatrix.NextState(r, curNumVotesState) - numVotes := int(math.Ceil(float64(len(keys)) * statePercentageArray[curNumVotesState])) + numVotes := int(math.Ceil(float64(len(accs)) * statePercentageArray[curNumVotesState])) // 2.2) select who votes and when - whoVotes := r.Perm(len(keys)) + whoVotes := r.Perm(len(accs)) // didntVote := whoVotes[numVotes:] whoVotes = whoVotes[:numVotes] votingPeriod := k.GetVotingProcedure(ctx).VotingPeriod fops := make([]simulation.FutureOperation, numVotes+1) for i := 0; i < numVotes; i++ { whenVote := ctx.BlockHeader().Time.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second) - fops[i] = simulation.FutureOperation{BlockTime: whenVote, Op: operationSimulateMsgVote(k, sk, keys[whoVotes[i]], proposalID)} + fops[i] = simulation.FutureOperation{BlockTime: whenVote, Op: operationSimulateMsgVote(k, sk, accs[whoVotes[i]], proposalID)} } // 3) Make an operation to ensure slashes were done correctly. (Really should be a future invariant) // TODO: Find a way to check if a validator was slashed other than just checking their balance a block @@ -84,8 +82,8 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe // Note: Currently doesn't ensure that the proposal txt is in JSON form func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operation { handler := gov.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { - sender := simulation.RandomKey(r, keys) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { + sender := simulation.RandomAcc(r, accs) msg, err := simulationCreateMsgSubmitProposal(r, sender) if err != nil { return "", nil, err @@ -111,14 +109,13 @@ func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, sk stake.Keeper, return } -func simulationCreateMsgSubmitProposal(r *rand.Rand, sender crypto.PrivKey) (msg gov.MsgSubmitProposal, err error) { - addr := sdk.AccAddress(sender.PubKey().Address()) +func simulationCreateMsgSubmitProposal(r *rand.Rand, sender simulation.Account) (msg gov.MsgSubmitProposal, err error) { deposit := randomDeposit(r) msg = gov.NewMsgSubmitProposal( simulation.RandStringOfLength(r, 5), simulation.RandStringOfLength(r, 5), gov.ProposalTypeText, - addr, + sender.Address, deposit, ) if msg.ValidateBasic() != nil { @@ -129,15 +126,14 @@ func simulationCreateMsgSubmitProposal(r *rand.Rand, sender crypto.PrivKey) (msg // SimulateMsgDeposit func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation { - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { - key := simulation.RandomKey(r, keys) - addr := sdk.AccAddress(key.PubKey().Address()) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + acc := simulation.RandomAcc(r, accs) proposalID, ok := randomProposalID(r, k, ctx) if !ok { return "no-operation", nil, nil } deposit := randomDeposit(r) - msg := gov.NewMsgDeposit(addr, proposalID, deposit) + msg := gov.NewMsgDeposit(acc.Address, proposalID, deposit) if msg.ValidateBasic() != nil { return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) } @@ -159,14 +155,14 @@ func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation { // SimulateMsgVote // nolint: unparam func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation { - return operationSimulateMsgVote(k, sk, nil, -1) + return operationSimulateMsgVote(k, sk, simulation.Account{}, -1) } // nolint: unparam -func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey, proposalID int64) simulation.Operation { - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { - if key == nil { - key = simulation.RandomKey(r, keys) +func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, acc simulation.Account, proposalID int64) simulation.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + if acc.Equals(simulation.Account{}) { + acc = simulation.RandomAcc(r, accs) } var ok bool @@ -177,10 +173,9 @@ func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey, return "no-operation", nil, nil } } - addr := sdk.AccAddress(key.PubKey().Address()) option := randomVotingOption(r) - msg := gov.NewMsgVote(addr, proposalID, option) + msg := gov.NewMsgVote(acc.Address, proposalID, option) if msg.ValidateBasic() != nil { return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) } diff --git a/x/gov/simulation/sim_test.go b/x/gov/simulation/sim_test.go index 35a11fb07..b0317d116 100644 --- a/x/gov/simulation/sim_test.go +++ b/x/gov/simulation/sim_test.go @@ -6,7 +6,6 @@ import ( "testing" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" @@ -43,12 +42,12 @@ func TestGovWithRandomMessages(t *testing.T) { panic(err) } - appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage { - mock.RandomSetGenesis(r, mapp, accs, []string{"stake"}) + appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage { + simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"}) return json.RawMessage("{}") } - setup := func(r *rand.Rand, privKeys []crypto.PrivKey) { + setup := func(r *rand.Rand, accs []simulation.Account) { ctx := mapp.NewContext(false, abci.Header{}) stake.InitGenesis(ctx, stakeKeeper, stake.DefaultGenesisState()) gov.InitGenesis(ctx, govKeeper, gov.DefaultGenesisState()) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 96dbff572..25748e15b 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -15,17 +15,15 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/mock" ) // Simulate tests application by sending random messages. func Simulate(t *testing.T, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, + appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, ops []WeightedOperation, setups []RandSetup, invariants []Invariant, numBlocks int, blockSize int, commit bool) error { @@ -33,16 +31,16 @@ func Simulate(t *testing.T, app *baseapp.BaseApp, return SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit) } -func initChain(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress, setups []RandSetup, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage) (validators map[string]mockValidator) { - res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, keys, accs)}) +func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseapp.BaseApp, + appStateFn func(r *rand.Rand, accounts []Account) json.RawMessage) (validators map[string]mockValidator) { + res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)}) validators = make(map[string]mockValidator) for _, validator := range res.Validators { validators[string(validator.Address)] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)} } for i := 0; i < len(setups); i++ { - setups[i](r, keys) + setups[i](r, accounts) } return @@ -56,7 +54,7 @@ func randTimestamp(r *rand.Rand) time.Time { // SimulateFromSeed tests an application by running the provided // operations, testing the provided invariants, but using the provided seed. func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, + appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, seed int64, ops []WeightedOperation, setups []RandSetup, invariants []Invariant, numBlocks int, blockSize int, commit bool) (simError error) { @@ -69,7 +67,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, fmt.Printf("Starting the simulation from time %v, unixtime %v\n", timestamp.UTC().Format(time.UnixDate), timestamp.Unix()) timeDiff := maxTimePerBlock - minTimePerBlock - keys, accs := mock.GeneratePrivKeyAddressPairsFromRand(r, numKeys) + accs := RandomAccounts(r, numKeys) // Setup event stats events := make(map[string]uint) @@ -77,7 +75,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, events[what]++ } - validators := initChain(r, keys, accs, setups, app, appStateFn) + validators := initChain(r, accs, setups, app, appStateFn) header := abci.Header{Height: 0, Time: timestamp} opCount := 0 @@ -139,10 +137,10 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, thisBlockSize := getBlockSize(r, blockSize) // Run queued operations. Ignores blocksize if blocksize is too small - numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, keys, logWriter, displayLogs, event) - numQueuedTimeOpsRan := runQueuedTimeOperations(timeOperationQueue, header.Time, tb, r, app, ctx, keys, logWriter, displayLogs, event) + numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, accs, logWriter, displayLogs, event) + numQueuedTimeOpsRan := runQueuedTimeOperations(timeOperationQueue, header.Time, tb, r, app, ctx, accs, logWriter, displayLogs, event) thisBlockSize = thisBlockSize - numQueuedOpsRan - numQueuedTimeOpsRan - operations := blockSimulator(thisBlockSize, r, app, ctx, keys, header, logWriter) + operations := blockSimulator(thisBlockSize, r, app, ctx, accs, header, logWriter) opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan res := app.EndBlock(abci.RequestEndBlock{}) @@ -176,7 +174,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, // Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize // memory overhead func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, totalNumBlocks int, displayLogs func()) func( - blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, privKeys []crypto.PrivKey, header abci.Header, logWriter func(string)) (opCount int) { + blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { totalOpWeight := 0 for i := 0; i < len(ops); i++ { totalOpWeight += ops[i].Weight @@ -193,9 +191,9 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event f return ops[0].Op } return func(blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - keys []crypto.PrivKey, header abci.Header, logWriter func(string)) (opCount int) { + accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { for j := 0; j < blocksize; j++ { - logUpdate, futureOps, err := selectOp(r)(r, app, ctx, keys, event) + logUpdate, futureOps, err := selectOp(r)(r, app, ctx, accounts, event) if err != nil { displayLogs() tb.Fatalf("error on operation %d within block %d, %v", header.Height, opCount, err) @@ -264,14 +262,14 @@ func queueOperations(queuedOperations map[int][]Operation, queuedTimeOperations // nolint: errcheck func runQueuedOperations(queueOperations map[int][]Operation, height int, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - privKeys []crypto.PrivKey, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { + accounts []Account, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { if queuedOps, ok := queueOperations[height]; ok { numOps := len(queuedOps) for i := 0; i < numOps; i++ { // For now, queued operations cannot queue more operations. // If a need arises for us to support queued messages to queue more messages, this can // be changed. - logUpdate, _, err := queuedOps[i](r, app, ctx, privKeys, event) + logUpdate, _, err := queuedOps[i](r, app, ctx, accounts, event) logWriter(logUpdate) if err != nil { displayLogs() @@ -285,14 +283,14 @@ func runQueuedOperations(queueOperations map[int][]Operation, height int, tb tes } func runQueuedTimeOperations(queueOperations []FutureOperation, currentTime time.Time, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - privKeys []crypto.PrivKey, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { + accounts []Account, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { numOpsRan = 0 for len(queueOperations) > 0 && currentTime.After(queueOperations[0].BlockTime) { // For now, queued operations cannot queue more operations. // If a need arises for us to support queued messages to queue more messages, this can // be changed. - logUpdate, _, err := queueOperations[0].Op(r, app, ctx, privKeys, event) + logUpdate, _, err := queueOperations[0].Op(r, app, ctx, accounts, event) logWriter(logUpdate) if err != nil { displayLogs() diff --git a/x/mock/simulation/types.go b/x/mock/simulation/types.go index a32edea6d..2e68a428e 100644 --- a/x/mock/simulation/types.go +++ b/x/mock/simulation/types.go @@ -23,11 +23,11 @@ type ( // Operations can optionally provide a list of "FutureOperations" to run later // These will be ran at the beginning of the corresponding block. Operation func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - privKeys []crypto.PrivKey, event func(string), + accounts []Account, event func(string), ) (action string, futureOperations []FutureOperation, err error) // RandSetup performs the random setup the mock module needs. - RandSetup func(r *rand.Rand, privKeys []crypto.PrivKey) + RandSetup func(r *rand.Rand, accounts []Account) // An Invariant is a function which tests a particular invariant. // If the invariant has been broken, it should return an error @@ -35,6 +35,15 @@ type ( // The simulator will then halt and print the logs. Invariant func(app *baseapp.BaseApp) error + // Account contains a privkey, pubkey, address tuple + // eventually more useful data can be placed in here. + // (e.g. number of coins) + Account struct { + PrivKey crypto.PrivKey + PubKey crypto.PubKey + Address sdk.AccAddress + } + mockValidator struct { val abci.Validator livenessState int @@ -70,3 +79,8 @@ func PeriodicInvariant(invariant Invariant, period int, offset int) Invariant { return nil } } + +// nolint +func (acc Account) Equals(acc2 Account) bool { + return acc.Address.Equals(acc2.Address) +} diff --git a/x/mock/simulation/util.go b/x/mock/simulation/util.go index a46d6dd54..54bfabc73 100644 --- a/x/mock/simulation/util.go +++ b/x/mock/simulation/util.go @@ -9,10 +9,12 @@ import ( "testing" "time" - "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mock" ) // shamelessly copied from https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang#31832326 @@ -56,10 +58,10 @@ func DisplayEvents(events map[string]uint) { } } -// Pick a random key from an array -func RandomKey(r *rand.Rand, keys []crypto.PrivKey) crypto.PrivKey { - return keys[r.Intn( - len(keys), +// RandomAcc pick a random account from an array +func RandomAcc(r *rand.Rand, accs []Account) Account { + return accs[r.Intn( + len(accs), )] } @@ -68,6 +70,25 @@ func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int { return sdk.NewInt(int64(r.Intn(int(max.Int64())))) } +// RandomAccounts generates n random accounts +func RandomAccounts(r *rand.Rand, n int) []Account { + accs := make([]Account, n) + for i := 0; i < n; i++ { + // don't need that much entropy for simulation + privkeySeed := make([]byte, 15) + r.Read(privkeySeed) + useSecp := r.Int63()%2 == 0 + if useSecp { + accs[i].PrivKey = secp256k1.GenPrivKeySecp256k1(privkeySeed) + } else { + accs[i].PrivKey = ed25519.GenPrivKeyFromSecret(privkeySeed) + } + accs[i].PubKey = accs[i].PrivKey.PubKey() + accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address()) + } + return accs +} + // Builds a function to add logs for this particular block func addLogMessage(testingmode bool, blockLogBuilders []*strings.Builder, height int) func(string) { if testingmode { @@ -92,6 +113,15 @@ func assertAllInvariants(t *testing.T, app *baseapp.BaseApp, invariants []Invari } } +// RandomSetGenesis wraps mock.RandomSetGenesis, but using simulation accounts +func RandomSetGenesis(r *rand.Rand, app *mock.App, accs []Account, denoms []string) { + addrs := make([]sdk.AccAddress, len(accs)) + for i := 0; i < len(accs); i++ { + addrs[i] = accs[i].Address + } + mock.RandomSetGenesis(r, app, addrs, denoms) +} + // Creates a function to print out the logs func logPrinter(testingmode bool, logs []*strings.Builder) func() { if testingmode { diff --git a/x/slashing/simulation/msgs.go b/x/slashing/simulation/msgs.go index da9340baf..2b09226f2 100644 --- a/x/slashing/simulation/msgs.go +++ b/x/slashing/simulation/msgs.go @@ -4,8 +4,6 @@ import ( "fmt" "math/rand" - "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/mock/simulation" @@ -14,9 +12,9 @@ import ( // SimulateMsgUnjail func SimulateMsgUnjail(k slashing.Keeper) simulation.Operation { - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { - key := simulation.RandomKey(r, keys) - address := sdk.ValAddress(key.PubKey().Address()) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + acc := simulation.RandomAcc(r, accs) + address := sdk.ValAddress(acc.Address) msg := slashing.NewMsgUnjail(address) if msg.ValidateBasic() != nil { return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) diff --git a/x/stake/simulation/msgs.go b/x/stake/simulation/msgs.go index 5b2bc1ee8..906716ed1 100644 --- a/x/stake/simulation/msgs.go +++ b/x/stake/simulation/msgs.go @@ -11,22 +11,20 @@ import ( "github.com/cosmos/cosmos-sdk/x/mock/simulation" "github.com/cosmos/cosmos-sdk/x/stake" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" ) // SimulateMsgCreateValidator func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { denom := k.GetParams(ctx).BondDenom description := stake.Description{ Moniker: simulation.RandStringOfLength(r, 10), } - key := simulation.RandomKey(r, keys) - pubkey := key.PubKey() - address := sdk.ValAddress(pubkey.Address()) - amount := m.GetAccount(ctx, sdk.AccAddress(address)).GetCoins().AmountOf(denom) + acc := simulation.RandomAcc(r, accs) + address := sdk.ValAddress(acc.Address) + amount := m.GetAccount(ctx, acc.Address).GetCoins().AmountOf(denom) if amount.GT(sdk.ZeroInt()) { amount = simulation.RandomAmount(r, amount) } @@ -36,8 +34,8 @@ func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation msg := stake.MsgCreateValidator{ Description: description, ValidatorAddr: address, - DelegatorAddr: sdk.AccAddress(address), - PubKey: pubkey, + DelegatorAddr: acc.Address, + PubKey: acc.PubKey, Delegation: sdk.NewCoin(denom, amount), } if msg.ValidateBasic() != nil { @@ -58,7 +56,7 @@ func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation // SimulateMsgEditValidator func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { description := stake.Description{ Moniker: simulation.RandStringOfLength(r, 10), @@ -66,9 +64,8 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation { Website: simulation.RandStringOfLength(r, 10), Details: simulation.RandStringOfLength(r, 10), } - key := simulation.RandomKey(r, keys) - pubkey := key.PubKey() - address := sdk.ValAddress(pubkey.Address()) + acc := simulation.RandomAcc(r, accs) + address := sdk.ValAddress(acc.Address) msg := stake.MsgEditValidator{ Description: description, ValidatorAddr: address, @@ -90,13 +87,13 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation { // SimulateMsgDelegate func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { denom := k.GetParams(ctx).BondDenom - validatorKey := simulation.RandomKey(r, keys) - validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address()) - delegatorKey := simulation.RandomKey(r, keys) - delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address()) + validatorAcc := simulation.RandomAcc(r, accs) + validatorAddress := sdk.ValAddress(validatorAcc.Address) + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom) if amount.GT(sdk.ZeroInt()) { amount = simulation.RandomAmount(r, amount) @@ -126,13 +123,13 @@ func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operat // SimulateMsgBeginUnbonding func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { denom := k.GetParams(ctx).BondDenom - validatorKey := simulation.RandomKey(r, keys) - validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address()) - delegatorKey := simulation.RandomKey(r, keys) - delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address()) + validatorAcc := simulation.RandomAcc(r, accs) + validatorAddress := sdk.ValAddress(validatorAcc.Address) + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom) if amount.GT(sdk.ZeroInt()) { amount = simulation.RandomAmount(r, amount) @@ -162,12 +159,12 @@ func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation. // SimulateMsgCompleteUnbonding func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { - validatorKey := simulation.RandomKey(r, keys) - validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address()) - delegatorKey := simulation.RandomKey(r, keys) - delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address()) + validatorAcc := simulation.RandomAcc(r, accs) + validatorAddress := sdk.ValAddress(validatorAcc.Address) + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address msg := stake.MsgCompleteUnbonding{ DelegatorAddr: delegatorAddress, ValidatorAddr: validatorAddress, @@ -189,15 +186,15 @@ func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation { // SimulateMsgBeginRedelegate func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { denom := k.GetParams(ctx).BondDenom - sourceValidatorKey := simulation.RandomKey(r, keys) - sourceValidatorAddress := sdk.ValAddress(sourceValidatorKey.PubKey().Address()) - destValidatorKey := simulation.RandomKey(r, keys) - destValidatorAddress := sdk.ValAddress(destValidatorKey.PubKey().Address()) - delegatorKey := simulation.RandomKey(r, keys) - delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address()) + sourceValidatorAcc := simulation.RandomAcc(r, accs) + sourceValidatorAddress := sdk.ValAddress(sourceValidatorAcc.Address) + destValidatorAcc := simulation.RandomAcc(r, accs) + destValidatorAddress := sdk.ValAddress(destValidatorAcc.Address) + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address // TODO amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom) if amount.GT(sdk.ZeroInt()) { @@ -229,14 +226,14 @@ func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation // SimulateMsgCompleteRedelegate func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { - validatorSrcKey := simulation.RandomKey(r, keys) - validatorSrcAddress := sdk.ValAddress(validatorSrcKey.PubKey().Address()) - validatorDstKey := simulation.RandomKey(r, keys) - validatorDstAddress := sdk.ValAddress(validatorDstKey.PubKey().Address()) - delegatorKey := simulation.RandomKey(r, keys) - delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address()) + validatorSrcAcc := simulation.RandomAcc(r, accs) + validatorSrcAddress := sdk.ValAddress(validatorSrcAcc.Address) + validatorDstAcc := simulation.RandomAcc(r, accs) + validatorDstAddress := sdk.ValAddress(validatorDstAcc.Address) + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address msg := stake.MsgCompleteRedelegate{ DelegatorAddr: delegatorAddress, ValidatorSrcAddr: validatorSrcAddress, @@ -259,7 +256,7 @@ func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.Operation { // Setup // nolint: errcheck func Setup(mapp *mock.App, k stake.Keeper) simulation.RandSetup { - return func(r *rand.Rand, privKeys []crypto.PrivKey) { + return func(r *rand.Rand, accs []simulation.Account) { ctx := mapp.NewContext(false, abci.Header{}) gen := stake.DefaultGenesisState() gen.Params.InflationMax = sdk.NewDec(0) diff --git a/x/stake/simulation/sim_test.go b/x/stake/simulation/sim_test.go index 53b9d826c..b81555f03 100644 --- a/x/stake/simulation/sim_test.go +++ b/x/stake/simulation/sim_test.go @@ -6,7 +6,6 @@ import ( "testing" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" @@ -38,8 +37,8 @@ func TestStakeWithRandomMessages(t *testing.T) { panic(err) } - appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage { - mock.RandomSetGenesis(r, mapp, accs, []string{"stake"}) + appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage { + simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"}) return json.RawMessage("{}") }