cosmos-sdk/x/mock/simulation/operation.go

113 lines
3.2 KiB
Go

package simulation
import (
"math/rand"
"sort"
"time"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Operation runs a state machine transition, and ensures the transition
// happened as expected. The operation could be running and testing a fuzzed
// transaction, or doing the same for a message.
//
// For ease of debugging, an operation returns a descriptive message "action",
// which details what this fuzzed state machine transition actually did.
//
// Operations can optionally provide a list of "FutureOperations" to run later
// These will be ran at the beginning of the corresponding block.
type Operation func(r *rand.Rand, app *baseapp.BaseApp,
ctx sdk.Context, accounts []Account, event func(string)) (
action string, futureOps []FutureOperation, err error)
// queue of operations
type OperationQueue map[int][]Operation
func newOperationQueue() OperationQueue {
operationQueue := make(OperationQueue)
return operationQueue
}
// adds all future operations into the operation queue.
func queueOperations(queuedOps OperationQueue,
queuedTimeOps []FutureOperation, futureOps []FutureOperation) {
if futureOps == nil {
return
}
for _, futureOp := range futureOps {
if futureOp.BlockHeight != 0 {
if val, ok := queuedOps[futureOp.BlockHeight]; ok {
queuedOps[futureOp.BlockHeight] = append(val, futureOp.Op)
} else {
queuedOps[futureOp.BlockHeight] = []Operation{futureOp.Op}
}
continue
}
// TODO: Replace with proper sorted data structure, so don't have the
// copy entire slice
index := sort.Search(
len(queuedTimeOps),
func(i int) bool {
return queuedTimeOps[i].BlockTime.After(futureOp.BlockTime)
},
)
queuedTimeOps = append(queuedTimeOps, FutureOperation{})
copy(queuedTimeOps[index+1:], queuedTimeOps[index:])
queuedTimeOps[index] = futureOp
}
}
//________________________________________________________________________
// 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.
type FutureOperation struct {
BlockHeight int
BlockTime time.Time
Op Operation
}
//________________________________________________________________________
// WeightedOperation is an operation with associated weight.
// This is used to bias the selection operation within the simulator.
type WeightedOperation struct {
Weight int
Op Operation
}
// WeightedOperations is the group of all weighted operations to simulate.
type WeightedOperations []WeightedOperation
func (w WeightedOperations) totalWeight() int {
totalOpWeight := 0
for i := 0; i < len(w); i++ {
totalOpWeight += w[i].Weight
}
return totalOpWeight
}
type selectOpFn func(r *rand.Rand) Operation
func (w WeightedOperations) getSelectOpFn() selectOpFn {
totalOpWeight := ops.totalWeight()
return func(r *rand.Rand) Operation {
x := r.Intn(totalOpWeight)
for i := 0; i < len(ops); i++ {
if x <= ops[i].Weight {
return ops[i].Op
}
x -= ops[i].Weight
}
// shouldn't happen
return ops[0].Op
}
}