github.com/onflow/flow-go@v0.33.17/module/execution/scripts.go (about) 1 package execution 2 3 import ( 4 "context" 5 6 "github.com/onflow/flow-go/fvm/environment" 7 8 "github.com/rs/zerolog" 9 10 "github.com/onflow/flow-go/engine/execution/computation" 11 "github.com/onflow/flow-go/engine/execution/computation/query" 12 "github.com/onflow/flow-go/fvm" 13 "github.com/onflow/flow-go/fvm/storage/derived" 14 "github.com/onflow/flow-go/fvm/storage/snapshot" 15 "github.com/onflow/flow-go/model/flow" 16 "github.com/onflow/flow-go/module" 17 "github.com/onflow/flow-go/storage" 18 ) 19 20 // RegisterAtHeight returns register value for provided register ID at the block height. 21 // Even if the register wasn't indexed at the provided height, returns the highest height the register was indexed at. 22 // If the register with the ID was not indexed at all return nil value and no error. 23 // Expected errors: 24 // - storage.ErrHeightNotIndexed if the given height was not indexed yet or lower than the first indexed height. 25 type RegisterAtHeight func(ID flow.RegisterID, height uint64) (flow.RegisterValue, error) 26 27 type ScriptExecutor interface { 28 // ExecuteAtBlockHeight executes provided script against the block height. 29 // A result value is returned encoded as byte array. An error will be returned if script 30 // doesn't successfully execute. 31 // Expected errors: 32 // - storage.ErrNotFound if block or register value at height was not found. 33 // - storage.ErrHeightNotIndexed if the data for the block height is not available 34 ExecuteAtBlockHeight( 35 ctx context.Context, 36 script []byte, 37 arguments [][]byte, 38 height uint64, 39 ) ([]byte, error) 40 41 // GetAccountAtBlockHeight returns a Flow account by the provided address and block height. 42 // Expected errors: 43 // - storage.ErrHeightNotIndexed if the data for the block height is not available 44 GetAccountAtBlockHeight(ctx context.Context, address flow.Address, height uint64) (*flow.Account, error) 45 } 46 47 var _ ScriptExecutor = (*Scripts)(nil) 48 49 type Scripts struct { 50 executor *query.QueryExecutor 51 headers storage.Headers 52 registerAtHeight RegisterAtHeight 53 } 54 55 func NewScripts( 56 log zerolog.Logger, 57 metrics module.ExecutionMetrics, 58 chainID flow.ChainID, 59 entropy query.EntropyProviderPerBlock, 60 header storage.Headers, 61 registerAtHeight RegisterAtHeight, 62 queryConf query.QueryConfig, 63 ) (*Scripts, error) { 64 vm := fvm.NewVirtualMachine() 65 66 options := computation.DefaultFVMOptions(chainID, false, false) 67 blocks := environment.NewBlockFinder(header) 68 options = append(options, fvm.WithBlocks(blocks)) // add blocks for getBlocks calls in scripts 69 vmCtx := fvm.NewContext(options...) 70 71 derivedChainData, err := derived.NewDerivedChainData(derived.DefaultDerivedDataCacheSize) 72 if err != nil { 73 return nil, err 74 } 75 76 queryExecutor := query.NewQueryExecutor( 77 queryConf, 78 log, 79 metrics, 80 vm, 81 vmCtx, 82 derivedChainData, 83 entropy, 84 ) 85 86 return &Scripts{ 87 executor: queryExecutor, 88 headers: header, 89 registerAtHeight: registerAtHeight, 90 }, nil 91 } 92 93 // ExecuteAtBlockHeight executes provided script against the block height. 94 // A result value is returned encoded as byte array. An error will be returned if script 95 // doesn't successfully execute. 96 // Expected errors: 97 // - Script execution related errors 98 // - storage.ErrHeightNotIndexed if the data for the block height is not available 99 func (s *Scripts) ExecuteAtBlockHeight( 100 ctx context.Context, 101 script []byte, 102 arguments [][]byte, 103 height uint64, 104 ) ([]byte, error) { 105 106 snap, header, err := s.snapshotWithBlock(height) 107 if err != nil { 108 return nil, err 109 } 110 111 return s.executor.ExecuteScript(ctx, script, arguments, header, snap) 112 } 113 114 // GetAccountAtBlockHeight returns a Flow account by the provided address and block height. 115 // Expected errors: 116 // - Script execution related errors 117 // - storage.ErrHeightNotIndexed if the data for the block height is not available 118 func (s *Scripts) GetAccountAtBlockHeight(ctx context.Context, address flow.Address, height uint64) (*flow.Account, error) { 119 snap, header, err := s.snapshotWithBlock(height) 120 if err != nil { 121 return nil, err 122 } 123 124 return s.executor.GetAccount(ctx, address, header, snap) 125 } 126 127 // snapshotWithBlock is a common function for executing scripts and get account functionality. 128 // It creates a storage snapshot that is needed by the FVM to execute scripts. 129 func (s *Scripts) snapshotWithBlock(height uint64) (snapshot.StorageSnapshot, *flow.Header, error) { 130 header, err := s.headers.ByHeight(height) 131 if err != nil { 132 return nil, nil, err 133 } 134 135 storageSnapshot := snapshot.NewReadFuncStorageSnapshot(func(ID flow.RegisterID) (flow.RegisterValue, error) { 136 return s.registerAtHeight(ID, height) 137 }) 138 139 return storageSnapshot, header, nil 140 }