github.com/Finschia/finschia-sdk@v0.49.1/x/simulation/operation.go (about) 1 package simulation 2 3 import ( 4 "encoding/json" 5 "math/rand" 6 "sort" 7 8 "github.com/Finschia/finschia-sdk/types/simulation" 9 ) 10 11 // entry kinds for use within OperationEntry 12 const ( 13 BeginBlockEntryKind = "begin_block" 14 EndBlockEntryKind = "end_block" 15 MsgEntryKind = "msg" 16 QueuedMsgEntryKind = "queued_msg" 17 ) 18 19 // OperationEntry - an operation entry for logging (ex. BeginBlock, EndBlock, XxxMsg, etc) 20 type OperationEntry struct { 21 EntryKind string `json:"entry_kind" yaml:"entry_kind"` 22 Height int64 `json:"height" yaml:"height"` 23 Order int64 `json:"order" yaml:"order"` 24 Operation json.RawMessage `json:"operation" yaml:"operation"` 25 } 26 27 // NewOperationEntry creates a new OperationEntry instance 28 func NewOperationEntry(entry string, height, order int64, op json.RawMessage) OperationEntry { 29 return OperationEntry{ 30 EntryKind: entry, 31 Height: height, 32 Order: order, 33 Operation: op, 34 } 35 } 36 37 // BeginBlockEntry - operation entry for begin block 38 func BeginBlockEntry(height int64) OperationEntry { 39 return NewOperationEntry(BeginBlockEntryKind, height, -1, nil) 40 } 41 42 // EndBlockEntry - operation entry for end block 43 func EndBlockEntry(height int64) OperationEntry { 44 return NewOperationEntry(EndBlockEntryKind, height, -1, nil) 45 } 46 47 // MsgEntry - operation entry for standard msg 48 func MsgEntry(height, order int64, opMsg simulation.OperationMsg) OperationEntry { 49 return NewOperationEntry(MsgEntryKind, height, order, opMsg.MustMarshal()) 50 } 51 52 // QueuedMsgEntry creates an operation entry for a given queued message. 53 func QueuedMsgEntry(height int64, opMsg simulation.OperationMsg) OperationEntry { 54 return NewOperationEntry(QueuedMsgEntryKind, height, -1, opMsg.MustMarshal()) 55 } 56 57 // MustMarshal marshals the operation entry, panic on error. 58 func (oe OperationEntry) MustMarshal() json.RawMessage { 59 out, err := json.Marshal(oe) 60 if err != nil { 61 panic(err) 62 } 63 64 return out 65 } 66 67 // OperationQueue defines an object for a queue of operations 68 type OperationQueue map[int][]simulation.Operation 69 70 // NewOperationQueue creates a new OperationQueue instance. 71 func NewOperationQueue() OperationQueue { 72 return make(OperationQueue) 73 } 74 75 // queueOperations adds all future operations into the operation queue. 76 func queueOperations(queuedOps OperationQueue, queuedTimeOps, futureOps []simulation.FutureOperation) { 77 if futureOps == nil { 78 return 79 } 80 81 for _, futureOp := range futureOps { 82 if futureOp.BlockHeight != 0 { 83 if val, ok := queuedOps[futureOp.BlockHeight]; ok { 84 queuedOps[futureOp.BlockHeight] = append(val, futureOp.Op) 85 } else { 86 queuedOps[futureOp.BlockHeight] = []simulation.Operation{futureOp.Op} 87 } 88 89 continue 90 } 91 92 // TODO: Replace with proper sorted data structure, so don't have the 93 // copy entire slice 94 index := sort.Search( 95 len(queuedTimeOps), 96 func(i int) bool { 97 return queuedTimeOps[i].BlockTime.After(futureOp.BlockTime) 98 }, 99 ) 100 101 queuedTimeOps = append(queuedTimeOps, simulation.FutureOperation{}) 102 copy(queuedTimeOps[index+1:], queuedTimeOps[index:]) 103 queuedTimeOps[index] = futureOp 104 } 105 } 106 107 // WeightedOperation is an operation with associated weight. 108 // This is used to bias the selection operation within the simulator. 109 type WeightedOperation struct { 110 weight int 111 op simulation.Operation 112 } 113 114 func (w WeightedOperation) Weight() int { 115 return w.weight 116 } 117 118 func (w WeightedOperation) Op() simulation.Operation { 119 return w.op 120 } 121 122 // NewWeightedOperation creates a new WeightedOperation instance 123 func NewWeightedOperation(weight int, op simulation.Operation) WeightedOperation { 124 return WeightedOperation{ 125 weight: weight, 126 op: op, 127 } 128 } 129 130 // WeightedOperations is the group of all weighted operations to simulate. 131 type WeightedOperations []simulation.WeightedOperation 132 133 func (ops WeightedOperations) totalWeight() int { 134 totalOpWeight := 0 135 for _, op := range ops { 136 totalOpWeight += op.Weight() 137 } 138 139 return totalOpWeight 140 } 141 142 func (ops WeightedOperations) getSelectOpFn() simulation.SelectOpFn { 143 totalOpWeight := ops.totalWeight() 144 145 return func(r *rand.Rand) simulation.Operation { 146 x := r.Intn(totalOpWeight) 147 for i := 0; i < len(ops); i++ { 148 if x <= ops[i].Weight() { 149 return ops[i].Op() 150 } 151 152 x -= ops[i].Weight() 153 } 154 // shouldn't happen 155 return ops[0].Op() 156 } 157 }