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  }