Skip to content

Commit

Permalink
queue time operations simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
sunnya97 committed Sep 18, 2018
1 parent b09e859 commit 654cc2d
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 55 deletions.
10 changes: 5 additions & 5 deletions x/bank/simulation/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ 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) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, fTOps []simulation.FutureTimeOperation, err error) {
fromKey := simulation.RandomKey(r, keys)
fromAddr := sdk.AccAddress(fromKey.PubKey().Address())
toKey := simulation.RandomKey(r, keys)
Expand All @@ -33,13 +33,13 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation
initFromCoins := mapper.GetAccount(ctx, fromAddr).GetCoins()

if len(initFromCoins) == 0 {
return "skipping, no coins at all", nil, nil
return "skipping, no coins at all", nil, nil, nil
}

denomIndex := r.Intn(len(initFromCoins))
amt, goErr := randPositiveInt(r, initFromCoins[denomIndex].Amount)
if goErr != nil {
return "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, nil, nil
return "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, nil, nil, nil
}

action = fmt.Sprintf("%s is sending %s %s to %s",
Expand All @@ -56,11 +56,11 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation
}
goErr = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromKey})
if goErr != nil {
return "", nil, goErr
return "", nil, nil, goErr
}
event("bank/sendAndVerifyMsgSend/ok")

return action, nil, nil
return action, nil, nil, nil
}
}

Expand Down
30 changes: 15 additions & 15 deletions x/gov/simulation/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,17 @@ 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, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, fTOps []simulation.FutureTimeOperation, err error) {
// 1) submit proposal now
sender := simulation.RandomKey(r, keys)
msg, err := simulationCreateMsgSubmitProposal(r, sender)
if err != nil {
return "", nil, err
return "", nil, nil, err
}
action, ok := simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event)
// don't schedule votes if proposal failed
if !ok {
return action, nil, nil
return action, nil, nil, nil
}
proposalID := k.GetLastProposalID(ctx)
// 2) Schedule operations for votes
Expand All @@ -75,22 +75,22 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe
// TODO: Find a way to check if a validator was slashed other than just checking their balance a block
// before and after.

return action, fops, nil
return action, fops, nil, nil
}
}

// SimulateMsgSubmitProposal simulates a msg Submit Proposal
// 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) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, fTOps []simulation.FutureTimeOperation, err error) {
sender := simulation.RandomKey(r, keys)
msg, err := simulationCreateMsgSubmitProposal(r, sender)
if err != nil {
return "", nil, err
return "", nil, nil, err
}
action, _ = simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event)
return action, nil, nil
return action, nil, nil, nil
}
}

Expand Down Expand Up @@ -128,17 +128,17 @@ 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) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, fTOp []simulation.FutureTimeOperation, err error) {
key := simulation.RandomKey(r, keys)
addr := sdk.AccAddress(key.PubKey().Address())
proposalID, ok := randomProposalID(r, k, ctx)
if !ok {
return "no-operation", nil, nil
return "no-operation", nil, nil, nil
}
deposit := randomDeposit(r)
msg := gov.NewMsgDeposit(addr, proposalID, deposit)
if msg.ValidateBasic() != nil {
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
return "", nil, nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
}
ctx, write := ctx.CacheContext()
result := gov.NewHandler(k)(ctx, msg)
Expand All @@ -151,7 +151,7 @@ func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation {
}
event(fmt.Sprintf("gov/MsgDeposit/%v", result.IsOK()))
action = fmt.Sprintf("TestMsgDeposit: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
return action, nil, nil
return action, nil, nil, nil
}
}

Expand All @@ -163,7 +163,7 @@ func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation {

// 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) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, fTOp []simulation.FutureTimeOperation, err error) {
if key == nil {
key = simulation.RandomKey(r, keys)
}
Expand All @@ -173,15 +173,15 @@ func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey,
if proposalID < 0 {
proposalID, ok = randomProposalID(r, k, ctx)
if !ok {
return "no-operation", nil, nil
return "no-operation", nil, nil, nil
}
}
addr := sdk.AccAddress(key.PubKey().Address())
option := randomVotingOption(r)

msg := gov.NewMsgVote(addr, proposalID, option)
if msg.ValidateBasic() != nil {
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
return "", nil, nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
}

ctx, write := ctx.CacheContext()
Expand All @@ -192,7 +192,7 @@ func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey,

event(fmt.Sprintf("gov/MsgVote/%v", result.IsOK()))
action = fmt.Sprintf("TestMsgVote: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
return action, nil, nil
return action, nil, nil, nil
}
}

Expand Down
50 changes: 44 additions & 6 deletions x/mock/simulation/random_simulate_blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,14 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
request := RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastSigningValidators, event, header)
// These are operations which have been queued by previous operations
operationQueue := make(map[int][]Operation)
timeOperationQueue := []FutureTimeOperation{}
var blockLogBuilders []*strings.Builder

