github.com/onflow/flow-go@v0.33.17/storage/badger/headers.go (about)

     1  // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED
     2  
     3  package badger
     4  
     5  import (
     6  	"fmt"
     7  
     8  	"github.com/dgraph-io/badger/v2"
     9  
    10  	"github.com/onflow/flow-go/model/flow"
    11  	"github.com/onflow/flow-go/module"
    12  	"github.com/onflow/flow-go/module/metrics"
    13  	"github.com/onflow/flow-go/storage/badger/operation"
    14  	"github.com/onflow/flow-go/storage/badger/procedure"
    15  	"github.com/onflow/flow-go/storage/badger/transaction"
    16  )
    17  
    18  // Headers implements a simple read-only header storage around a badger DB.
    19  type Headers struct {
    20  	db          *badger.DB
    21  	cache       *Cache[flow.Identifier, *flow.Header]
    22  	heightCache *Cache[uint64, flow.Identifier]
    23  }
    24  
    25  func NewHeaders(collector module.CacheMetrics, db *badger.DB) *Headers {
    26  
    27  	store := func(blockID flow.Identifier, header *flow.Header) func(*transaction.Tx) error {
    28  		return transaction.WithTx(operation.InsertHeader(blockID, header))
    29  	}
    30  
    31  	// CAUTION: should only be used to index FINALIZED blocks by their
    32  	// respective height
    33  	storeHeight := func(height uint64, id flow.Identifier) func(*transaction.Tx) error {
    34  		return transaction.WithTx(operation.IndexBlockHeight(height, id))
    35  	}
    36  
    37  	retrieve := func(blockID flow.Identifier) func(tx *badger.Txn) (*flow.Header, error) {
    38  		var header flow.Header
    39  		return func(tx *badger.Txn) (*flow.Header, error) {
    40  			err := operation.RetrieveHeader(blockID, &header)(tx)
    41  			return &header, err
    42  		}
    43  	}
    44  
    45  	retrieveHeight := func(height uint64) func(tx *badger.Txn) (flow.Identifier, error) {
    46  		return func(tx *badger.Txn) (flow.Identifier, error) {
    47  			var id flow.Identifier
    48  			err := operation.LookupBlockHeight(height, &id)(tx)
    49  			return id, err
    50  		}
    51  	}
    52  
    53  	h := &Headers{
    54  		db: db,
    55  		cache: newCache(collector, metrics.ResourceHeader,
    56  			withLimit[flow.Identifier, *flow.Header](4*flow.DefaultTransactionExpiry),
    57  			withStore(store),
    58  			withRetrieve(retrieve)),
    59  
    60  		heightCache: newCache(collector, metrics.ResourceFinalizedHeight,
    61  			withLimit[uint64, flow.Identifier](4*flow.DefaultTransactionExpiry),
    62  			withStore(storeHeight),
    63  			withRetrieve(retrieveHeight)),
    64  	}
    65  
    66  	return h
    67  }
    68  
    69  func (h *Headers) storeTx(header *flow.Header) func(*transaction.Tx) error {
    70  	return h.cache.PutTx(header.ID(), header)
    71  }
    72  
    73  func (h *Headers) retrieveTx(blockID flow.Identifier) func(*badger.Txn) (*flow.Header, error) {
    74  	return func(tx *badger.Txn) (*flow.Header, error) {
    75  		val, err := h.cache.Get(blockID)(tx)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		return val, nil
    80  	}
    81  }
    82  
    83  // results in `storage.ErrNotFound` for unknown height
    84  func (h *Headers) retrieveIdByHeightTx(height uint64) func(*badger.Txn) (flow.Identifier, error) {
    85  	return func(tx *badger.Txn) (flow.Identifier, error) {
    86  		blockID, err := h.heightCache.Get(height)(tx)
    87  		if err != nil {
    88  			return flow.ZeroID, fmt.Errorf("failed to retrieve block ID for height %d: %w", height, err)
    89  		}
    90  		return blockID, nil
    91  	}
    92  }
    93  
    94  func (h *Headers) Store(header *flow.Header) error {
    95  	return operation.RetryOnConflictTx(h.db, transaction.Update, h.storeTx(header))
    96  }
    97  
    98  func (h *Headers) ByBlockID(blockID flow.Identifier) (*flow.Header, error) {
    99  	tx := h.db.NewTransaction(false)
   100  	defer tx.Discard()
   101  	return h.retrieveTx(blockID)(tx)
   102  }
   103  
   104  func (h *Headers) ByHeight(height uint64) (*flow.Header, error) {
   105  	tx := h.db.NewTransaction(false)
   106  	defer tx.Discard()
   107  
   108  	blockID, err := h.retrieveIdByHeightTx(height)(tx)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	return h.retrieveTx(blockID)(tx)
   113  }
   114  
   115  // Exists returns true if a header with the given ID has been stored.
   116  // No errors are expected during normal operation.
   117  func (h *Headers) Exists(blockID flow.Identifier) (bool, error) {
   118  	// if the block is in the cache, return true
   119  	if ok := h.cache.IsCached(blockID); ok {
   120  		return ok, nil
   121  	}
   122  	// otherwise, check badger store
   123  	var exists bool
   124  	err := h.db.View(operation.BlockExists(blockID, &exists))
   125  	if err != nil {
   126  		return false, fmt.Errorf("could not check existence: %w", err)
   127  	}
   128  	return exists, nil
   129  }
   130  
   131  // BlockIDByHeight returns the block ID that is finalized at the given height. It is an optimized
   132  // version of `ByHeight` that skips retrieving the block. Expected errors during normal operations:
   133  //   - `storage.ErrNotFound` if no finalized block is known at given height.
   134  func (h *Headers) BlockIDByHeight(height uint64) (flow.Identifier, error) {
   135  	tx := h.db.NewTransaction(false)
   136  	defer tx.Discard()
   137  
   138  	blockID, err := h.retrieveIdByHeightTx(height)(tx)
   139  	if err != nil {
   140  		return flow.ZeroID, fmt.Errorf("could not lookup block id by height %d: %w", height, err)
   141  	}
   142  	return blockID, nil
   143  }
   144  
   145  func (h *Headers) ByParentID(parentID flow.Identifier) ([]*flow.Header, error) {
   146  	var blockIDs flow.IdentifierList
   147  	err := h.db.View(procedure.LookupBlockChildren(parentID, &blockIDs))
   148  	if err != nil {
   149  		return nil, fmt.Errorf("could not look up children: %w", err)
   150  	}
   151  	headers := make([]*flow.Header, 0, len(blockIDs))
   152  	for _, blockID := range blockIDs {
   153  		header, err := h.ByBlockID(blockID)
   154  		if err != nil {
   155  			return nil, fmt.Errorf("could not retrieve child (%x): %w", blockID, err)
   156  		}
   157  		headers = append(headers, header)
   158  	}
   159  	return headers, nil
   160  }
   161  
   162  func (h *Headers) FindHeaders(filter func(header *flow.Header) bool) ([]flow.Header, error) {
   163  	blocks := make([]flow.Header, 0, 1)
   164  	err := h.db.View(operation.FindHeaders(filter, &blocks))
   165  	return blocks, err
   166  }
   167  
   168  // RollbackExecutedBlock update the executed block header to the given header.
   169  // only useful for execution node to roll back executed block height
   170  func (h *Headers) RollbackExecutedBlock(header *flow.Header) error {
   171  	return operation.RetryOnConflict(h.db.Update, func(txn *badger.Txn) error {
   172  		var blockID flow.Identifier
   173  		err := operation.RetrieveExecutedBlock(&blockID)(txn)
   174  		if err != nil {
   175  			return fmt.Errorf("cannot lookup executed block: %w", err)
   176  		}
   177  
   178  		var highest flow.Header
   179  		err = operation.RetrieveHeader(blockID, &highest)(txn)
   180  		if err != nil {
   181  			return fmt.Errorf("cannot retrieve executed header: %w", err)
   182  		}
   183  
   184  		// only rollback if the given height is below the current executed height
   185  		if header.Height >= highest.Height {
   186  			return fmt.Errorf("cannot roolback. expect the target height %v to be lower than highest executed height %v, but actually is not",
   187  				header.Height, highest.Height,
   188  			)
   189  		}
   190  
   191  		err = operation.UpdateExecutedBlock(header.ID())(txn)
   192  		if err != nil {
   193  			return fmt.Errorf("cannot update highest executed block: %w", err)
   194  		}
   195  
   196  		return nil
   197  	})
   198  }