github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/environment/blocks.go (about) 1 package environment 2 3 import ( 4 "fmt" 5 6 "github.com/onflow/cadence/runtime/stdlib" 7 8 "github.com/onflow/cadence/runtime" 9 10 "github.com/onflow/flow-go/fvm/errors" 11 "github.com/onflow/flow-go/model/flow" 12 "github.com/onflow/flow-go/storage" 13 ) 14 15 type Blocks interface { 16 // ByHeight returns the block at the given height in the chain ending in `header` (or finalized 17 // if `header` is nil). This enables querying un-finalized blocks by height with respect to the 18 // chain defined by the block we are executing. 19 ByHeightFrom(height uint64, header *flow.Header) (*flow.Header, error) 20 } 21 22 // BlocksFinder finds blocks and return block headers 23 type BlocksFinder struct { 24 storage storage.Headers 25 } 26 27 // NewBlockFinder constructs a new block finder 28 func NewBlockFinder(storage storage.Headers) Blocks { 29 return &BlocksFinder{storage: storage} 30 } 31 32 // ByHeightFrom returns the block header by height. 33 func (finder *BlocksFinder) ByHeightFrom(height uint64, header *flow.Header) (*flow.Header, error) { 34 if header == nil { 35 byHeight, err := finder.storage.ByHeight(height) 36 if err != nil { 37 return nil, err 38 } 39 return byHeight, nil 40 } 41 42 if header.Height == height { 43 return header, nil 44 } 45 46 if height > header.Height { 47 // TODO figure out min height and enforce it to be bigger than min height 48 minHeight := 0 49 msg := fmt.Sprintf("requested height (%d) is not in the range(%d, %d)", height, minHeight, header.Height) 50 err := errors.NewValueErrorf(fmt.Sprint(height), msg) 51 return nil, fmt.Errorf("cannot retrieve block parent: %w", err) 52 } 53 54 id := header.ParentID 55 56 // travel chain back 57 for { 58 // recent block should be in cache so this is supposed to be fast 59 parent, err := finder.storage.ByBlockID(id) 60 if err != nil { 61 failure := errors.NewBlockFinderFailure(err) 62 return nil, fmt.Errorf("cannot retrieve block parent: %w", failure) 63 } 64 if parent.Height == height { 65 return parent, nil 66 } 67 68 _, err = finder.storage.ByHeight(parent.Height) 69 // if height isn't finalized, move to parent 70 if err != nil && errors.Is(err, storage.ErrNotFound) { 71 id = parent.ParentID 72 continue 73 } 74 // any other error bubbles up 75 if err != nil { 76 failure := errors.NewBlockFinderFailure(err) 77 return nil, fmt.Errorf("cannot retrieve block parent: %w", failure) 78 } 79 //if parent is finalized block, we can just use finalized chain 80 // to get desired height 81 return finder.storage.ByHeight(height) 82 } 83 } 84 85 // NoopBlockFinder implements the Blocks interface. It is used in the 86 // bootstrapping process. 87 type NoopBlockFinder struct{} 88 89 func (NoopBlockFinder) ByHeightFrom(_ uint64, _ *flow.Header) (*flow.Header, error) { 90 return nil, nil 91 } 92 93 func runtimeBlockFromHeader(header *flow.Header) runtime.Block { 94 return runtime.Block{ 95 Height: header.Height, 96 View: header.View, 97 Hash: stdlib.BlockHash(header.ID()), 98 Timestamp: header.Timestamp.UnixNano(), 99 } 100 }