cosmos-sdk/x/simulation/operation.go

229 lines
6.8 KiB
Go
Raw Normal View History

2019-03-14 11:13:15 -07:00
package simulation
import (
"encoding/json"
"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
update simulation operations to use BaseApp (#4946) * update operations to use baseapp * updates and cleanup operations * update operations * restructure sim ops params * rename sim /operations/msg.go to /operations.go * move GenTx to a helper pkg to avoid circle deps * rm msg.ValidateBasic * changelog * random fees; delete auth's DeductFees sim operation * add chain-id for sig verification * Update x/simulation/account.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * fix bank, gov and distr errors * fix staking and slashing errors; increase prob for send enabled * increase gas x10 * make format * fix some distr and staking edge cases * fix all edge cases * golang ci * rename acc vars; default no fees to 0stake * cleanup; check for exchange rate and skip invalid ops * fixes * check for max entries * add pubkey to genaccounts * fix gov bug * update staking sim ops * fix small redelegation error * fix small self delegation on unjail * rm inf loop on random val/accs * copy array * add ok boolean to RandomValidator return values * format * Update x/bank/simulation/operations.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * Update simapp/helpers/test_helpers.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * address @colin-axner comments * add genaccount pubkey validation * fix test * update operations and move RandomFees to x/simulation * update gov ops * address @alexanderbez comments * avoid modifications to config * reorder params * changelog * Update x/distribution/simulation/genesis.go Co-Authored-By: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> * remove named return values * ensure all operations are simulated * golangci * add nolint * disable whitespace and funlen linter * disable godox * add TODO on unjail * update ops weights * remove dup * update godoc * x/slashing/simulation/operations.go linting * x/staking/simulation/operations.go linting * update operations format * x/bank/simulation/operations.go linting * x/distribution/simulation/operations.go linting * x/staking/simulation/operations.go linting * start changes: make bank simulate send multiple coins, code cleanup * fix nondeterminism bug * fix txsiglimit err * fix multisend bug * simplify simulation, cleanup opt privkey args * make slashing test invalid unjail msgs * Update simapp/state.go * golangCI changes
2019-10-23 02:14:45 -07:00
// happened as expected. The operation could be running and testing a fuzzed
2019-03-14 11:13:15 -07:00
// 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,
update simulation operations to use BaseApp (#4946) * update operations to use baseapp * updates and cleanup operations * update operations * restructure sim ops params * rename sim /operations/msg.go to /operations.go * move GenTx to a helper pkg to avoid circle deps * rm msg.ValidateBasic * changelog * random fees; delete auth's DeductFees sim operation * add chain-id for sig verification * Update x/simulation/account.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * fix bank, gov and distr errors * fix staking and slashing errors; increase prob for send enabled * increase gas x10 * make format * fix some distr and staking edge cases * fix all edge cases * golang ci * rename acc vars; default no fees to 0stake * cleanup; check for exchange rate and skip invalid ops * fixes * check for max entries * add pubkey to genaccounts * fix gov bug * update staking sim ops * fix small redelegation error * fix small self delegation on unjail * rm inf loop on random val/accs * copy array * add ok boolean to RandomValidator return values * format * Update x/bank/simulation/operations.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * Update simapp/helpers/test_helpers.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * address @colin-axner comments * add genaccount pubkey validation * fix test * update operations and move RandomFees to x/simulation * update gov ops * address @alexanderbez comments * avoid modifications to config * reorder params * changelog * Update x/distribution/simulation/genesis.go Co-Authored-By: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> * remove named return values * ensure all operations are simulated * golangci * add nolint * disable whitespace and funlen linter * disable godox * add TODO on unjail * update ops weights * remove dup * update godoc * x/slashing/simulation/operations.go linting * x/staking/simulation/operations.go linting * update operations format * x/bank/simulation/operations.go linting * x/distribution/simulation/operations.go linting * x/staking/simulation/operations.go linting * start changes: make bank simulate send multiple coins, code cleanup * fix nondeterminism bug * fix txsiglimit err * fix multisend bug * simplify simulation, cleanup opt privkey args * make slashing test invalid unjail msgs * Update simapp/state.go * golangCI changes
2019-10-23 02:14:45 -07:00
ctx sdk.Context, accounts []Account, chainID string) (
2019-03-14 11:13:15 -07:00
OperationMsg OperationMsg, futureOps []FutureOperation, err error)
// entry kinds for use within OperationEntry
const (
BeginBlockEntryKind = "begin_block"
EndBlockEntryKind = "end_block"
MsgEntryKind = "msg"
QueuedMsgEntryKind = "queued_msg"
2019-03-14 11:13:15 -07:00
)
// OperationEntry - an operation entry for logging (ex. BeginBlock, EndBlock, XxxMsg, etc)
type OperationEntry struct {
2019-07-05 16:25:56 -07:00
EntryKind string `json:"entry_kind" yaml:"entry_kind"`
Height int64 `json:"height" yaml:"height"`
Order int64 `json:"order" yaml:"order"`
Operation json.RawMessage `json:"operation" yaml:"operation"`
2019-03-14 11:13:15 -07:00
}
// NewOperationEntry creates a new OperationEntry instance
func NewOperationEntry(entry string, height, order int64, op json.RawMessage) OperationEntry {
2019-03-14 11:13:15 -07:00
return OperationEntry{
EntryKind: entry,
2019-03-14 11:13:15 -07:00
Height: height,
Order: order,
Operation: op,
2019-03-14 11:13:15 -07:00
}
}
// BeginBlockEntry - operation entry for begin block
func BeginBlockEntry(height int64) OperationEntry {
return NewOperationEntry(BeginBlockEntryKind, height, -1, nil)
}
2019-03-14 11:13:15 -07:00
// EndBlockEntry - operation entry for end block
func EndBlockEntry(height int64) OperationEntry {
return NewOperationEntry(EndBlockEntryKind, height, -1, nil)
2019-03-14 11:13:15 -07:00
}
// MsgEntry - operation entry for standard msg
func MsgEntry(height, order int64, opMsg OperationMsg) OperationEntry {
return NewOperationEntry(MsgEntryKind, height, order, opMsg.MustMarshal())
2019-03-14 11:13:15 -07:00
}
// QueuedMsgEntry creates an operation entry for a given queued message.
2019-03-14 11:13:15 -07:00
func QueuedMsgEntry(height int64, opMsg OperationMsg) OperationEntry {
return NewOperationEntry(QueuedMsgEntryKind, height, -1, opMsg.MustMarshal())
2019-03-14 11:13:15 -07:00
}
// MustMarshal marshals the operation entry, panic on error.
2019-03-14 11:13:15 -07:00
func (oe OperationEntry) MustMarshal() json.RawMessage {
out, err := json.Marshal(oe)
if err != nil {
panic(err)
}
return out
}
//_____________________________________________________________________
// OperationMsg - structure for operation output
type OperationMsg struct {
update simulation operations to use BaseApp (#4946) * update operations to use baseapp * updates and cleanup operations * update operations * restructure sim ops params * rename sim /operations/msg.go to /operations.go * move GenTx to a helper pkg to avoid circle deps * rm msg.ValidateBasic * changelog * random fees; delete auth's DeductFees sim operation * add chain-id for sig verification * Update x/simulation/account.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * fix bank, gov and distr errors * fix staking and slashing errors; increase prob for send enabled * increase gas x10 * make format * fix some distr and staking edge cases * fix all edge cases * golang ci * rename acc vars; default no fees to 0stake * cleanup; check for exchange rate and skip invalid ops * fixes * check for max entries * add pubkey to genaccounts * fix gov bug * update staking sim ops * fix small redelegation error * fix small self delegation on unjail * rm inf loop on random val/accs * copy array * add ok boolean to RandomValidator return values * format * Update x/bank/simulation/operations.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * Update simapp/helpers/test_helpers.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * address @colin-axner comments * add genaccount pubkey validation * fix test * update operations and move RandomFees to x/simulation * update gov ops * address @alexanderbez comments * avoid modifications to config * reorder params * changelog * Update x/distribution/simulation/genesis.go Co-Authored-By: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> * remove named return values * ensure all operations are simulated * golangci * add nolint * disable whitespace and funlen linter * disable godox * add TODO on unjail * update ops weights * remove dup * update godoc * x/slashing/simulation/operations.go linting * x/staking/simulation/operations.go linting * update operations format * x/bank/simulation/operations.go linting * x/distribution/simulation/operations.go linting * x/staking/simulation/operations.go linting * start changes: make bank simulate send multiple coins, code cleanup * fix nondeterminism bug * fix txsiglimit err * fix multisend bug * simplify simulation, cleanup opt privkey args * make slashing test invalid unjail msgs * Update simapp/state.go * golangCI changes
2019-10-23 02:14:45 -07:00
Route string `json:"route" yaml:"route"` // msg route (i.e module name)
Name string `json:"name" yaml:"name"` // operation name (msg Type or "no-operation")
Comment string `json:"comment" yaml:"comment"` // additional comment
OK bool `json:"ok" yaml:"ok"` // success
Msg json.RawMessage `json:"msg" yaml:"msg"` // JSON encoded msg
2019-03-14 11:13:15 -07:00
}
// NewOperationMsgBasic creates a new operation message from raw input.
2019-03-14 11:13:15 -07:00
func NewOperationMsgBasic(route, name, comment string, ok bool, msg []byte) OperationMsg {
return OperationMsg{
Route: route,
Name: name,
Comment: comment,
OK: ok,
Msg: msg,
}
}
// NewOperationMsg - create a new operation message from sdk.Msg
func NewOperationMsg(msg sdk.Msg, ok bool, comment string) OperationMsg {
return NewOperationMsgBasic(msg.Route(), msg.Type(), comment, ok, msg.GetSignBytes())
}
2019-03-14 11:13:15 -07:00
// NoOpMsg - create a no-operation message
func NoOpMsg(route string) OperationMsg {
return NewOperationMsgBasic(route, "no-operation", "", false, nil)
2019-03-14 11:13:15 -07:00
}
// log entry text for this operation msg
func (om OperationMsg) String() string {
out, err := json.Marshal(om)
if err != nil {
panic(err)
}
return string(out)
}
// MustMarshal Marshals the operation msg, panic on error
2019-03-14 11:13:15 -07:00
func (om OperationMsg) MustMarshal() json.RawMessage {
out, err := json.Marshal(om)
if err != nil {
panic(err)
}
return out
}
// LogEvent adds an event for the events stats
func (om OperationMsg) LogEvent(eventLogger func(route, op, evResult string)) {
2019-03-14 11:13:15 -07:00
pass := "ok"
if !om.OK {
pass = "failure"
}
eventLogger(om.Route, om.Name, pass)
2019-03-14 11:13:15 -07:00
}
// OperationQueue defines an object for a queue of operations
2019-03-14 11:13:15 -07:00
type OperationQueue map[int][]Operation
// NewOperationQueue creates a new OperationQueue instance.
func NewOperationQueue() OperationQueue {
return make(OperationQueue)
2019-03-14 11:13:15 -07:00
}
// queueOperations adds all future operations into the operation queue.
2019-03-14 11:13:15 -07:00
func queueOperations(queuedOps OperationQueue,
queuedTimeOps []FutureOperation, futureOps []FutureOperation) {
if futureOps == nil {
return
}
for _, futureOp := range futureOps {
2019-10-17 06:47:35 -07:00
futureOp := futureOp
2019-03-14 11:13:15 -07:00
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 (ops WeightedOperations) totalWeight() int {
totalOpWeight := 0
for _, op := range ops {
totalOpWeight += op.Weight
}
return totalOpWeight
}
type selectOpFn func(r *rand.Rand) Operation
func (ops 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
}
}