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  }