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 }