github.com/koko1123/flow-go-1@v0.29.6/fvm/fvm.go (about)

     1  package fvm
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/koko1123/flow-go-1/fvm/derived"
     8  	"github.com/koko1123/flow-go-1/fvm/environment"
     9  	errors "github.com/koko1123/flow-go-1/fvm/errors"
    10  	"github.com/koko1123/flow-go-1/fvm/meter"
    11  	"github.com/koko1123/flow-go-1/fvm/state"
    12  	"github.com/koko1123/flow-go-1/model/flow"
    13  )
    14  
    15  type ProcedureType string
    16  
    17  const (
    18  	BootstrapProcedureType   = ProcedureType("bootstrap")
    19  	TransactionProcedureType = ProcedureType("transaction")
    20  	ScriptProcedureType      = ProcedureType("script")
    21  )
    22  
    23  type ProcedureExecutor interface {
    24  	Preprocess() error
    25  	Execute() error
    26  	Cleanup()
    27  }
    28  
    29  func Run(executor ProcedureExecutor) error {
    30  	defer executor.Cleanup()
    31  
    32  	err := executor.Preprocess()
    33  	if err != nil {
    34  		return err
    35  	}
    36  
    37  	return executor.Execute()
    38  }
    39  
    40  // An Procedure is an operation (or set of operations) that reads or writes ledger state.
    41  type Procedure interface {
    42  	NewExecutor(
    43  		ctx Context,
    44  		txnState *state.TransactionState,
    45  		derivedTxnData *derived.DerivedTransactionData,
    46  	) ProcedureExecutor
    47  
    48  	ComputationLimit(ctx Context) uint64
    49  	MemoryLimit(ctx Context) uint64
    50  	ShouldDisableMemoryAndInteractionLimits(ctx Context) bool
    51  
    52  	Type() ProcedureType
    53  
    54  	// The initial snapshot time is used as part of OCC validation to ensure
    55  	// there are no read-write conflict amongst transactions.  Note that once
    56  	// we start supporting parallel preprocessing/execution, a transaction may
    57  	// operation on mutliple snapshots.
    58  	//
    59  	// For scripts, since they can only be executed after the block has been
    60  	// executed, the initial snapshot time is EndOfBlockExecutionTime.
    61  	InitialSnapshotTime() derived.LogicalTime
    62  
    63  	// For transactions, the execution time is TxIndex.  For scripts, the
    64  	// execution time is EndOfBlockExecutionTime.
    65  	ExecutionTime() derived.LogicalTime
    66  }
    67  
    68  // VM runs procedures
    69  type VM interface {
    70  	Run(Context, Procedure, state.View) error
    71  	GetAccount(Context, flow.Address, state.View) (*flow.Account, error)
    72  }
    73  
    74  var _ VM = (*VirtualMachine)(nil)
    75  
    76  // A VirtualMachine augments the Cadence runtime with Flow host functionality.
    77  type VirtualMachine struct {
    78  }
    79  
    80  func NewVirtualMachine() *VirtualMachine {
    81  	return &VirtualMachine{}
    82  }
    83  
    84  // Run runs a procedure against a ledger in the given context.
    85  func (vm *VirtualMachine) Run(
    86  	ctx Context,
    87  	proc Procedure,
    88  	v state.View,
    89  ) error {
    90  	derivedBlockData := ctx.DerivedBlockData
    91  	if derivedBlockData == nil {
    92  		derivedBlockData = derived.NewEmptyDerivedBlockDataWithTransactionOffset(
    93  			uint32(proc.ExecutionTime()))
    94  	}
    95  
    96  	var derivedTxnData *derived.DerivedTransactionData
    97  	var err error
    98  	switch proc.Type() {
    99  	case ScriptProcedureType:
   100  		derivedTxnData, err = derivedBlockData.NewSnapshotReadDerivedTransactionData(
   101  			proc.InitialSnapshotTime(),
   102  			proc.ExecutionTime())
   103  	case TransactionProcedureType, BootstrapProcedureType:
   104  		derivedTxnData, err = derivedBlockData.NewDerivedTransactionData(
   105  			proc.InitialSnapshotTime(),
   106  			proc.ExecutionTime())
   107  	default:
   108  		return fmt.Errorf("invalid proc type: %v", proc.Type())
   109  	}
   110  
   111  	if err != nil {
   112  		return fmt.Errorf("error creating derived transaction data: %w", err)
   113  	}
   114  
   115  	txnState := state.NewTransactionState(
   116  		v,
   117  		state.DefaultParameters().
   118  			WithMeterParameters(getBasicMeterParameters(ctx, proc)).
   119  			WithMaxKeySizeAllowed(ctx.MaxStateKeySize).
   120  			WithMaxValueSizeAllowed(ctx.MaxStateValueSize))
   121  
   122  	err = Run(proc.NewExecutor(ctx, txnState, derivedTxnData))
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	// Note: it is safe to skip committing derived data for non-normal
   128  	// transactions (i.e., bootstrap and script) since these do not invalidate
   129  	// derived data entries.
   130  	if proc.Type() == TransactionProcedureType {
   131  		// NOTE: It is not safe to ignore derivedTxnData' commit error for
   132  		// transactions that trigger derived data invalidation.
   133  		return derivedTxnData.Commit()
   134  	}
   135  
   136  	return nil
   137  }
   138  
   139  // GetAccount returns an account by address or an error if none exists.
   140  func (vm *VirtualMachine) GetAccount(
   141  	ctx Context,
   142  	address flow.Address,
   143  	v state.View,
   144  ) (
   145  	*flow.Account,
   146  	error,
   147  ) {
   148  	txnState := state.NewTransactionState(
   149  		v,
   150  		state.DefaultParameters().
   151  			WithMaxKeySizeAllowed(ctx.MaxStateKeySize).
   152  			WithMaxValueSizeAllowed(ctx.MaxStateValueSize).
   153  			WithMeterParameters(
   154  				meter.DefaultParameters().
   155  					WithStorageInteractionLimit(ctx.MaxStateInteractionSize)))
   156  
   157  	derivedBlockData := ctx.DerivedBlockData
   158  	if derivedBlockData == nil {
   159  		derivedBlockData = derived.NewEmptyDerivedBlockData()
   160  	}
   161  
   162  	derviedTxnData, err := derivedBlockData.NewSnapshotReadDerivedTransactionData(
   163  		derived.EndOfBlockExecutionTime,
   164  		derived.EndOfBlockExecutionTime)
   165  	if err != nil {
   166  		return nil, fmt.Errorf(
   167  			"error creating derived transaction data for GetAccount: %w",
   168  			err)
   169  	}
   170  
   171  	env := environment.NewScriptEnvironment(
   172  		context.Background(),
   173  		ctx.TracerSpan,
   174  		ctx.EnvironmentParams,
   175  		txnState,
   176  		derviedTxnData)
   177  	account, err := env.GetAccount(address)
   178  	if err != nil {
   179  		if errors.IsLedgerFailure(err) {
   180  			return nil, fmt.Errorf(
   181  				"cannot get account, this error usually happens if the "+
   182  					"reference block for this query is not set to a recent "+
   183  					"block: %w",
   184  				err)
   185  		}
   186  		return nil, fmt.Errorf("cannot get account: %w", err)
   187  	}
   188  	return account, nil
   189  }