github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/executionParameters.go (about)

     1  package fvm
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math"
     7  
     8  	"github.com/onflow/cadence"
     9  	"github.com/onflow/cadence/runtime/common"
    10  
    11  	"github.com/onflow/flow-go/fvm/blueprints"
    12  	"github.com/onflow/flow-go/fvm/environment"
    13  	"github.com/onflow/flow-go/fvm/errors"
    14  	"github.com/onflow/flow-go/fvm/meter"
    15  	"github.com/onflow/flow-go/fvm/storage"
    16  	"github.com/onflow/flow-go/fvm/storage/derived"
    17  	"github.com/onflow/flow-go/fvm/storage/state"
    18  )
    19  
    20  func ProcedureStateParameters(
    21  	ctx Context,
    22  	proc Procedure,
    23  ) state.StateParameters {
    24  	return state.DefaultParameters().
    25  		WithMeterParameters(getBasicMeterParameters(ctx, proc)).
    26  		WithMaxKeySizeAllowed(ctx.MaxStateKeySize).
    27  		WithMaxValueSizeAllowed(ctx.MaxStateValueSize)
    28  }
    29  
    30  // getBasicMeterParameters returns the set of meter parameters used for
    31  // general procedure execution.  Subparts of the procedure execution may
    32  // specify custom meter parameters via nested transactions.
    33  func getBasicMeterParameters(
    34  	ctx Context,
    35  	proc Procedure,
    36  ) meter.MeterParameters {
    37  	params := meter.DefaultParameters().
    38  		WithComputationLimit(uint(proc.ComputationLimit(ctx))).
    39  		WithMemoryLimit(proc.MemoryLimit(ctx)).
    40  		WithEventEmitByteLimit(ctx.EventCollectionByteSizeLimit).
    41  		WithStorageInteractionLimit(ctx.MaxStateInteractionSize)
    42  
    43  	// NOTE: The memory limit (and interaction limit) may be overridden by the
    44  	// environment.  We need to ignore the override in that case.
    45  	if proc.ShouldDisableMemoryAndInteractionLimits(ctx) {
    46  		params = params.WithMemoryLimit(math.MaxUint64).
    47  			WithStorageInteractionLimit(math.MaxUint64)
    48  	}
    49  
    50  	return params
    51  }
    52  
    53  // getBodyMeterParameters returns the set of meter parameters used for
    54  // transaction/script body execution.
    55  func getBodyMeterParameters(
    56  	ctx Context,
    57  	proc Procedure,
    58  	txnState storage.TransactionPreparer,
    59  ) (
    60  	meter.MeterParameters,
    61  	error,
    62  ) {
    63  	procParams := getBasicMeterParameters(ctx, proc)
    64  
    65  	overrides, err := txnState.GetMeterParamOverrides(
    66  		txnState,
    67  		NewMeterParamOverridesComputer(ctx, txnState))
    68  	if err != nil {
    69  		return procParams, err
    70  	}
    71  
    72  	if overrides.ComputationWeights != nil {
    73  		procParams = procParams.WithComputationWeights(
    74  			overrides.ComputationWeights)
    75  	}
    76  
    77  	if overrides.MemoryWeights != nil {
    78  		procParams = procParams.WithMemoryWeights(overrides.MemoryWeights)
    79  	}
    80  
    81  	if overrides.MemoryLimit != nil {
    82  		procParams = procParams.WithMemoryLimit(*overrides.MemoryLimit)
    83  	}
    84  
    85  	// NOTE: The memory limit (and interaction limit) may be overridden by the
    86  	// environment.  We need to ignore the override in that case.
    87  	if proc.ShouldDisableMemoryAndInteractionLimits(ctx) {
    88  		procParams = procParams.WithMemoryLimit(math.MaxUint64).
    89  			WithStorageInteractionLimit(math.MaxUint64)
    90  	}
    91  
    92  	return procParams, nil
    93  }
    94  
    95  type MeterParamOverridesComputer struct {
    96  	ctx      Context
    97  	txnState storage.TransactionPreparer
    98  }
    99  
   100  func NewMeterParamOverridesComputer(
   101  	ctx Context,
   102  	txnState storage.TransactionPreparer,
   103  ) MeterParamOverridesComputer {
   104  	return MeterParamOverridesComputer{
   105  		ctx:      ctx,
   106  		txnState: txnState,
   107  	}
   108  }
   109  
   110  func (computer MeterParamOverridesComputer) Compute(
   111  	_ state.NestedTransactionPreparer,
   112  	_ struct{},
   113  ) (
   114  	derived.MeterParamOverrides,
   115  	error,
   116  ) {
   117  	var overrides derived.MeterParamOverrides
   118  	var err error
   119  	computer.txnState.RunWithAllLimitsDisabled(func() {
   120  		overrides, err = computer.getMeterParamOverrides()
   121  	})
   122  
   123  	if err != nil {
   124  		return overrides, fmt.Errorf(
   125  			"error getting environment meter parameter overrides: %w",
   126  			err)
   127  	}
   128  
   129  	return overrides, nil
   130  }
   131  
   132  func (computer MeterParamOverridesComputer) getMeterParamOverrides() (
   133  	derived.MeterParamOverrides,
   134  	error,
   135  ) {
   136  	// Check that the service account exists because all the settings are
   137  	// stored in it
   138  	serviceAddress := computer.ctx.Chain.ServiceAddress()
   139  	service := common.Address(serviceAddress)
   140  
   141  	env := environment.NewScriptEnv(
   142  		context.Background(),
   143  		computer.ctx.TracerSpan,
   144  		computer.ctx.EnvironmentParams,
   145  		computer.txnState)
   146  
   147  	overrides := derived.MeterParamOverrides{}
   148  
   149  	// set the property if no error, but if the error is a fatal error then
   150  	// return it
   151  	setIfOk := func(prop string, err error, setter func()) (fatal error) {
   152  		err, fatal = errors.SplitErrorTypes(err)
   153  		if fatal != nil {
   154  			// this is a fatal error. return it
   155  			computer.ctx.Logger.
   156  				Error().
   157  				Err(fatal).
   158  				Msgf("error getting %s", prop)
   159  			return fatal
   160  		}
   161  		if err != nil {
   162  			// this is a general error.
   163  			// could be that no setting was present in the state,
   164  			// or that the setting was not parseable,
   165  			// or some other deterministic thing.
   166  			computer.ctx.Logger.
   167  				Debug().
   168  				Err(err).
   169  				Msgf("could not set %s. Using defaults", prop)
   170  			return nil
   171  		}
   172  		// everything is ok. do the setting
   173  		setter()
   174  		return nil
   175  	}
   176  
   177  	computationWeights, err := GetExecutionEffortWeights(env, service)
   178  	err = setIfOk(
   179  		"execution effort weights",
   180  		err,
   181  		func() { overrides.ComputationWeights = computationWeights })
   182  	if err != nil {
   183  		return overrides, err
   184  	}
   185  
   186  	memoryWeights, err := GetExecutionMemoryWeights(env, service)
   187  	err = setIfOk(
   188  		"execution memory weights",
   189  		err,
   190  		func() { overrides.MemoryWeights = memoryWeights })
   191  	if err != nil {
   192  		return overrides, err
   193  	}
   194  
   195  	memoryLimit, err := GetExecutionMemoryLimit(env, service)
   196  	err = setIfOk(
   197  		"execution memory limit",
   198  		err,
   199  		func() { overrides.MemoryLimit = &memoryLimit })
   200  	if err != nil {
   201  		return overrides, err
   202  	}
   203  
   204  	return overrides, nil
   205  }
   206  
   207  func getExecutionWeights[KindType common.ComputationKind | common.MemoryKind](
   208  	env environment.Environment,
   209  	service common.Address,
   210  	path cadence.Path,
   211  	defaultWeights map[KindType]uint64,
   212  ) (
   213  	map[KindType]uint64,
   214  	error,
   215  ) {
   216  	runtime := env.BorrowCadenceRuntime()
   217  	defer env.ReturnCadenceRuntime(runtime)
   218  
   219  	value, err := runtime.ReadStored(service, path)
   220  
   221  	if err != nil {
   222  		// this might be fatal, return as is
   223  		return nil, err
   224  	}
   225  
   226  	weightsRaw, ok := cadenceValueToWeights(value)
   227  	if !ok {
   228  		// this is a non-fatal error. It is expected if the weights are not
   229  		// set up on the network yet.
   230  		return nil, errors.NewCouldNotGetExecutionParameterFromStateError(
   231  			service.Hex(),
   232  			path.String())
   233  	}
   234  
   235  	// Merge the default weights with the weights from the state.
   236  	// This allows for weights that are not set in the state, to be set by default.
   237  	// In case the network is stuck because of a transaction using an FVM feature that has 0 weight
   238  	// (or is not metered at all), the defaults can be changed and the network restarted
   239  	// instead of trying to change the weights with a transaction.
   240  	weights := make(map[KindType]uint64, len(defaultWeights))
   241  	for k, v := range defaultWeights {
   242  		weights[k] = v
   243  	}
   244  	for k, v := range weightsRaw {
   245  		weights[KindType(k)] = v
   246  	}
   247  
   248  	return weights, nil
   249  }
   250  
   251  // cadenceValueToWeights converts a cadence value to a map of weights used for
   252  // metering
   253  func cadenceValueToWeights(value cadence.Value) (map[uint]uint64, bool) {
   254  	dict, ok := value.(cadence.Dictionary)
   255  	if !ok {
   256  		return nil, false
   257  	}
   258  
   259  	result := make(map[uint]uint64, len(dict.Pairs))
   260  	for _, p := range dict.Pairs {
   261  		key, ok := p.Key.(cadence.UInt64)
   262  		if !ok {
   263  			return nil, false
   264  		}
   265  
   266  		value, ok := p.Value.(cadence.UInt64)
   267  		if !ok {
   268  			return nil, false
   269  		}
   270  
   271  		result[uint(key)] = uint64(value)
   272  	}
   273  
   274  	return result, true
   275  }
   276  
   277  // GetExecutionEffortWeights reads stored execution effort weights from the service account
   278  func GetExecutionEffortWeights(
   279  	env environment.Environment,
   280  	service common.Address,
   281  ) (
   282  	computationWeights meter.ExecutionEffortWeights,
   283  	err error,
   284  ) {
   285  	return getExecutionWeights(
   286  		env,
   287  		service,
   288  		blueprints.TransactionFeesExecutionEffortWeightsPath,
   289  		meter.DefaultComputationWeights)
   290  }
   291  
   292  // GetExecutionMemoryWeights reads stored execution memory weights from the service account
   293  func GetExecutionMemoryWeights(
   294  	env environment.Environment,
   295  	service common.Address,
   296  ) (
   297  	memoryWeights meter.ExecutionMemoryWeights,
   298  	err error,
   299  ) {
   300  	return getExecutionWeights(
   301  		env,
   302  		service,
   303  		blueprints.TransactionFeesExecutionMemoryWeightsPath,
   304  		meter.DefaultMemoryWeights)
   305  }
   306  
   307  // GetExecutionMemoryLimit reads the stored execution memory limit from the service account
   308  func GetExecutionMemoryLimit(
   309  	env environment.Environment,
   310  	service common.Address,
   311  ) (
   312  	memoryLimit uint64,
   313  	err error,
   314  ) {
   315  	runtime := env.BorrowCadenceRuntime()
   316  	defer env.ReturnCadenceRuntime(runtime)
   317  
   318  	value, err := runtime.ReadStored(
   319  		service,
   320  		blueprints.TransactionFeesExecutionMemoryLimitPath)
   321  	if err != nil {
   322  		// this might be fatal, return as is
   323  		return 0, err
   324  	}
   325  
   326  	memoryLimitRaw, ok := value.(cadence.UInt64)
   327  	if value == nil || !ok {
   328  		// this is a non-fatal error. It is expected if the weights are not set up on the network yet.
   329  		return 0, errors.NewCouldNotGetExecutionParameterFromStateError(
   330  			service.Hex(),
   331  			blueprints.TransactionFeesExecutionMemoryLimitPath.String())
   332  	}
   333  
   334  	return uint64(memoryLimitRaw), nil
   335  }