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

     1  package forks
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/mock"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/onflow/flow-go/consensus/hotstuff"
    12  	"github.com/onflow/flow-go/consensus/hotstuff/mocks"
    13  	"github.com/onflow/flow-go/consensus/hotstuff/model"
    14  	"github.com/onflow/flow-go/model/flow"
    15  	mockmodule "github.com/onflow/flow-go/module/mock"
    16  )
    17  
    18  /*****************************************************************************
    19   * NOTATION:                                                                 *
    20   * A block is denoted as [◄(<qc_number>) <block_view_number>].               *
    21   * For example, [◄(1) 2] means: a block of view 2 that has a QC for view 1.  *
    22   *****************************************************************************/
    23  
    24  // TestInitialization verifies that at initialization, Forks reports:
    25  //   - the root / genesis block as finalized
    26  //   - it has no finalization proof for the root / genesis block (block and its finalization is trusted)
    27  func TestInitialization(t *testing.T) {
    28  	forks, _ := newForks(t)
    29  	requireOnlyGenesisBlockFinalized(t, forks)
    30  	_, hasProof := forks.FinalityProof()
    31  	require.False(t, hasProof)
    32  }
    33  
    34  // TestFinalize_Direct1Chain tests adding a direct 1-chain on top of the genesis block:
    35  //   - receives [◄(1) 2] [◄(2) 5]
    36  //
    37  // Expected behaviour:
    38  //   - On the one hand, Forks should not finalize any _additional_ blocks, because there is
    39  //     no finalizable 2-chain for [◄(1) 2]. Hence, finalization no events should be emitted.
    40  //   - On the other hand, after adding the two blocks, Forks has enough knowledge to construct
    41  //     a FinalityProof for the genesis block.
    42  func TestFinalize_Direct1Chain(t *testing.T) {
    43  	builder := NewBlockBuilder().
    44  		Add(1, 2).
    45  		Add(2, 3)
    46  	blocks, err := builder.Blocks()
    47  	require.Nil(t, err)
    48  
    49  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
    50  		forks, _ := newForks(t)
    51  
    52  		// adding block [◄(1) 2] should not finalize anything
    53  		// as the genesis block is trusted, there should be no FinalityProof available for it
    54  		require.NoError(t, forks.AddValidatedBlock(blocks[0]))
    55  		requireOnlyGenesisBlockFinalized(t, forks)
    56  		_, hasProof := forks.FinalityProof()
    57  		require.False(t, hasProof)
    58  
    59  		// After adding block [◄(2) 3], Forks has enough knowledge to construct a FinalityProof for the
    60  		// genesis block. However, finalization remains at the genesis block, so no events should be emitted.
    61  		expectedFinalityProof := makeFinalityProof(t, builder.GenesisBlock().Block, blocks[0], blocks[1].QC)
    62  		require.NoError(t, forks.AddValidatedBlock(blocks[1]))
    63  		requireLatestFinalizedBlock(t, forks, builder.GenesisBlock().Block)
    64  		requireFinalityProof(t, forks, expectedFinalityProof)
    65  	})
    66  
    67  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
    68  		forks, _ := newForks(t)
    69  
    70  		// After adding CertifiedBlock [◄(1) 2] ◄(2), Forks has enough knowledge to construct a FinalityProof for
    71  		// the genesis block. However, finalization remains at the genesis block, so no events should be emitted.
    72  		expectedFinalityProof := makeFinalityProof(t, builder.GenesisBlock().Block, blocks[0], blocks[1].QC)
    73  		c, err := model.NewCertifiedBlock(blocks[0], blocks[1].QC)
    74  		require.NoError(t, err)
    75  
    76  		require.NoError(t, forks.AddCertifiedBlock(&c))
    77  		requireLatestFinalizedBlock(t, forks, builder.GenesisBlock().Block)
    78  		requireFinalityProof(t, forks, expectedFinalityProof)
    79  	})
    80  }
    81  
    82  // TestFinalize_Direct2Chain tests adding a direct 1-chain on a direct 1-chain (direct 2-chain).
    83  //   - receives [◄(1) 2] [◄(2) 3] [◄(3) 4]
    84  //   - Forks should finalize [◄(1) 2]
    85  func TestFinalize_Direct2Chain(t *testing.T) {
    86  	blocks, err := NewBlockBuilder().
    87  		Add(1, 2).
    88  		Add(2, 3).
    89  		Add(3, 4).
    90  		Blocks()
    91  	require.Nil(t, err)
    92  	expectedFinalityProof := makeFinalityProof(t, blocks[0], blocks[1], blocks[2].QC)
    93  
    94  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
    95  		forks, _ := newForks(t)
    96  		require.Nil(t, addValidatedBlockToForks(forks, blocks))
    97  
    98  		requireLatestFinalizedBlock(t, forks, blocks[0])
    99  		requireFinalityProof(t, forks, expectedFinalityProof)
   100  	})
   101  
   102  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   103  		forks, _ := newForks(t)
   104  		require.Nil(t, addCertifiedBlocksToForks(forks, blocks))
   105  
   106  		requireLatestFinalizedBlock(t, forks, blocks[0])
   107  		requireFinalityProof(t, forks, expectedFinalityProof)
   108  	})
   109  }
   110  
   111  // TestFinalize_DirectIndirect2Chain tests adding an indirect 1-chain on a direct 1-chain.
   112  // receives [◄(1) 2] [◄(2) 3] [◄(3) 5]
   113  // it should finalize [◄(1) 2]
   114  func TestFinalize_DirectIndirect2Chain(t *testing.T) {
   115  	blocks, err := NewBlockBuilder().
   116  		Add(1, 2).
   117  		Add(2, 3).
   118  		Add(3, 5).
   119  		Blocks()
   120  	require.Nil(t, err)
   121  	expectedFinalityProof := makeFinalityProof(t, blocks[0], blocks[1], blocks[2].QC)
   122  
   123  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   124  		forks, _ := newForks(t)
   125  		require.Nil(t, addValidatedBlockToForks(forks, blocks))
   126  
   127  		requireLatestFinalizedBlock(t, forks, blocks[0])
   128  		requireFinalityProof(t, forks, expectedFinalityProof)
   129  	})
   130  
   131  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   132  		forks, _ := newForks(t)
   133  		require.Nil(t, addCertifiedBlocksToForks(forks, blocks))
   134  
   135  		requireLatestFinalizedBlock(t, forks, blocks[0])
   136  		requireFinalityProof(t, forks, expectedFinalityProof)
   137  	})
   138  }
   139  
   140  // TestFinalize_IndirectDirect2Chain tests adding a direct 1-chain on an indirect 1-chain.
   141  //   - Forks receives [◄(1) 3] [◄(3) 5] [◄(7) 7]
   142  //   - it should not finalize any blocks because there is no finalizable 2-chain.
   143  func TestFinalize_IndirectDirect2Chain(t *testing.T) {
   144  	blocks, err := NewBlockBuilder().
   145  		Add(1, 3).
   146  		Add(3, 5).
   147  		Add(5, 7).
   148  		Blocks()
   149  	require.Nil(t, err)
   150  
   151  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   152  		forks, _ := newForks(t)
   153  		require.Nil(t, addValidatedBlockToForks(forks, blocks))
   154  
   155  		requireOnlyGenesisBlockFinalized(t, forks)
   156  		_, hasProof := forks.FinalityProof()
   157  		require.False(t, hasProof)
   158  	})
   159  
   160  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   161  		forks, _ := newForks(t)
   162  		require.Nil(t, addCertifiedBlocksToForks(forks, blocks))
   163  
   164  		requireOnlyGenesisBlockFinalized(t, forks)
   165  		_, hasProof := forks.FinalityProof()
   166  		require.False(t, hasProof)
   167  	})
   168  }
   169  
   170  // TestFinalize_Direct2ChainOnIndirect tests adding a direct 2-chain on an indirect 2-chain:
   171  //   - ingesting [◄(1) 3] [◄(3) 5] [◄(5) 6] [◄(6) 7] [◄(7) 8]
   172  //   - should result in finalization of [◄(5) 6]
   173  func TestFinalize_Direct2ChainOnIndirect(t *testing.T) {
   174  	blocks, err := NewBlockBuilder().
   175  		Add(1, 3).
   176  		Add(3, 5).
   177  		Add(5, 6).
   178  		Add(6, 7).
   179  		Add(7, 8).
   180  		Blocks()
   181  	require.Nil(t, err)
   182  	expectedFinalityProof := makeFinalityProof(t, blocks[2], blocks[3], blocks[4].QC)
   183  
   184  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   185  		forks, _ := newForks(t)
   186  		require.Nil(t, addValidatedBlockToForks(forks, blocks))
   187  
   188  		requireLatestFinalizedBlock(t, forks, blocks[2])
   189  		requireFinalityProof(t, forks, expectedFinalityProof)
   190  	})
   191  
   192  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   193  		forks, _ := newForks(t)
   194  		require.Nil(t, addCertifiedBlocksToForks(forks, blocks))
   195  
   196  		requireLatestFinalizedBlock(t, forks, blocks[2])
   197  		requireFinalityProof(t, forks, expectedFinalityProof)
   198  	})
   199  }
   200  
   201  // TestFinalize_Direct2ChainOnDirect tests adding a sequence of direct 2-chains:
   202  //   - ingesting [◄(1) 2] [◄(2) 3] [◄(3) 4] [◄(4) 5] [◄(5) 6]
   203  //   - should result in finalization of [◄(3) 4]
   204  func TestFinalize_Direct2ChainOnDirect(t *testing.T) {
   205  	blocks, err := NewBlockBuilder().
   206  		Add(1, 2).
   207  		Add(2, 3).
   208  		Add(3, 4).
   209  		Add(4, 5).
   210  		Add(5, 6).
   211  		Blocks()
   212  	require.Nil(t, err)
   213  	expectedFinalityProof := makeFinalityProof(t, blocks[2], blocks[3], blocks[4].QC)
   214  
   215  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   216  		forks, _ := newForks(t)
   217  		require.Nil(t, addValidatedBlockToForks(forks, blocks))
   218  
   219  		requireLatestFinalizedBlock(t, forks, blocks[2])
   220  		requireFinalityProof(t, forks, expectedFinalityProof)
   221  	})
   222  
   223  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   224  		forks, _ := newForks(t)
   225  		require.Nil(t, addCertifiedBlocksToForks(forks, blocks))
   226  
   227  		requireLatestFinalizedBlock(t, forks, blocks[2])
   228  		requireFinalityProof(t, forks, expectedFinalityProof)
   229  	})
   230  }
   231  
   232  // TestFinalize_Multiple2Chains tests the case where a block can be finalized by different 2-chains.
   233  //   - ingesting [◄(1) 2] [◄(2) 3] [◄(3) 5] [◄(3) 6] [◄(3) 7]
   234  //   - should result in finalization of [◄(1) 2]
   235  func TestFinalize_Multiple2Chains(t *testing.T) {
   236  	blocks, err := NewBlockBuilder().
   237  		Add(1, 2).
   238  		Add(2, 3).
   239  		Add(3, 5).
   240  		Add(3, 6).
   241  		Add(3, 7).
   242  		Blocks()
   243  	require.Nil(t, err)
   244  	expectedFinalityProof := makeFinalityProof(t, blocks[0], blocks[1], blocks[2].QC)
   245  
   246  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   247  		forks, _ := newForks(t)
   248  		require.Nil(t, addValidatedBlockToForks(forks, blocks))
   249  
   250  		requireLatestFinalizedBlock(t, forks, blocks[0])
   251  		requireFinalityProof(t, forks, expectedFinalityProof)
   252  	})
   253  
   254  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   255  		forks, _ := newForks(t)
   256  		require.Nil(t, addCertifiedBlocksToForks(forks, blocks))
   257  
   258  		requireLatestFinalizedBlock(t, forks, blocks[0])
   259  		requireFinalityProof(t, forks, expectedFinalityProof)
   260  	})
   261  }
   262  
   263  // TestFinalize_OrphanedFork tests that we can finalize a block which causes a conflicting fork to be orphaned.
   264  // We ingest the the following block tree:
   265  //
   266  //	[◄(1) 2] [◄(2) 3]
   267  //	         [◄(2) 4] [◄(4) 5] [◄(5) 6]
   268  //
   269  // which should result in finalization of [◄(2) 4] and pruning of [◄(2) 3]
   270  func TestFinalize_OrphanedFork(t *testing.T) {
   271  	blocks, err := NewBlockBuilder().
   272  		Add(1, 2). // [◄(1) 2]
   273  		Add(2, 3). // [◄(2) 3], should eventually be pruned
   274  		Add(2, 4). // [◄(2) 4], should eventually be finalized
   275  		Add(4, 5). // [◄(4) 5]
   276  		Add(5, 6). // [◄(5) 6]
   277  		Blocks()
   278  	require.Nil(t, err)
   279  	expectedFinalityProof := makeFinalityProof(t, blocks[2], blocks[3], blocks[4].QC)
   280  
   281  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   282  		forks, _ := newForks(t)
   283  		require.Nil(t, addValidatedBlockToForks(forks, blocks))
   284  
   285  		require.False(t, forks.IsKnownBlock(blocks[1].BlockID))
   286  		requireLatestFinalizedBlock(t, forks, blocks[2])
   287  		requireFinalityProof(t, forks, expectedFinalityProof)
   288  	})
   289  
   290  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   291  		forks, _ := newForks(t)
   292  		require.Nil(t, addCertifiedBlocksToForks(forks, blocks))
   293  
   294  		require.False(t, forks.IsKnownBlock(blocks[1].BlockID))
   295  		requireLatestFinalizedBlock(t, forks, blocks[2])
   296  		requireFinalityProof(t, forks, expectedFinalityProof)
   297  	})
   298  }
   299  
   300  // TestDuplication tests that delivering the same block/qc multiple times has
   301  // the same end state as delivering the block/qc once.
   302  //   - Forks receives [◄(1) 2] [◄(2) 3] [◄(2) 3] [◄(3) 4] [◄(3) 4] [◄(4) 5] [◄(4) 5]
   303  //   - it should finalize [◄(2) 3]
   304  func TestDuplication(t *testing.T) {
   305  	blocks, err := NewBlockBuilder().
   306  		Add(1, 2).
   307  		Add(2, 3).
   308  		Add(2, 3).
   309  		Add(3, 4).
   310  		Add(3, 4).
   311  		Add(4, 5).
   312  		Add(4, 5).
   313  		Blocks()
   314  	require.Nil(t, err)
   315  	expectedFinalityProof := makeFinalityProof(t, blocks[1], blocks[3], blocks[5].QC)
   316  
   317  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   318  		forks, _ := newForks(t)
   319  		require.Nil(t, addValidatedBlockToForks(forks, blocks))
   320  
   321  		requireLatestFinalizedBlock(t, forks, blocks[1])
   322  		requireFinalityProof(t, forks, expectedFinalityProof)
   323  	})
   324  
   325  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   326  		forks, _ := newForks(t)
   327  		require.Nil(t, addCertifiedBlocksToForks(forks, blocks))
   328  
   329  		requireLatestFinalizedBlock(t, forks, blocks[1])
   330  		requireFinalityProof(t, forks, expectedFinalityProof)
   331  	})
   332  }
   333  
   334  // TestIgnoreBlocksBelowFinalizedView tests that blocks below finalized view are ignored.
   335  //   - Forks receives [◄(1) 2] [◄(2) 3] [◄(3) 4] [◄(1) 5]
   336  //   - it should finalize [◄(1) 2]
   337  func TestIgnoreBlocksBelowFinalizedView(t *testing.T) {
   338  	builder := NewBlockBuilder().
   339  		Add(1, 2). // [◄(1) 2]
   340  		Add(2, 3). // [◄(2) 3]
   341  		Add(3, 4). // [◄(3) 4]
   342  		Add(1, 5)  // [◄(1) 5]
   343  	blocks, err := builder.Blocks()
   344  	require.Nil(t, err)
   345  	expectedFinalityProof := makeFinalityProof(t, blocks[0], blocks[1], blocks[2].QC)
   346  
   347  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   348  		// initialize forks and add first 3 blocks:
   349  		//  * block [◄(1) 2] should then be finalized
   350  		//  * and block [1] should be pruned
   351  		forks, _ := newForks(t)
   352  		require.Nil(t, addValidatedBlockToForks(forks, blocks[:3]))
   353  
   354  		// sanity checks to confirm correct test setup
   355  		requireLatestFinalizedBlock(t, forks, blocks[0])
   356  		requireFinalityProof(t, forks, expectedFinalityProof)
   357  		require.False(t, forks.IsKnownBlock(builder.GenesisBlock().ID()))
   358  
   359  		// adding block [◄(1) 5]: note that QC is _below_ the pruning threshold, i.e. cannot resolve the parent
   360  		// * Forks should store block, despite the parent already being pruned
   361  		// * finalization should not change
   362  		orphanedBlock := blocks[3]
   363  		require.Nil(t, forks.AddValidatedBlock(orphanedBlock))
   364  		require.True(t, forks.IsKnownBlock(orphanedBlock.BlockID))
   365  		requireLatestFinalizedBlock(t, forks, blocks[0])
   366  		requireFinalityProof(t, forks, expectedFinalityProof)
   367  	})
   368  
   369  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   370  		// initialize forks and add first 3 blocks:
   371  		//  * block [◄(1) 2] should then be finalized
   372  		//  * and block [1] should be pruned
   373  		forks, _ := newForks(t)
   374  		require.Nil(t, addCertifiedBlocksToForks(forks, blocks[:3]))
   375  		// sanity checks to confirm correct test setup
   376  		requireLatestFinalizedBlock(t, forks, blocks[0])
   377  		requireFinalityProof(t, forks, expectedFinalityProof)
   378  		require.False(t, forks.IsKnownBlock(builder.GenesisBlock().ID()))
   379  
   380  		// adding block [◄(1) 5]: note that QC is _below_ the pruning threshold, i.e. cannot resolve the parent
   381  		// * Forks should store block, despite the parent already being pruned
   382  		// * finalization should not change
   383  		certBlockWithUnknownParent := toCertifiedBlock(t, blocks[3])
   384  		require.Nil(t, forks.AddCertifiedBlock(certBlockWithUnknownParent))
   385  		require.True(t, forks.IsKnownBlock(certBlockWithUnknownParent.Block.BlockID))
   386  		requireLatestFinalizedBlock(t, forks, blocks[0])
   387  		requireFinalityProof(t, forks, expectedFinalityProof)
   388  	})
   389  }
   390  
   391  // TestDoubleProposal tests that the DoubleProposal notification is emitted when two different
   392  // blocks for the same view are added. We ingest the the following block tree:
   393  //
   394  //	               / [◄(1) 2]
   395  //			[1]
   396  //	               \ [◄(1) 2']
   397  //
   398  // which should result in a DoubleProposal event referencing the blocks [◄(1) 2] and [◄(1) 2']
   399  func TestDoubleProposal(t *testing.T) {
   400  	blocks, err := NewBlockBuilder().
   401  		Add(1, 2).                // [◄(1) 2]
   402  		AddVersioned(1, 2, 0, 1). // [◄(1) 2']
   403  		Blocks()
   404  	require.Nil(t, err)
   405  
   406  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   407  		forks, notifier := newForks(t)
   408  		notifier.On("OnDoubleProposeDetected", blocks[1], blocks[0]).Once()
   409  
   410  		err = addValidatedBlockToForks(forks, blocks)
   411  		require.Nil(t, err)
   412  	})
   413  
   414  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   415  		forks, notifier := newForks(t)
   416  		notifier.On("OnDoubleProposeDetected", blocks[1], blocks[0]).Once()
   417  
   418  		err = forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[0])) // add [◄(1) 2]  as certified block
   419  		require.Nil(t, err)
   420  		err = forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[1])) // add [◄(1) 2']  as certified block
   421  		require.Nil(t, err)
   422  	})
   423  }
   424  
   425  // TestConflictingQCs checks that adding 2 conflicting QCs should return model.ByzantineThresholdExceededError
   426  // We ingest the following block tree:
   427  //
   428  //	[◄(1) 2] [◄(2) 3]   [◄(3) 4]  [◄(4) 6]
   429  //	         [◄(2) 3']  [◄(3') 5]
   430  //
   431  // which should result in a `ByzantineThresholdExceededError`, because conflicting blocks 3 and 3' both have QCs
   432  func TestConflictingQCs(t *testing.T) {
   433  	blocks, err := NewBlockBuilder().
   434  		Add(1, 2).                // [◄(1) 2]
   435  		Add(2, 3).                // [◄(2) 3]
   436  		AddVersioned(2, 3, 0, 1). // [◄(2) 3']
   437  		Add(3, 4).                // [◄(3) 4]
   438  		Add(4, 6).                // [◄(4) 6]
   439  		AddVersioned(3, 5, 1, 0). // [◄(3') 5]
   440  		Blocks()
   441  	require.Nil(t, err)
   442  
   443  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   444  		forks, notifier := newForks(t)
   445  		notifier.On("OnDoubleProposeDetected", blocks[2], blocks[1]).Return(nil)
   446  
   447  		err = addValidatedBlockToForks(forks, blocks)
   448  		assert.True(t, model.IsByzantineThresholdExceededError(err))
   449  	})
   450  
   451  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   452  		forks, notifier := newForks(t)
   453  		notifier.On("OnDoubleProposeDetected", blocks[2], blocks[1]).Return(nil)
   454  
   455  		// As [◄(3') 5] is not certified, it will not be added to Forks. However, its QC ◄(3') is
   456  		// delivered to Forks as part of the *certified* block [◄(2) 3'].
   457  		err = addCertifiedBlocksToForks(forks, blocks)
   458  		assert.True(t, model.IsByzantineThresholdExceededError(err))
   459  	})
   460  }
   461  
   462  // TestConflictingFinalizedForks checks that finalizing 2 conflicting forks should return model.ByzantineThresholdExceededError
   463  // We ingest the the following block tree:
   464  //
   465  //	[◄(1) 2] [◄(2) 3] [◄(3) 4] [◄(4) 5]
   466  //	         [◄(2) 6] [◄(6) 7] [◄(7) 8]
   467  //
   468  // Here, both blocks [◄(2) 3] and [◄(2) 6] satisfy the finalization condition, i.e. we have a fork
   469  // in the finalized blocks, which should result in a model.ByzantineThresholdExceededError exception.
   470  func TestConflictingFinalizedForks(t *testing.T) {
   471  	blocks, err := NewBlockBuilder().
   472  		Add(1, 2).
   473  		Add(2, 3).
   474  		Add(3, 4).
   475  		Add(4, 5). // finalizes [◄(2) 3]
   476  		Add(2, 6).
   477  		Add(6, 7).
   478  		Add(7, 8). // finalizes [◄(2) 6], conflicting with conflicts with [◄(2) 3]
   479  		Blocks()
   480  	require.Nil(t, err)
   481  
   482  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   483  		forks, _ := newForks(t)
   484  		err = addValidatedBlockToForks(forks, blocks)
   485  		assert.True(t, model.IsByzantineThresholdExceededError(err))
   486  	})
   487  
   488  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   489  		forks, _ := newForks(t)
   490  		err = addCertifiedBlocksToForks(forks, blocks)
   491  		assert.True(t, model.IsByzantineThresholdExceededError(err))
   492  	})
   493  }
   494  
   495  // TestAddDisconnectedBlock checks that adding a block which does not connect to the
   496  // latest finalized block returns a `model.MissingBlockError`
   497  //   - receives [◄(2) 3]
   498  //   - should return `model.MissingBlockError`, because the parent is above the pruning
   499  //     threshold, but Forks does not know its parent
   500  func TestAddDisconnectedBlock(t *testing.T) {
   501  	blocks, err := NewBlockBuilder().
   502  		Add(1, 2). // we will skip this block [◄(1) 2]
   503  		Add(2, 3). // [◄(2) 3]
   504  		Blocks()
   505  	require.Nil(t, err)
   506  
   507  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   508  		forks, _ := newForks(t)
   509  		err := forks.AddValidatedBlock(blocks[1])
   510  		require.Error(t, err)
   511  		assert.True(t, model.IsMissingBlockError(err))
   512  	})
   513  
   514  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   515  		forks, _ := newForks(t)
   516  		err := forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[1]))
   517  		require.Error(t, err)
   518  		assert.True(t, model.IsMissingBlockError(err))
   519  	})
   520  }
   521  
   522  // TestGetBlock tests that we can retrieve stored blocks. Here, we test that
   523  // attempting to retrieve nonexistent or pruned blocks fails without causing an exception.
   524  //   - Forks receives [◄(1) 2] [◄(2) 3] [◄(3) 4], then [◄(4) 5]
   525  //   - should finalize [◄(1) 2], then [◄(2) 3]
   526  func TestGetBlock(t *testing.T) {
   527  	blocks, err := NewBlockBuilder().
   528  		Add(1, 2). // [◄(1) 2]
   529  		Add(2, 3). // [◄(2) 3]
   530  		Add(3, 4). // [◄(3) 4]
   531  		Add(4, 5). // [◄(4) 5]
   532  		Blocks()
   533  	require.Nil(t, err)
   534  
   535  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   536  		blocksAddedFirst := blocks[:3] // [◄(1) 2] [◄(2) 3] [◄(3) 4]
   537  		remainingBlock := blocks[3]    // [◄(4) 5]
   538  		forks, _ := newForks(t)
   539  
   540  		// should be unable to retrieve a block before it is added
   541  		_, ok := forks.GetBlock(blocks[0].BlockID)
   542  		assert.False(t, ok)
   543  
   544  		// add first 3 blocks - should finalize [◄(1) 2]
   545  		err = addValidatedBlockToForks(forks, blocksAddedFirst)
   546  		require.Nil(t, err)
   547  
   548  		// should be able to retrieve all stored blocks
   549  		for _, block := range blocksAddedFirst {
   550  			b, ok := forks.GetBlock(block.BlockID)
   551  			assert.True(t, ok)
   552  			assert.Equal(t, block, b)
   553  		}
   554  
   555  		// add remaining block [◄(4) 5] - should finalize [◄(2) 3] and prune [◄(1) 2]
   556  		require.Nil(t, forks.AddValidatedBlock(remainingBlock))
   557  
   558  		// should be able to retrieve just added block
   559  		b, ok := forks.GetBlock(remainingBlock.BlockID)
   560  		assert.True(t, ok)
   561  		assert.Equal(t, remainingBlock, b)
   562  
   563  		// should be unable to retrieve pruned block
   564  		_, ok = forks.GetBlock(blocksAddedFirst[0].BlockID)
   565  		assert.False(t, ok)
   566  	})
   567  
   568  	// Caution: finalization is driven by QCs. Therefore, we include the QC for block 3
   569  	// in the first batch of blocks that we add. This is analogous to previous test case,
   570  	// except that we are delivering the QC ◄(3) as part of the certified block of view 2
   571  	//   [◄(2) 3] ◄(3)
   572  	// while in the previous sub-test, the QC ◄(3) was delivered as part of block [◄(3) 4]
   573  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   574  		blocksAddedFirst := toCertifiedBlocks(t, blocks[:2]...) // [◄(1) 2] [◄(2) 3] ◄(3)
   575  		remainingBlock := toCertifiedBlock(t, blocks[2])        // [◄(3) 4] ◄(4)
   576  		forks, _ := newForks(t)
   577  
   578  		// should be unable to retrieve a block before it is added
   579  		_, ok := forks.GetBlock(blocks[0].BlockID)
   580  		assert.False(t, ok)
   581  
   582  		// add first blocks - should finalize [◄(1) 2]
   583  		err := forks.AddCertifiedBlock(blocksAddedFirst[0])
   584  		require.Nil(t, err)
   585  		err = forks.AddCertifiedBlock(blocksAddedFirst[1])
   586  		require.Nil(t, err)
   587  
   588  		// should be able to retrieve all stored blocks
   589  		for _, block := range blocksAddedFirst {
   590  			b, ok := forks.GetBlock(block.Block.BlockID)
   591  			assert.True(t, ok)
   592  			assert.Equal(t, block.Block, b)
   593  		}
   594  
   595  		// add remaining block [◄(4) 5] - should finalize [◄(2) 3] and prune [◄(1) 2]
   596  		require.Nil(t, forks.AddCertifiedBlock(remainingBlock))
   597  
   598  		// should be able to retrieve just added block
   599  		b, ok := forks.GetBlock(remainingBlock.Block.BlockID)
   600  		assert.True(t, ok)
   601  		assert.Equal(t, remainingBlock.Block, b)
   602  
   603  		// should be unable to retrieve pruned block
   604  		_, ok = forks.GetBlock(blocksAddedFirst[0].Block.BlockID)
   605  		assert.False(t, ok)
   606  	})
   607  }
   608  
   609  // TestGetBlocksForView tests retrieving blocks for a view (also including double proposals).
   610  //   - Forks receives [◄(1) 2] [◄(2) 4] [◄(2) 4'],
   611  //     where [◄(2) 4'] is a double proposal, because it has the same view as [◄(2) 4]
   612  //
   613  // Expected behaviour:
   614  //   - Forks should store all the blocks
   615  //   - Forks should emit a `OnDoubleProposeDetected` notification
   616  //   - we can retrieve all blocks, including the double proposals
   617  func TestGetBlocksForView(t *testing.T) {
   618  	blocks, err := NewBlockBuilder().
   619  		Add(1, 2).                // [◄(1) 2]
   620  		Add(2, 4).                // [◄(2) 4]
   621  		AddVersioned(2, 4, 0, 1). // [◄(2) 4']
   622  		Blocks()
   623  	require.Nil(t, err)
   624  
   625  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   626  		forks, notifier := newForks(t)
   627  		notifier.On("OnDoubleProposeDetected", blocks[2], blocks[1]).Once()
   628  
   629  		err = addValidatedBlockToForks(forks, blocks)
   630  		require.Nil(t, err)
   631  
   632  		// expect 1 block at view 2
   633  		storedBlocks := forks.GetBlocksForView(2)
   634  		assert.Len(t, storedBlocks, 1)
   635  		assert.Equal(t, blocks[0], storedBlocks[0])
   636  
   637  		// expect 2 blocks at view 4
   638  		storedBlocks = forks.GetBlocksForView(4)
   639  		assert.Len(t, storedBlocks, 2)
   640  		assert.ElementsMatch(t, blocks[1:], storedBlocks)
   641  
   642  		// expect 0 blocks at view 3
   643  		storedBlocks = forks.GetBlocksForView(3)
   644  		assert.Len(t, storedBlocks, 0)
   645  	})
   646  
   647  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   648  		forks, notifier := newForks(t)
   649  		notifier.On("OnDoubleProposeDetected", blocks[2], blocks[1]).Once()
   650  
   651  		err := forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[0]))
   652  		require.Nil(t, err)
   653  		err = forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[1]))
   654  		require.Nil(t, err)
   655  		err = forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[2]))
   656  		require.Nil(t, err)
   657  
   658  		// expect 1 block at view 2
   659  		storedBlocks := forks.GetBlocksForView(2)
   660  		assert.Len(t, storedBlocks, 1)
   661  		assert.Equal(t, blocks[0], storedBlocks[0])
   662  
   663  		// expect 2 blocks at view 4
   664  		storedBlocks = forks.GetBlocksForView(4)
   665  		assert.Len(t, storedBlocks, 2)
   666  		assert.ElementsMatch(t, blocks[1:], storedBlocks)
   667  
   668  		// expect 0 blocks at view 3
   669  		storedBlocks = forks.GetBlocksForView(3)
   670  		assert.Len(t, storedBlocks, 0)
   671  	})
   672  }
   673  
   674  // TestNotifications tests that Forks emits the expected events:
   675  //   - Forks receives [◄(1) 2] [◄(2) 3] [◄(3) 4]
   676  //
   677  // Expected Behaviour:
   678  //   - Each of the ingested blocks should result in an `OnBlockIncorporated` notification
   679  //   - Forks should finalize [◄(1) 2], resulting in a `MakeFinal` event and an `OnFinalizedBlock` event
   680  func TestNotifications(t *testing.T) {
   681  	builder := NewBlockBuilder().
   682  		Add(1, 2).
   683  		Add(2, 3).
   684  		Add(3, 4)
   685  	blocks, err := builder.Blocks()
   686  	require.Nil(t, err)
   687  
   688  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   689  		notifier := &mocks.Consumer{}
   690  		// 4 blocks including the genesis are incorporated
   691  		notifier.On("OnBlockIncorporated", mock.Anything).Return(nil).Times(4)
   692  		notifier.On("OnFinalizedBlock", blocks[0]).Once()
   693  		finalizationCallback := mockmodule.NewFinalizer(t)
   694  		finalizationCallback.On("MakeFinal", blocks[0].BlockID).Return(nil).Once()
   695  
   696  		forks, err := New(builder.GenesisBlock(), finalizationCallback, notifier)
   697  		require.NoError(t, err)
   698  		require.NoError(t, addValidatedBlockToForks(forks, blocks))
   699  	})
   700  
   701  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   702  		notifier := &mocks.Consumer{}
   703  		// 4 blocks including the genesis are incorporated
   704  		notifier.On("OnBlockIncorporated", mock.Anything).Return(nil).Times(4)
   705  		notifier.On("OnFinalizedBlock", blocks[0]).Once()
   706  		finalizationCallback := mockmodule.NewFinalizer(t)
   707  		finalizationCallback.On("MakeFinal", blocks[0].BlockID).Return(nil).Once()
   708  
   709  		forks, err := New(builder.GenesisBlock(), finalizationCallback, notifier)
   710  		require.NoError(t, err)
   711  		require.NoError(t, addCertifiedBlocksToForks(forks, blocks))
   712  	})
   713  }
   714  
   715  // TestFinalizingMultipleBlocks tests that `OnFinalizedBlock` notifications are emitted in correct order
   716  // when there are multiple blocks finalized by adding a _single_ block.
   717  //   - receiving [◄(1) 3] [◄(3) 5] [◄(5) 7] [◄(7) 11] [◄(11) 12] should not finalize any blocks,
   718  //     because there is no 2-chain with the first chain link being a _direct_ 1-chain
   719  //   - adding [◄(12) 22] should finalize up to block [◄(6) 11]
   720  //
   721  // This test verifies the following expected properties:
   722  //  1. Safety under reentrancy:
   723  //     While Forks is single-threaded, there is still the possibility of reentrancy. Specifically, the
   724  //     consumers of our finalization events are served by the goroutine executing Forks. It is conceivable
   725  //     that a consumer might access Forks and query the latest finalization proof. This would be legal, if
   726  //     the component supplying the goroutine to Forks also consumes the notifications. Therefore, for API
   727  //     safety, we require forks to _first update_ its `FinalityProof()` before it emits _any_ events.
   728  //  2. For each finalized block, `finalizationCallback` event is executed _before_ `OnFinalizedBlock` notifications.
   729  //  3. Blocks are finalized in order of increasing height (without skipping any blocks).
   730  func TestFinalizingMultipleBlocks(t *testing.T) {
   731  	builder := NewBlockBuilder().
   732  		Add(1, 3).   // index 0: [◄(1) 2]
   733  		Add(3, 5).   // index 1: [◄(2) 4]
   734  		Add(5, 7).   // index 2: [◄(4) 6]
   735  		Add(7, 11).  // index 3: [◄(6) 11] -- expected to be finalized
   736  		Add(11, 12). // index 4: [◄(11) 12]
   737  		Add(12, 22)  // index 5: [◄(12) 22]
   738  	blocks, err := builder.Blocks()
   739  	require.Nil(t, err)
   740  
   741  	// The Finality Proof should right away point to the _latest_ finalized block. Subsequently emitting
   742  	// Finalization events for lower blocks is fine, because notifications are guaranteed to be
   743  	// _eventually_ arriving. I.e. consumers expect notifications / events to be potentially lagging behind.
   744  	expectedFinalityProof := makeFinalityProof(t, blocks[3], blocks[4], blocks[5].QC)
   745  
   746  	setupForksAndAssertions := func() (*Forks, *mockmodule.Finalizer, *mocks.Consumer) {
   747  		// initialize Forks with custom event consumers so we can check order of emitted events
   748  		notifier := &mocks.Consumer{}
   749  		finalizationCallback := mockmodule.NewFinalizer(t)
   750  		notifier.On("OnBlockIncorporated", mock.Anything).Return(nil)
   751  		forks, err := New(builder.GenesisBlock(), finalizationCallback, notifier)
   752  		require.NoError(t, err)
   753  
   754  		// expecting finalization of [◄(1) 2] [◄(2) 4] [◄(4) 6] [◄(6) 11] in this order
   755  		blocksAwaitingFinalization := toBlockAwaitingFinalization(blocks[:4])
   756  
   757  		finalizationCallback.On("MakeFinal", mock.Anything).Run(func(args mock.Arguments) {
   758  			requireFinalityProof(t, forks, expectedFinalityProof) // Requirement 1: forks should _first update_ its `FinalityProof()` before it emits _any_ events
   759  
   760  			// Requirement 3: finalized in order of increasing height (without skipping any blocks).
   761  			expectedNextFinalizationEvents := blocksAwaitingFinalization[0]
   762  			require.Equal(t, expectedNextFinalizationEvents.Block.BlockID, args[0])
   763  
   764  			// Requirement 2: finalized block, `finalizationCallback` event is executed _before_ `OnFinalizedBlock` notifications.
   765  			// no duplication of events under normal operations expected
   766  			require.False(t, expectedNextFinalizationEvents.MakeFinalCalled)
   767  			require.False(t, expectedNextFinalizationEvents.OnFinalizedBlockEmitted)
   768  			expectedNextFinalizationEvents.MakeFinalCalled = true
   769  		}).Return(nil).Times(4)
   770  
   771  		notifier.On("OnFinalizedBlock", mock.Anything).Run(func(args mock.Arguments) {
   772  			requireFinalityProof(t, forks, expectedFinalityProof) // Requirement 1: forks should _first update_ its `FinalityProof()` before it emits _any_ events
   773  
   774  			// Requirement 3: finalized in order of increasing height (without skipping any blocks).
   775  			expectedNextFinalizationEvents := blocksAwaitingFinalization[0]
   776  			require.Equal(t, expectedNextFinalizationEvents.Block, args[0])
   777  
   778  			// Requirement 2: finalized block, `finalizationCallback` event is executed _before_ `OnFinalizedBlock` notifications.
   779  			// no duplication of events under normal operations expected
   780  			require.True(t, expectedNextFinalizationEvents.MakeFinalCalled)
   781  			require.False(t, expectedNextFinalizationEvents.OnFinalizedBlockEmitted)
   782  			expectedNextFinalizationEvents.OnFinalizedBlockEmitted = true
   783  
   784  			// At this point, `MakeFinal` and `OnFinalizedBlock` have both been emitted for the block, so we are done with it
   785  			blocksAwaitingFinalization = blocksAwaitingFinalization[1:]
   786  		}).Times(4)
   787  
   788  		return forks, finalizationCallback, notifier
   789  	}
   790  
   791  	t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) {
   792  		forks, finalizationCallback, notifier := setupForksAndAssertions()
   793  		err = addValidatedBlockToForks(forks, blocks[:5]) // adding [◄(1) 2] [◄(2) 4] [◄(4) 6] [◄(6) 11] [◄(11) 12]
   794  		require.Nil(t, err)
   795  		requireOnlyGenesisBlockFinalized(t, forks) // finalization should still be at the genesis block
   796  
   797  		require.NoError(t, forks.AddValidatedBlock(blocks[5])) // adding [◄(12) 22] should trigger finalization events
   798  		requireFinalityProof(t, forks, expectedFinalityProof)
   799  		finalizationCallback.AssertExpectations(t)
   800  		notifier.AssertExpectations(t)
   801  	})
   802  
   803  	t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) {
   804  		forks, finalizationCallback, notifier := setupForksAndAssertions()
   805  		// adding [◄(1) 2] [◄(2) 4] [◄(4) 6] [◄(6) 11] ◄(11)
   806  		require.NoError(t, forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[0])))
   807  		require.NoError(t, forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[1])))
   808  		require.NoError(t, forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[2])))
   809  		require.NoError(t, forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[3])))
   810  		require.Nil(t, err)
   811  		requireOnlyGenesisBlockFinalized(t, forks) // finalization should still be at the genesis block
   812  
   813  		// adding certified block [◄(11) 12] ◄(12) should trigger finalization events
   814  		require.NoError(t, forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[4])))
   815  		requireFinalityProof(t, forks, expectedFinalityProof)
   816  		finalizationCallback.AssertExpectations(t)
   817  		notifier.AssertExpectations(t)
   818  	})
   819  }
   820  
   821  //* ************************************* internal functions ************************************* */
   822  
   823  func newForks(t *testing.T) (*Forks, *mocks.Consumer) {
   824  	notifier := mocks.NewConsumer(t)
   825  	notifier.On("OnBlockIncorporated", mock.Anything).Return(nil).Maybe()
   826  	notifier.On("OnFinalizedBlock", mock.Anything).Maybe()
   827  	finalizationCallback := mockmodule.NewFinalizer(t)
   828  	finalizationCallback.On("MakeFinal", mock.Anything).Return(nil).Maybe()
   829  
   830  	genesisBQ := makeGenesis()
   831  
   832  	forks, err := New(genesisBQ, finalizationCallback, notifier)
   833  
   834  	require.Nil(t, err)
   835  	return forks, notifier
   836  }
   837  
   838  // addValidatedBlockToForks adds all the given blocks to Forks, in order.
   839  // If any errors occur, returns the first one.
   840  func addValidatedBlockToForks(forks *Forks, blocks []*model.Block) error {
   841  	for _, block := range blocks {
   842  		err := forks.AddValidatedBlock(block)
   843  		if err != nil {
   844  			return fmt.Errorf("test failed to add block for view %d: %w", block.View, err)
   845  		}
   846  	}
   847  	return nil
   848  }
   849  
   850  // addCertifiedBlocksToForks iterates over all blocks, caches them locally in a map,
   851  // constructs certified blocks whenever possible and adds the certified blocks to forks,
   852  // Note: if blocks is a single fork, the _last block_ in the slice will not be added,
   853  //
   854  //	because there is no qc for it
   855  //
   856  // If any errors occur, returns the first one.
   857  func addCertifiedBlocksToForks(forks *Forks, blocks []*model.Block) error {
   858  	uncertifiedBlocks := make(map[flow.Identifier]*model.Block)
   859  	for _, b := range blocks {
   860  		uncertifiedBlocks[b.BlockID] = b
   861  		parentID := b.QC.BlockID
   862  		parent, found := uncertifiedBlocks[parentID]
   863  		if !found {
   864  			continue
   865  		}
   866  		delete(uncertifiedBlocks, parentID)
   867  
   868  		certParent, err := model.NewCertifiedBlock(parent, b.QC)
   869  		if err != nil {
   870  			return fmt.Errorf("test failed to creat certified block for view %d: %w", certParent.Block.View, err)
   871  		}
   872  		err = forks.AddCertifiedBlock(&certParent)
   873  		if err != nil {
   874  			return fmt.Errorf("test failed to add certified block for view %d: %w", certParent.Block.View, err)
   875  		}
   876  	}
   877  
   878  	return nil
   879  }
   880  
   881  // requireLatestFinalizedBlock asserts that the latest finalized block has the given view and qc view.
   882  func requireLatestFinalizedBlock(t *testing.T, forks *Forks, expectedFinalized *model.Block) {
   883  	require.Equal(t, expectedFinalized, forks.FinalizedBlock(), "finalized block is not as expected")
   884  	require.Equal(t, forks.FinalizedView(), expectedFinalized.View, "FinalizedView returned wrong value")
   885  }
   886  
   887  // requireOnlyGenesisBlockFinalized asserts that no blocks have been finalized beyond the genesis block.
   888  // Caution: does not inspect output of `forks.FinalityProof()`
   889  func requireOnlyGenesisBlockFinalized(t *testing.T, forks *Forks) {
   890  	genesis := makeGenesis()
   891  	require.Equal(t, forks.FinalizedBlock(), genesis.Block, "finalized block is not the genesis block")
   892  	require.Equal(t, forks.FinalizedBlock().View, genesis.Block.View)
   893  	require.Equal(t, forks.FinalizedBlock().View, genesis.CertifyingQC.View)
   894  	require.Equal(t, forks.FinalizedView(), genesis.Block.View, "finalized block has wrong qc")
   895  
   896  	finalityProof, isKnown := forks.FinalityProof()
   897  	require.Nil(t, finalityProof, "expecting finality proof to be nil for genesis block at initialization")
   898  	require.False(t, isKnown, "no finality proof should be known for genesis block at initialization")
   899  }
   900  
   901  // requireNoBlocksFinalized asserts that no blocks have been finalized (genesis is latest finalized block).
   902  func requireFinalityProof(t *testing.T, forks *Forks, expectedFinalityProof *hotstuff.FinalityProof) {
   903  	finalityProof, isKnown := forks.FinalityProof()
   904  	require.True(t, isKnown)
   905  	require.Equal(t, expectedFinalityProof, finalityProof)
   906  	require.Equal(t, forks.FinalizedBlock(), expectedFinalityProof.Block)
   907  	require.Equal(t, forks.FinalizedView(), expectedFinalityProof.Block.View)
   908  }
   909  
   910  // toCertifiedBlock generates a QC for the given block and returns their combination as a certified block
   911  func toCertifiedBlock(t *testing.T, block *model.Block) *model.CertifiedBlock {
   912  	qc := &flow.QuorumCertificate{
   913  		View:    block.View,
   914  		BlockID: block.BlockID,
   915  	}
   916  	cb, err := model.NewCertifiedBlock(block, qc)
   917  	require.Nil(t, err)
   918  	return &cb
   919  }
   920  
   921  // toCertifiedBlocks generates a QC for the given block and returns their combination as a certified blocks
   922  func toCertifiedBlocks(t *testing.T, blocks ...*model.Block) []*model.CertifiedBlock {
   923  	certBlocks := make([]*model.CertifiedBlock, 0, len(blocks))
   924  	for _, b := range blocks {
   925  		certBlocks = append(certBlocks, toCertifiedBlock(t, b))
   926  	}
   927  	return certBlocks
   928  }
   929  
   930  func makeFinalityProof(t *testing.T, block *model.Block, directChild *model.Block, qcCertifyingChild *flow.QuorumCertificate) *hotstuff.FinalityProof {
   931  	c, err := model.NewCertifiedBlock(directChild, qcCertifyingChild) // certified child of FinalizedBlock
   932  	require.NoError(t, err)
   933  	return &hotstuff.FinalityProof{Block: block, CertifiedChild: c}
   934  }
   935  
   936  // blockAwaitingFinalization is intended for tracking finalization events and their order for a specific block
   937  type blockAwaitingFinalization struct {
   938  	Block                   *model.Block
   939  	MakeFinalCalled         bool // indicates whether `Finalizer.MakeFinal` was called
   940  	OnFinalizedBlockEmitted bool // indicates whether `OnFinalizedBlockCalled` notification was emitted
   941  }
   942  
   943  // toBlockAwaitingFinalization creates a `blockAwaitingFinalization` tracker for each input block
   944  func toBlockAwaitingFinalization(blocks []*model.Block) []*blockAwaitingFinalization {
   945  	trackers := make([]*blockAwaitingFinalization, 0, len(blocks))
   946  	for _, b := range blocks {
   947  		tracker := &blockAwaitingFinalization{b, false, false}
   948  		trackers = append(trackers, tracker)
   949  	}
   950  	return trackers
   951  }