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  }