github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/forks/forks.go (about)

     1  package forks
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/flow-go/consensus/hotstuff"
     7  	"github.com/onflow/flow-go/consensus/hotstuff/model"
     8  	"github.com/onflow/flow-go/model/flow"
     9  	"github.com/onflow/flow-go/module"
    10  	"github.com/onflow/flow-go/module/forest"
    11  )
    12  
    13  // Forks enforces structural validity of the consensus state and implements
    14  // finalization rules as defined in Jolteon consensus https://arxiv.org/abs/2106.10362
    15  // The same approach has later been adopted by the Diem team resulting in DiemBFT v4:
    16  // https://developers.diem.com/papers/diem-consensus-state-machine-replication-in-the-diem-blockchain/2021-08-17.pdf
    17  // Forks is NOT safe for concurrent use by multiple goroutines.
    18  type Forks struct {
    19  	finalizationCallback module.Finalizer
    20  	notifier             hotstuff.FollowerConsumer
    21  	forest               forest.LevelledForest
    22  	trustedRoot          *model.CertifiedBlock
    23  
    24  	// finalityProof holds the latest finalized block including the certified child as proof of finality.
    25  	// CAUTION: is nil, when Forks has not yet finalized any blocks beyond the finalized root block it was initialized with
    26  	finalityProof *hotstuff.FinalityProof
    27  }
    28  
    29  var _ hotstuff.Forks = (*Forks)(nil)
    30  
    31  func New(trustedRoot *model.CertifiedBlock, finalizationCallback module.Finalizer, notifier hotstuff.FollowerConsumer) (*Forks, error) {
    32  	if (trustedRoot.Block.BlockID != trustedRoot.CertifyingQC.BlockID) || (trustedRoot.Block.View != trustedRoot.CertifyingQC.View) {
    33  		return nil, model.NewConfigurationErrorf("invalid root: root QC is not pointing to root block")
    34  	}
    35  
    36  	forks := Forks{
    37  		finalizationCallback: finalizationCallback,
    38  		notifier:             notifier,
    39  		forest:               *forest.NewLevelledForest(trustedRoot.Block.View),
    40  		trustedRoot:          trustedRoot,
    41  		finalityProof:        nil,
    42  	}
    43  
    44  	// verify and add root block to levelled forest
    45  	err := forks.EnsureBlockIsValidExtension(trustedRoot.Block)
    46  	if err != nil {
    47  		return nil, fmt.Errorf("invalid root block %v: %w", trustedRoot.ID(), err)
    48  	}
    49  	forks.forest.AddVertex(ToBlockContainer2(trustedRoot.Block))
    50  	return &forks, nil
    51  }
    52  
    53  // FinalizedView returns the largest view number where a finalized block is known
    54  func (f *Forks) FinalizedView() uint64 {
    55  	if f.finalityProof == nil {
    56  		return f.trustedRoot.Block.View
    57  	}
    58  	return f.finalityProof.Block.View
    59  }
    60  
    61  // FinalizedBlock returns the finalized block with the largest view number
    62  func (f *Forks) FinalizedBlock() *model.Block {
    63  	if f.finalityProof == nil {
    64  		return f.trustedRoot.Block
    65  	}
    66  	return f.finalityProof.Block
    67  }
    68  
    69  // FinalityProof returns the latest finalized block and a certified child from
    70  // the subsequent view, which proves finality.
    71  // CAUTION: method returns (nil, false), when Forks has not yet finalized any
    72  // blocks beyond the finalized root block it was initialized with.
    73  func (f *Forks) FinalityProof() (*hotstuff.FinalityProof, bool) {
    74  	return f.finalityProof, f.finalityProof != nil
    75  }
    76  
    77  // GetBlock returns (BlockProposal, true) if the block with the specified
    78  // id was found and (nil, false) otherwise.
    79  func (f *Forks) GetBlock(blockID flow.Identifier) (*model.Block, bool) {
    80  	blockContainer, hasBlock := f.forest.GetVertex(blockID)
    81  	if !hasBlock {
    82  		return nil, false
    83  	}
    84  	return blockContainer.(*BlockContainer).Block(), true
    85  }
    86  
    87  // GetBlocksForView returns all known blocks for the given view
    88  func (f *Forks) GetBlocksForView(view uint64) []*model.Block {
    89  	vertexIterator := f.forest.GetVerticesAtLevel(view)
    90  	blocks := make([]*model.Block, 0, 1) // in the vast majority of cases, there will only be one proposal for a particular view
    91  	for vertexIterator.HasNext() {
    92  		v := vertexIterator.NextVertex()
    93  		blocks = append(blocks, v.(*BlockContainer).Block())
    94  	}
    95  	return blocks
    96  }
    97  
    98  // IsKnownBlock checks whether block is known.
    99  func (f *Forks) IsKnownBlock(blockID flow.Identifier) bool {
   100  	_, hasBlock := f.forest.GetVertex(blockID)
   101  	return hasBlock
   102  }
   103  
   104  // IsProcessingNeeded determines whether the given block needs processing,
   105  // based on the block's view and hash.
   106  // Returns false if any of the following conditions applies
   107  //   - block view is _below_ the most recently finalized block
   108  //   - the block already exists in the consensus state
   109  //
   110  // UNVALIDATED: expects block to pass Forks.EnsureBlockIsValidExtension(block)
   111  func (f *Forks) IsProcessingNeeded(block *model.Block) bool {
   112  	if block.View < f.FinalizedView() || f.IsKnownBlock(block.BlockID) {
   113  		return false
   114  	}
   115  	return true
   116  }
   117  
   118  // EnsureBlockIsValidExtension checks that the given block is a valid extension to the tree
   119  // of blocks already stored (no state modifications). Specifically, the following conditions
   120  // are enforced, which are critical to the correctness of Forks:
   121  //
   122  //  1. If a block with the same ID is already stored, their views must be identical.
   123  //  2. The block's view must be strictly larger than the view of its parent.
   124  //  3. The parent must already be stored (or below the pruning height).
   125  //
   126  // Exclusions to these rules (by design):
   127  // Let W denote the view of block's parent (i.e. W := block.QC.View) and F the latest
   128  // finalized view.
   129  //
   130  //	  (i) If block.View < F, adding the block would be a no-op. Such blocks are considered
   131  //	      compatible (principle of vacuous truth), i.e. we skip checking 1, 2, 3.
   132  //	 (ii) If block.View == F, we do not inspect the QC / parent at all (skip 2 and 3).
   133  //	      This exception is important for compatability with genesis or spork-root blocks,
   134  //	      which do not contain a QC.
   135  //	(iii) If block.View > F, but block.QC.View < F the parent has already been pruned. In
   136  //	      this case, we omit rule 3. (principle of vacuous truth applied to the parent)
   137  //
   138  // We assume that all blocks are fully verified. A valid block must satisfy all consistency
   139  // requirements; otherwise we have a bug in the compliance layer.
   140  //
   141  // Error returns:
   142  //   - model.MissingBlockError if the parent of the input proposal does not exist in the
   143  //     forest (but is above the pruned view). Represents violation of condition 3.
   144  //   - model.InvalidBlockError if the block violates condition 1. or 2.
   145  //   - generic error in case of unexpected bug or internal state corruption
   146  func (f *Forks) EnsureBlockIsValidExtension(block *model.Block) error {
   147  	if block.View < f.forest.LowestLevel { // exclusion (i)
   148  		return nil
   149  	}
   150  
   151  	// LevelledForest enforces conditions 1. and 2. including the respective exclusions (ii) and (iii).
   152  	blockContainer := ToBlockContainer2(block)
   153  	err := f.forest.VerifyVertex(blockContainer)
   154  	if err != nil {
   155  		if forest.IsInvalidVertexError(err) {
   156  			return model.NewInvalidBlockErrorf(block, "not a valid vertex for block tree: %w", err)
   157  		}
   158  		return fmt.Errorf("block tree generated unexpected error validating vertex: %w", err)
   159  	}
   160  
   161  	// Condition 3:
   162  	// LevelledForest implements a more generalized algorithm that also works for disjoint graphs.
   163  	// Therefore, LevelledForest _not_ enforce condition 3. Here, we additionally require that the
   164  	// pending blocks form a tree (connected graph), i.e. we need to enforce condition 3
   165  	if (block.View == f.forest.LowestLevel) || (block.QC.View < f.forest.LowestLevel) { // exclusion (ii) and (iii)
   166  		return nil
   167  	}
   168  	// For a block whose parent is _not_ below the pruning height, we expect the parent to be known.
   169  	if _, isParentKnown := f.forest.GetVertex(block.QC.BlockID); !isParentKnown { // missing parent
   170  		return model.MissingBlockError{
   171  			View:    block.QC.View,
   172  			BlockID: block.QC.BlockID,
   173  		}
   174  	}
   175  	return nil
   176  }
   177  
   178  // AddCertifiedBlock appends the given certified block to the tree of pending
   179  // blocks and updates the latest finalized block (if finalization progressed).
   180  // Unless the parent is below the pruning threshold (latest finalized view), we
   181  // require that the parent is already stored in Forks. Calling this method with
   182  // previously processed blocks leaves the consensus state invariant (though,
   183  // it will potentially cause some duplicate processing).
   184  //
   185  // Possible error returns:
   186  //   - model.MissingBlockError if the parent does not exist in the forest (but is above
   187  //     the pruned view). From the perspective of Forks, this error is benign (no-op).
   188  //   - model.InvalidBlockError if the block is invalid (see `Forks.EnsureBlockIsValidExtension`
   189  //     for details). From the perspective of Forks, this error is benign (no-op). However, we
   190  //     assume all blocks are fully verified, i.e. they should satisfy all consistency
   191  //     requirements. Hence, this error is likely an indicator of a bug in the compliance layer.
   192  //   - model.ByzantineThresholdExceededError if conflicting QCs or conflicting finalized
   193  //     blocks have been detected (violating a foundational consensus guarantees). This
   194  //     indicates that there are 1/3+ Byzantine nodes (weighted by stake) in the network,
   195  //     breaking the safety guarantees of HotStuff (or there is a critical bug / data
   196  //     corruption). Forks cannot recover from this exception.
   197  //   - All other errors are potential symptoms of bugs or state corruption.
   198  func (f *Forks) AddCertifiedBlock(certifiedBlock *model.CertifiedBlock) error {
   199  	if !f.IsProcessingNeeded(certifiedBlock.Block) {
   200  		return nil
   201  	}
   202  
   203  	// Check proposal for byzantine evidence, store it and emit `OnBlockIncorporated` notification.
   204  	// Note: `checkForByzantineEvidence` only inspects the block, but _not_ its certifying QC. Hence,
   205  	// we have to additionally check here, whether the certifying QC conflicts with any known QCs.
   206  	err := f.checkForByzantineEvidence(certifiedBlock.Block)
   207  	if err != nil {
   208  		return fmt.Errorf("cannot check for Byzantine evidence in certified block %v: %w", certifiedBlock.Block.BlockID, err)
   209  	}
   210  	err = f.checkForConflictingQCs(certifiedBlock.CertifyingQC)
   211  	if err != nil {
   212  		return fmt.Errorf("certifying QC for block %v failed check for conflicts: %w", certifiedBlock.Block.BlockID, err)
   213  	}
   214  	f.forest.AddVertex(ToBlockContainer2(certifiedBlock.Block))
   215  	f.notifier.OnBlockIncorporated(certifiedBlock.Block)
   216  
   217  	// Update finality status:
   218  	err = f.checkForAdvancingFinalization(certifiedBlock)
   219  	if err != nil {
   220  		return fmt.Errorf("updating finalization failed: %w", err)
   221  	}
   222  	return nil
   223  }
   224  
   225  // AddValidatedBlock appends the validated block to the tree of pending
   226  // blocks and updates the latest finalized block (if applicable). Unless the parent is
   227  // below the pruning threshold (latest finalized view), we require that the parent is
   228  // already stored in Forks. Calling this method with previously processed blocks
   229  // leaves the consensus state invariant (though, it will potentially cause some
   230  // duplicate processing).
   231  // Notes:
   232  //   - Method `AddCertifiedBlock(..)` should be used preferably, if a QC certifying
   233  //     `block` is already known. This is generally the case for the consensus follower.
   234  //     Method `AddValidatedBlock` is intended for active consensus participants, which fully
   235  //     validate blocks (incl. payload), i.e. QCs are processed as part of validated proposals.
   236  //
   237  // Possible error returns:
   238  //   - model.MissingBlockError if the parent does not exist in the forest (but is above
   239  //     the pruned view). From the perspective of Forks, this error is benign (no-op).
   240  //   - model.InvalidBlockError if the block is invalid (see `Forks.EnsureBlockIsValidExtension`
   241  //     for details). From the perspective of Forks, this error is benign (no-op). However, we
   242  //     assume all blocks are fully verified, i.e. they should satisfy all consistency
   243  //     requirements. Hence, this error is likely an indicator of a bug in the compliance layer.
   244  //   - model.ByzantineThresholdExceededError if conflicting QCs or conflicting finalized
   245  //     blocks have been detected (violating a foundational consensus guarantees). This
   246  //     indicates that there are 1/3+ Byzantine nodes (weighted by stake) in the network,
   247  //     breaking the safety guarantees of HotStuff (or there is a critical bug / data
   248  //     corruption). Forks cannot recover from this exception.
   249  //   - All other errors are potential symptoms of bugs or state corruption.
   250  func (f *Forks) AddValidatedBlock(proposal *model.Block) error {
   251  	if !f.IsProcessingNeeded(proposal) {
   252  		return nil
   253  	}
   254  
   255  	// Check proposal for byzantine evidence, store it and emit `OnBlockIncorporated` notification:
   256  	err := f.checkForByzantineEvidence(proposal)
   257  	if err != nil {
   258  		return fmt.Errorf("cannot check Byzantine evidence for block %v: %w", proposal.BlockID, err)
   259  	}
   260  	f.forest.AddVertex(ToBlockContainer2(proposal))
   261  	f.notifier.OnBlockIncorporated(proposal)
   262  
   263  	// Update finality status: In the implementation, our notion of finality is based on certified blocks.
   264  	// The certified parent essentially combines the parent, with the QC contained in block, to drive finalization.
   265  	parent, found := f.GetBlock(proposal.QC.BlockID)
   266  	if !found {
   267  		// Not finding the parent means it is already pruned; hence this block does not change the finalization state.
   268  		return nil
   269  	}
   270  	certifiedParent, err := model.NewCertifiedBlock(parent, proposal.QC)
   271  	if err != nil {
   272  		return fmt.Errorf("mismatching QC with parent (corrupted Forks state):%w", err)
   273  	}
   274  	err = f.checkForAdvancingFinalization(&certifiedParent)
   275  	if err != nil {
   276  		return fmt.Errorf("updating finalization failed: %w", err)
   277  	}
   278  	return nil
   279  }
   280  
   281  // checkForByzantineEvidence inspects whether the given `block` together with the already
   282  // known information yields evidence of byzantine behaviour. Furthermore, the method enforces
   283  // that `block` is a valid extension of the tree of pending blocks. If the block is a double
   284  // proposal, we emit an `OnBlockIncorporated` notification. Though, provided the block is a
   285  // valid extension of the block tree by itself, it passes this method without an error.
   286  //
   287  // Possible error returns:
   288  //   - model.MissingBlockError if the parent does not exist in the forest (but is above
   289  //     the pruned view). From the perspective of Forks, this error is benign (no-op).
   290  //   - model.InvalidBlockError if the block is invalid (see `Forks.EnsureBlockIsValidExtension`
   291  //     for details). From the perspective of Forks, this error is benign (no-op). However, we
   292  //     assume all blocks are fully verified, i.e. they should satisfy all consistency
   293  //     requirements. Hence, this error is likely an indicator of a bug in the compliance layer.
   294  //   - model.ByzantineThresholdExceededError if conflicting QCs have been detected.
   295  //     Forks cannot recover from this exception.
   296  //   - All other errors are potential symptoms of bugs or state corruption.
   297  func (f *Forks) checkForByzantineEvidence(block *model.Block) error {
   298  	err := f.EnsureBlockIsValidExtension(block)
   299  	if err != nil {
   300  		return fmt.Errorf("consistency check on block failed: %w", err)
   301  	}
   302  	err = f.checkForConflictingQCs(block.QC)
   303  	if err != nil {
   304  		return fmt.Errorf("checking QC for conflicts failed: %w", err)
   305  	}
   306  	f.checkForDoubleProposal(block)
   307  	return nil
   308  }
   309  
   310  // checkForConflictingQCs checks if QC conflicts with a stored Quorum Certificate.
   311  // In case a conflicting QC is found, an ByzantineThresholdExceededError is returned.
   312  // Two Quorum Certificates q1 and q2 are defined as conflicting iff:
   313  //
   314  //	q1.View == q2.View AND q1.BlockID ≠ q2.BlockID
   315  //
   316  // This means there are two Quorums for conflicting blocks at the same view.
   317  // Per 'Observation 1' from the Jolteon paper https://arxiv.org/pdf/2106.10362v1.pdf,
   318  // two conflicting QCs can exist if and only if the Byzantine threshold is exceeded.
   319  // Error returns:
   320  //   - model.ByzantineThresholdExceededError if conflicting QCs have been detected.
   321  //     Forks cannot recover from this exception.
   322  //   - All other errors are potential symptoms of bugs or state corruption.
   323  func (f *Forks) checkForConflictingQCs(qc *flow.QuorumCertificate) error {
   324  	it := f.forest.GetVerticesAtLevel(qc.View)
   325  	for it.HasNext() {
   326  		otherBlock := it.NextVertex() // by construction, must have same view as qc.View
   327  		if qc.BlockID != otherBlock.VertexID() {
   328  			// * we have just found another block at the same view number as qc.View but with different hash
   329  			// * if this block has a child c, this child will have
   330  			//   c.qc.view = parentView
   331  			//   c.qc.ID != parentBlockID
   332  			// => conflicting qc
   333  			otherChildren := f.forest.GetChildren(otherBlock.VertexID())
   334  			if otherChildren.HasNext() {
   335  				otherChild := otherChildren.NextVertex().(*BlockContainer).Block()
   336  				conflictingQC := otherChild.QC
   337  				return model.ByzantineThresholdExceededError{Evidence: fmt.Sprintf(
   338  					"conflicting QCs at view %d: %v and %v",
   339  					qc.View, qc.BlockID, conflictingQC.BlockID,
   340  				)}
   341  			}
   342  		}
   343  	}
   344  	return nil
   345  }
   346  
   347  // checkForDoubleProposal checks if the input proposal is a double proposal.
   348  // A double proposal occurs when two proposals with the same view exist in Forks.
   349  // If there is a double proposal, notifier.OnDoubleProposeDetected is triggered.
   350  func (f *Forks) checkForDoubleProposal(block *model.Block) {
   351  	it := f.forest.GetVerticesAtLevel(block.View)
   352  	for it.HasNext() {
   353  		otherVertex := it.NextVertex() // by construction, must have same view as block
   354  		otherBlock := otherVertex.(*BlockContainer).Block()
   355  		if block.BlockID != otherBlock.BlockID {
   356  			f.notifier.OnDoubleProposeDetected(block, otherBlock)
   357  		}
   358  	}
   359  }
   360  
   361  // checkForAdvancingFinalization checks whether observing certifiedBlock leads to progress of
   362  // finalization. This function should be called every time a new block is added to Forks. If the new
   363  // block is the head of a 2-chain satisfying the finalization rule, we update `Forks.finalityProof` to
   364  // the new latest finalized block. Calling this method with previously-processed blocks leaves the
   365  // consensus state invariant.
   366  // UNVALIDATED: assumes that relevant block properties are consistent with previous blocks
   367  // Error returns:
   368  //   - model.MissingBlockError if the parent does not exist in the forest (but is above
   369  //     the pruned view). From the perspective of Forks, this error is benign (no-op).
   370  //   - model.ByzantineThresholdExceededError in case we detect a finalization fork (violating
   371  //     a foundational consensus guarantee). This indicates that there are 1/3+ Byzantine nodes
   372  //     (weighted by stake) in the network, breaking the safety guarantees of HotStuff (or there
   373  //     is a critical bug / data corruption). Forks cannot recover from this exception.
   374  //   - generic error in case of unexpected bug or internal state corruption
   375  func (f *Forks) checkForAdvancingFinalization(certifiedBlock *model.CertifiedBlock) error {
   376  	// We prune all blocks in forest which are below the most recently finalized block.
   377  	// Hence, we have a pruned ancestry if and only if either of the following conditions applies:
   378  	//    (a) If a block's parent view (i.e. block.QC.View) is below the most recently finalized block.
   379  	//    (b) If a block's view is equal to the most recently finalized block.
   380  	// Caution:
   381  	// * Under normal operation, case (b) is covered by the logic for case (a)
   382  	// * However, the existence of a genesis block requires handling case (b) explicitly:
   383  	//   The root block is specified and trusted by the node operator. If the root block is the
   384  	//   genesis block, it might not contain a QC pointing to a parent (as there is no parent).
   385  	//   In this case, condition (a) cannot be evaluated.
   386  	lastFinalizedView := f.FinalizedView()
   387  	if (certifiedBlock.View() <= lastFinalizedView) || (certifiedBlock.Block.QC.View < lastFinalizedView) {
   388  		// Repeated blocks are expected during normal operations. We enter this code block if and only
   389  		// if the parent's view is _below_ the last finalized block. It is straight forward to show:
   390  		// Lemma: Let B be a block whose 2-chain reaches beyond the last finalized block
   391  		//        => B will not update the locked or finalized block
   392  		return nil
   393  	}
   394  
   395  	// retrieve parent; always expected to succeed, because we passed the checks above
   396  	qcForParent := certifiedBlock.Block.QC
   397  	parentVertex, parentBlockKnown := f.forest.GetVertex(qcForParent.BlockID)
   398  	if !parentBlockKnown {
   399  		return model.MissingBlockError{View: qcForParent.View, BlockID: qcForParent.BlockID}
   400  	}
   401  	parentBlock := parentVertex.(*BlockContainer).Block()
   402  
   403  	// Note: we assume that all stored blocks pass Forks.EnsureBlockIsValidExtension(block);
   404  	//       specifically, that Proposal's ViewNumber is strictly monotonically
   405  	//       increasing which is enforced by LevelledForest.VerifyVertex(...)
   406  	// We denote:
   407  	//  * a DIRECT 1-chain as '<-'
   408  	//  * a general 1-chain as '<~' (direct or indirect)
   409  	// Jolteon's rule for finalizing `parentBlock` is
   410  	//     parentBlock <- Block <~ certifyingQC    (i.e. a DIRECT 1-chain PLUS any 1-chain)
   411  	//                   ╰─────────────────────╯
   412  	//                       certifiedBlock
   413  	// Hence, we can finalize `parentBlock` as head of a 2-chain,
   414  	// if and only if `Block.View` is exactly 1 higher than the view of `parentBlock`
   415  	if parentBlock.View+1 != certifiedBlock.View() {
   416  		return nil
   417  	}
   418  
   419  	// `parentBlock` is now finalized:
   420  	//  * While Forks is single-threaded, there is still the possibility of reentrancy. Specifically, the
   421  	//    consumers of our finalization events are served by the goroutine executing Forks. It is conceivable
   422  	//    that a consumer might access Forks and query the latest finalization proof. This would be legal, if
   423  	//    the component supplying the goroutine to Forks also consumes the notifications.
   424  	//  * Therefore, for API safety, we want to first update Fork's `finalityProof` before we emit any notifications.
   425  
   426  	// Advancing finalization step (i): we collect all blocks for finalization (no notifications are emitted)
   427  	blocksToBeFinalized, err := f.collectBlocksForFinalization(qcForParent)
   428  	if err != nil {
   429  		return fmt.Errorf("advancing finalization to block %v from view %d failed: %w", qcForParent.BlockID, qcForParent.View, err)
   430  	}
   431  
   432  	// Advancing finalization step (ii): update `finalityProof` and prune `LevelledForest`
   433  	f.finalityProof = &hotstuff.FinalityProof{Block: parentBlock, CertifiedChild: *certifiedBlock}
   434  	err = f.forest.PruneUpToLevel(f.FinalizedView())
   435  	if err != nil {
   436  		return fmt.Errorf("pruning levelled forest failed unexpectedly: %w", err)
   437  	}
   438  
   439  	// Advancing finalization step (iii): iterate over the blocks from (i) and emit finalization events
   440  	for _, b := range blocksToBeFinalized {
   441  		// first notify other critical components about finalized block - all errors returned here are fatal exceptions
   442  		err = f.finalizationCallback.MakeFinal(b.BlockID)
   443  		if err != nil {
   444  			return fmt.Errorf("finalization error in other component: %w", err)
   445  		}
   446  
   447  		// notify less important components about finalized block
   448  		f.notifier.OnFinalizedBlock(b)
   449  	}
   450  	return nil
   451  }
   452  
   453  // collectBlocksForFinalization collects and returns all newly finalized blocks up to (and including)
   454  // the block pointed to by `qc`. The blocks are listed in order of increasing height.
   455  // Error returns:
   456  //   - model.ByzantineThresholdExceededError in case we detect a finalization fork (violating
   457  //     a foundational consensus guarantee). This indicates that there are 1/3+ Byzantine nodes
   458  //     (weighted by stake) in the network, breaking the safety guarantees of HotStuff (or there
   459  //     is a critical bug / data corruption). Forks cannot recover from this exception.
   460  //   - generic error in case of bug or internal state corruption
   461  func (f *Forks) collectBlocksForFinalization(qc *flow.QuorumCertificate) ([]*model.Block, error) {
   462  	lastFinalized := f.FinalizedBlock()
   463  	if qc.View < lastFinalized.View {
   464  		return nil, model.ByzantineThresholdExceededError{Evidence: fmt.Sprintf(
   465  			"finalizing block with view %d which is lower than previously finalized block at view %d",
   466  			qc.View, lastFinalized.View,
   467  		)}
   468  	}
   469  	if qc.View == lastFinalized.View { // no new blocks to be finalized
   470  		return nil, nil
   471  	}
   472  
   473  	// Collect all blocks that are pending finalization in slice. While we crawl the blocks starting
   474  	// from the newest finalized block backwards (decreasing views), we would like to return them in
   475  	// order of _increasing_ view. Therefore, we fill the slice starting with the highest index.
   476  	l := qc.View - lastFinalized.View // l is an upper limit to the number of blocks that can be maximally finalized
   477  	blocksToBeFinalized := make([]*model.Block, l)
   478  	for qc.View > lastFinalized.View {
   479  		b, ok := f.GetBlock(qc.BlockID)
   480  		if !ok {
   481  			return nil, fmt.Errorf("failed to get block (view=%d, blockID=%x) for finalization", qc.View, qc.BlockID)
   482  		}
   483  		l--
   484  		blocksToBeFinalized[l] = b
   485  		qc = b.QC // move to parent
   486  	}
   487  	// Now, `l` is the index where we stored the oldest block that should be finalized. Note that `l`
   488  	// might be larger than zero, if some views have no finalized blocks. Hence, `blocksToBeFinalized`
   489  	// might start with nil entries, which we remove:
   490  	blocksToBeFinalized = blocksToBeFinalized[l:]
   491  
   492  	// qc should now point to the latest finalized block. Otherwise, the
   493  	// consensus committee is compromised (or we have a critical internal bug).
   494  	if qc.View < lastFinalized.View {
   495  		return nil, model.ByzantineThresholdExceededError{Evidence: fmt.Sprintf(
   496  			"finalizing block with view %d which is lower than previously finalized block at view %d",
   497  			qc.View, lastFinalized.View,
   498  		)}
   499  	}
   500  	if qc.View == lastFinalized.View && lastFinalized.BlockID != qc.BlockID {
   501  		return nil, model.ByzantineThresholdExceededError{Evidence: fmt.Sprintf(
   502  			"finalizing blocks with view %d at conflicting forks: %x and %x",
   503  			qc.View, qc.BlockID, lastFinalized.BlockID,
   504  		)}
   505  	}
   506  
   507  	return blocksToBeFinalized, nil
   508  }