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