if testingMode {
blockLogBuilders = make([]*strings.Builder, numBlocks)
}
displayLogs := logPrinter(testingMode, blockLogBuilders)
blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, numBlocks, displayLogs)
blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, displayLogs)
if !testingMode {
b.ResetTimer()
} else {
Expand Down Expand Up @@ -140,8 +141,10 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
// Run queued operations. Ignores blocksize if blocksize is too small
numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, keys, logWriter, displayLogs, event)
thisBlockSize -= numQueuedOpsRan
numQueuedTimeOpsRan := runQueuedTimeOperations(timeOperationQueue, header.Time, tb, r, app, ctx, keys, logWriter, displayLogs, event)
thisBlockSize -= numQueuedTimeOpsRan
operations := blockSimulator(thisBlockSize, r, app, ctx, keys, header, logWriter)
opCount += operations + numQueuedOpsRan
opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan

res := app.EndBlock(abci.RequestEndBlock{})
header.Height++
Expand Down Expand Up @@ -173,7 +176,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, totalNumBlocks int, displayLogs func()) func(
func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureTimeOperation, 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) {
totalOpWeight := 0
for i := 0; i < len(ops); i++ {
Expand All @@ -193,14 +196,15 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event f
return func(blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
keys []crypto.PrivKey, 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, futureTimeOps, err := selectOp(r)(r, app, ctx, keys, event)
if err != nil {
displayLogs()
tb.Fatalf("error on operation %d within block %d, %v", header.Height, opCount, err)
}
logWriter(logUpdate)

queueOperations(operationQueue, futureOps)
queueTimeOperations(timeOperationQueue, futureTimeOps)
if testingMode {
if onOperation {
assertAllInvariants(t, app, invariants, displayLogs)
Expand Down Expand Up @@ -238,7 +242,7 @@ func getBlockSize(r *rand.Rand, blockSize int) int {
}
}

// adds all future operations into the operation queue.
// adds all future Height based operations into the operation queue.
func queueOperations(queuedOperations map[int][]Operation, futureOperations []FutureOperation) {
if futureOperations == nil {
return
Expand All @@ -261,7 +265,7 @@ func runQueuedOperations(queueOperations map[int][]Operation, height int, tb tes
// 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, privKeys, event)
logWriter(logUpdate)
if err != nil {
displayLogs()
Expand All @@ -274,6 +278,40 @@ func runQueuedOperations(queueOperations map[int][]Operation, height int, tb tes
return 0
}

// adds all future Time-based operations into the operation queue.
func queueTimeOperations(queuedTimeOperations []FutureTimeOperation, futureTimeOperations []FutureTimeOperation) {
if queuedTimeOperations == nil {
return
}
for _, futureOp := range futureTimeOperations {
index := sort.Search(len(queuedTimeOperations), func(i int) bool { return queuedTimeOperations[i].BlockTime.After(futureOp.BlockTime) })
queuedTimeOperations = append(queuedTimeOperations, FutureTimeOperation{})
copy(queuedTimeOperations[index+1:], queuedTimeOperations[index:])
queuedTimeOperations[index] = futureOp
}
}

// nolint: errcheck
func runQueuedTimeOperations(queueOperations []FutureTimeOperation, 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) {

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)
logWriter(logUpdate)
if err != nil {
displayLogs()
tb.FailNow()
}
queueOperations = queueOperations[1:]
numOpsRan++
}
return numOpsRan
}

func getKeys(validators map[string]mockValidator) []string {
keys := make([]string, len(validators))
i := 0
Expand Down
10 changes: 9 additions & 1 deletion x/mock/simulation/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package simulation

import (
"math/rand"
"time"

"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -23,7 +24,7 @@ type (
// 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),
) (action string, futureOperations []FutureOperation, err error)
) (action string, futureOperations []FutureOperation, FutureTimeOperations []FutureTimeOperation, err error)

// RandSetup performs the random setup the mock module needs.
RandSetup func(r *rand.Rand, privKeys []crypto.PrivKey)
Expand All @@ -48,6 +49,13 @@ type (
Op Operation
}

// FutureTimeOperation is an operation which will be ran at the
// first block after the provided BlockTime.
FutureTimeOperation struct {
BlockTime time.Time
Op Operation
}

// WeightedOperation is an operation with associated weight.
// This is used to bias the selection operation within the simulator.
WeightedOperation struct {
Expand Down
6 changes: 3 additions & 3 deletions x/slashing/simulation/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ 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) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, fTOp []simulation.FutureTimeOperation, err error) {
key := simulation.RandomKey(r, keys)
address := sdk.ValAddress(key.PubKey().Address())
msg := slashing.NewMsgUnjail(address)
if msg.ValidateBasic() != nil {
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
return "", nil, nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
}
ctx, write := ctx.CacheContext()
result := slashing.NewHandler(k)(ctx, msg)
Expand All @@ -28,6 +28,6 @@ func SimulateMsgUnjail(k slashing.Keeper) simulation.Operation {
}
event(fmt.Sprintf("slashing/MsgUnjail/%v", result.IsOK()))
action = fmt.Sprintf("TestMsgUnjail: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
return action, nil, nil
return action, nil, nil, nil
}
}
Loading

0 comments on commit 654cc2d

Please sign in to comment.