github.com/koko1123/flow-go-1@v0.29.6/storage/badger/procedure/cluster.go (about)

     1  package procedure
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/dgraph-io/badger/v3"
     7  
     8  	"github.com/koko1123/flow-go-1/model/cluster"
     9  	"github.com/koko1123/flow-go-1/model/flow"
    10  	"github.com/koko1123/flow-go-1/storage/badger/operation"
    11  )
    12  
    13  // This file implements storage functions for blocks in cluster consensus.
    14  
    15  // InsertClusterBlock inserts a cluster consensus block, updating all
    16  // associated indexes.
    17  func InsertClusterBlock(block *cluster.Block) func(*badger.Txn) error {
    18  	return func(tx *badger.Txn) error {
    19  
    20  		// check payload integrity
    21  		if block.Header.PayloadHash != block.Payload.Hash() {
    22  			return fmt.Errorf("computed payload hash does not match header")
    23  		}
    24  
    25  		// store the block header
    26  		blockID := block.ID()
    27  		err := operation.InsertHeader(blockID, block.Header)(tx)
    28  		if err != nil {
    29  			return fmt.Errorf("could not insert header: %w", err)
    30  		}
    31  
    32  		// insert the block payload
    33  		err = InsertClusterPayload(blockID, block.Payload)(tx)
    34  		if err != nil {
    35  			return fmt.Errorf("could not insert payload: %w", err)
    36  		}
    37  
    38  		// index the child block for recovery
    39  		err = IndexNewBlock(blockID, block.Header.ParentID)(tx)
    40  		if err != nil {
    41  			return fmt.Errorf("could not index new block: %w", err)
    42  		}
    43  		return nil
    44  	}
    45  }
    46  
    47  // RetrieveClusterBlock retrieves a cluster consensus block by block ID.
    48  func RetrieveClusterBlock(blockID flow.Identifier, block *cluster.Block) func(*badger.Txn) error {
    49  	return func(tx *badger.Txn) error {
    50  
    51  		// retrieve the block header
    52  		var header flow.Header
    53  		err := operation.RetrieveHeader(blockID, &header)(tx)
    54  		if err != nil {
    55  			return fmt.Errorf("could not retrieve header: %w", err)
    56  		}
    57  
    58  		// retrieve payload
    59  		var payload cluster.Payload
    60  		err = RetrieveClusterPayload(blockID, &payload)(tx)
    61  		if err != nil {
    62  			return fmt.Errorf("could not retrieve payload: %w", err)
    63  		}
    64  
    65  		// overwrite block
    66  		*block = cluster.Block{
    67  			Header:  &header,
    68  			Payload: &payload,
    69  		}
    70  
    71  		return nil
    72  	}
    73  }
    74  
    75  // RetrieveLatestFinalizedClusterHeader retrieves the latest finalized for the
    76  // given cluster chain ID.
    77  func RetrieveLatestFinalizedClusterHeader(chainID flow.ChainID, final *flow.Header) func(tx *badger.Txn) error {
    78  	return func(tx *badger.Txn) error {
    79  		var boundary uint64
    80  		err := operation.RetrieveClusterFinalizedHeight(chainID, &boundary)(tx)
    81  		if err != nil {
    82  			return fmt.Errorf("could not retrieve boundary: %w", err)
    83  		}
    84  
    85  		var finalID flow.Identifier
    86  		err = operation.LookupClusterBlockHeight(chainID, boundary, &finalID)(tx)
    87  		if err != nil {
    88  			return fmt.Errorf("could not retrieve final ID: %w", err)
    89  		}
    90  
    91  		err = operation.RetrieveHeader(finalID, final)(tx)
    92  		if err != nil {
    93  			return fmt.Errorf("could not retrieve finalized header: %w", err)
    94  		}
    95  
    96  		return nil
    97  	}
    98  }
    99  
   100  // FinalizeClusterBlock finalizes a block in cluster consensus.
   101  func FinalizeClusterBlock(blockID flow.Identifier) func(*badger.Txn) error {
   102  	return func(tx *badger.Txn) error {
   103  
   104  		// retrieve the header to check the parent
   105  		var header flow.Header
   106  		err := operation.RetrieveHeader(blockID, &header)(tx)
   107  		if err != nil {
   108  			return fmt.Errorf("could not retrieve header: %w", err)
   109  		}
   110  
   111  		// get the chain ID, which determines which cluster state to query
   112  		chainID := header.ChainID
   113  
   114  		// retrieve the current finalized state boundary
   115  		var boundary uint64
   116  		err = operation.RetrieveClusterFinalizedHeight(chainID, &boundary)(tx)
   117  		if err != nil {
   118  			return fmt.Errorf("could not retrieve boundary: %w", err)
   119  		}
   120  
   121  		// retrieve the ID of the boundary head
   122  		var headID flow.Identifier
   123  		err = operation.LookupClusterBlockHeight(chainID, boundary, &headID)(tx)
   124  		if err != nil {
   125  			return fmt.Errorf("could not retrieve head: %w", err)
   126  		}
   127  
   128  		// check that the head ID is the parent of the block we finalize
   129  		if header.ParentID != headID {
   130  			return fmt.Errorf("can't finalize non-child of chain head")
   131  		}
   132  
   133  		// insert block view -> ID mapping
   134  		err = operation.IndexClusterBlockHeight(chainID, header.Height, header.ID())(tx)
   135  		if err != nil {
   136  			return fmt.Errorf("could not insert view->ID mapping: %w", err)
   137  		}
   138  
   139  		// update the finalized boundary
   140  		err = operation.UpdateClusterFinalizedHeight(chainID, header.Height)(tx)
   141  		if err != nil {
   142  			return fmt.Errorf("could not update finalized boundary: %w", err)
   143  		}
   144  
   145  		// NOTE: we don't want to prune forks that have become invalid here, so
   146  		// that we can keep validating entities and generating slashing
   147  		// challenges for some time - the pruning should happen some place else
   148  		// after a certain delay of blocks
   149  
   150  		return nil
   151  	}
   152  }
   153  
   154  // InsertClusterPayload inserts the payload for a cluster block. It inserts
   155  // both the collection and all constituent transactions, allowing duplicates.
   156  func InsertClusterPayload(blockID flow.Identifier, payload *cluster.Payload) func(*badger.Txn) error {
   157  	return func(tx *badger.Txn) error {
   158  
   159  		// cluster payloads only contain a single collection, allow duplicates,
   160  		// because it is valid for two competing forks to have the same payload.
   161  		light := payload.Collection.Light()
   162  		err := operation.SkipDuplicates(operation.InsertCollection(&light))(tx)
   163  		if err != nil {
   164  			return fmt.Errorf("could not insert payload collection: %w", err)
   165  		}
   166  
   167  		// insert constituent transactions
   168  		for _, colTx := range payload.Collection.Transactions {
   169  			err = operation.SkipDuplicates(operation.InsertTransaction(colTx.ID(), colTx))(tx)
   170  			if err != nil {
   171  				return fmt.Errorf("could not insert payload transaction: %w", err)
   172  			}
   173  		}
   174  
   175  		// index the transaction IDs within the collection
   176  		txIDs := payload.Collection.Light().Transactions
   177  		err = operation.SkipDuplicates(operation.IndexCollectionPayload(blockID, txIDs))(tx)
   178  		if err != nil {
   179  			return fmt.Errorf("could not index collection: %w", err)
   180  		}
   181  
   182  		// insert the reference block ID
   183  		err = operation.IndexReferenceBlockByClusterBlock(blockID, payload.ReferenceBlockID)(tx)
   184  		if err != nil {
   185  			return fmt.Errorf("could not insert reference block ID: %w", err)
   186  		}
   187  
   188  		return nil
   189  	}
   190  }
   191  
   192  // RetrieveClusterPayload retrieves a cluster consensus block payload by block ID.
   193  func RetrieveClusterPayload(blockID flow.Identifier, payload *cluster.Payload) func(*badger.Txn) error {
   194  	return func(tx *badger.Txn) error {
   195  
   196  		// lookup the reference block ID
   197  		var refID flow.Identifier
   198  		err := operation.LookupReferenceBlockByClusterBlock(blockID, &refID)(tx)
   199  		if err != nil {
   200  			return fmt.Errorf("could not retrieve reference block ID: %w", err)
   201  		}
   202  
   203  		// lookup collection transaction IDs
   204  		var txIDs []flow.Identifier
   205  		err = operation.LookupCollectionPayload(blockID, &txIDs)(tx)
   206  		if err != nil {
   207  			return fmt.Errorf("could not look up collection payload: %w", err)
   208  		}
   209  
   210  		colTransactions := make([]*flow.TransactionBody, 0, len(txIDs))
   211  		// retrieve individual transactions
   212  		for _, txID := range txIDs {
   213  			var nextTx flow.TransactionBody
   214  			err = operation.RetrieveTransaction(txID, &nextTx)(tx)
   215  			if err != nil {
   216  				return fmt.Errorf("could not retrieve transaction: %w", err)
   217  			}
   218  			colTransactions = append(colTransactions, &nextTx)
   219  		}
   220  
   221  		*payload = cluster.PayloadFromTransactions(refID, colTransactions...)
   222  
   223  		return nil
   224  	}
   225  }