Skip to content

Commit

Permalink
add meter method which returns remaining computation for given comput…
Browse files Browse the repository at this point in the history
…ation kind
  • Loading branch information
turbolent committed Jul 12, 2024
1 parent f78e81b commit a3f26d7
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 7 deletions.
14 changes: 7 additions & 7 deletions fvm/environment/meter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package environment
import (
"context"

"github.com/onflow/cadence/runtime"
"github.com/onflow/cadence/runtime/common"

"github.com/onflow/flow-go/fvm/errors"
Expand Down Expand Up @@ -69,18 +70,13 @@ var MainnetExecutionEffortWeights = meter.ExecutionEffortWeights{
}

type Meter interface {
MeterComputation(common.ComputationKind, uint) error
ComputationUsed() (uint64, error)
runtime.MeterInterface

ComputationIntensities() meter.MeteredComputationIntensities
ComputationAvailable(common.ComputationKind, uint) bool

MeterMemory(usage common.MemoryUsage) error
MemoryUsed() (uint64, error)

MeterEmittedEvent(byteSize uint64) error
TotalEmittedEventBytes() uint64

InteractionUsed() (uint64, error)
}

type meterImpl struct {
Expand Down Expand Up @@ -111,6 +107,10 @@ func (meter *meterImpl) ComputationAvailable(
return meter.txnState.ComputationAvailable(kind, intensity)
}

func (meter *meterImpl) ComputationRemaining(kind common.ComputationKind) uint {
return meter.txnState.ComputationRemaining(kind)
}

func (meter *meterImpl) ComputationUsed() (uint64, error) {
return meter.txnState.TotalComputationUsed(), nil
}
Expand Down
14 changes: 14 additions & 0 deletions fvm/meter/computation_meter.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,24 @@ func (m *ComputationMeter) ComputationAvailable(
if !ok {
return true
}

potentialComputationUsage := m.computationUsed + w*uint64(intensity)
return potentialComputationUsage <= m.params.computationLimit
}

// ComputationRemaining returns the remaining computation left in the transaction for the given type
func (m *ComputationMeter) ComputationRemaining(kind common.ComputationKind) uint {
w, ok := m.params.computationWeights[kind]
// if not found return has capacity
// given the behaviour of MeterComputation is ignoring intensities without a set weight
if !ok {
return math.MaxUint
}

remainingComputationUsage := m.params.computationLimit - m.computationUsed
return uint(remainingComputationUsage / w)
}

// ComputationIntensities returns all the measured computational intensities
func (m *ComputationMeter) ComputationIntensities() MeteredComputationIntensities {
return m.computationIntensities
Expand Down
21 changes: 21 additions & 0 deletions fvm/meter/meter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,27 @@ func TestWeightedComputationMetering(t *testing.T) {
require.True(t, m.ComputationAvailable(1, 10))
})

t.Run("check computation remaining", func(t *testing.T) {
m := meter.NewMeter(
meter.DefaultParameters().
WithComputationLimit(10).
WithComputationWeights(
map[common.ComputationKind]uint64{0: 1 << meter.MeterExecutionInternalPrecisionBytes}),
)

remaining := m.ComputationRemaining(0)
require.Equal(t, uint(10), remaining)

err := m.MeterComputation(0, 1)
require.NoError(t, err)
require.Equal(t, uint64(1), m.TotalComputationUsed())

require.Equal(t, uint(9), m.ComputationRemaining(0))

// test a type without a weight (default zero)
require.Equal(t, uint(math.MaxUint), m.ComputationRemaining(1))
})

t.Run("merge meters", func(t *testing.T) {
compKind := common.ComputationKind(0)
m := meter.NewMeter(
Expand Down
15 changes: 15 additions & 0 deletions fvm/storage/state/execution_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package state

import (
"fmt"
"math"

"github.com/onflow/cadence/runtime/common"
"github.com/onflow/crypto/hash"
Expand Down Expand Up @@ -235,6 +236,20 @@ func (state *ExecutionState) ComputationAvailable(kind common.ComputationKind, i
return true
}

// ComputationRemaining returns the available computation capacity without metering
func (state *ExecutionState) ComputationRemaining(kind common.ComputationKind) uint {
if state.finalized {
// if state is finalized return 0
return 0
}

if state.enforceLimits {
return state.meter.ComputationRemaining(kind)
}

return math.MaxUint
}

// TotalComputationUsed returns total computation used
func (state *ExecutionState) TotalComputationUsed() uint64 {
return state.meter.TotalComputationUsed()
Expand Down
5 changes: 5 additions & 0 deletions fvm/storage/state/transaction_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func (id NestedTransactionId) StateForTestingOnly() *ExecutionState {
type Meter interface {
MeterComputation(kind common.ComputationKind, intensity uint) error
ComputationAvailable(kind common.ComputationKind, intensity uint) bool
ComputationRemaining(kind common.ComputationKind) uint
ComputationIntensities() meter.MeteredComputationIntensities
TotalComputationLimit() uint
TotalComputationUsed() uint64
Expand Down Expand Up @@ -451,6 +452,10 @@ func (txnState *transactionState) ComputationAvailable(
return txnState.current().ComputationAvailable(kind, intensity)
}

func (txnState *transactionState) ComputationRemaining(kind common.ComputationKind) uint {
return txnState.current().ComputationRemaining(kind)
}

func (txnState *transactionState) MeterMemory(
kind common.MemoryKind,
intensity uint,
Expand Down

0 comments on commit a3f26d7

Please sign in to comment.