github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/state/protocol/badger/snapshot_test.go (about)

     1  package badger_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"math/rand"
     7  	"testing"
     8  
     9  	"github.com/dgraph-io/badger/v2"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/onflow/flow-go/model/flow"
    14  	"github.com/onflow/flow-go/model/flow/factory"
    15  	"github.com/onflow/flow-go/model/flow/filter"
    16  	"github.com/onflow/flow-go/module/signature"
    17  	statepkg "github.com/onflow/flow-go/state"
    18  	"github.com/onflow/flow-go/state/protocol"
    19  	bprotocol "github.com/onflow/flow-go/state/protocol/badger"
    20  	"github.com/onflow/flow-go/state/protocol/inmem"
    21  	"github.com/onflow/flow-go/state/protocol/prg"
    22  	"github.com/onflow/flow-go/state/protocol/protocol_state/kvstore"
    23  	"github.com/onflow/flow-go/state/protocol/util"
    24  	"github.com/onflow/flow-go/storage"
    25  	"github.com/onflow/flow-go/utils/unittest"
    26  )
    27  
    28  // TestUnknownReferenceBlock tests queries for snapshots which should be unknown.
    29  // We use this fixture:
    30  //   - Root height: 100
    31  //   - Heights [100, 110] are finalized
    32  //   - Height 111 is unfinalized
    33  func TestUnknownReferenceBlock(t *testing.T) {
    34  	rootHeight := uint64(100)
    35  	participants := unittest.IdentityListFixture(5, unittest.WithAllRoles())
    36  	rootSnapshot := unittest.RootSnapshotFixture(participants, func(block *flow.Block) {
    37  		block.Header.Height = rootHeight
    38  	})
    39  	rootProtocolStateID := getRootProtocolStateID(t, rootSnapshot)
    40  
    41  	util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
    42  		// build some finalized non-root blocks (heights 101-110)
    43  		head := unittest.BlockWithParentFixture(rootSnapshot.Encodable().Head)
    44  		head.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
    45  		buildFinalizedBlock(t, state, head)
    46  
    47  		const nBlocks = 10
    48  		for i := 1; i < nBlocks; i++ {
    49  			next := unittest.BlockWithParentProtocolState(head)
    50  			buildFinalizedBlock(t, state, next)
    51  			head = next
    52  		}
    53  		// build an unfinalized block (height 111)
    54  		buildBlock(t, state, unittest.BlockWithParentProtocolState(head))
    55  
    56  		finalizedHeader, err := state.Final().Head()
    57  		require.NoError(t, err)
    58  
    59  		t.Run("below root height", func(t *testing.T) {
    60  			_, err := state.AtHeight(rootHeight - 1).Head()
    61  			assert.ErrorIs(t, err, statepkg.ErrUnknownSnapshotReference)
    62  		})
    63  		t.Run("above finalized height, non-existent height", func(t *testing.T) {
    64  			_, err := state.AtHeight(finalizedHeader.Height + 100).Head()
    65  			assert.ErrorIs(t, err, statepkg.ErrUnknownSnapshotReference)
    66  		})
    67  		t.Run("above finalized height, existent height", func(t *testing.T) {
    68  			_, err := state.AtHeight(finalizedHeader.Height + 1).Head()
    69  			assert.ErrorIs(t, err, statepkg.ErrUnknownSnapshotReference)
    70  		})
    71  		t.Run("unknown block ID", func(t *testing.T) {
    72  			_, err := state.AtBlockID(unittest.IdentifierFixture()).Head()
    73  			assert.ErrorIs(t, err, statepkg.ErrUnknownSnapshotReference)
    74  		})
    75  	})
    76  }
    77  
    78  func TestHead(t *testing.T) {
    79  	participants := unittest.IdentityListFixture(5, unittest.WithAllRoles())
    80  	rootSnapshot := unittest.RootSnapshotFixture(participants)
    81  	head, err := rootSnapshot.Head()
    82  	require.NoError(t, err)
    83  	util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) {
    84  
    85  		t.Run("works with block number", func(t *testing.T) {
    86  			retrieved, err := state.AtHeight(head.Height).Head()
    87  			require.NoError(t, err)
    88  			require.Equal(t, head.ID(), retrieved.ID())
    89  		})
    90  
    91  		t.Run("works with block id", func(t *testing.T) {
    92  			retrieved, err := state.AtBlockID(head.ID()).Head()
    93  			require.NoError(t, err)
    94  			require.Equal(t, head.ID(), retrieved.ID())
    95  		})
    96  
    97  		t.Run("works with finalized block", func(t *testing.T) {
    98  			retrieved, err := state.Final().Head()
    99  			require.NoError(t, err)
   100  			require.Equal(t, head.ID(), retrieved.ID())
   101  		})
   102  	})
   103  }
   104  
   105  // TestSnapshot_Params tests retrieving global protocol state parameters from
   106  // a protocol state snapshot.
   107  func TestSnapshot_Params(t *testing.T) {
   108  	participants := unittest.IdentityListFixture(5, unittest.WithAllRoles())
   109  	rootSnapshot := unittest.RootSnapshotFixture(participants)
   110  	rootProtocolStateID := getRootProtocolStateID(t, rootSnapshot)
   111  
   112  	expectedChainID := rootSnapshot.Params().ChainID()
   113  	expectedSporkID := rootSnapshot.Params().SporkID()
   114  	expectedProtocolVersion := rootSnapshot.Params().ProtocolVersion()
   115  
   116  	rootHeader, err := rootSnapshot.Head()
   117  	require.NoError(t, err)
   118  
   119  	util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   120  		// build some non-root blocks
   121  		head := rootHeader
   122  		const nBlocks = 10
   123  		for i := 0; i < nBlocks; i++ {
   124  			next := unittest.BlockWithParentFixture(head)
   125  			next.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   126  			buildFinalizedBlock(t, state, next)
   127  			head = next.Header
   128  		}
   129  
   130  		// test params from both root, final, and in between
   131  		snapshots := []protocol.Snapshot{
   132  			state.AtHeight(0),
   133  			state.AtHeight(uint64(rand.Intn(nBlocks))),
   134  			state.Final(),
   135  		}
   136  		for _, snapshot := range snapshots {
   137  			t.Run("should be able to get chain ID from snapshot", func(t *testing.T) {
   138  				chainID := snapshot.Params().ChainID()
   139  				assert.Equal(t, expectedChainID, chainID)
   140  			})
   141  			t.Run("should be able to get spork ID from snapshot", func(t *testing.T) {
   142  				sporkID := snapshot.Params().SporkID()
   143  				assert.Equal(t, expectedSporkID, sporkID)
   144  			})
   145  			t.Run("should be able to get protocol version from snapshot", func(t *testing.T) {
   146  				protocolVersion := snapshot.Params().ProtocolVersion()
   147  				assert.Equal(t, expectedProtocolVersion, protocolVersion)
   148  			})
   149  		}
   150  	})
   151  }
   152  
   153  // TestSnapshot_Descendants builds a sample chain with next structure:
   154  //
   155  //	A (finalized) <- B <- C <- D <- E <- F
   156  //	              <- G <- H <- I <- J
   157  //
   158  // snapshot.Descendants has to return [B, C, D, E, F, G, H, I, J].
   159  func TestSnapshot_Descendants(t *testing.T) {
   160  	participants := unittest.IdentityListFixture(5, unittest.WithAllRoles())
   161  	rootSnapshot := unittest.RootSnapshotFixture(participants)
   162  	rootProtocolStateID := getRootProtocolStateID(t, rootSnapshot)
   163  	head, err := rootSnapshot.Head()
   164  	require.NoError(t, err)
   165  	util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   166  		var expectedBlocks []flow.Identifier
   167  		for i := 5; i > 3; i-- {
   168  			parent := head
   169  			for n := 0; n < i; n++ {
   170  				block := unittest.BlockWithParentFixture(parent)
   171  				block.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   172  				err := state.Extend(context.Background(), block)
   173  				require.NoError(t, err)
   174  				expectedBlocks = append(expectedBlocks, block.ID())
   175  				parent = block.Header
   176  			}
   177  		}
   178  
   179  		pendingBlocks, err := state.AtBlockID(head.ID()).Descendants()
   180  		require.NoError(t, err)
   181  		require.ElementsMatch(t, expectedBlocks, pendingBlocks)
   182  	})
   183  }
   184  
   185  func TestIdentities(t *testing.T) {
   186  	identities := unittest.IdentityListFixture(5, unittest.WithAllRoles())
   187  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   188  	util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) {
   189  
   190  		t.Run("no filter", func(t *testing.T) {
   191  			actual, err := state.Final().Identities(filter.Any)
   192  			require.NoError(t, err)
   193  			assert.ElementsMatch(t, identities, actual)
   194  		})
   195  
   196  		t.Run("single identity", func(t *testing.T) {
   197  			expected := identities[rand.Intn(len(identities))]
   198  			actual, err := state.Final().Identity(expected.NodeID)
   199  			require.NoError(t, err)
   200  			assert.Equal(t, expected, actual)
   201  		})
   202  
   203  		t.Run("filtered", func(t *testing.T) {
   204  			sample, err := identities.SamplePct(0.1)
   205  			require.NoError(t, err)
   206  			filters := []flow.IdentityFilter[flow.Identity]{
   207  				filter.HasRole[flow.Identity](flow.RoleCollection),
   208  				filter.HasNodeID[flow.Identity](sample.NodeIDs()...),
   209  				filter.HasInitialWeight[flow.Identity](true),
   210  				filter.IsValidCurrentEpochParticipant,
   211  			}
   212  
   213  			for _, filterfunc := range filters {
   214  				expected := identities.Filter(filterfunc)
   215  				actual, err := state.Final().Identities(filterfunc)
   216  				require.NoError(t, err)
   217  				assert.ElementsMatch(t, expected, actual)
   218  			}
   219  		})
   220  	})
   221  }
   222  
   223  func TestClusters(t *testing.T) {
   224  	nClusters := 3
   225  	nCollectors := 7
   226  
   227  	collectors := unittest.IdentityListFixture(nCollectors, unittest.WithRole(flow.RoleCollection))
   228  	identities := append(unittest.IdentityListFixture(4, unittest.WithAllRolesExcept(flow.RoleCollection)), collectors...)
   229  
   230  	root, result, seal := unittest.BootstrapFixture(identities)
   231  	qc := unittest.QuorumCertificateFixture(unittest.QCWithRootBlockID(root.ID()))
   232  	setup := result.ServiceEvents[0].Event.(*flow.EpochSetup)
   233  	commit := result.ServiceEvents[1].Event.(*flow.EpochCommit)
   234  	setup.Assignments = unittest.ClusterAssignment(uint(nClusters), collectors.ToSkeleton())
   235  	clusterQCs := unittest.QuorumCertificatesFromAssignments(setup.Assignments)
   236  	commit.ClusterQCs = flow.ClusterQCVoteDatasFromQCs(clusterQCs)
   237  	seal.ResultID = result.ID()
   238  	root.Payload.ProtocolStateID = kvstore.NewDefaultKVStore(
   239  		inmem.ProtocolStateFromEpochServiceEvents(setup, commit).ID()).ID()
   240  
   241  	rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, qc)
   242  	require.NoError(t, err)
   243  
   244  	util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) {
   245  		expectedClusters, err := factory.NewClusterList(setup.Assignments, collectors.ToSkeleton())
   246  		require.NoError(t, err)
   247  		actualClusters, err := state.Final().Epochs().Current().Clustering()
   248  		require.NoError(t, err)
   249  
   250  		require.Equal(t, nClusters, len(expectedClusters))
   251  		require.Equal(t, len(expectedClusters), len(actualClusters))
   252  
   253  		for i := 0; i < nClusters; i++ {
   254  			expected := expectedClusters[i]
   255  			actual := actualClusters[i]
   256  
   257  			assert.Equal(t, len(expected), len(actual))
   258  			assert.Equal(t, expected.ID(), actual.ID())
   259  		}
   260  	})
   261  }
   262  
   263  // TestSealingSegment tests querying sealing segment with respect to various snapshots.
   264  //
   265  // For each valid sealing segment, we also test bootstrapping with this sealing segment.
   266  func TestSealingSegment(t *testing.T) {
   267  	identities := unittest.CompleteIdentitySet()
   268  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   269  	rootProtocolStateID := getRootProtocolStateID(t, rootSnapshot)
   270  	head, err := rootSnapshot.Head()
   271  	require.NoError(t, err)
   272  
   273  	t.Run("root sealing segment", func(t *testing.T) {
   274  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   275  			expected, err := rootSnapshot.SealingSegment()
   276  			require.NoError(t, err)
   277  			actual, err := state.AtBlockID(head.ID()).SealingSegment()
   278  			require.NoError(t, err)
   279  
   280  			assert.Len(t, actual.ExecutionResults, 1)
   281  			assert.Len(t, actual.Blocks, 1)
   282  			assert.Empty(t, actual.ExtraBlocks)
   283  			unittest.AssertEqualBlocksLenAndOrder(t, expected.Blocks, actual.Blocks)
   284  
   285  			assertSealingSegmentBlocksQueryableAfterBootstrap(t, state.AtBlockID(head.ID()))
   286  		})
   287  	})
   288  
   289  	// test sealing segment for non-root segment where the latest seal is the
   290  	// root seal, but the segment contains more than the root block.
   291  	// ROOT <- B1
   292  	// Expected sealing segment: [ROOT, B1], extra blocks: []
   293  	t.Run("non-root with root seal as latest seal", func(t *testing.T) {
   294  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   295  			// build an extra block on top of root
   296  			block1 := unittest.BlockWithParentFixture(head)
   297  			block1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   298  			buildFinalizedBlock(t, state, block1)
   299  
   300  			segment, err := state.AtBlockID(block1.ID()).SealingSegment()
   301  			require.NoError(t, err)
   302  
   303  			// build a valid child B2 to ensure we have a QC
   304  			buildBlock(t, state, unittest.BlockWithParentProtocolState(block1))
   305  
   306  			// sealing segment should contain B1 and B2
   307  			// B2 is reference of snapshot, B1 is latest sealed
   308  			unittest.AssertEqualBlocksLenAndOrder(t, []*flow.Block{rootSnapshot.Encodable().SealingSegment.Sealed(), block1}, segment.Blocks)
   309  			assert.Len(t, segment.ExecutionResults, 1)
   310  			assert.Empty(t, segment.ExtraBlocks)
   311  			assertSealingSegmentBlocksQueryableAfterBootstrap(t, state.AtBlockID(block1.ID()))
   312  		})
   313  	})
   314  
   315  	// test sealing segment for non-root segment with simple sealing structure
   316  	// (no blocks in between reference block and latest sealed)
   317  	// ROOT <- B1 <- B2(R1) <- B3(S1)
   318  	// Expected sealing segment: [B1, B2, B3], extra blocks: [ROOT]
   319  	t.Run("non-root", func(t *testing.T) {
   320  		util.RunWithFullProtocolStateAndMutator(t, rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState, mutableState protocol.MutableProtocolState) {
   321  			// build a block to seal
   322  			block1 := unittest.BlockWithParentFixture(head)
   323  			block1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   324  			buildFinalizedBlock(t, state, block1)
   325  
   326  			receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   327  
   328  			block2 := unittest.BlockWithParentFixture(block1.Header)
   329  			block2.SetPayload(unittest.PayloadFixture(
   330  				unittest.WithReceipts(receipt1),
   331  				unittest.WithProtocolStateID(rootProtocolStateID),
   332  			))
   333  			buildFinalizedBlock(t, state, block2)
   334  
   335  			// build a block sealing block1
   336  			block3 := unittest.BlockWithParentProtocolState(block2)
   337  
   338  			seals := []*flow.Seal{seal1}
   339  			block3.SetPayload(flow.Payload{
   340  				Seals:           seals,
   341  				ProtocolStateID: calculateExpectedStateId(t, mutableState)(block3.Header, seals),
   342  			})
   343  			buildFinalizedBlock(t, state, block3)
   344  
   345  			segment, err := state.AtBlockID(block3.ID()).SealingSegment()
   346  			require.NoError(t, err)
   347  
   348  			require.Len(t, segment.ExtraBlocks, 1)
   349  			assert.Equal(t, segment.ExtraBlocks[0].Header.Height, head.Height)
   350  
   351  			// build a valid child B3 to ensure we have a QC
   352  			buildBlock(t, state, unittest.BlockWithParentProtocolState(block3))
   353  
   354  			// sealing segment should contain B1, B2, B3
   355  			// B3 is reference of snapshot, B1 is latest sealed
   356  			unittest.AssertEqualBlocksLenAndOrder(t, []*flow.Block{block1, block2, block3}, segment.Blocks)
   357  			assert.Len(t, segment.ExecutionResults, 1)
   358  			assertSealingSegmentBlocksQueryableAfterBootstrap(t, state.AtBlockID(block3.ID()))
   359  		})
   360  	})
   361  
   362  	// test sealing segment for sealing segment with a large number of blocks
   363  	// between the reference block and latest sealed
   364  	// ROOT <- B1 <- .... <- BN(S1)
   365  	// Expected sealing segment: [B1, ..., BN], extra blocks: [ROOT]
   366  	t.Run("long sealing segment", func(t *testing.T) {
   367  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   368  
   369  			// build a block to seal
   370  			block1 := unittest.BlockWithParentFixture(head)
   371  			block1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   372  			buildFinalizedBlock(t, state, block1)
   373  
   374  			receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   375  
   376  			parent := block1
   377  			// build a large chain of intermediary blocks
   378  			for i := 0; i < 100; i++ {
   379  				next := unittest.BlockWithParentProtocolState(parent)
   380  				if i == 0 {
   381  					// Repetitions of the same receipt in one fork would be a protocol violation.
   382  					// Hence, we include the result only once in the direct child of B1.
   383  					next.SetPayload(unittest.PayloadFixture(
   384  						unittest.WithReceipts(receipt1),
   385  						unittest.WithProtocolStateID(parent.Payload.ProtocolStateID),
   386  					))
   387  				}
   388  				buildFinalizedBlock(t, state, next)
   389  				parent = next
   390  			}
   391  
   392  			// build the block sealing block 1
   393  			blockN := unittest.BlockWithParentFixture(parent.Header)
   394  			blockN.SetPayload(unittest.PayloadFixture(
   395  				unittest.WithSeals(seal1),
   396  				unittest.WithProtocolStateID(rootProtocolStateID),
   397  			))
   398  			buildFinalizedBlock(t, state, blockN)
   399  
   400  			segment, err := state.AtBlockID(blockN.ID()).SealingSegment()
   401  			require.NoError(t, err)
   402  
   403  			assert.Len(t, segment.ExecutionResults, 1)
   404  			// sealing segment should cover range [B1, BN]
   405  			assert.Len(t, segment.Blocks, 102)
   406  			assert.Len(t, segment.ExtraBlocks, 1)
   407  			assert.Equal(t, segment.ExtraBlocks[0].Header.Height, head.Height)
   408  			// first and last blocks should be B1, BN
   409  			assert.Equal(t, block1.ID(), segment.Blocks[0].ID())
   410  			assert.Equal(t, blockN.ID(), segment.Blocks[101].ID())
   411  			assertSealingSegmentBlocksQueryableAfterBootstrap(t, state.AtBlockID(blockN.ID()))
   412  		})
   413  	})
   414  
   415  	// test sealing segment where the segment blocks contain seals for
   416  	// ancestor blocks prior to the sealing segment
   417  	// ROOT <- B1 <- B2(R1) <- B3 <- B4(R2, S1) <- B5 <- B6(S2)
   418  	// Expected sealing segment: [B2, B3, B4], Extra blocks: [ROOT, B1]
   419  	t.Run("overlapping sealing segment", func(t *testing.T) {
   420  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   421  
   422  			block1 := unittest.BlockWithParentFixture(head)
   423  			block1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   424  			buildFinalizedBlock(t, state, block1)
   425  			receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   426  
   427  			block2 := unittest.BlockWithParentFixture(block1.Header)
   428  			block2.SetPayload(unittest.PayloadFixture(
   429  				unittest.WithReceipts(receipt1),
   430  				unittest.WithProtocolStateID(rootProtocolStateID),
   431  			))
   432  			buildFinalizedBlock(t, state, block2)
   433  
   434  			receipt2, seal2 := unittest.ReceiptAndSealForBlock(block2)
   435  
   436  			block3 := unittest.BlockWithParentProtocolState(block2)
   437  			buildFinalizedBlock(t, state, block3)
   438  
   439  			block4 := unittest.BlockWithParentFixture(block3.Header)
   440  			block4.SetPayload(unittest.PayloadFixture(
   441  				unittest.WithReceipts(receipt2),
   442  				unittest.WithSeals(seal1),
   443  				unittest.WithProtocolStateID(rootProtocolStateID),
   444  			))
   445  			buildFinalizedBlock(t, state, block4)
   446  
   447  			block5 := unittest.BlockWithParentProtocolState(block4)
   448  			buildFinalizedBlock(t, state, block5)
   449  
   450  			block6 := unittest.BlockWithParentFixture(block5.Header)
   451  			block6.SetPayload(unittest.PayloadFixture(
   452  				unittest.WithSeals(seal2),
   453  				unittest.WithProtocolStateID(rootProtocolStateID),
   454  			))
   455  			buildFinalizedBlock(t, state, block6)
   456  
   457  			segment, err := state.AtBlockID(block6.ID()).SealingSegment()
   458  			require.NoError(t, err)
   459  
   460  			// build a valid child to ensure we have a QC
   461  			buildBlock(t, state, unittest.BlockWithParentProtocolState(block6))
   462  
   463  			// sealing segment should be [B2, B3, B4, B5, B6]
   464  			require.Len(t, segment.Blocks, 5)
   465  			unittest.AssertEqualBlocksLenAndOrder(t, []*flow.Block{block2, block3, block4, block5, block6}, segment.Blocks)
   466  			unittest.AssertEqualBlocksLenAndOrder(t, []*flow.Block{block1}, segment.ExtraBlocks[1:])
   467  			require.Len(t, segment.ExecutionResults, 1)
   468  
   469  			assertSealingSegmentBlocksQueryableAfterBootstrap(t, state.AtBlockID(block6.ID()))
   470  		})
   471  	})
   472  
   473  	// test sealing segment where you have a chain that is 5 blocks long and the block 5 has a seal for block 2
   474  	// block 2 also contains a receipt but no result.
   475  	// ROOT -> B1(Result_A, Receipt_A_1) -> B2(Result_B, Receipt_B, Receipt_A_2) -> B3(Receipt_C, Result_C) -> B4 -> B5(Seal_C)
   476  	// the segment for B5 should be `[B2,B3,B4,B5] + [Result_A]`
   477  	t.Run("sealing segment with 4 blocks and 1 execution result decoupled", func(t *testing.T) {
   478  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   479  			// simulate scenario where execution result is missing from block payload
   480  			// SealingSegment() should get result from results db and store it on ExecutionReceipts
   481  			// field on SealingSegment
   482  			resultA := unittest.ExecutionResultFixture()
   483  			receiptA1 := unittest.ExecutionReceiptFixture(unittest.WithResult(resultA))
   484  			receiptA2 := unittest.ExecutionReceiptFixture(unittest.WithResult(resultA))
   485  
   486  			// receipt b also contains result b
   487  			receiptB := unittest.ExecutionReceiptFixture()
   488  
   489  			block1 := unittest.BlockWithParentFixture(head)
   490  			block1.SetPayload(unittest.PayloadFixture(
   491  				unittest.WithReceipts(receiptA1),
   492  				unittest.WithProtocolStateID(rootProtocolStateID),
   493  			))
   494  
   495  			block2 := unittest.BlockWithParentFixture(block1.Header)
   496  			block2.SetPayload(unittest.PayloadFixture(
   497  				unittest.WithReceipts(receiptB),
   498  				unittest.WithReceiptsAndNoResults(receiptA2),
   499  				unittest.WithProtocolStateID(rootProtocolStateID),
   500  			))
   501  			receiptC, sealC := unittest.ReceiptAndSealForBlock(block2)
   502  
   503  			block3 := unittest.BlockWithParentFixture(block2.Header)
   504  			block3.SetPayload(unittest.PayloadFixture(
   505  				unittest.WithReceipts(receiptC),
   506  				unittest.WithProtocolStateID(rootProtocolStateID),
   507  			))
   508  
   509  			block4 := unittest.BlockWithParentProtocolState(block3)
   510  
   511  			block5 := unittest.BlockWithParentFixture(block4.Header)
   512  			block5.SetPayload(unittest.PayloadFixture(
   513  				unittest.WithSeals(sealC),
   514  				unittest.WithProtocolStateID(rootProtocolStateID),
   515  			))
   516  
   517  			buildFinalizedBlock(t, state, block1)
   518  			buildFinalizedBlock(t, state, block2)
   519  			buildFinalizedBlock(t, state, block3)
   520  			buildFinalizedBlock(t, state, block4)
   521  			buildFinalizedBlock(t, state, block5)
   522  
   523  			segment, err := state.AtBlockID(block5.ID()).SealingSegment()
   524  			require.NoError(t, err)
   525  
   526  			// build a valid child to ensure we have a QC
   527  			buildBlock(t, state, unittest.BlockWithParentProtocolState(block5))
   528  
   529  			require.Len(t, segment.Blocks, 4)
   530  			unittest.AssertEqualBlocksLenAndOrder(t, []*flow.Block{block2, block3, block4, block5}, segment.Blocks)
   531  			require.Contains(t, segment.ExecutionResults, resultA)
   532  			require.Len(t, segment.ExecutionResults, 2)
   533  
   534  			assertSealingSegmentBlocksQueryableAfterBootstrap(t, state.AtBlockID(block5.ID()))
   535  		})
   536  	})
   537  
   538  	// test sealing segment where you have a chain that is 5 blocks long and the block 5 has a seal for block 2.
   539  	// even though block2 & block3 both reference ResultA it should be added to the segment execution results list once.
   540  	// block3 also references ResultB, so it should exist in the segment execution results as well.
   541  	// root -> B1[Result_A, Receipt_A_1] -> B2[Result_B, Receipt_B, Receipt_A_2] -> B3[Receipt_B_2, Receipt_for_seal, Receipt_A_3] -> B4 -> B5 (Seal_B2)
   542  	t.Run("sealing segment with 4 blocks and 2 execution result decoupled", func(t *testing.T) {
   543  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   544  			// simulate scenario where execution result is missing from block payload
   545  			// SealingSegment() should get result from results db and store it on ExecutionReceipts
   546  			// field on SealingSegment
   547  			resultA := unittest.ExecutionResultFixture()
   548  
   549  			// 3 execution receipts for Result_A
   550  			receiptA1 := unittest.ExecutionReceiptFixture(unittest.WithResult(resultA))
   551  			receiptA2 := unittest.ExecutionReceiptFixture(unittest.WithResult(resultA))
   552  			receiptA3 := unittest.ExecutionReceiptFixture(unittest.WithResult(resultA))
   553  
   554  			// receipt b also contains result b
   555  			receiptB := unittest.ExecutionReceiptFixture()
   556  			// get second receipt for Result_B, now we have 2 receipts for a single execution result
   557  			receiptB2 := unittest.ExecutionReceiptFixture(unittest.WithResult(&receiptB.ExecutionResult))
   558  
   559  			block1 := unittest.BlockWithParentFixture(head)
   560  			block1.SetPayload(unittest.PayloadFixture(
   561  				unittest.WithReceipts(receiptA1),
   562  				unittest.WithProtocolStateID(rootProtocolStateID),
   563  			))
   564  
   565  			block2 := unittest.BlockWithParentFixture(block1.Header)
   566  			block2.SetPayload(unittest.PayloadFixture(
   567  				unittest.WithReceipts(receiptB),
   568  				unittest.WithReceiptsAndNoResults(receiptA2),
   569  				unittest.WithProtocolStateID(rootProtocolStateID),
   570  			))
   571  
   572  			receiptForSeal, seal := unittest.ReceiptAndSealForBlock(block2)
   573  
   574  			block3 := unittest.BlockWithParentFixture(block2.Header)
   575  			block3.SetPayload(unittest.PayloadFixture(
   576  				unittest.WithReceipts(receiptForSeal),
   577  				unittest.WithReceiptsAndNoResults(receiptB2, receiptA3),
   578  				unittest.WithProtocolStateID(rootProtocolStateID),
   579  			))
   580  
   581  			block4 := unittest.BlockWithParentProtocolState(block3)
   582  
   583  			block5 := unittest.BlockWithParentFixture(block4.Header)
   584  			block5.SetPayload(unittest.PayloadFixture(
   585  				unittest.WithSeals(seal),
   586  				unittest.WithProtocolStateID(rootProtocolStateID),
   587  			))
   588  
   589  			buildFinalizedBlock(t, state, block1)
   590  			buildFinalizedBlock(t, state, block2)
   591  			buildFinalizedBlock(t, state, block3)
   592  			buildFinalizedBlock(t, state, block4)
   593  			buildFinalizedBlock(t, state, block5)
   594  
   595  			segment, err := state.AtBlockID(block5.ID()).SealingSegment()
   596  			require.NoError(t, err)
   597  
   598  			// build a valid child to ensure we have a QC
   599  			buildBlock(t, state, unittest.BlockWithParentProtocolState(block5))
   600  
   601  			require.Len(t, segment.Blocks, 4)
   602  			unittest.AssertEqualBlocksLenAndOrder(t, []*flow.Block{block2, block3, block4, block5}, segment.Blocks)
   603  			require.Contains(t, segment.ExecutionResults, resultA)
   604  			// ResultA should only be added once even though it is referenced in 2 different blocks
   605  			require.Len(t, segment.ExecutionResults, 2)
   606  
   607  			assertSealingSegmentBlocksQueryableAfterBootstrap(t, state.AtBlockID(block5.ID()))
   608  		})
   609  	})
   610  
   611  	// Test the case where the reference block of the snapshot contains no seal.
   612  	// We should consider the latest seal in a prior block.
   613  	// ROOT <- B1 <- B2(R1) <- B3 <- B4(S1) <- B5
   614  	// Expected sealing segment: [B1, B2, B3, B4, B5], Extra blocks: [ROOT]
   615  	t.Run("sealing segment where highest block in segment does not seal lowest", func(t *testing.T) {
   616  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   617  			// build a block to seal
   618  			block1 := unittest.BlockWithParentFixture(head)
   619  			block1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   620  			buildFinalizedBlock(t, state, block1)
   621  
   622  			// build a block sealing block1
   623  			block2 := unittest.BlockWithParentFixture(block1.Header)
   624  			receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   625  			block2.SetPayload(unittest.PayloadFixture(
   626  				unittest.WithReceipts(receipt1),
   627  				unittest.WithProtocolStateID(rootProtocolStateID),
   628  			))
   629  			buildFinalizedBlock(t, state, block2)
   630  
   631  			block3 := unittest.BlockWithParentProtocolState(block2)
   632  			buildFinalizedBlock(t, state, block3)
   633  
   634  			block4 := unittest.BlockWithParentFixture(block3.Header)
   635  			block4.SetPayload(unittest.PayloadFixture(
   636  				unittest.WithSeals(seal1),
   637  				unittest.WithProtocolStateID(rootProtocolStateID),
   638  			))
   639  			buildFinalizedBlock(t, state, block4)
   640  
   641  			block5 := unittest.BlockWithParentProtocolState(block4)
   642  			buildFinalizedBlock(t, state, block5)
   643  
   644  			snapshot := state.AtBlockID(block5.ID())
   645  
   646  			// build a valid child to ensure we have a QC
   647  			buildFinalizedBlock(t, state, unittest.BlockWithParentProtocolState(block5))
   648  
   649  			segment, err := snapshot.SealingSegment()
   650  			require.NoError(t, err)
   651  			// sealing segment should contain B1 and B5
   652  			// B5 is reference of snapshot, B1 is latest sealed
   653  			unittest.AssertEqualBlocksLenAndOrder(t, []*flow.Block{block1, block2, block3, block4, block5}, segment.Blocks)
   654  			assert.Len(t, segment.ExecutionResults, 1)
   655  
   656  			assertSealingSegmentBlocksQueryableAfterBootstrap(t, snapshot)
   657  		})
   658  	})
   659  
   660  	// Root <- B1 <- B2 <- ... <- B700(Seal_B699)
   661  	// Expected sealing segment: [B699, B700], Extra blocks: [B98, B99, ..., B698]
   662  	// where DefaultTransactionExpiry = 600
   663  	t.Run("test extra blocks contain exactly DefaultTransactionExpiry number of blocks below the sealed block", func(t *testing.T) {
   664  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   665  			root := unittest.BlockWithParentFixture(head)
   666  			root.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   667  			buildFinalizedBlock(t, state, root)
   668  
   669  			blocks := make([]*flow.Block, 0, flow.DefaultTransactionExpiry+3)
   670  			parent := root
   671  			for i := 0; i < flow.DefaultTransactionExpiry+1; i++ {
   672  				next := unittest.BlockWithParentProtocolState(parent)
   673  				next.Header.View = next.Header.Height + 1 // set view so we are still in the same epoch
   674  				buildFinalizedBlock(t, state, next)
   675  				blocks = append(blocks, next)
   676  				parent = next
   677  			}
   678  
   679  			// last sealed block
   680  			lastSealedBlock := parent
   681  			lastReceipt, lastSeal := unittest.ReceiptAndSealForBlock(lastSealedBlock)
   682  			prevLastBlock := unittest.BlockWithParentFixture(lastSealedBlock.Header)
   683  			prevLastBlock.SetPayload(unittest.PayloadFixture(
   684  				unittest.WithReceipts(lastReceipt),
   685  				unittest.WithProtocolStateID(rootProtocolStateID),
   686  			))
   687  			buildFinalizedBlock(t, state, prevLastBlock)
   688  
   689  			// last finalized block
   690  			lastBlock := unittest.BlockWithParentFixture(prevLastBlock.Header)
   691  			lastBlock.SetPayload(unittest.PayloadFixture(
   692  				unittest.WithSeals(lastSeal),
   693  				unittest.WithProtocolStateID(rootProtocolStateID),
   694  			))
   695  			buildFinalizedBlock(t, state, lastBlock)
   696  
   697  			// build a valid child to ensure we have a QC
   698  			buildFinalizedBlock(t, state, unittest.BlockWithParentProtocolState(lastBlock))
   699  
   700  			snapshot := state.AtBlockID(lastBlock.ID())
   701  			segment, err := snapshot.SealingSegment()
   702  			require.NoError(t, err)
   703  
   704  			assert.Equal(t, lastBlock.Header, segment.Highest().Header)
   705  			assert.Equal(t, lastBlock.Header, segment.Finalized().Header)
   706  			assert.Equal(t, lastSealedBlock.Header, segment.Sealed().Header)
   707  
   708  			// there are DefaultTransactionExpiry number of blocks in total
   709  			unittest.AssertEqualBlocksLenAndOrder(t, blocks[:flow.DefaultTransactionExpiry], segment.ExtraBlocks)
   710  			assert.Len(t, segment.ExtraBlocks, flow.DefaultTransactionExpiry)
   711  			assertSealingSegmentBlocksQueryableAfterBootstrap(t, snapshot)
   712  
   713  		})
   714  	})
   715  	// Test the case where the reference block of the snapshot contains seals for blocks that are lower than the lowest sealing segment's block.
   716  	// This test case specifically checks if sealing segment includes both highest and lowest block sealed by head.
   717  	// ROOT <- B1 <- B2 <- B3(Seal_B1) <- B4 <- ... <- LastBlock(Seal_B2, Seal_B3, Seal_B4)
   718  	// Expected sealing segment: [B4, ..., B5], Extra blocks: [Root, B1, B2, B3]
   719  	t.Run("highest block seals outside segment", func(t *testing.T) {
   720  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   721  			// build a block to seal
   722  			block1 := unittest.BlockWithParentFixture(head)
   723  			block1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   724  			buildFinalizedBlock(t, state, block1)
   725  
   726  			// build a block sealing block1
   727  			block2 := unittest.BlockWithParentFixture(block1.Header)
   728  			receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   729  			block2.SetPayload(unittest.PayloadFixture(
   730  				unittest.WithReceipts(receipt1),
   731  				unittest.WithProtocolStateID(rootProtocolStateID),
   732  			))
   733  			buildFinalizedBlock(t, state, block2)
   734  
   735  			receipt2, seal2 := unittest.ReceiptAndSealForBlock(block2)
   736  			block3 := unittest.BlockWithParentFixture(block2.Header)
   737  			block3.SetPayload(unittest.PayloadFixture(
   738  				unittest.WithSeals(seal1),
   739  				unittest.WithReceipts(receipt2),
   740  				unittest.WithProtocolStateID(rootProtocolStateID),
   741  			))
   742  			buildFinalizedBlock(t, state, block3)
   743  
   744  			receipt3, seal3 := unittest.ReceiptAndSealForBlock(block3)
   745  			block4 := unittest.BlockWithParentFixture(block3.Header)
   746  			block4.SetPayload(unittest.PayloadFixture(
   747  				unittest.WithReceipts(receipt3),
   748  				unittest.WithProtocolStateID(rootProtocolStateID),
   749  			))
   750  			buildFinalizedBlock(t, state, block4)
   751  
   752  			// build chain, so it's long enough to not target blocks as inside of flow.DefaultTransactionExpiry window.
   753  			parent := block4
   754  			for i := 0; i < 1.5*flow.DefaultTransactionExpiry; i++ {
   755  				next := unittest.BlockWithParentProtocolState(parent)
   756  				next.Header.View = next.Header.Height + 1 // set view so we are still in the same epoch
   757  				buildFinalizedBlock(t, state, next)
   758  				parent = next
   759  			}
   760  
   761  			receipt4, seal4 := unittest.ReceiptAndSealForBlock(block4)
   762  			prevLastBlock := unittest.BlockWithParentFixture(parent.Header)
   763  			prevLastBlock.SetPayload(unittest.PayloadFixture(
   764  				unittest.WithReceipts(receipt4),
   765  				unittest.WithProtocolStateID(rootProtocolStateID),
   766  			))
   767  			buildFinalizedBlock(t, state, prevLastBlock)
   768  
   769  			// since result and seal cannot be part of the same block, we need to build another block
   770  			lastBlock := unittest.BlockWithParentFixture(prevLastBlock.Header)
   771  			lastBlock.SetPayload(unittest.PayloadFixture(
   772  				unittest.WithSeals(seal2, seal3, seal4),
   773  				unittest.WithProtocolStateID(rootProtocolStateID),
   774  			))
   775  			buildFinalizedBlock(t, state, lastBlock)
   776  
   777  			snapshot := state.AtBlockID(lastBlock.ID())
   778  
   779  			// build a valid child to ensure we have a QC
   780  			buildFinalizedBlock(t, state, unittest.BlockWithParentProtocolState(lastBlock))
   781  
   782  			segment, err := snapshot.SealingSegment()
   783  			require.NoError(t, err)
   784  			assert.Equal(t, lastBlock.Header, segment.Highest().Header)
   785  			assert.Equal(t, block4.Header, segment.Sealed().Header)
   786  			root := rootSnapshot.Encodable().SealingSegment.Sealed()
   787  			unittest.AssertEqualBlocksLenAndOrder(t, []*flow.Block{root, block1, block2, block3}, segment.ExtraBlocks)
   788  			assert.Len(t, segment.ExecutionResults, 2)
   789  
   790  			assertSealingSegmentBlocksQueryableAfterBootstrap(t, snapshot)
   791  		})
   792  	})
   793  }
   794  
   795  // TestSealingSegment_FailureCases verifies that SealingSegment construction fails with expected sentinel
   796  // errors in case the caller violates the API contract:
   797  //  1. The lowest block that can serve as head of a SealingSegment is the node's local root block.
   798  //  2. Unfinalized blocks cannot serve as head of a SealingSegment. There are two distinct sub-cases:
   799  //     (2a) A pending block is chosen as head; at this height no block has been finalized.
   800  //     (2b) An orphaned block is chosen as head; at this height a block other than the orphaned has been finalized.
   801  func TestSealingSegment_FailureCases(t *testing.T) {
   802  	sporkRootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet())
   803  	rootProtocolStateID := getRootProtocolStateID(t, sporkRootSnapshot)
   804  	sporkRoot, err := sporkRootSnapshot.Head()
   805  	require.NoError(t, err)
   806  
   807  	// SCENARIO 1.
   808  	// Here, we want to specifically test correct handling of the edge case, where a block exists in storage
   809  	// that has _lower height_ than the node's local root block. Such blocks are typically contained in the
   810  	// bootstrapping data, such that all entities referenced in the local root block can be resolved.
   811  	// Is is possible to retrieve blocks that are lower than the local root block from storage, directly
   812  	// via their ID. Despite these blocks existing in storage, SealingSegment construction should be
   813  	// because the known history is potentially insufficient when going below the root block.
   814  	t.Run("sealing segment from block below local state root", func(t *testing.T) {
   815  		// Step I: constructing bootstrapping snapshot with some short history:
   816  		//
   817  		//          ╭───── finalized blocks ─────╮
   818  		//    <-  b1  <-  b2(result(b1))  <-  b3(seal(b1))  <-
   819  		//                                    └── head ──┘
   820  		//
   821  		b1 := unittest.BlockWithParentFixture(sporkRoot) // construct block b1, append to state and finalize
   822  		b1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   823  		receipt, seal := unittest.ReceiptAndSealForBlock(b1)
   824  		b2 := unittest.BlockWithParentFixture(b1.Header) // construct block b2, append to state and finalize
   825  		b2.SetPayload(unittest.PayloadFixture(
   826  			unittest.WithReceipts(receipt),
   827  			unittest.WithProtocolStateID(rootProtocolStateID),
   828  		))
   829  		b3 := unittest.BlockWithParentFixture(b2.Header) // construct block b3 with seal for b1, append it to state and finalize
   830  		b3.SetPayload(unittest.PayloadFixture(
   831  			unittest.WithSeals(seal),
   832  			unittest.WithProtocolStateID(rootProtocolStateID),
   833  		))
   834  
   835  		multipleBlockSnapshot := snapshotAfter(t, sporkRootSnapshot, func(state *bprotocol.FollowerState, mutableState protocol.MutableProtocolState) protocol.Snapshot {
   836  			for _, b := range []*flow.Block{b1, b2, b3} {
   837  				buildFinalizedBlock(t, state, b)
   838  			}
   839  			b4 := unittest.BlockWithParentProtocolState(b3)
   840  			require.NoError(t, state.ExtendCertified(context.Background(), b4, unittest.CertifyBlock(b4.Header))) // add child of b3 to ensure we have a QC for b3
   841  			return state.AtBlockID(b3.ID())
   842  		})
   843  
   844  		// Step 2: bootstrapping new state based on sealing segment whose head is block b3.
   845  		// Thereby, the state should have b3 as its local root block. In addition, the blocks contained in the sealing
   846  		// segment, such as b2 should be stored in the state.
   847  		util.RunWithFollowerProtocolState(t, multipleBlockSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   848  			localStateRootBlock := state.Params().FinalizedRoot()
   849  			assert.Equal(t, b3.ID(), localStateRootBlock.ID())
   850  
   851  			// verify that b2 is known to the protocol state, but constructing a sealing segment fails
   852  			_, err = state.AtBlockID(b2.ID()).Head()
   853  			require.NoError(t, err)
   854  			_, err = state.AtBlockID(b2.ID()).SealingSegment()
   855  			assert.ErrorIs(t, err, protocol.ErrSealingSegmentBelowRootBlock)
   856  
   857  			// lowest block that allows for sealing segment construction is root block:
   858  			_, err = state.AtBlockID(b3.ID()).SealingSegment()
   859  			require.NoError(t, err)
   860  		})
   861  	})
   862  
   863  	// SCENARIO 2a: A pending block is chosen as head; at this height no block has been finalized.
   864  	t.Run("sealing segment from unfinalized, pending block", func(t *testing.T) {
   865  		util.RunWithFollowerProtocolState(t, sporkRootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   866  			// add _unfinalized_ blocks b1 and b2 to state (block b5 is necessary, so b1 has a QC, which is a consistency requirement for subsequent finality)
   867  			b1 := unittest.BlockWithParentFixture(sporkRoot)
   868  			b1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   869  			b2 := unittest.BlockWithParentFixture(b1.Header)
   870  			b2.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   871  			require.NoError(t, state.ExtendCertified(context.Background(), b1, b2.Header.QuorumCertificate()))
   872  			require.NoError(t, state.ExtendCertified(context.Background(), b2, unittest.CertifyBlock(b2.Header))) // adding block b5 (providing required QC for b1)
   873  
   874  			// consistency check: there should be no finalized block in the protocol state at height `b1.Height`
   875  			_, err := state.AtHeight(b1.Header.Height).Head() // expect statepkg.ErrUnknownSnapshotReference as only finalized blocks are indexed by height
   876  			assert.ErrorIs(t, err, statepkg.ErrUnknownSnapshotReference)
   877  
   878  			// requesting a sealing segment from block b1 should fail, as b1 is not yet finalized
   879  			_, err = state.AtBlockID(b1.ID()).SealingSegment()
   880  			assert.True(t, protocol.IsUnfinalizedSealingSegmentError(err))
   881  		})
   882  	})
   883  
   884  	// SCENARIO 2b: An orphaned block is chosen as head; at this height a block other than the orphaned has been finalized.
   885  	t.Run("sealing segment from orphaned block", func(t *testing.T) {
   886  		util.RunWithFollowerProtocolState(t, sporkRootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
   887  			orphaned := unittest.BlockWithParentFixture(sporkRoot)
   888  			orphaned.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   889  			orphanedChild := unittest.BlockWithParentProtocolState(orphaned)
   890  			require.NoError(t, state.ExtendCertified(context.Background(), orphaned, orphanedChild.Header.QuorumCertificate()))
   891  			require.NoError(t, state.ExtendCertified(context.Background(), orphanedChild, unittest.CertifyBlock(orphanedChild.Header)))
   892  			block := unittest.BlockWithParentFixture(sporkRoot)
   893  			block.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   894  			buildFinalizedBlock(t, state, block)
   895  
   896  			// consistency check: the finalized block at height `orphaned.Height` should be different than `orphaned`
   897  			h, err := state.AtHeight(orphaned.Header.Height).Head()
   898  			require.NoError(t, err)
   899  			require.NotEqual(t, h.ID(), orphaned.ID())
   900  
   901  			// requesting a sealing segment from orphaned block should fail, as it is not finalized
   902  			_, err = state.AtBlockID(orphaned.ID()).SealingSegment()
   903  			assert.True(t, protocol.IsUnfinalizedSealingSegmentError(err))
   904  		})
   905  	})
   906  
   907  }
   908  
   909  // TestBootstrapSealingSegmentWithExtraBlocks test sealing segment where the segment blocks contain collection
   910  // guarantees referencing blocks prior to the sealing segment. After bootstrapping from sealing segment we should be able to
   911  // extend with B7 with contains a guarantee referring B1.
   912  // ROOT <- B1 <- B2(R1) <- B3 <- B4(S1) <- B5 <- B6(S2)
   913  // Expected sealing segment: [B2, B3, B4, B5, B6], Extra blocks: [ROOT, B1]
   914  func TestBootstrapSealingSegmentWithExtraBlocks(t *testing.T) {
   915  	identities := unittest.CompleteIdentitySet()
   916  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   917  	rootProtocolStateID := getRootProtocolStateID(t, rootSnapshot)
   918  	rootEpoch := rootSnapshot.Epochs().Current()
   919  	cluster, err := rootEpoch.Cluster(0)
   920  	require.NoError(t, err)
   921  	collID := cluster.Members()[0].NodeID
   922  	head, err := rootSnapshot.Head()
   923  	require.NoError(t, err)
   924  	util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   925  		block1 := unittest.BlockWithParentFixture(head)
   926  		block1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
   927  		buildFinalizedBlock(t, state, block1)
   928  		receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   929  
   930  		block2 := unittest.BlockWithParentFixture(block1.Header)
   931  		block2.SetPayload(unittest.PayloadFixture(
   932  			unittest.WithReceipts(receipt1),
   933  			unittest.WithProtocolStateID(rootProtocolStateID),
   934  		))
   935  		buildFinalizedBlock(t, state, block2)
   936  
   937  		receipt2, seal2 := unittest.ReceiptAndSealForBlock(block2)
   938  
   939  		block3 := unittest.BlockWithParentProtocolState(block2)
   940  		buildFinalizedBlock(t, state, block3)
   941  
   942  		block4 := unittest.BlockWithParentFixture(block3.Header)
   943  		block4.SetPayload(unittest.PayloadFixture(
   944  			unittest.WithReceipts(receipt2),
   945  			unittest.WithSeals(seal1),
   946  			unittest.WithProtocolStateID(rootProtocolStateID),
   947  		))
   948  		buildFinalizedBlock(t, state, block4)
   949  
   950  		block5 := unittest.BlockWithParentProtocolState(block4)
   951  		buildFinalizedBlock(t, state, block5)
   952  
   953  		block6 := unittest.BlockWithParentFixture(block5.Header)
   954  		block6.SetPayload(unittest.PayloadFixture(
   955  			unittest.WithSeals(seal2),
   956  			unittest.WithProtocolStateID(rootProtocolStateID),
   957  		))
   958  		buildFinalizedBlock(t, state, block6)
   959  
   960  		snapshot := state.AtBlockID(block6.ID())
   961  		segment, err := snapshot.SealingSegment()
   962  		require.NoError(t, err)
   963  
   964  		// build a valid child to ensure we have a QC
   965  		buildBlock(t, state, unittest.BlockWithParentProtocolState(block6))
   966  
   967  		// sealing segment should be [B2, B3, B4, B5, B6]
   968  		require.Len(t, segment.Blocks, 5)
   969  		unittest.AssertEqualBlocksLenAndOrder(t, []*flow.Block{block2, block3, block4, block5, block6}, segment.Blocks)
   970  		unittest.AssertEqualBlocksLenAndOrder(t, []*flow.Block{block1}, segment.ExtraBlocks[1:])
   971  		require.Len(t, segment.ExecutionResults, 1)
   972  
   973  		assertSealingSegmentBlocksQueryableAfterBootstrap(t, snapshot)
   974  
   975  		// bootstrap from snapshot
   976  		util.RunWithFullProtocolState(t, snapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
   977  			block7 := unittest.BlockWithParentFixture(block6.Header)
   978  			guarantee := unittest.CollectionGuaranteeFixture(unittest.WithCollRef(block1.ID()))
   979  			guarantee.ChainID = cluster.ChainID()
   980  
   981  			signerIndices, err := signature.EncodeSignersToIndices(
   982  				[]flow.Identifier{collID}, []flow.Identifier{collID})
   983  			require.NoError(t, err)
   984  			guarantee.SignerIndices = signerIndices
   985  
   986  			block7.SetPayload(unittest.PayloadFixture(
   987  				unittest.WithGuarantees(guarantee),
   988  				unittest.WithProtocolStateID(block6.Payload.ProtocolStateID),
   989  			))
   990  			buildBlock(t, state, block7)
   991  		})
   992  	})
   993  }
   994  
   995  func TestLatestSealedResult(t *testing.T) {
   996  	identities := unittest.CompleteIdentitySet()
   997  	rootSnapshot := unittest.RootSnapshotFixture(identities)
   998  	rootProtocolStateID := getRootProtocolStateID(t, rootSnapshot)
   999  
  1000  	t.Run("root snapshot", func(t *testing.T) {
  1001  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
  1002  			gotResult, gotSeal, err := state.Final().SealedResult()
  1003  			require.NoError(t, err)
  1004  			expectedResult, expectedSeal, err := rootSnapshot.SealedResult()
  1005  			require.NoError(t, err)
  1006  
  1007  			assert.Equal(t, expectedResult.ID(), gotResult.ID())
  1008  			assert.Equal(t, expectedSeal, gotSeal)
  1009  		})
  1010  	})
  1011  
  1012  	t.Run("non-root snapshot", func(t *testing.T) {
  1013  		head, err := rootSnapshot.Head()
  1014  		require.NoError(t, err)
  1015  
  1016  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
  1017  			block1 := unittest.BlockWithParentFixture(head)
  1018  			block1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
  1019  
  1020  			block2 := unittest.BlockWithParentProtocolState(block1)
  1021  
  1022  			receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
  1023  			block2.SetPayload(unittest.PayloadFixture(
  1024  				unittest.WithReceipts(receipt1),
  1025  				unittest.WithProtocolStateID(rootProtocolStateID),
  1026  			))
  1027  			block3 := unittest.BlockWithParentFixture(block2.Header)
  1028  			block3.SetPayload(unittest.PayloadFixture(
  1029  				unittest.WithSeals(seal1),
  1030  				unittest.WithProtocolStateID(rootProtocolStateID)))
  1031  
  1032  			receipt2, seal2 := unittest.ReceiptAndSealForBlock(block2)
  1033  			receipt3, seal3 := unittest.ReceiptAndSealForBlock(block3)
  1034  			block4 := unittest.BlockWithParentFixture(block3.Header)
  1035  			block4.SetPayload(unittest.PayloadFixture(
  1036  				unittest.WithReceipts(receipt2, receipt3),
  1037  				unittest.WithProtocolStateID(rootProtocolStateID),
  1038  			))
  1039  			block5 := unittest.BlockWithParentFixture(block4.Header)
  1040  			block5.SetPayload(unittest.PayloadFixture(
  1041  				unittest.WithSeals(seal2, seal3),
  1042  				unittest.WithProtocolStateID(rootProtocolStateID),
  1043  			))
  1044  
  1045  			err = state.ExtendCertified(context.Background(), block1, block2.Header.QuorumCertificate())
  1046  			require.NoError(t, err)
  1047  
  1048  			err = state.ExtendCertified(context.Background(), block2, block3.Header.QuorumCertificate())
  1049  			require.NoError(t, err)
  1050  
  1051  			err = state.ExtendCertified(context.Background(), block3, block4.Header.QuorumCertificate())
  1052  			require.NoError(t, err)
  1053  
  1054  			// B1 <- B2(R1) <- B3(S1)
  1055  			// querying B3 should return result R1, seal S1
  1056  			t.Run("reference block contains seal", func(t *testing.T) {
  1057  				gotResult, gotSeal, err := state.AtBlockID(block3.ID()).SealedResult()
  1058  				require.NoError(t, err)
  1059  				assert.Equal(t, block2.Payload.Results[0], gotResult)
  1060  				assert.Equal(t, block3.Payload.Seals[0], gotSeal)
  1061  			})
  1062  
  1063  			err = state.ExtendCertified(context.Background(), block4, block5.Header.QuorumCertificate())
  1064  			require.NoError(t, err)
  1065  
  1066  			// B1 <- B2(S1) <- B3(S1) <- B4(R2,R3)
  1067  			// querying B3 should still return (R1,S1) even though they are in parent block
  1068  			t.Run("reference block contains no seal", func(t *testing.T) {
  1069  				gotResult, gotSeal, err := state.AtBlockID(block4.ID()).SealedResult()
  1070  				require.NoError(t, err)
  1071  				assert.Equal(t, &receipt1.ExecutionResult, gotResult)
  1072  				assert.Equal(t, seal1, gotSeal)
  1073  			})
  1074  
  1075  			// B1 <- B2(R1) <- B3(S1) <- B4(R2,R3) <- B5(S2,S3)
  1076  			// There are two seals in B5 - should return latest by height (S3,R3)
  1077  			t.Run("reference block contains multiple seals", func(t *testing.T) {
  1078  				err = state.ExtendCertified(context.Background(), block5, unittest.CertifyBlock(block5.Header))
  1079  				require.NoError(t, err)
  1080  
  1081  				gotResult, gotSeal, err := state.AtBlockID(block5.ID()).SealedResult()
  1082  				require.NoError(t, err)
  1083  				assert.Equal(t, &receipt3.ExecutionResult, gotResult)
  1084  				assert.Equal(t, seal3, gotSeal)
  1085  			})
  1086  		})
  1087  	})
  1088  }
  1089  
  1090  // test retrieving quorum certificate and seed
  1091  func TestQuorumCertificate(t *testing.T) {
  1092  	identities := unittest.IdentityListFixture(5, unittest.WithAllRoles())
  1093  	rootSnapshot := unittest.RootSnapshotFixture(identities)
  1094  	rootProtocolStateID := getRootProtocolStateID(t, rootSnapshot)
  1095  	head, err := rootSnapshot.Head()
  1096  	require.NoError(t, err)
  1097  
  1098  	// should not be able to get QC or random beacon seed from a block with no children
  1099  	t.Run("no QC available", func(t *testing.T) {
  1100  		util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
  1101  
  1102  			// create a block to query
  1103  			block1 := unittest.BlockWithParentFixture(head)
  1104  			block1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
  1105  			err := state.Extend(context.Background(), block1)
  1106  			require.NoError(t, err)
  1107  
  1108  			_, err = state.AtBlockID(block1.ID()).QuorumCertificate()
  1109  			assert.ErrorIs(t, err, storage.ErrNotFound)
  1110  
  1111  			_, err = state.AtBlockID(block1.ID()).RandomSource()
  1112  			assert.ErrorIs(t, err, storage.ErrNotFound)
  1113  		})
  1114  	})
  1115  
  1116  	// should be able to get QC and random beacon seed from root block
  1117  	t.Run("root block", func(t *testing.T) {
  1118  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
  1119  			// since we bootstrap with a root snapshot, this will be the root block
  1120  			_, err := state.AtBlockID(head.ID()).QuorumCertificate()
  1121  			assert.NoError(t, err)
  1122  			randomSeed, err := state.AtBlockID(head.ID()).RandomSource()
  1123  			assert.NoError(t, err)
  1124  			assert.Equal(t, len(randomSeed), prg.RandomSourceLength)
  1125  		})
  1126  	})
  1127  
  1128  	// should be able to get QC and random beacon seed from a certified block
  1129  	t.Run("follower-block-processable", func(t *testing.T) {
  1130  		util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) {
  1131  
  1132  			// add a block so we aren't testing against root
  1133  			block1 := unittest.BlockWithParentFixture(head)
  1134  			block1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
  1135  			certifyingQC := unittest.CertifyBlock(block1.Header)
  1136  			err := state.ExtendCertified(context.Background(), block1, certifyingQC)
  1137  			require.NoError(t, err)
  1138  
  1139  			// should be able to get QC/seed
  1140  			qc, err := state.AtBlockID(block1.ID()).QuorumCertificate()
  1141  			assert.NoError(t, err)
  1142  
  1143  			assert.Equal(t, certifyingQC.SignerIndices, qc.SignerIndices)
  1144  			assert.Equal(t, certifyingQC.SigData, qc.SigData)
  1145  			assert.Equal(t, block1.Header.View, qc.View)
  1146  
  1147  			_, err = state.AtBlockID(block1.ID()).RandomSource()
  1148  			require.NoError(t, err)
  1149  		})
  1150  	})
  1151  
  1152  	// should be able to get QC and random beacon seed from a block with child(has to be certified)
  1153  	t.Run("participant-block-processable", func(t *testing.T) {
  1154  		util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState) {
  1155  			// create a block to query
  1156  			block1 := unittest.BlockWithParentFixture(head)
  1157  			block1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(rootProtocolStateID)))
  1158  			err := state.Extend(context.Background(), block1)
  1159  			require.NoError(t, err)
  1160  
  1161  			_, err = state.AtBlockID(block1.ID()).QuorumCertificate()
  1162  			assert.ErrorIs(t, err, storage.ErrNotFound)
  1163  
  1164  			block2 := unittest.BlockWithParentProtocolState(block1)
  1165  			err = state.Extend(context.Background(), block2)
  1166  			require.NoError(t, err)
  1167  
  1168  			qc, err := state.AtBlockID(block1.ID()).QuorumCertificate()
  1169  			require.NoError(t, err)
  1170  
  1171  			// should have view matching block1 view
  1172  			assert.Equal(t, block1.Header.View, qc.View)
  1173  			assert.Equal(t, block1.ID(), qc.BlockID)
  1174  		})
  1175  	})
  1176  }
  1177  
  1178  // test that we can query current/next/previous epochs from a snapshot
  1179  func TestSnapshot_EpochQuery(t *testing.T) {
  1180  	identities := unittest.CompleteIdentitySet()
  1181  	rootSnapshot := unittest.RootSnapshotFixture(identities)
  1182  	result, _, err := rootSnapshot.SealedResult()
  1183  	require.NoError(t, err)
  1184  
  1185  	util.RunWithFullProtocolStateAndMutator(t, rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState, mutableState protocol.MutableProtocolState) {
  1186  		epoch1Counter := result.ServiceEvents[0].Event.(*flow.EpochSetup).Counter
  1187  		epoch2Counter := epoch1Counter + 1
  1188  
  1189  		epochBuilder := unittest.NewEpochBuilder(t, mutableState, state)
  1190  		// build epoch 1 (prepare epoch 2)
  1191  		epochBuilder.
  1192  			BuildEpoch().
  1193  			CompleteEpoch()
  1194  		// build epoch 2 (prepare epoch 3)
  1195  		epochBuilder.
  1196  			BuildEpoch().
  1197  			CompleteEpoch()
  1198  
  1199  		// get heights of each phase in built epochs
  1200  		epoch1, ok := epochBuilder.EpochHeights(1)
  1201  		require.True(t, ok)
  1202  		epoch2, ok := epochBuilder.EpochHeights(2)
  1203  		require.True(t, ok)
  1204  
  1205  		// we should be able to query the current epoch from any block
  1206  		t.Run("Current", func(t *testing.T) {
  1207  			t.Run("epoch 1", func(t *testing.T) {
  1208  				for _, height := range epoch1.Range() {
  1209  					counter, err := state.AtHeight(height).Epochs().Current().Counter()
  1210  					require.NoError(t, err)
  1211  					assert.Equal(t, epoch1Counter, counter)
  1212  				}
  1213  			})
  1214  
  1215  			t.Run("epoch 2", func(t *testing.T) {
  1216  				for _, height := range epoch2.Range() {
  1217  					counter, err := state.AtHeight(height).Epochs().Current().Counter()
  1218  					require.NoError(t, err)
  1219  					assert.Equal(t, epoch2Counter, counter)
  1220  				}
  1221  			})
  1222  		})
  1223  
  1224  		// we should be unable to query next epoch before it is defined by EpochSetup
  1225  		// event, afterward we should be able to query next epoch
  1226  		t.Run("Next", func(t *testing.T) {
  1227  			t.Run("epoch 1: before next epoch available", func(t *testing.T) {
  1228  				for _, height := range epoch1.StakingRange() {
  1229  					_, err := state.AtHeight(height).Epochs().Next().Counter()
  1230  					assert.Error(t, err)
  1231  					assert.True(t, errors.Is(err, protocol.ErrNextEpochNotSetup))
  1232  				}
  1233  			})
  1234  
  1235  			t.Run("epoch 2: after next epoch available", func(t *testing.T) {
  1236  				for _, height := range append(epoch1.SetupRange(), epoch1.CommittedRange()...) {
  1237  					counter, err := state.AtHeight(height).Epochs().Next().Counter()
  1238  					require.NoError(t, err)
  1239  					assert.Equal(t, epoch2Counter, counter)
  1240  				}
  1241  			})
  1242  		})
  1243  
  1244  		// we should get a sentinel error when querying previous epoch from the
  1245  		// first epoch after the root block, otherwise we should always be able
  1246  		// to query previous epoch
  1247  		t.Run("Previous", func(t *testing.T) {
  1248  			t.Run("epoch 1", func(t *testing.T) {
  1249  				for _, height := range epoch1.Range() {
  1250  					_, err := state.AtHeight(height).Epochs().Previous().Counter()
  1251  					assert.Error(t, err)
  1252  					assert.True(t, errors.Is(err, protocol.ErrNoPreviousEpoch))
  1253  				}
  1254  			})
  1255  
  1256  			t.Run("epoch 2", func(t *testing.T) {
  1257  				for _, height := range epoch2.Range() {
  1258  					counter, err := state.AtHeight(height).Epochs().Previous().Counter()
  1259  					require.NoError(t, err)
  1260  					assert.Equal(t, epoch1Counter, counter)
  1261  				}
  1262  			})
  1263  		})
  1264  	})
  1265  }
  1266  
  1267  // test that querying the first view of an epoch returns the appropriate value
  1268  func TestSnapshot_EpochFirstView(t *testing.T) {
  1269  	identities := unittest.CompleteIdentitySet()
  1270  	rootSnapshot := unittest.RootSnapshotFixture(identities)
  1271  	head, err := rootSnapshot.Head()
  1272  	require.NoError(t, err)
  1273  	result, _, err := rootSnapshot.SealedResult()
  1274  	require.NoError(t, err)
  1275  
  1276  	util.RunWithFullProtocolStateAndMutator(t, rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState, mutableState protocol.MutableProtocolState) {
  1277  
  1278  		epochBuilder := unittest.NewEpochBuilder(t, mutableState, state)
  1279  		// build epoch 1 (prepare epoch 2)
  1280  		epochBuilder.
  1281  			BuildEpoch().
  1282  			CompleteEpoch()
  1283  		// build epoch 2 (prepare epoch 3)
  1284  		epochBuilder.
  1285  			BuildEpoch().
  1286  			CompleteEpoch()
  1287  
  1288  		// get heights of each phase in built epochs
  1289  		epoch1, ok := epochBuilder.EpochHeights(1)
  1290  		require.True(t, ok)
  1291  		epoch2, ok := epochBuilder.EpochHeights(2)
  1292  		require.True(t, ok)
  1293  
  1294  		// figure out the expected first views of the epochs
  1295  		epoch1FirstView := head.View
  1296  		epoch2FirstView := result.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView + 1
  1297  
  1298  		// check first view for snapshots within epoch 1, with respect to a
  1299  		// snapshot in either epoch 1 or epoch 2 (testing Current and Previous)
  1300  		t.Run("epoch 1", func(t *testing.T) {
  1301  
  1302  			// test w.r.t. epoch 1 snapshot
  1303  			t.Run("Current", func(t *testing.T) {
  1304  				for _, height := range epoch1.Range() {
  1305  					actualFirstView, err := state.AtHeight(height).Epochs().Current().FirstView()
  1306  					require.NoError(t, err)
  1307  					assert.Equal(t, epoch1FirstView, actualFirstView)
  1308  				}
  1309  			})
  1310  
  1311  			// test w.r.t. epoch 2 snapshot
  1312  			t.Run("Previous", func(t *testing.T) {
  1313  				for _, height := range epoch2.Range() {
  1314  					actualFirstView, err := state.AtHeight(height).Epochs().Previous().FirstView()
  1315  					require.NoError(t, err)
  1316  					assert.Equal(t, epoch1FirstView, actualFirstView)
  1317  				}
  1318  			})
  1319  		})
  1320  
  1321  		// check first view for snapshots within epoch 2, with respect to a
  1322  		// snapshot in either epoch 1 or epoch 2 (testing Next and Current)
  1323  		t.Run("epoch 2", func(t *testing.T) {
  1324  
  1325  			// test w.r.t. epoch 1 snapshot
  1326  			t.Run("Next", func(t *testing.T) {
  1327  				for _, height := range append(epoch1.SetupRange(), epoch1.CommittedRange()...) {
  1328  					actualFirstView, err := state.AtHeight(height).Epochs().Next().FirstView()
  1329  					require.NoError(t, err)
  1330  					assert.Equal(t, epoch2FirstView, actualFirstView)
  1331  				}
  1332  			})
  1333  
  1334  			// test w.r.t. epoch 2 snapshot
  1335  			t.Run("Current", func(t *testing.T) {
  1336  				for _, height := range epoch2.Range() {
  1337  					actualFirstView, err := state.AtHeight(height).Epochs().Current().FirstView()
  1338  					require.NoError(t, err)
  1339  					assert.Equal(t, epoch2FirstView, actualFirstView)
  1340  				}
  1341  			})
  1342  		})
  1343  	})
  1344  }
  1345  
  1346  // TestSnapshot_EpochHeightBoundaries tests querying epoch height boundaries in various conditions.
  1347  //   - FirstHeight should be queryable as soon as the epoch's first block is finalized,
  1348  //     otherwise should return protocol.ErrUnknownEpochBoundary
  1349  //   - FinalHeight should be queryable as soon as the next epoch's first block is finalized,
  1350  //     otherwise should return protocol.ErrUnknownEpochBoundary
  1351  func TestSnapshot_EpochHeightBoundaries(t *testing.T) {
  1352  	identities := unittest.CompleteIdentitySet()
  1353  	rootSnapshot := unittest.RootSnapshotFixture(identities)
  1354  	head, err := rootSnapshot.Head()
  1355  	require.NoError(t, err)
  1356  
  1357  	util.RunWithFullProtocolStateAndMutator(t, rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState, mutableState protocol.MutableProtocolState) {
  1358  
  1359  		epochBuilder := unittest.NewEpochBuilder(t, mutableState, state)
  1360  
  1361  		epoch1FirstHeight := head.Height
  1362  		t.Run("first epoch - EpochStaking phase", func(t *testing.T) {
  1363  			// first height of started current epoch should be known
  1364  			firstHeight, err := state.Final().Epochs().Current().FirstHeight()
  1365  			require.NoError(t, err)
  1366  			assert.Equal(t, epoch1FirstHeight, firstHeight)
  1367  			// final height of not completed current epoch should be unknown
  1368  			_, err = state.Final().Epochs().Current().FinalHeight()
  1369  			assert.ErrorIs(t, err, protocol.ErrUnknownEpochBoundary)
  1370  		})
  1371  
  1372  		// build first epoch (but don't complete it yet)
  1373  		epochBuilder.BuildEpoch()
  1374  
  1375  		t.Run("first epoch - EpochCommitted phase", func(t *testing.T) {
  1376  			// first height of started current epoch should be known
  1377  			firstHeight, err := state.Final().Epochs().Current().FirstHeight()
  1378  			require.NoError(t, err)
  1379  			assert.Equal(t, epoch1FirstHeight, firstHeight)
  1380  			// final height of not completed current epoch should be unknown
  1381  			_, err = state.Final().Epochs().Current().FinalHeight()
  1382  			assert.ErrorIs(t, err, protocol.ErrUnknownEpochBoundary)
  1383  			// first and final height of not started next epoch should be unknown
  1384  			_, err = state.Final().Epochs().Next().FirstHeight()
  1385  			assert.ErrorIs(t, err, protocol.ErrUnknownEpochBoundary)
  1386  			_, err = state.Final().Epochs().Next().FinalHeight()
  1387  			assert.ErrorIs(t, err, protocol.ErrUnknownEpochBoundary)
  1388  		})
  1389  
  1390  		// complete epoch 1 (enter epoch 2)
  1391  		epochBuilder.CompleteEpoch()
  1392  		epoch1Heights, ok := epochBuilder.EpochHeights(1)
  1393  		require.True(t, ok)
  1394  		epoch1FinalHeight := epoch1Heights.FinalHeight()
  1395  		epoch2FirstHeight := epoch1FinalHeight + 1
  1396  
  1397  		t.Run("second epoch - EpochStaking phase", func(t *testing.T) {
  1398  			// first and final height of completed previous epoch should be known
  1399  			firstHeight, err := state.Final().Epochs().Previous().FirstHeight()
  1400  			require.NoError(t, err)
  1401  			assert.Equal(t, epoch1FirstHeight, firstHeight)
  1402  			finalHeight, err := state.Final().Epochs().Previous().FinalHeight()
  1403  			require.NoError(t, err)
  1404  			assert.Equal(t, epoch1FinalHeight, finalHeight)
  1405  
  1406  			// first height of started current epoch should be known
  1407  			firstHeight, err = state.Final().Epochs().Current().FirstHeight()
  1408  			require.NoError(t, err)
  1409  			assert.Equal(t, epoch2FirstHeight, firstHeight)
  1410  			// final height of not completed current epoch should be unknown
  1411  			_, err = state.Final().Epochs().Current().FinalHeight()
  1412  			assert.ErrorIs(t, err, protocol.ErrUnknownEpochBoundary)
  1413  		})
  1414  	})
  1415  }
  1416  
  1417  // Test querying identities in different epoch phases. During staking phase we
  1418  // should see identities from last epoch and current epoch. After staking phase
  1419  // we should see identities from current epoch and next epoch. Identities from
  1420  // a non-current epoch should have weight 0. Identities that exist in consecutive
  1421  // epochs should be de-duplicated.
  1422  func TestSnapshot_CrossEpochIdentities(t *testing.T) {
  1423  
  1424  	// start with 20 identities in epoch 1
  1425  	epoch1Identities := unittest.IdentityListFixture(20, unittest.WithAllRoles())
  1426  	// 1 identity added at epoch 2 that was not present in epoch 1
  1427  	addedAtEpoch2 := unittest.IdentityFixture()
  1428  	// 1 identity removed in epoch 2 that was present in epoch 1
  1429  	removedAtEpoch2 := epoch1Identities[rand.Intn(len(epoch1Identities))]
  1430  	// epoch 2 has partial overlap with epoch 1
  1431  	epoch2Identities := append(
  1432  		epoch1Identities.Filter(filter.Not(filter.HasNodeID[flow.Identity](removedAtEpoch2.NodeID))),
  1433  		addedAtEpoch2)
  1434  	// epoch 3 has no overlap with epoch 2
  1435  	epoch3Identities := unittest.IdentityListFixture(10, unittest.WithAllRoles())
  1436  
  1437  	rootSnapshot := unittest.RootSnapshotFixture(epoch1Identities)
  1438  	util.RunWithFullProtocolStateAndMutator(t, rootSnapshot, func(db *badger.DB, state *bprotocol.ParticipantState, mutableState protocol.MutableProtocolState) {
  1439  
  1440  		epochBuilder := unittest.NewEpochBuilder(t, mutableState, state)
  1441  		// build epoch 1 (prepare epoch 2)
  1442  		epochBuilder.
  1443  			UsingSetupOpts(unittest.WithParticipants(epoch2Identities.ToSkeleton())).
  1444  			BuildEpoch().
  1445  			CompleteEpoch()
  1446  		// build epoch 2 (prepare epoch 3)
  1447  		epochBuilder.
  1448  			UsingSetupOpts(unittest.WithParticipants(epoch3Identities.ToSkeleton())).
  1449  			BuildEpoch().
  1450  			CompleteEpoch()
  1451  
  1452  		// get heights of each phase in built epochs
  1453  		epoch1, ok := epochBuilder.EpochHeights(1)
  1454  		require.True(t, ok)
  1455  		epoch2, ok := epochBuilder.EpochHeights(2)
  1456  		require.True(t, ok)
  1457  
  1458  		t.Run("should be able to query at root block", func(t *testing.T) {
  1459  			root := state.Params().FinalizedRoot()
  1460  			snapshot := state.AtHeight(root.Height)
  1461  			identities, err := snapshot.Identities(filter.Any)
  1462  			require.NoError(t, err)
  1463  
  1464  			// should have the right number of identities
  1465  			assert.Equal(t, len(epoch1Identities), len(identities))
  1466  			// should have all epoch 1 identities
  1467  			assert.ElementsMatch(t, epoch1Identities, identities)
  1468  		})
  1469  
  1470  		t.Run("should include next epoch after staking phase", func(t *testing.T) {
  1471  
  1472  			// get a snapshot from setup phase and commit phase of epoch 1
  1473  			snapshots := []protocol.Snapshot{state.AtHeight(epoch1.Setup), state.AtHeight(epoch1.Committed)}
  1474  
  1475  			for _, snapshot := range snapshots {
  1476  				phase, err := snapshot.Phase()
  1477  				require.NoError(t, err)
  1478  
  1479  				t.Run("phase: "+phase.String(), func(t *testing.T) {
  1480  					identities, err := snapshot.Identities(filter.Any)
  1481  					require.NoError(t, err)
  1482  
  1483  					// should have the right number of identities
  1484  					assert.Equal(t, len(epoch1Identities)+1, len(identities))
  1485  					// all current epoch identities should match configuration from EpochSetup event
  1486  					assert.ElementsMatch(t, epoch1Identities, identities.Filter(epoch1Identities.Selector()))
  1487  
  1488  					// should contain single identity for next epoch with status `flow.EpochParticipationStatusJoining`
  1489  					nextEpochIdentity := identities.Filter(filter.HasNodeID[flow.Identity](addedAtEpoch2.NodeID))[0]
  1490  					assert.Equal(t, flow.EpochParticipationStatusJoining, nextEpochIdentity.EpochParticipationStatus,
  1491  						"expect joining status since we are in setup & commit phase")
  1492  					assert.Equal(t, addedAtEpoch2.IdentitySkeleton, nextEpochIdentity.IdentitySkeleton,
  1493  						"expect skeleton to be identical")
  1494  				})
  1495  			}
  1496  		})
  1497  
  1498  		t.Run("should include previous epoch in staking phase", func(t *testing.T) {
  1499  
  1500  			// get a snapshot from staking phase of epoch 2
  1501  			snapshot := state.AtHeight(epoch2.Staking)
  1502  			identities, err := snapshot.Identities(filter.Any)
  1503  			require.NoError(t, err)
  1504  
  1505  			// should have the right number of identities
  1506  			assert.Equal(t, len(epoch2Identities)+1, len(identities))
  1507  			// all current epoch identities should match configuration from EpochSetup event
  1508  			assert.ElementsMatch(t, epoch2Identities, identities.Filter(epoch2Identities.Selector()))
  1509  
  1510  			// should contain single identity from previous epoch with status `flow.EpochParticipationStatusLeaving`
  1511  			lastEpochIdentity := identities.Filter(filter.HasNodeID[flow.Identity](removedAtEpoch2.NodeID))[0]
  1512  			assert.Equal(t, flow.EpochParticipationStatusLeaving, lastEpochIdentity.EpochParticipationStatus,
  1513  				"expect leaving status since we are in staking phase")
  1514  			assert.Equal(t, removedAtEpoch2.IdentitySkeleton, lastEpochIdentity.IdentitySkeleton,
  1515  				"expect skeleton to be identical")
  1516  		})
  1517  
  1518  		t.Run("should not include previous epoch after staking phase", func(t *testing.T) {
  1519  
  1520  			// get a snapshot from setup phase and commit phase of epoch 2
  1521  			snapshots := []protocol.Snapshot{state.AtHeight(epoch2.Setup), state.AtHeight(epoch2.Committed)}
  1522  
  1523  			for _, snapshot := range snapshots {
  1524  				phase, err := snapshot.Phase()
  1525  				require.NoError(t, err)
  1526  
  1527  				t.Run("phase: "+phase.String(), func(t *testing.T) {
  1528  					identities, err := snapshot.Identities(filter.Any)
  1529  					require.NoError(t, err)
  1530  
  1531  					// should have the right number of identities
  1532  					assert.Equal(t, len(epoch2Identities)+len(epoch3Identities), len(identities))
  1533  					// all current epoch identities should match configuration from EpochSetup event
  1534  					assert.ElementsMatch(t, epoch2Identities, identities.Filter(epoch2Identities.Selector()))
  1535  
  1536  					// should contain next epoch's identities with status `flow.EpochParticipationStatusJoining`
  1537  					for _, expected := range epoch3Identities {
  1538  						actual, exists := identities.ByNodeID(expected.NodeID)
  1539  						require.True(t, exists)
  1540  						assert.Equal(t, flow.EpochParticipationStatusJoining, actual.EpochParticipationStatus,
  1541  							"expect joining status since we are in setup & commit phase")
  1542  						assert.Equal(t, expected.IdentitySkeleton, actual.IdentitySkeleton,
  1543  							"expect skeleton to be identical")
  1544  					}
  1545  				})
  1546  			}
  1547  		})
  1548  	})
  1549  }
  1550  
  1551  // test that we can retrieve identities after a spork where the parent ID of the
  1552  // root block is non-nil
  1553  func TestSnapshot_PostSporkIdentities(t *testing.T) {
  1554  	expected := unittest.CompleteIdentitySet()
  1555  	root, result, seal := unittest.BootstrapFixture(expected, func(block *flow.Block) {
  1556  		block.Header.ParentID = unittest.IdentifierFixture()
  1557  	})
  1558  	qc := unittest.QuorumCertificateFixture(unittest.QCWithRootBlockID(root.ID()))
  1559  
  1560  	rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, qc)
  1561  	require.NoError(t, err)
  1562  
  1563  	util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) {
  1564  		actual, err := state.Final().Identities(filter.Any)
  1565  		require.NoError(t, err)
  1566  		assert.ElementsMatch(t, expected, actual)
  1567  	})
  1568  }