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