Skip to content

Commit

Permalink
R4R: Simulation Queued Time Operations (cosmos#2348)
Browse files Browse the repository at this point in the history
* closes cosmos#2349
* queue time operations simulation
* removed FutureTimeOperation
  • Loading branch information
sunnya97 authored and jaekwon committed Sep 19, 2018
1 parent b09e859 commit 5b8a499
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 10 deletions.
1 change: 1 addition & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ FEATURES
* [querier] added custom querier functionality, so ABCI query requests can be handled by keepers
* [simulation] [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) allow operations to specify future operations
* [simulation] [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) Add benchmarking capabilities, with makefile commands "test_sim_gaia_benchmark, test_sim_gaia_profile"
* [simulation] [\#2349](https://github.com/cosmos/cosmos-sdk/issues/2349) Add time-based future scheduled operations to simulator

* Tendermint

Expand Down
2 changes: 1 addition & 1 deletion x/gov/simulation/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe
fops := make([]simulation.FutureOperation, numVotes+1)
for i := 0; i < numVotes; i++ {
whenVote := ctx.BlockHeight() + r.Int63n(votingPeriod)
fops[i] = simulation.FutureOperation{int(whenVote), operationSimulateMsgVote(k, sk, keys[whoVotes[i]], proposalID)}
fops[i] = simulation.FutureOperation{BlockHeight: int(whenVote), Op: operationSimulateMsgVote(k, sk, keys[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
Expand Down
48 changes: 39 additions & 9 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 := []FutureOperation{}
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 @@ -139,9 +140,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 = thisBlockSize - numQueuedOpsRan - 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 +175,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 []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) {
totalOpWeight := 0
for i := 0; i < len(ops); i++ {
Expand All @@ -200,7 +202,7 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event f
}
logWriter(logUpdate)

queueOperations(operationQueue, futureOps)
queueOperations(operationQueue, timeOperationQueue, futureOps)
if testingMode {
if onOperation {
assertAllInvariants(t, app, invariants, displayLogs)
Expand Down Expand Up @@ -239,15 +241,23 @@ func getBlockSize(r *rand.Rand, blockSize int) int {
}

// adds all future operations into the operation queue.
func queueOperations(queuedOperations map[int][]Operation, futureOperations []FutureOperation) {
func queueOperations(queuedOperations map[int][]Operation, queuedTimeOperations []FutureOperation, futureOperations []FutureOperation) {
if futureOperations == nil {
return
}
for _, futureOp := range futureOperations {
if val, ok := queuedOperations[futureOp.BlockHeight]; ok {
queuedOperations[futureOp.BlockHeight] = append(val, futureOp.Op)
if futureOp.BlockHeight != 0 {
if val, ok := queuedOperations[futureOp.BlockHeight]; ok {
queuedOperations[futureOp.BlockHeight] = append(val, futureOp.Op)
} else {
queuedOperations[futureOp.BlockHeight] = []Operation{futureOp.Op}
}
} else {
queuedOperations[futureOp.BlockHeight] = []Operation{futureOp.Op}
// TODO: Replace with proper sorted data structure, so don't have the copy entire slice
index := sort.Search(len(queuedTimeOperations), func(i int) bool { return queuedTimeOperations[i].BlockTime.After(futureOp.BlockTime) })
queuedTimeOperations = append(queuedTimeOperations, FutureOperation{})
copy(queuedTimeOperations[index+1:], queuedTimeOperations[index:])
queuedTimeOperations[index] = futureOp
}
}
}
Expand All @@ -274,6 +284,26 @@ func runQueuedOperations(queueOperations map[int][]Operation, height int, tb tes
return 0
}

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) {

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
3 changes: 3 additions & 0 deletions 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 Down Expand Up @@ -41,10 +42,12 @@ type (

// FutureOperation is an operation which will be ran at the
// beginning of the provided BlockHeight.
// If both a BlockHeight and BlockTime are specified, it will use the BlockHeight.
// In the (likely) event that multiple operations are queued at the same
// block height, they will execute in a FIFO pattern.
FutureOperation struct {
BlockHeight int
BlockTime time.Time
Op Operation
}

Expand Down

0 comments on commit 5b8a499

Please sign in to comment.