github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/simulation/operation.go (about) 1 package simulation 2 3 import ( 4 "encoding/json" 5 "math/rand" 6 "sort" 7 "time" 8 9 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/baseapp" 10 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 11 ) 12 13 // Operation runs a state machine transition, and ensures the transition 14 // happened as expected. The operation could be running and testing a fuzzed 15 // transaction, or doing the same for a message. 16 // 17 // For ease of debugging, an operation returns a descriptive message "action", 18 // which details what this fuzzed state machine transition actually did. 19 // 20 // Operations can optionally provide a list of "FutureOperations" to run later 21 // These will be ran at the beginning of the corresponding block. 22 type Operation func(r *rand.Rand, app *baseapp.BaseApp, 23 ctx sdk.Context, accounts []Account, chainID string) ( 24 OperationMsg OperationMsg, futureOps []FutureOperation, err error) 25 26 // entry kinds for use within OperationEntry 27 const ( 28 BeginBlockEntryKind = "begin_block" 29 EndBlockEntryKind = "end_block" 30 MsgEntryKind = "msg" 31 QueuedMsgEntryKind = "queued_msg" 32 ) 33 34 // OperationEntry - an operation entry for logging (ex. BeginBlock, EndBlock, XxxMsg, etc) 35 type OperationEntry struct { 36 EntryKind string `json:"entry_kind" yaml:"entry_kind"` 37 Height int64 `json:"height" yaml:"height"` 38 Order int64 `json:"order" yaml:"order"` 39 Operation json.RawMessage `json:"operation" yaml:"operation"` 40 } 41 42 // NewOperationEntry creates a new OperationEntry instance 43 func NewOperationEntry(entry string, height, order int64, op json.RawMessage) OperationEntry { 44 return OperationEntry{ 45 EntryKind: entry, 46 Height: height, 47 Order: order, 48 Operation: op, 49 } 50 } 51 52 // BeginBlockEntry - operation entry for begin block 53 func BeginBlockEntry(height int64) OperationEntry { 54 return NewOperationEntry(BeginBlockEntryKind, height, -1, nil) 55 } 56 57 // EndBlockEntry - operation entry for end block 58 func EndBlockEntry(height int64) OperationEntry { 59 return NewOperationEntry(EndBlockEntryKind, height, -1, nil) 60 } 61 62 // MsgEntry - operation entry for standard msg 63 func MsgEntry(height, order int64, opMsg OperationMsg) OperationEntry { 64 return NewOperationEntry(MsgEntryKind, height, order, opMsg.MustMarshal()) 65 } 66 67 // QueuedMsgEntry creates an operation entry for a given queued message. 68 func QueuedMsgEntry(height int64, opMsg OperationMsg) OperationEntry { 69 return NewOperationEntry(QueuedMsgEntryKind, height, -1, opMsg.MustMarshal()) 70 } 71 72 // MustMarshal marshals the operation entry, panic on error. 73 func (oe OperationEntry) MustMarshal() json.RawMessage { 74 out, err := json.Marshal(oe) 75 if err != nil { 76 panic(err) 77 } 78 return out 79 } 80 81 //_____________________________________________________________________ 82 83 // OperationMsg - structure for operation output 84 type OperationMsg struct { 85 Route string `json:"route" yaml:"route"` // msg route (i.e module name) 86 Name string `json:"name" yaml:"name"` // operation name (msg Type or "no-operation") 87 Comment string `json:"comment" yaml:"comment"` // additional comment 88 OK bool `json:"ok" yaml:"ok"` // success 89 Msg json.RawMessage `json:"msg" yaml:"msg"` // JSON encoded msg 90 } 91 92 // NewOperationMsgBasic creates a new operation message from raw input. 93 func NewOperationMsgBasic(route, name, comment string, ok bool, msg []byte) OperationMsg { 94 return OperationMsg{ 95 Route: route, 96 Name: name, 97 Comment: comment, 98 OK: ok, 99 Msg: msg, 100 } 101 } 102 103 // NewOperationMsg - create a new operation message from sdk.Msg 104 func NewOperationMsg(msg sdk.Msg, ok bool, comment string) OperationMsg { 105 return NewOperationMsgBasic(msg.Route(), msg.Type(), comment, ok, msg.GetSignBytes()) 106 } 107 108 // NoOpMsg - create a no-operation message 109 func NoOpMsg(route string) OperationMsg { 110 return NewOperationMsgBasic(route, "no-operation", "", false, nil) 111 } 112 113 // log entry text for this operation msg 114 func (om OperationMsg) String() string { 115 out, err := json.Marshal(om) 116 if err != nil { 117 panic(err) 118 } 119 return string(out) 120 } 121 122 // MustMarshal Marshals the operation msg, panic on error 123 func (om OperationMsg) MustMarshal() json.RawMessage { 124 out, err := json.Marshal(om) 125 if err != nil { 126 panic(err) 127 } 128 return out 129 } 130 131 // LogEvent adds an event for the events stats 132 func (om OperationMsg) LogEvent(eventLogger func(route, op, evResult string)) { 133 pass := "ok" 134 if !om.OK { 135 pass = "failure" 136 } 137 eventLogger(om.Route, om.Name, pass) 138 } 139 140 // OperationQueue defines an object for a queue of operations 141 type OperationQueue map[int][]Operation 142 143 // NewOperationQueue creates a new OperationQueue instance. 144 func NewOperationQueue() OperationQueue { 145 return make(OperationQueue) 146 } 147 148 // queueOperations adds all future operations into the operation queue. 149 func queueOperations(queuedOps OperationQueue, 150 queuedTimeOps []FutureOperation, futureOps []FutureOperation) { 151 152 if futureOps == nil { 153 return 154 } 155 156 for _, futureOp := range futureOps { 157 futureOp := futureOp 158 if futureOp.BlockHeight != 0 { 159 if val, ok := queuedOps[futureOp.BlockHeight]; ok { 160 queuedOps[futureOp.BlockHeight] = append(val, futureOp.Op) 161 } else { 162 queuedOps[futureOp.BlockHeight] = []Operation{futureOp.Op} 163 } 164 continue 165 } 166 167 // TODO: Replace with proper sorted data structure, so don't have the 168 // copy entire slice 169 index := sort.Search( 170 len(queuedTimeOps), 171 func(i int) bool { 172 return queuedTimeOps[i].BlockTime.After(futureOp.BlockTime) 173 }, 174 ) 175 queuedTimeOps = append(queuedTimeOps, FutureOperation{}) 176 copy(queuedTimeOps[index+1:], queuedTimeOps[index:]) 177 queuedTimeOps[index] = futureOp 178 } 179 } 180 181 //________________________________________________________________________ 182 183 // FutureOperation is an operation which will be ran at the beginning of the 184 // provided BlockHeight. If both a BlockHeight and BlockTime are specified, it 185 // will use the BlockHeight. In the (likely) event that multiple operations 186 // are queued at the same block height, they will execute in a FIFO pattern. 187 type FutureOperation struct { 188 BlockHeight int 189 BlockTime time.Time 190 Op Operation 191 } 192 193 //________________________________________________________________________ 194 195 // WeightedOperation is an operation with associated weight. 196 // This is used to bias the selection operation within the simulator. 197 type WeightedOperation struct { 198 Weight int 199 Op Operation 200 } 201 202 // NewWeightedOperation creates a new WeightedOperation instance 203 func NewWeightedOperation(weight int, op Operation) WeightedOperation { 204 return WeightedOperation{ 205 Weight: weight, 206 Op: op, 207 } 208 } 209 210 // WeightedOperations is the group of all weighted operations to simulate. 211 type WeightedOperations []WeightedOperation 212 213 func (ops WeightedOperations) totalWeight() int { 214 totalOpWeight := 0 215 for _, op := range ops { 216 totalOpWeight += op.Weight 217 } 218 return totalOpWeight 219 } 220 221 type selectOpFn func(r *rand.Rand) Operation 222 223 func (ops WeightedOperations) getSelectOpFn() selectOpFn { 224 totalOpWeight := ops.totalWeight() 225 return func(r *rand.Rand) Operation { 226 x := r.Intn(totalOpWeight) 227 for i := 0; i < len(ops); i++ { 228 if x <= ops[i].Weight { 229 return ops[i].Op 230 } 231 x -= ops[i].Weight 232 } 233 // shouldn't happen 234 return ops[0].Op 235 } 236 }