github.com/Finschia/finschia-sdk@v0.48.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 []simulation.FutureOperation, futureOps []simulation.FutureOperation) { 77 if futureOps == nil { 78 return 79 } 80 81 for _, futureOp := range futureOps { 82 futureOp := futureOp 83 if futureOp.BlockHeight != 0 { 84 if val, ok := queuedOps[futureOp.BlockHeight]; ok { 85 queuedOps[futureOp.BlockHeight] = append(val, futureOp.Op) 86 } else { 87 queuedOps[futureOp.BlockHeight] = []simulation.Operation{futureOp.Op} 88 } 89 90 continue 91 } 92 93 // TODO: Replace with proper sorted data structure, so don't have the 94 // copy entire slice 95 index := sort.Search( 96 len(queuedTimeOps), 97 func(i int) bool { 98 return queuedTimeOps[i].BlockTime.After(futureOp.BlockTime) 99 }, 100 ) 101 102 queuedTimeOps = append(queuedTimeOps, simulation.FutureOperation{}) 103 copy(queuedTimeOps[index+1:], queuedTimeOps[index:]) 104 queuedTimeOps[index] = futureOp 105 } 106 } 107 108 // WeightedOperation is an operation with associated weight. 109 // This is used to bias the selection operation within the simulator. 110 type WeightedOperation struct { 111 weight int 112 op simulation.Operation 113 } 114 115 func (w WeightedOperation) Weight() int { 116 return w.weight 117 } 118 119 func (w WeightedOperation) Op() simulation.Operation { 120 return w.op 121 } 122 123 // NewWeightedOperation creates a new WeightedOperation instance 124 func NewWeightedOperation(weight int, op simulation.Operation) WeightedOperation { 125 return WeightedOperation{ 126 weight: weight, 127 op: op, 128 } 129 } 130 131 // WeightedOperations is the group of all weighted operations to simulate. 132 type WeightedOperations []simulation.WeightedOperation 133 134 func (ops WeightedOperations) totalWeight() int { 135 totalOpWeight := 0 136 for _, op := range ops { 137 totalOpWeight += op.Weight() 138 } 139 140 return totalOpWeight 141 } 142 143 func (ops WeightedOperations) getSelectOpFn() simulation.SelectOpFn { 144 totalOpWeight := ops.totalWeight() 145 146 return func(r *rand.Rand) simulation.Operation { 147 x := r.Intn(totalOpWeight) 148 for i := 0; i < len(ops); i++ { 149 if x <= ops[i].Weight() { 150 return ops[i].Op() 151 } 152 153 x -= ops[i].Weight() 154 } 155 // shouldn't happen 156 return ops[0].Op() 157 } 158 }