github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/model/flow/sealing_segment_test.go (about)

     1  package flow_test
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  	"github.com/stretchr/testify/suite"
    10  
    11  	"github.com/onflow/flow-go/model/flow"
    12  	"github.com/onflow/flow-go/utils/unittest"
    13  )
    14  
    15  // SealingSegmentSuite is the test suite for sealing segment construction and validation.
    16  // Notation:
    17  // B1 - block 1
    18  // R1 - execution result+receipt for block 1
    19  // S1 - seal for block 1
    20  // R* - execution result+receipt for some block before the segment
    21  // S* - seal for some block before the segment
    22  type SealingSegmentSuite struct {
    23  	suite.Suite
    24  
    25  	results              map[flow.Identifier]*flow.ExecutionResult
    26  	sealsByBlockID       map[flow.Identifier]*flow.Seal
    27  	protocolStateEntries map[flow.Identifier]*flow.ProtocolStateEntryWrapper
    28  	// bootstrap each test case with a block which is before, and receipt+seal for the block
    29  	priorBlock             *flow.Block
    30  	priorReceipt           *flow.ExecutionReceipt
    31  	priorSeal              *flow.Seal
    32  	defaultProtocolStateID flow.Identifier
    33  
    34  	builder *flow.SealingSegmentBuilder
    35  }
    36  
    37  func TestSealingSegmentSuite(t *testing.T) {
    38  	suite.Run(t, new(SealingSegmentSuite))
    39  }
    40  
    41  // addResult adds the result to the suite mapping.
    42  func (suite *SealingSegmentSuite) addResult(result *flow.ExecutionResult) {
    43  	suite.results[result.ID()] = result
    44  }
    45  
    46  // addSeal adds the seal as being the latest w.r.t. the block ID.
    47  func (suite *SealingSegmentSuite) addSeal(blockID flow.Identifier, seal *flow.Seal) {
    48  	suite.sealsByBlockID[blockID] = seal
    49  }
    50  
    51  func (suite *SealingSegmentSuite) addProtocolStateEntry(protocolStateID flow.Identifier, entry *flow.ProtocolStateEntryWrapper) {
    52  	suite.protocolStateEntries[protocolStateID] = entry
    53  }
    54  
    55  // GetResult gets a result by ID from the map in the suite.
    56  func (suite *SealingSegmentSuite) GetResult(resultID flow.Identifier) (*flow.ExecutionResult, error) {
    57  	result, ok := suite.results[resultID]
    58  	if !ok {
    59  		return nil, fmt.Errorf("not found")
    60  	}
    61  	return result, nil
    62  }
    63  
    64  // GetSealByBlockID gets a seal by block ID from the map in the suite.
    65  func (suite *SealingSegmentSuite) GetSealByBlockID(blockID flow.Identifier) (*flow.Seal, error) {
    66  	seal, ok := suite.sealsByBlockID[blockID]
    67  	if !ok {
    68  		return nil, fmt.Errorf("not found")
    69  	}
    70  	return seal, nil
    71  }
    72  
    73  // GetProtocolStateEntry gets a protocol state entry from the map in the suite.
    74  func (suite *SealingSegmentSuite) GetProtocolStateEntry(protocolStateID flow.Identifier) (*flow.ProtocolStateEntryWrapper, error) {
    75  	entry, ok := suite.protocolStateEntries[protocolStateID]
    76  	if !ok {
    77  		return nil, fmt.Errorf("not found")
    78  	}
    79  	return entry, nil
    80  }
    81  
    82  // SetupTest resets maps and creates a new builder for a new test case.
    83  func (suite *SealingSegmentSuite) SetupTest() {
    84  	suite.results = make(map[flow.Identifier]*flow.ExecutionResult)
    85  	suite.sealsByBlockID = make(map[flow.Identifier]*flow.Seal)
    86  	suite.protocolStateEntries = make(map[flow.Identifier]*flow.ProtocolStateEntryWrapper)
    87  	suite.builder = flow.NewSealingSegmentBuilder(suite.GetResult, suite.GetSealByBlockID, suite.GetProtocolStateEntry)
    88  
    89  	suite.defaultProtocolStateID = unittest.IdentifierFixture()
    90  	suite.protocolStateEntries[suite.defaultProtocolStateID] = suite.ProtocolStateEntryWrapperFixture()
    91  
    92  	priorBlock := suite.BlockFixture()
    93  	priorReceipt, priorSeal := unittest.ReceiptAndSealForBlock(&priorBlock)
    94  	suite.results[priorReceipt.ExecutionResult.ID()] = &priorReceipt.ExecutionResult
    95  	suite.priorBlock = &priorBlock
    96  	suite.priorReceipt = priorReceipt
    97  	suite.priorSeal = priorSeal
    98  }
    99  
   100  // BlockFixture returns a Block fixture with the default protocol state ID.
   101  func (suite *SealingSegmentSuite) BlockFixture() flow.Block {
   102  	block := unittest.BlockFixture()
   103  	block.SetPayload(suite.PayloadFixture())
   104  	return block
   105  }
   106  
   107  // PayloadFixture returns a Payload fixture with the default protocol state ID.
   108  func (suite *SealingSegmentSuite) PayloadFixture(opts ...func(payload *flow.Payload)) flow.Payload {
   109  	opts = append(opts, unittest.WithProtocolStateID(suite.defaultProtocolStateID))
   110  	return unittest.PayloadFixture(opts...)
   111  }
   112  
   113  // BlockWithParentFixture returns a Block fixtures with the default protocol state.
   114  func (suite *SealingSegmentSuite) BlockWithParentFixture(parent *flow.Header) *flow.Block {
   115  	block := unittest.BlockWithParentFixture(parent)
   116  	block.SetPayload(suite.PayloadFixture())
   117  	return block
   118  }
   119  
   120  // ProtocolStateEntryWrapperFixture returns a ProtocolStateEntryWrapper.
   121  // For these tests, only the ID matters, so we can just return an empty struct.
   122  func (suite *SealingSegmentSuite) ProtocolStateEntryWrapperFixture() *flow.ProtocolStateEntryWrapper {
   123  	return &flow.ProtocolStateEntryWrapper{}
   124  }
   125  
   126  // FirstBlock returns a first block which contains a seal and receipt referencing
   127  // priorBlock (this is the simplest case for a sealing segment).
   128  func (suite *SealingSegmentSuite) FirstBlock() *flow.Block {
   129  	block := suite.BlockFixture()
   130  	block.SetPayload(suite.PayloadFixture(
   131  		unittest.WithSeals(suite.priorSeal),
   132  		unittest.WithReceipts(suite.priorReceipt),
   133  	))
   134  	suite.addSeal(block.ID(), suite.priorSeal)
   135  	return &block
   136  }
   137  
   138  // AddBlocks is a short-hand for adding a sequence of blocks, in order.
   139  // No errors are expected.
   140  func (suite *SealingSegmentSuite) AddBlocks(blocks ...*flow.Block) {
   141  	latestSeal := suite.priorSeal
   142  	for _, block := range blocks {
   143  		// before adding block, ensure its latest seal is indexed in suite
   144  		// convention for this test: seals are ordered by height of the sealed block
   145  		for _, seal := range block.Payload.Seals {
   146  			latestSeal = seal
   147  		}
   148  		suite.addSeal(block.ID(), latestSeal)
   149  		err := suite.builder.AddBlock(block)
   150  		require.NoError(suite.T(), err)
   151  	}
   152  }
   153  
   154  // Tests the case where a receipt in the segment references a result outside it.
   155  // The result should still be included in the sealing segment.
   156  //
   157  // B1(R*,S*) <- B2(R1) <- B4(S1)
   158  func (suite *SealingSegmentSuite) TestBuild_MissingResultFromReceipt() {
   159  
   160  	// B1 contains a receipt (but no result) and seal for a prior block
   161  	block1 := suite.BlockFixture()
   162  	block1.SetPayload(suite.PayloadFixture(unittest.WithReceiptsAndNoResults(suite.priorReceipt), unittest.WithSeals(suite.priorSeal)))
   163  
   164  	block2 := suite.BlockWithParentFixture(block1.Header)
   165  	receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1)
   166  	block2.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt1)))
   167  
   168  	block3 := suite.BlockWithParentFixture(block2.Header)
   169  	block3.SetPayload(suite.PayloadFixture(unittest.WithSeals(seal1)))
   170  
   171  	suite.AddBlocks(&block1, block2, block3)
   172  
   173  	segment, err := suite.builder.SealingSegment()
   174  	require.NoError(suite.T(), err)
   175  	require.NoError(suite.T(), segment.Validate())
   176  
   177  	unittest.AssertEqualBlocksLenAndOrder(suite.T(), []*flow.Block{&block1, block2, block3}, segment.Blocks)
   178  	require.Equal(suite.T(), 1, segment.ExecutionResults.Size())
   179  	require.Equal(suite.T(), suite.priorReceipt.ExecutionResult.ID(), segment.ExecutionResults[0].ID())
   180  }
   181  
   182  // Tests the case where the first block contains no seal.
   183  // The latest seal as of the first block should still be included in the segment.
   184  //
   185  // B1 <- B2(R1) <- B3(S1)
   186  func (suite *SealingSegmentSuite) TestBuild_MissingFirstBlockSeal() {
   187  
   188  	// B1 contains an empty payload
   189  	block1 := suite.BlockFixture()
   190  	// latest seal as of B1 is priorSeal
   191  	suite.sealsByBlockID[block1.ID()] = suite.priorSeal
   192  
   193  	receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1)
   194  	block2 := suite.BlockWithParentFixture(block1.Header)
   195  	block2.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt1)))
   196  
   197  	block3 := suite.BlockWithParentFixture(block2.Header)
   198  	block3.SetPayload(suite.PayloadFixture(unittest.WithSeals(seal1)))
   199  
   200  	suite.AddBlocks(&block1, block2, block3)
   201  
   202  	segment, err := suite.builder.SealingSegment()
   203  	require.NoError(suite.T(), err)
   204  	require.NoError(suite.T(), segment.Validate())
   205  
   206  	unittest.AssertEqualBlocksLenAndOrder(suite.T(), []*flow.Block{&block1, block2, block3}, segment.Blocks)
   207  	// should contain priorSeal as first seal
   208  	require.Equal(suite.T(), suite.priorSeal, segment.FirstSeal)
   209  	// should contain result referenced by first seal
   210  	require.Equal(suite.T(), 1, segment.ExecutionResults.Size())
   211  	require.Equal(suite.T(), suite.priorReceipt.ExecutionResult.ID(), segment.ExecutionResults[0].ID())
   212  }
   213  
   214  // Tests the case where a seal contained in a segment block payloads references
   215  // a missing result. The result should still be included in the segment.
   216  //
   217  // B1(S*,R*) <- B2(R1,S**) <- B3(S1)
   218  func (suite *SealingSegmentSuite) TestBuild_MissingResultFromPayloadSeal() {
   219  
   220  	block1 := suite.FirstBlock()
   221  
   222  	// create a seal referencing some past receipt/block
   223  	pastResult := unittest.ExecutionResultFixture()
   224  	suite.addResult(pastResult)
   225  	pastSeal := unittest.Seal.Fixture()
   226  	pastSeal.ResultID = pastResult.ID()
   227  
   228  	receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   229  	block2 := suite.BlockWithParentFixture(block1.Header)
   230  	block2.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt1), unittest.WithSeals(pastSeal)))
   231  
   232  	block3 := suite.BlockWithParentFixture(block2.Header)
   233  	block3.SetPayload(suite.PayloadFixture(unittest.WithSeals(seal1)))
   234  
   235  	suite.AddBlocks(block1, block2, block3)
   236  
   237  	segment, err := suite.builder.SealingSegment()
   238  	require.NoError(suite.T(), err)
   239  	require.NoError(suite.T(), segment.Validate())
   240  
   241  	unittest.AssertEqualBlocksLenAndOrder(suite.T(), []*flow.Block{block1, block2, block3}, segment.Blocks)
   242  	require.Equal(suite.T(), 1, segment.ExecutionResults.Size())
   243  	require.Equal(suite.T(), pastResult.ID(), segment.ExecutionResults[0].ID())
   244  }
   245  
   246  // Tests the case where the final block in the segment contains both a seal
   247  // for lowest, and a seal for a block above lowest. This should be considered
   248  // an invalid segment.
   249  //
   250  // B1(S*,R*) <- B2 <- B3(R1,R2) <- B4(S1,S2)
   251  func (suite *SealingSegmentSuite) TestBuild_WrongLatestSeal() {
   252  
   253  	block1 := suite.FirstBlock()
   254  	block2 := suite.BlockWithParentFixture(block1.Header)
   255  
   256  	receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   257  	receipt2, seal2 := unittest.ReceiptAndSealForBlock(block2)
   258  
   259  	block3 := suite.BlockWithParentFixture(block2.Header)
   260  	block3.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt1, receipt2)))
   261  
   262  	block4 := suite.BlockWithParentFixture(block3.Header)
   263  	block4.SetPayload(suite.PayloadFixture(unittest.WithSeals(seal1, seal2)))
   264  
   265  	suite.AddBlocks(block1, block2, block3, block4)
   266  
   267  	_, err := suite.builder.SealingSegment()
   268  	require.True(suite.T(), flow.IsInvalidSealingSegmentError(err))
   269  }
   270  
   271  // Tests the case where the final block in the segment seals multiple
   272  // blocks, but the latest sealed is still lowest, hence it is a valid
   273  // sealing segment.
   274  //
   275  // B1(S*,R*) <- B2(R1) <- B3(S**,S1)
   276  func (suite *SealingSegmentSuite) TestBuild_MultipleFinalBlockSeals() {
   277  
   278  	block1 := suite.FirstBlock()
   279  
   280  	receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   281  	// create a seal referencing some past receipt/block
   282  	pastResult := unittest.ExecutionResultFixture()
   283  	suite.addResult(pastResult)
   284  	pastSeal := unittest.Seal.Fixture()
   285  	pastSeal.ResultID = pastResult.ID()
   286  
   287  	block2 := suite.BlockWithParentFixture(block1.Header)
   288  	block2.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt1)))
   289  
   290  	block3 := suite.BlockWithParentFixture(block2.Header)
   291  	block3.SetPayload(suite.PayloadFixture(unittest.WithSeals(pastSeal, seal1)))
   292  
   293  	suite.AddBlocks(block1, block2, block3)
   294  
   295  	segment, err := suite.builder.SealingSegment()
   296  	require.NoError(suite.T(), err)
   297  
   298  	unittest.AssertEqualBlocksLenAndOrder(suite.T(), []*flow.Block{block1, block2, block3}, segment.Blocks)
   299  	require.Equal(suite.T(), 1, segment.ExecutionResults.Size())
   300  	require.Equal(suite.T(), pastResult.ID(), segment.ExecutionResults[0].ID())
   301  	require.NoError(suite.T(), segment.Validate())
   302  }
   303  
   304  // TestBuild_RootSegment tests we can build a valid root sealing segment.
   305  func (suite *SealingSegmentSuite) TestBuild_RootSegment() {
   306  
   307  	root, result, seal := unittest.BootstrapFixture(unittest.IdentityListFixture(5, unittest.WithAllRoles()))
   308  	suite.sealsByBlockID[root.ID()] = seal
   309  	suite.addProtocolStateEntry(root.Payload.ProtocolStateID, suite.ProtocolStateEntryWrapperFixture())
   310  	suite.addResult(result)
   311  	err := suite.builder.AddBlock(root)
   312  	require.NoError(suite.T(), err)
   313  
   314  	segment, err := suite.builder.SealingSegment()
   315  	require.NoError(suite.T(), err)
   316  	require.NoError(suite.T(), segment.Validate())
   317  
   318  	unittest.AssertEqualBlocksLenAndOrder(suite.T(), []*flow.Block{root}, segment.Blocks)
   319  	require.Equal(suite.T(), segment.Highest().ID(), root.ID())
   320  	require.Equal(suite.T(), segment.Sealed().ID(), root.ID())
   321  }
   322  
   323  // TestBuild_RootSegmentWrongView tests that we return ErrSegmentInvalidRootView for
   324  // a single-block sealing segment with a block view not equal to 0.
   325  func (suite *SealingSegmentSuite) TestBuild_RootSegmentWrongView() {
   326  
   327  	root, result, seal := unittest.BootstrapFixture(
   328  		unittest.IdentityListFixture(5, unittest.WithAllRoles()),
   329  		func(block *flow.Block) {
   330  			block.Header.View = 10 // invalid root block view
   331  		})
   332  	suite.sealsByBlockID[root.ID()] = seal
   333  	suite.addProtocolStateEntry(root.Payload.ProtocolStateID, suite.ProtocolStateEntryWrapperFixture())
   334  	suite.addResult(result)
   335  	err := suite.builder.AddBlock(root)
   336  	require.NoError(suite.T(), err)
   337  
   338  	_, err = suite.builder.SealingSegment()
   339  	require.Error(suite.T(), err)
   340  }
   341  
   342  // Test the case when the highest block in the segment does not contain seals but
   343  // the first ancestor of the highest block does contain a seal for lowest,
   344  // we return a valid sealing segment.
   345  //
   346  // B1(S*) <- B2(R1) <- B3(S1) <- B4
   347  func (suite *SealingSegmentSuite) TestBuild_HighestContainsNoSeals() {
   348  	block1 := suite.FirstBlock()
   349  
   350  	receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   351  	block2 := suite.BlockWithParentFixture(block1.Header)
   352  	block2.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt1)))
   353  
   354  	block3 := suite.BlockWithParentFixture(block2.Header)
   355  	block3.SetPayload(suite.PayloadFixture(unittest.WithSeals(seal1)))
   356  
   357  	block4 := suite.BlockWithParentFixture(block3.Header)
   358  
   359  	suite.AddBlocks(block1, block2, block3, block4)
   360  
   361  	segment, err := suite.builder.SealingSegment()
   362  	require.NoError(suite.T(), err)
   363  	require.NoError(suite.T(), segment.Validate())
   364  
   365  	unittest.AssertEqualBlocksLenAndOrder(suite.T(), []*flow.Block{block1, block2, block3, block4}, segment.Blocks)
   366  }
   367  
   368  // Test that we should return InvalidSealingSegmentError if highest block contains
   369  // seals but does not contain seal for lowest, when sealing segment is built.
   370  //
   371  // B1(S*) <- B2(R1) <- B3(S1,R2) <- B4(S2)
   372  func (suite *SealingSegmentSuite) TestBuild_HighestContainsWrongSeal() {
   373  	block1 := suite.FirstBlock()
   374  
   375  	receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   376  	block2 := suite.BlockWithParentFixture(block1.Header)
   377  	block2.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt1)))
   378  
   379  	receipt2, seal2 := unittest.ReceiptAndSealForBlock(block2)
   380  	block3 := suite.BlockWithParentFixture(block2.Header)
   381  	block3.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt2), unittest.WithSeals(seal1)))
   382  
   383  	// highest block contains wrong seal - invalid
   384  	block4 := suite.BlockWithParentFixture(block3.Header)
   385  	block4.SetPayload(suite.PayloadFixture(unittest.WithSeals(seal2)))
   386  
   387  	suite.AddBlocks(block1, block2, block3, block4)
   388  
   389  	_, err := suite.builder.SealingSegment()
   390  	require.True(suite.T(), flow.IsInvalidSealingSegmentError(err))
   391  }
   392  
   393  // Test that we should return InvalidSealingSegmentError if highest block contains
   394  // no seals and first ancestor with seals does not seal lowest, when sealing
   395  // segment is built
   396  //
   397  // B1(S*) <- B2(R1) <- B3(S1,R2) <- B4(S2) <- B5
   398  func (suite *SealingSegmentSuite) TestBuild_HighestAncestorContainsWrongSeal() {
   399  	block1 := suite.FirstBlock()
   400  
   401  	receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1)
   402  	block2 := suite.BlockWithParentFixture(block1.Header)
   403  	block2.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt1)))
   404  
   405  	receipt2, seal2 := unittest.ReceiptAndSealForBlock(block2)
   406  	block3 := suite.BlockWithParentFixture(block2.Header)
   407  	block3.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt2), unittest.WithSeals(seal1)))
   408  
   409  	// ancestor of highest block contains wrong seal - invalid
   410  	block4 := suite.BlockWithParentFixture(block3.Header)
   411  	block4.SetPayload(suite.PayloadFixture(unittest.WithSeals(seal2)))
   412  
   413  	block5 := suite.BlockWithParentFixture(block4.Header)
   414  
   415  	suite.AddBlocks(block1, block2, block3, block4, block5)
   416  
   417  	_, err := suite.builder.SealingSegment()
   418  	require.True(suite.T(), flow.IsInvalidSealingSegmentError(err))
   419  }
   420  
   421  // TestBuild_ChangingProtocolStateID_Blocks tests constructing a sealing segment where
   422  // the primary segment (`Blocks`) contains blocks with different protocol state entries.
   423  // In this test, B1 commits to the default protocol state ID, and blocks B2 and B3
   424  // commit to a different protocol state ID (PS2).
   425  // B1(R*,S*) <- B2(R1,PS2) <- B3(S1,PS2)
   426  func (suite *SealingSegmentSuite) TestBuild_ChangingProtocolStateID_Blocks() {
   427  	block1 := suite.BlockFixture()
   428  	block1.SetPayload(suite.PayloadFixture(unittest.WithReceipts(suite.priorReceipt), unittest.WithSeals(suite.priorSeal)))
   429  
   430  	protocolStateID2 := unittest.IdentifierFixture()
   431  	suite.addProtocolStateEntry(protocolStateID2, suite.ProtocolStateEntryWrapperFixture())
   432  
   433  	block2 := suite.BlockWithParentFixture(block1.Header)
   434  	receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1)
   435  	block2.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt1), unittest.WithProtocolStateID(protocolStateID2)))
   436  
   437  	block3 := suite.BlockWithParentFixture(block2.Header)
   438  	block3.SetPayload(suite.PayloadFixture(unittest.WithSeals(seal1), unittest.WithProtocolStateID(protocolStateID2)))
   439  
   440  	suite.AddBlocks(&block1, block2, block3)
   441  
   442  	segment, err := suite.builder.SealingSegment()
   443  	require.NoError(suite.T(), err)
   444  	require.NoError(suite.T(), segment.Validate())
   445  
   446  	unittest.AssertEqualBlocksLenAndOrder(suite.T(), []*flow.Block{&block1, block2, block3}, segment.Blocks)
   447  	// resulting segment must contain both protocol state IDs
   448  	assert.Len(suite.T(), segment.ProtocolStateEntries, 2)
   449  	_, ok := segment.ProtocolStateEntries[suite.defaultProtocolStateID]
   450  	assert.True(suite.T(), ok)
   451  	_, ok = segment.ProtocolStateEntries[protocolStateID2]
   452  	assert.True(suite.T(), ok)
   453  }
   454  
   455  // TestBuild_ChangingProtocolStateID_ExtraBlocks tests constructing a sealing segment where
   456  // `ExtraBlocks` contains blocks with different protocol state entries.
   457  // In this test, blocks B1, B2, and B3 commits to the default protocol state ID.
   458  // Extra blocks EB1 and EB2 commit to a different protocol state ID (PS2).
   459  // EB2(PS2) <- EB1 <- B1(R*,S*) <- B2(R1) <- B3(S1)
   460  func (suite *SealingSegmentSuite) TestBuild_ChangingProtocolStateID_ExtraBlocks() {
   461  	block1 := suite.BlockFixture()
   462  	block1.SetPayload(suite.PayloadFixture(unittest.WithReceipts(suite.priorReceipt), unittest.WithSeals(suite.priorSeal)))
   463  
   464  	block2 := suite.BlockWithParentFixture(block1.Header)
   465  	receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1)
   466  	block2.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt1)))
   467  
   468  	block3 := suite.BlockWithParentFixture(block2.Header)
   469  	block3.SetPayload(suite.PayloadFixture(unittest.WithSeals(seal1)))
   470  
   471  	suite.AddBlocks(&block1, block2, block3)
   472  
   473  	// construct two extra blocks that connect to the lowest block and add them to builder
   474  	protocolStateID2 := unittest.IdentifierFixture()
   475  	suite.addProtocolStateEntry(protocolStateID2, suite.ProtocolStateEntryWrapperFixture())
   476  
   477  	extraBlock1 := suite.BlockFixture()
   478  	extraBlock1.Header.Height = block1.Header.Height - 1
   479  	extraBlock1.SetPayload(unittest.PayloadFixture(unittest.WithProtocolStateID(protocolStateID2)))
   480  	err := suite.builder.AddExtraBlock(&extraBlock1)
   481  	require.NoError(suite.T(), err)
   482  
   483  	extraBlock2 := suite.BlockFixture()
   484  	extraBlock2.Header.Height = extraBlock1.Header.Height - 1
   485  	err = suite.builder.AddExtraBlock(&extraBlock2)
   486  	require.NoError(suite.T(), err)
   487  
   488  	segment, err := suite.builder.SealingSegment()
   489  	require.NoError(suite.T(), err)
   490  	err = segment.Validate()
   491  	require.NoError(suite.T(), err)
   492  
   493  	unittest.AssertEqualBlocksLenAndOrder(suite.T(), []*flow.Block{&block1, block2, block3}, segment.Blocks)
   494  	unittest.AssertEqualBlocksLenAndOrder(suite.T(), []*flow.Block{&extraBlock2, &extraBlock1}, segment.ExtraBlocks)
   495  	// resulting segment must contain both protocol state IDs
   496  	assert.Len(suite.T(), segment.ProtocolStateEntries, 2)
   497  	_, ok := segment.ProtocolStateEntries[suite.defaultProtocolStateID]
   498  	assert.True(suite.T(), ok)
   499  	_, ok = segment.ProtocolStateEntries[protocolStateID2]
   500  	assert.True(suite.T(), ok)
   501  }
   502  
   503  // Test that we should return InvalidSealingSegmentError if sealing segment is
   504  // built with no blocks.
   505  func (suite *SealingSegmentSuite) TestBuild_NoBlocks() {
   506  	builder := flow.NewSealingSegmentBuilder(nil, nil, nil)
   507  	_, err := builder.SealingSegment()
   508  	require.True(suite.T(), flow.IsInvalidSealingSegmentError(err))
   509  }
   510  
   511  // should return InvalidSealingSegmentError if block has invalid height
   512  func (suite *SealingSegmentSuite) TestAddBlock_InvalidHeight() {
   513  
   514  	block1 := suite.FirstBlock()
   515  	// block 2 has an invalid height
   516  	block2 := suite.BlockFixture()
   517  	block2.Header.Height = block1.Header.Height + 2
   518  
   519  	err := suite.builder.AddBlock(block1)
   520  	require.NoError(suite.T(), err)
   521  
   522  	err = suite.builder.AddBlock(&block2)
   523  	require.True(suite.T(), flow.IsInvalidSealingSegmentError(err))
   524  }
   525  
   526  // TestAddBlock_StorageError tests that errors in the resource getters bubble up.
   527  func TestAddBlock_StorageError(t *testing.T) {
   528  
   529  	t.Run("missing result", func(t *testing.T) {
   530  		// create a receipt to include in the first block, whose result is not in storage
   531  		missingReceipt := unittest.ExecutionReceiptFixture()
   532  		block1 := unittest.BlockFixture()
   533  		exception := fmt.Errorf("")
   534  		sealLookup := func(flow.Identifier) (*flow.Seal, error) { return unittest.Seal.Fixture(), nil }
   535  		resultLookup := func(flow.Identifier) (*flow.ExecutionResult, error) { return nil, exception }
   536  		protocolStateEntryLookup := func(flow.Identifier) (*flow.ProtocolStateEntryWrapper, error) {
   537  			return &flow.ProtocolStateEntryWrapper{}, nil
   538  		}
   539  		builder := flow.NewSealingSegmentBuilder(resultLookup, sealLookup, protocolStateEntryLookup)
   540  
   541  		block1.SetPayload(unittest.PayloadFixture(
   542  			unittest.WithReceiptsAndNoResults(missingReceipt),
   543  			unittest.WithSeals(unittest.Seal.Fixture(unittest.Seal.WithResult(&missingReceipt.ExecutionResult))),
   544  		))
   545  
   546  		err := builder.AddBlock(&block1)
   547  		require.ErrorIs(t, err, exception)
   548  	})
   549  
   550  	// create a first block which contains no seal, and the seal isn't in storage
   551  	t.Run("missing seal", func(t *testing.T) {
   552  		exception := fmt.Errorf("")
   553  		resultLookup := func(flow.Identifier) (*flow.ExecutionResult, error) { return unittest.ExecutionResultFixture(), nil }
   554  		sealLookup := func(flow.Identifier) (*flow.Seal, error) { return nil, exception }
   555  		protocolStateEntryLookup := func(flow.Identifier) (*flow.ProtocolStateEntryWrapper, error) {
   556  			return &flow.ProtocolStateEntryWrapper{}, nil
   557  		}
   558  		block1 := unittest.BlockFixture()
   559  		block1.SetPayload(flow.EmptyPayload())
   560  		builder := flow.NewSealingSegmentBuilder(resultLookup, sealLookup, protocolStateEntryLookup)
   561  
   562  		err := builder.AddBlock(&block1)
   563  		require.ErrorIs(t, err, exception)
   564  	})
   565  
   566  	t.Run("missing protocol state entry", func(t *testing.T) {
   567  		exception := fmt.Errorf("")
   568  		resultLookup := func(flow.Identifier) (*flow.ExecutionResult, error) { return unittest.ExecutionResultFixture(), nil }
   569  		sealLookup := func(flow.Identifier) (*flow.Seal, error) { return unittest.Seal.Fixture(), nil }
   570  		protocolStateEntryLookup := func(flow.Identifier) (*flow.ProtocolStateEntryWrapper, error) { return nil, exception }
   571  		block1 := unittest.BlockFixture()
   572  		block1.SetPayload(flow.EmptyPayload())
   573  		builder := flow.NewSealingSegmentBuilder(resultLookup, sealLookup, protocolStateEntryLookup)
   574  
   575  		err := builder.AddBlock(&block1)
   576  		require.ErrorIs(t, err, exception)
   577  	})
   578  }
   579  
   580  // TestAddExtraBlock tests different scenarios for adding extra blocks, covers happy and unhappy path scenarios.
   581  func (suite *SealingSegmentSuite) TestAddExtraBlock() {
   582  	// populate sealing segment with one block
   583  	firstBlock := suite.FirstBlock()
   584  	firstBlock.Header.Height += 100
   585  	suite.AddBlocks(firstBlock)
   586  
   587  	suite.T().Run("empty-segment", func(t *testing.T) {
   588  		builder := flow.NewSealingSegmentBuilder(nil, nil, nil)
   589  		block := suite.BlockFixture()
   590  		err := builder.AddExtraBlock(&block)
   591  		require.Error(t, err)
   592  	})
   593  	suite.T().Run("extra-block-does-not-connect", func(t *testing.T) {
   594  		// adding extra block that doesn't connect to the lowest is an error
   595  		extraBlock := suite.BlockFixture()
   596  		extraBlock.Header.Height = firstBlock.Header.Height + 10 // make sure it doesn't connect by height
   597  		err := suite.builder.AddExtraBlock(&extraBlock)
   598  		require.True(suite.T(), flow.IsInvalidSealingSegmentError(err))
   599  	})
   600  	suite.T().Run("extra-block-not-continuous", func(t *testing.T) {
   601  		builder := flow.NewSealingSegmentBuilder(suite.GetResult, suite.GetSealByBlockID, suite.GetProtocolStateEntry)
   602  		err := builder.AddBlock(firstBlock)
   603  		require.NoError(t, err)
   604  		extraBlock := suite.BlockFixture()
   605  		extraBlock.Header.Height = firstBlock.Header.Height - 1 // make it connect
   606  		err = builder.AddExtraBlock(&extraBlock)
   607  		require.NoError(t, err)
   608  		extraBlockWithSkip := suite.BlockFixture()
   609  		extraBlockWithSkip.Header.Height = extraBlock.Header.Height - 2 // skip one height
   610  		err = builder.AddExtraBlock(&extraBlockWithSkip)
   611  		require.True(suite.T(), flow.IsInvalidSealingSegmentError(err))
   612  	})
   613  	suite.T().Run("root-segment-extra-blocks", func(t *testing.T) {
   614  		builder := flow.NewSealingSegmentBuilder(suite.GetResult, suite.GetSealByBlockID, suite.GetProtocolStateEntry)
   615  		err := builder.AddBlock(firstBlock)
   616  		require.NoError(t, err)
   617  
   618  		extraBlock := suite.BlockFixture()
   619  		extraBlock.Header.Height = firstBlock.Header.Height - 1
   620  		err = builder.AddExtraBlock(&extraBlock)
   621  		require.NoError(t, err)
   622  		_, err = builder.SealingSegment()
   623  		// root segment cannot have extra blocks
   624  		require.Error(t, err)
   625  	})
   626  	suite.T().Run("happy-path", func(t *testing.T) {
   627  		// add a few blocks with results and seals to form a valid sealing segment
   628  		// B1(S*) <- B2(R1) <- B3(S1)
   629  
   630  		receipt, seal := unittest.ReceiptAndSealForBlock(firstBlock)
   631  		blockWithER := suite.BlockWithParentFixture(firstBlock.Header)
   632  		blockWithER.SetPayload(suite.PayloadFixture(unittest.WithReceipts(receipt)))
   633  
   634  		// add one more block, with seal to the ER
   635  		highestBlock := suite.BlockWithParentFixture(blockWithER.Header)
   636  		highestBlock.SetPayload(suite.PayloadFixture(unittest.WithSeals(seal)))
   637  
   638  		suite.AddBlocks(blockWithER, highestBlock)
   639  
   640  		// construct two extra blocks that connect to the lowest block and add them to builder
   641  		// EB2 <- EB1 <- B1(S*) <- B2(R1) <- B3(S1)
   642  		extraBlock := suite.BlockFixture()
   643  		extraBlock.Header.Height = firstBlock.Header.Height - 1
   644  		err := suite.builder.AddExtraBlock(&extraBlock)
   645  		require.NoError(t, err)
   646  		secondExtraBlock := suite.BlockFixture()
   647  		secondExtraBlock.Header.Height = extraBlock.Header.Height - 1
   648  		err = suite.builder.AddExtraBlock(&secondExtraBlock)
   649  		require.NoError(t, err)
   650  		segment, err := suite.builder.SealingSegment()
   651  		require.NoError(t, err)
   652  		err = segment.Validate()
   653  		require.NoError(t, err)
   654  	})
   655  }