github.com/onflow/flow-go@v0.33.17/fvm/script.go (about) 1 package fvm 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/hashicorp/go-multierror" 8 "github.com/onflow/cadence/runtime" 9 "github.com/onflow/cadence/runtime/common" 10 11 "github.com/onflow/flow-go/fvm/environment" 12 "github.com/onflow/flow-go/fvm/errors" 13 "github.com/onflow/flow-go/fvm/evm" 14 "github.com/onflow/flow-go/fvm/storage" 15 "github.com/onflow/flow-go/fvm/storage/logical" 16 "github.com/onflow/flow-go/fvm/systemcontracts" 17 "github.com/onflow/flow-go/model/flow" 18 "github.com/onflow/flow-go/model/hash" 19 ) 20 21 type ScriptProcedure struct { 22 ID flow.Identifier 23 Script []byte 24 Arguments [][]byte 25 RequestContext context.Context 26 } 27 28 func Script(code []byte) *ScriptProcedure { 29 scriptHash := hash.DefaultComputeHash(code) 30 31 return &ScriptProcedure{ 32 Script: code, 33 ID: flow.HashToID(scriptHash), 34 RequestContext: context.Background(), 35 } 36 } 37 38 func (proc *ScriptProcedure) WithArguments(args ...[]byte) *ScriptProcedure { 39 return &ScriptProcedure{ 40 ID: proc.ID, 41 Script: proc.Script, 42 RequestContext: proc.RequestContext, 43 Arguments: args, 44 } 45 } 46 47 func (proc *ScriptProcedure) WithRequestContext( 48 reqContext context.Context, 49 ) *ScriptProcedure { 50 return &ScriptProcedure{ 51 ID: proc.ID, 52 Script: proc.Script, 53 RequestContext: reqContext, 54 Arguments: proc.Arguments, 55 } 56 } 57 58 func NewScriptWithContextAndArgs( 59 code []byte, 60 reqContext context.Context, 61 args ...[]byte, 62 ) *ScriptProcedure { 63 scriptHash := hash.DefaultComputeHash(code) 64 return &ScriptProcedure{ 65 ID: flow.HashToID(scriptHash), 66 Script: code, 67 RequestContext: reqContext, 68 Arguments: args, 69 } 70 } 71 72 func (proc *ScriptProcedure) NewExecutor( 73 ctx Context, 74 txnState storage.TransactionPreparer, 75 ) ProcedureExecutor { 76 return newScriptExecutor(ctx, proc, txnState) 77 } 78 79 func (proc *ScriptProcedure) ComputationLimit(ctx Context) uint64 { 80 computationLimit := ctx.ComputationLimit 81 // if ctx.ComputationLimit is also zero, fallback to the default computation limit 82 if computationLimit == 0 { 83 computationLimit = DefaultComputationLimit 84 } 85 return computationLimit 86 } 87 88 func (proc *ScriptProcedure) MemoryLimit(ctx Context) uint64 { 89 memoryLimit := ctx.MemoryLimit 90 // if ctx.MemoryLimit is also zero, fallback to the default memory limit 91 if memoryLimit == 0 { 92 memoryLimit = DefaultMemoryLimit 93 } 94 return memoryLimit 95 } 96 97 func (proc *ScriptProcedure) ShouldDisableMemoryAndInteractionLimits( 98 ctx Context, 99 ) bool { 100 return ctx.DisableMemoryAndInteractionLimits 101 } 102 103 func (ScriptProcedure) Type() ProcedureType { 104 return ScriptProcedureType 105 } 106 107 func (proc *ScriptProcedure) ExecutionTime() logical.Time { 108 return logical.EndOfBlockExecutionTime 109 } 110 111 type scriptExecutor struct { 112 ctx Context 113 proc *ScriptProcedure 114 txnState storage.TransactionPreparer 115 116 env environment.Environment 117 118 output ProcedureOutput 119 } 120 121 func newScriptExecutor( 122 ctx Context, 123 proc *ScriptProcedure, 124 txnState storage.TransactionPreparer, 125 ) *scriptExecutor { 126 // update `ctx.EnvironmentParams` with the script info before 127 // creating the executor 128 scriptInfo := environment.NewScriptInfoParams(proc.Script, proc.Arguments) 129 ctx.EnvironmentParams.SetScriptInfoParams(scriptInfo) 130 return &scriptExecutor{ 131 ctx: ctx, 132 proc: proc, 133 txnState: txnState, 134 env: environment.NewScriptEnv( 135 proc.RequestContext, 136 ctx.TracerSpan, 137 ctx.EnvironmentParams, 138 txnState), 139 } 140 } 141 142 func (executor *scriptExecutor) Cleanup() { 143 // Do nothing. 144 } 145 146 func (executor *scriptExecutor) Output() ProcedureOutput { 147 return executor.output 148 } 149 150 func (executor *scriptExecutor) Preprocess() error { 151 // Do nothing. 152 return nil 153 } 154 155 func (executor *scriptExecutor) Execute() error { 156 err := executor.execute() 157 txError, failure := errors.SplitErrorTypes(err) 158 if failure != nil { 159 if errors.IsLedgerFailure(failure) { 160 return fmt.Errorf( 161 "cannot execute the script, this error usually happens if "+ 162 "the reference block for this script is not set to a "+ 163 "recent block: %w", 164 failure) 165 } 166 return failure 167 } 168 if txError != nil { 169 executor.output.Err = txError 170 } 171 172 return nil 173 } 174 175 func (executor *scriptExecutor) execute() error { 176 meterParams, err := getBodyMeterParameters( 177 executor.ctx, 178 executor.proc, 179 executor.txnState) 180 if err != nil { 181 return fmt.Errorf("error getting meter parameters: %w", err) 182 } 183 184 txnId, err := executor.txnState.BeginNestedTransactionWithMeterParams( 185 meterParams) 186 if err != nil { 187 return err 188 } 189 190 errs := errors.NewErrorsCollector() 191 errs.Collect(executor.executeScript()) 192 193 _, err = executor.txnState.CommitNestedTransaction(txnId) 194 errs.Collect(err) 195 196 return errs.ErrorOrNil() 197 } 198 199 func (executor *scriptExecutor) executeScript() error { 200 rt := executor.env.BorrowCadenceRuntime() 201 defer executor.env.ReturnCadenceRuntime(rt) 202 203 if executor.ctx.EVMEnabled { 204 chain := executor.ctx.Chain 205 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 206 err := evm.SetupEnvironment( 207 chain.ChainID(), 208 executor.env, 209 rt.ScriptRuntimeEnv, 210 chain.ServiceAddress(), 211 sc.FlowToken.Address, 212 ) 213 if err != nil { 214 return err 215 } 216 } 217 218 value, err := rt.ExecuteScript( 219 runtime.Script{ 220 Source: executor.proc.Script, 221 Arguments: executor.proc.Arguments, 222 }, 223 common.ScriptLocation(executor.proc.ID), 224 ) 225 populateErr := executor.output.PopulateEnvironmentValues(executor.env) 226 if err != nil { 227 return multierror.Append(err, populateErr) 228 } 229 230 executor.output.Value = value 231 return populateErr 232 }