github.com/koko1123/flow-go-1@v0.29.6/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/koko1123/flow-go-1/fvm/errors"
    11  	"github.com/koko1123/flow-go-1/model/flow"
    12  	"github.com/koko1123/flow-go-1/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  }