R4R: Simulation Queued Time Operations (#2348)
* closes #2349 * queue time operations simulation * removed FutureTimeOperation
This commit is contained in:
parent
b09e8599d9
commit
5b8a499fd0
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
@ -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++
|
||||
@ -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++ {
|
||||
@ -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)
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -2,6 +2,7 @@ package simulation
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user