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  }