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