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 }