github.com/onflow/flow-go@v0.33.17/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 type ComputationMeterParameters struct { 33 computationLimit uint64 34 computationWeights ExecutionEffortWeights 35 } 36 37 func DefaultComputationMeterParameters() ComputationMeterParameters { 38 return ComputationMeterParameters{ 39 computationLimit: math.MaxUint64, 40 computationWeights: DefaultComputationWeights, 41 } 42 } 43 44 func (params MeterParameters) WithComputationLimit(limit uint) MeterParameters { 45 newParams := params 46 newParams.computationLimit = uint64(limit) << MeterExecutionInternalPrecisionBytes 47 return newParams 48 } 49 50 func (params MeterParameters) WithComputationWeights( 51 weights ExecutionEffortWeights, 52 ) MeterParameters { 53 newParams := params 54 newParams.computationWeights = weights 55 return newParams 56 } 57 58 func (params ComputationMeterParameters) ComputationWeights() ExecutionEffortWeights { 59 return params.computationWeights 60 } 61 62 // TotalComputationLimit returns the total computation limit 63 func (params ComputationMeterParameters) TotalComputationLimit() uint { 64 return uint(params.computationLimit >> MeterExecutionInternalPrecisionBytes) 65 } 66 67 type ComputationMeter struct { 68 params ComputationMeterParameters 69 70 computationUsed uint64 71 computationIntensities MeteredComputationIntensities 72 } 73 74 func NewComputationMeter(params ComputationMeterParameters) ComputationMeter { 75 return ComputationMeter{ 76 params: params, 77 computationIntensities: make(MeteredComputationIntensities), 78 } 79 } 80 81 // MeterComputation captures computation usage and returns an error if it goes beyond the limit 82 func (m *ComputationMeter) MeterComputation( 83 kind common.ComputationKind, 84 intensity uint, 85 ) error { 86 m.computationIntensities[kind] += intensity 87 w, ok := m.params.computationWeights[kind] 88 if !ok { 89 return nil 90 } 91 m.computationUsed += w * uint64(intensity) 92 if m.computationUsed > m.params.computationLimit { 93 return errors.NewComputationLimitExceededError( 94 uint64(m.params.TotalComputationLimit())) 95 } 96 return nil 97 } 98 99 // ComputationAvailable returns true if enough computation is left in the transaction for the given intensity and type 100 func (m *ComputationMeter) ComputationAvailable( 101 kind common.ComputationKind, 102 intensity uint, 103 ) bool { 104 w, ok := m.params.computationWeights[kind] 105 // if not found return has capacity 106 // given the behaviour of MeterComputation is ignoring intensities without a set weight 107 if !ok { 108 return true 109 } 110 potentialComputationUsage := m.computationUsed + w*uint64(intensity) 111 return potentialComputationUsage <= m.params.computationLimit 112 } 113 114 // ComputationIntensities returns all the measured computational intensities 115 func (m *ComputationMeter) ComputationIntensities() MeteredComputationIntensities { 116 return m.computationIntensities 117 } 118 119 // TotalComputationUsed returns the total computation used 120 func (m *ComputationMeter) TotalComputationUsed() uint64 { 121 return m.computationUsed >> MeterExecutionInternalPrecisionBytes 122 } 123 124 func (m *ComputationMeter) Merge(child ComputationMeter) { 125 m.computationUsed = m.computationUsed + child.computationUsed 126 127 for key, intensity := range child.computationIntensities { 128 m.computationIntensities[key] += intensity 129 } 130 }