github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/meter/computation_meter.go (about) 1 package meter 2 3 import ( 4 "math" 5 6 "github.com/onflow/cadence/runtime/common" 7 8 "github.com/onflow/flow-go/fvm/errors" 9 ) 10 11 type MeteredComputationIntensities map[common.ComputationKind]uint 12 13 var ( 14 // DefaultComputationWeights is the default weights for computation intensities 15 // these weighs make the computation metering the same as it was before dynamic execution fees 16 // these weighs make the computation metering the same as it was before dynamic execution fees 17 DefaultComputationWeights = ExecutionEffortWeights{ 18 common.ComputationKindStatement: 1 << MeterExecutionInternalPrecisionBytes, 19 common.ComputationKindLoop: 1 << MeterExecutionInternalPrecisionBytes, 20 common.ComputationKindFunctionInvocation: 1 << MeterExecutionInternalPrecisionBytes, 21 } 22 ) 23 24 // MeterExecutionInternalPrecisionBytes are the amount of bytes that are used internally by the 25 // WeigthedMeter to allow for metering computation smaller than one unit of computation. 26 // This allows for more fine weights. A weight of 1 unit of computation is equal to 1<<16. 27 // The minimum possible weight is 1/65536. 28 const MeterExecutionInternalPrecisionBytes = 16 29 30 type ExecutionEffortWeights map[common.ComputationKind]uint64 31 32 func (weights ExecutionEffortWeights) ComputationFromIntensities(intensities MeteredComputationIntensities) uint64 { 33 var result uint64 34 for kind, weight := range weights { 35 intensity := uint64(intensities[kind]) 36 result += weight * intensity 37 } 38 return result >> MeterExecutionInternalPrecisionBytes 39 } 40 41 type ComputationMeterParameters struct { 42 computationLimit uint64 43 computationWeights ExecutionEffortWeights 44 } 45 46 func DefaultComputationMeterParameters() ComputationMeterParameters { 47 return ComputationMeterParameters{ 48 computationLimit: math.MaxUint64, 49 computationWeights: DefaultComputationWeights, 50 } 51 } 52 53 func (params MeterParameters) WithComputationLimit(limit uint) MeterParameters { 54 newParams := params 55 newParams.computationLimit = uint64(limit) << MeterExecutionInternalPrecisionBytes 56 return newParams 57 } 58 59 func (params MeterParameters) WithComputationWeights( 60 weights ExecutionEffortWeights, 61 ) MeterParameters { 62 newParams := params 63 newParams.computationWeights = weights 64 return newParams 65 } 66 67 func (params ComputationMeterParameters) ComputationWeights() ExecutionEffortWeights { 68 return params.computationWeights 69 } 70 71 // TotalComputationLimit returns the total computation limit 72 func (params ComputationMeterParameters) TotalComputationLimit() uint { 73 return uint(params.computationLimit >> MeterExecutionInternalPrecisionBytes) 74 } 75 76 type ComputationMeter struct { 77 params ComputationMeterParameters 78 79 computationUsed uint64 80 computationIntensities MeteredComputationIntensities 81 } 82 83 func NewComputationMeter(params ComputationMeterParameters) ComputationMeter { 84 return ComputationMeter{ 85 params: params, 86 computationIntensities: make(MeteredComputationIntensities), 87 } 88 } 89 90 // MeterComputation captures computation usage and returns an error if it goes beyond the limit 91 func (m *ComputationMeter) MeterComputation( 92 kind common.ComputationKind, 93 intensity uint, 94 ) error { 95 m.computationIntensities[kind] += intensity 96 w, ok := m.params.computationWeights[kind] 97 if !ok { 98 return nil 99 } 100 m.computationUsed += w * uint64(intensity) 101 if m.computationUsed > m.params.computationLimit { 102 return errors.NewComputationLimitExceededError( 103 uint64(m.params.TotalComputationLimit())) 104 } 105 return nil 106 } 107 108 // ComputationAvailable returns true if enough computation is left in the transaction for the given intensity and type 109 func (m *ComputationMeter) ComputationAvailable( 110 kind common.ComputationKind, 111 intensity uint, 112 ) bool { 113 w, ok := m.params.computationWeights[kind] 114 // if not found return has capacity 115 // given the behaviour of MeterComputation is ignoring intensities without a set weight 116 if !ok { 117 return true 118 } 119 potentialComputationUsage := m.computationUsed + w*uint64(intensity) 120 return potentialComputationUsage <= m.params.computationLimit 121 } 122 123 // ComputationIntensities returns all the measured computational intensities 124 func (m *ComputationMeter) ComputationIntensities() MeteredComputationIntensities { 125 return m.computationIntensities 126 } 127 128 // TotalComputationUsed returns the total computation used 129 func (m *ComputationMeter) TotalComputationUsed() uint64 { 130 return m.computationUsed >> MeterExecutionInternalPrecisionBytes 131 } 132 133 func (m *ComputationMeter) Merge(child ComputationMeter) { 134 m.computationUsed = m.computationUsed + child.computationUsed 135 136 for key, intensity := range child.computationIntensities { 137 m.computationIntensities[key] += intensity 138 } 139 }