github.com/onflow/flow-go@v0.33.17/state/protocol/badger/snapshot_test.go (about)

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