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