github.com/onflow/flow-go@v0.33.17/state/protocol/badger/state_test.go (about) 1 package badger_test 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "testing" 8 9 "github.com/dgraph-io/badger/v2" 10 "github.com/stretchr/testify/assert" 11 testmock "github.com/stretchr/testify/mock" 12 "github.com/stretchr/testify/require" 13 14 "github.com/onflow/flow-go/model/flow" 15 "github.com/onflow/flow-go/module/metrics" 16 "github.com/onflow/flow-go/module/mock" 17 "github.com/onflow/flow-go/state/protocol" 18 bprotocol "github.com/onflow/flow-go/state/protocol/badger" 19 "github.com/onflow/flow-go/state/protocol/inmem" 20 "github.com/onflow/flow-go/state/protocol/util" 21 protoutil "github.com/onflow/flow-go/state/protocol/util" 22 storagebadger "github.com/onflow/flow-go/storage/badger" 23 storutil "github.com/onflow/flow-go/storage/util" 24 "github.com/onflow/flow-go/utils/unittest" 25 ) 26 27 // TestBootstrapAndOpen verifies after bootstrapping with a root snapshot 28 // we should be able to open it and got the same state. 29 func TestBootstrapAndOpen(t *testing.T) { 30 31 // create a state root and bootstrap the protocol state with it 32 participants := unittest.CompleteIdentitySet() 33 rootSnapshot := unittest.RootSnapshotFixture(participants, func(block *flow.Block) { 34 block.Header.ParentID = unittest.IdentifierFixture() 35 }) 36 37 protoutil.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, _ *bprotocol.State) { 38 39 // expect the final view metric to be set to current epoch's final view 40 epoch := rootSnapshot.Epochs().Current() 41 finalView, err := epoch.FinalView() 42 require.NoError(t, err) 43 counter, err := epoch.Counter() 44 require.NoError(t, err) 45 phase, err := rootSnapshot.Phase() 46 require.NoError(t, err) 47 48 complianceMetrics := new(mock.ComplianceMetrics) 49 complianceMetrics.On("CommittedEpochFinalView", finalView).Once() 50 complianceMetrics.On("CurrentEpochCounter", counter).Once() 51 complianceMetrics.On("CurrentEpochPhase", phase).Once() 52 complianceMetrics.On("CurrentEpochFinalView", finalView).Once() 53 complianceMetrics.On("FinalizedHeight", testmock.Anything).Once() 54 complianceMetrics.On("SealedHeight", testmock.Anything).Once() 55 56 dkgPhase1FinalView, dkgPhase2FinalView, dkgPhase3FinalView, err := protocol.DKGPhaseViews(epoch) 57 require.NoError(t, err) 58 complianceMetrics.On("CurrentDKGPhase1FinalView", dkgPhase1FinalView).Once() 59 complianceMetrics.On("CurrentDKGPhase2FinalView", dkgPhase2FinalView).Once() 60 complianceMetrics.On("CurrentDKGPhase3FinalView", dkgPhase3FinalView).Once() 61 62 noopMetrics := new(metrics.NoopCollector) 63 all := storagebadger.InitAll(noopMetrics, db) 64 // protocol state has been bootstrapped, now open a protocol state with the database 65 state, err := bprotocol.OpenState( 66 complianceMetrics, 67 db, 68 all.Headers, 69 all.Seals, 70 all.Results, 71 all.Blocks, 72 all.QuorumCertificates, 73 all.Setups, 74 all.EpochCommits, 75 all.Statuses, 76 all.VersionBeacons, 77 ) 78 require.NoError(t, err) 79 80 complianceMetrics.AssertExpectations(t) 81 82 unittest.AssertSnapshotsEqual(t, rootSnapshot, state.Final()) 83 84 vb, err := state.Final().VersionBeacon() 85 require.NoError(t, err) 86 require.Nil(t, vb) 87 }) 88 } 89 90 // TestBootstrapAndOpen_EpochCommitted verifies after bootstrapping with a 91 // root snapshot from EpochCommitted phase we should be able to open it and 92 // got the same state. 93 func TestBootstrapAndOpen_EpochCommitted(t *testing.T) { 94 95 // create a state root and bootstrap the protocol state with it 96 participants := unittest.CompleteIdentitySet() 97 rootSnapshot := unittest.RootSnapshotFixture(participants, func(block *flow.Block) { 98 block.Header.ParentID = unittest.IdentifierFixture() 99 }) 100 rootBlock, err := rootSnapshot.Head() 101 require.NoError(t, err) 102 103 // build an epoch on the root state and return a snapshot from the committed phase 104 committedPhaseSnapshot := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { 105 unittest.NewEpochBuilder(t, state).BuildEpoch().CompleteEpoch() 106 107 // find the point where we transition to the epoch committed phase 108 for height := rootBlock.Height + 1; ; height++ { 109 phase, err := state.AtHeight(height).Phase() 110 require.NoError(t, err) 111 if phase == flow.EpochPhaseCommitted { 112 return state.AtHeight(height) 113 } 114 } 115 }) 116 117 protoutil.RunWithBootstrapState(t, committedPhaseSnapshot, func(db *badger.DB, _ *bprotocol.State) { 118 119 complianceMetrics := new(mock.ComplianceMetrics) 120 121 // expect the final view metric to be set to next epoch's final view 122 finalView, err := committedPhaseSnapshot.Epochs().Next().FinalView() 123 require.NoError(t, err) 124 complianceMetrics.On("CommittedEpochFinalView", finalView).Once() 125 126 // expect counter to be set to current epochs counter 127 counter, err := committedPhaseSnapshot.Epochs().Current().Counter() 128 require.NoError(t, err) 129 complianceMetrics.On("CurrentEpochCounter", counter).Once() 130 131 // expect epoch phase to be set to current phase 132 phase, err := committedPhaseSnapshot.Phase() 133 require.NoError(t, err) 134 complianceMetrics.On("CurrentEpochPhase", phase).Once() 135 136 currentEpochFinalView, err := committedPhaseSnapshot.Epochs().Current().FinalView() 137 require.NoError(t, err) 138 complianceMetrics.On("CurrentEpochFinalView", currentEpochFinalView).Once() 139 140 dkgPhase1FinalView, dkgPhase2FinalView, dkgPhase3FinalView, err := protocol.DKGPhaseViews(committedPhaseSnapshot.Epochs().Current()) 141 require.NoError(t, err) 142 complianceMetrics.On("CurrentDKGPhase1FinalView", dkgPhase1FinalView).Once() 143 complianceMetrics.On("CurrentDKGPhase2FinalView", dkgPhase2FinalView).Once() 144 complianceMetrics.On("CurrentDKGPhase3FinalView", dkgPhase3FinalView).Once() 145 complianceMetrics.On("FinalizedHeight", testmock.Anything).Once() 146 complianceMetrics.On("SealedHeight", testmock.Anything).Once() 147 148 noopMetrics := new(metrics.NoopCollector) 149 all := storagebadger.InitAll(noopMetrics, db) 150 state, err := bprotocol.OpenState( 151 complianceMetrics, 152 db, 153 all.Headers, 154 all.Seals, 155 all.Results, 156 all.Blocks, 157 all.QuorumCertificates, 158 all.Setups, 159 all.EpochCommits, 160 all.Statuses, 161 all.VersionBeacons, 162 ) 163 require.NoError(t, err) 164 165 // assert update final view was called 166 complianceMetrics.AssertExpectations(t) 167 168 unittest.AssertSnapshotsEqual(t, committedPhaseSnapshot, state.Final()) 169 }) 170 } 171 172 // TestBootstrap_EpochHeightBoundaries tests that epoch height indexes are indexed 173 // when they are available in the input snapshot. 174 func TestBootstrap_EpochHeightBoundaries(t *testing.T) { 175 t.Parallel() 176 // start with a regular post-spork root snapshot 177 rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) 178 epoch1FirstHeight := rootSnapshot.Encodable().Head.Height 179 180 t.Run("root snapshot", func(t *testing.T) { 181 util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { 182 // first height of started current epoch should be known 183 firstHeight, err := state.Final().Epochs().Current().FirstHeight() 184 require.NoError(t, err) 185 assert.Equal(t, epoch1FirstHeight, firstHeight) 186 // final height of not completed current epoch should be unknown 187 _, err = state.Final().Epochs().Current().FinalHeight() 188 assert.ErrorIs(t, err, protocol.ErrEpochTransitionNotFinalized) 189 }) 190 }) 191 192 t.Run("with next epoch", func(t *testing.T) { 193 after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { 194 builder := unittest.NewEpochBuilder(t, state) 195 builder.BuildEpoch().CompleteEpoch() 196 heights, ok := builder.EpochHeights(1) 197 require.True(t, ok) 198 return state.AtHeight(heights.Committed) 199 }) 200 201 bootstrap(t, after, func(state *bprotocol.State, err error) { 202 require.NoError(t, err) 203 // first height of started current epoch should be known 204 firstHeight, err := state.Final().Epochs().Current().FirstHeight() 205 assert.Equal(t, epoch1FirstHeight, firstHeight) 206 require.NoError(t, err) 207 // final height of not completed current epoch should be unknown 208 _, err = state.Final().Epochs().Current().FinalHeight() 209 assert.ErrorIs(t, err, protocol.ErrEpochTransitionNotFinalized) 210 // first and final height of not started next epoch should be unknown 211 _, err = state.Final().Epochs().Next().FirstHeight() 212 assert.ErrorIs(t, err, protocol.ErrEpochTransitionNotFinalized) 213 _, err = state.Final().Epochs().Next().FinalHeight() 214 assert.ErrorIs(t, err, protocol.ErrEpochTransitionNotFinalized) 215 }) 216 }) 217 t.Run("with previous epoch", func(t *testing.T) { 218 var epoch1FinalHeight uint64 219 var epoch2FirstHeight uint64 220 after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { 221 builder := unittest.NewEpochBuilder(t, state) 222 builder. 223 BuildEpoch().CompleteEpoch(). // build epoch 2 224 BuildEpoch() // build epoch 3 225 heights, ok := builder.EpochHeights(2) 226 epoch2FirstHeight = heights.FirstHeight() 227 epoch1FinalHeight = epoch2FirstHeight - 1 228 require.True(t, ok) 229 // return snapshot from within epoch 2 (middle epoch) 230 return state.AtHeight(heights.Setup) 231 }) 232 233 bootstrap(t, after, func(state *bprotocol.State, err error) { 234 require.NoError(t, err) 235 // first height of started current epoch should be known 236 firstHeight, err := state.Final().Epochs().Current().FirstHeight() 237 assert.Equal(t, epoch2FirstHeight, firstHeight) 238 require.NoError(t, err) 239 // final height of not completed current epoch should be unknown 240 _, err = state.Final().Epochs().Current().FinalHeight() 241 assert.ErrorIs(t, err, protocol.ErrEpochTransitionNotFinalized) 242 // first and final height of completed previous epoch should be known 243 firstHeight, err = state.Final().Epochs().Previous().FirstHeight() 244 require.NoError(t, err) 245 assert.Equal(t, firstHeight, epoch1FirstHeight) 246 finalHeight, err := state.Final().Epochs().Previous().FinalHeight() 247 require.NoError(t, err) 248 assert.Equal(t, finalHeight, epoch1FinalHeight) 249 }) 250 }) 251 } 252 253 // TestBootstrapNonRoot tests bootstrapping the protocol state from arbitrary states. 254 // 255 // NOTE: for all these cases, we build a final child block (CHILD). This is 256 // needed otherwise the parent block would not have a valid QC, since the QC 257 // is stored in the child. 258 func TestBootstrapNonRoot(t *testing.T) { 259 t.Parallel() 260 // start with a regular post-spork root snapshot 261 participants := unittest.CompleteIdentitySet() 262 rootSnapshot := unittest.RootSnapshotFixture(participants) 263 rootBlock, err := rootSnapshot.Head() 264 require.NoError(t, err) 265 266 // should be able to bootstrap from snapshot after sealing a non-root block 267 // ROOT <- B1 <- B2(R1) <- B3(S1) <- CHILD 268 t.Run("with sealed block", func(t *testing.T) { 269 after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { 270 block1 := unittest.BlockWithParentFixture(rootBlock) 271 buildFinalizedBlock(t, state, block1) 272 273 receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1) 274 block2 := unittest.BlockWithParentFixture(block1.Header) 275 block2.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt1))) 276 buildFinalizedBlock(t, state, block2) 277 278 block3 := unittest.BlockWithParentFixture(block2.Header) 279 block3.SetPayload(unittest.PayloadFixture(unittest.WithSeals(seal1))) 280 buildFinalizedBlock(t, state, block3) 281 282 child := unittest.BlockWithParentFixture(block3.Header) 283 buildBlock(t, state, child) 284 285 return state.AtBlockID(block3.ID()) 286 }) 287 288 bootstrap(t, after, func(state *bprotocol.State, err error) { 289 require.NoError(t, err) 290 unittest.AssertSnapshotsEqual(t, after, state.Final()) 291 // should be able to read all QCs 292 segment, err := state.Final().SealingSegment() 293 require.NoError(t, err) 294 for _, block := range segment.Blocks { 295 snapshot := state.AtBlockID(block.ID()) 296 _, err := snapshot.QuorumCertificate() 297 require.NoError(t, err) 298 _, err = snapshot.RandomSource() 299 require.NoError(t, err) 300 } 301 }) 302 }) 303 304 t.Run("with setup next epoch", func(t *testing.T) { 305 after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { 306 unittest.NewEpochBuilder(t, state).BuildEpoch() 307 308 // find the point where we transition to the epoch setup phase 309 for height := rootBlock.Height + 1; ; height++ { 310 phase, err := state.AtHeight(height).Phase() 311 require.NoError(t, err) 312 if phase == flow.EpochPhaseSetup { 313 return state.AtHeight(height) 314 } 315 } 316 }) 317 318 bootstrap(t, after, func(state *bprotocol.State, err error) { 319 require.NoError(t, err) 320 unittest.AssertSnapshotsEqual(t, after, state.Final()) 321 }) 322 }) 323 324 t.Run("with committed next epoch", func(t *testing.T) { 325 after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { 326 unittest.NewEpochBuilder(t, state).BuildEpoch().CompleteEpoch() 327 328 // find the point where we transition to the epoch committed phase 329 for height := rootBlock.Height + 1; ; height++ { 330 phase, err := state.AtHeight(height).Phase() 331 require.NoError(t, err) 332 if phase == flow.EpochPhaseCommitted { 333 return state.AtHeight(height) 334 } 335 } 336 }) 337 338 bootstrap(t, after, func(state *bprotocol.State, err error) { 339 require.NoError(t, err) 340 unittest.AssertSnapshotsEqual(t, after, state.Final()) 341 }) 342 }) 343 344 t.Run("with previous and next epoch", func(t *testing.T) { 345 after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { 346 unittest.NewEpochBuilder(t, state). 347 BuildEpoch().CompleteEpoch(). // build epoch 2 348 BuildEpoch() // build epoch 3 349 350 // find a snapshot from epoch setup phase in epoch 2 351 epoch1Counter, err := rootSnapshot.Epochs().Current().Counter() 352 require.NoError(t, err) 353 for height := rootBlock.Height + 1; ; height++ { 354 snap := state.AtHeight(height) 355 counter, err := snap.Epochs().Current().Counter() 356 require.NoError(t, err) 357 phase, err := snap.Phase() 358 require.NoError(t, err) 359 if phase == flow.EpochPhaseSetup && counter == epoch1Counter+1 { 360 return snap 361 } 362 } 363 }) 364 365 bootstrap(t, after, func(state *bprotocol.State, err error) { 366 require.NoError(t, err) 367 unittest.AssertSnapshotsEqual(t, after, state.Final()) 368 }) 369 }) 370 } 371 372 func TestBootstrap_InvalidIdentities(t *testing.T) { 373 t.Run("duplicate node ID", func(t *testing.T) { 374 participants := unittest.CompleteIdentitySet() 375 dupeIDIdentity := unittest.IdentityFixture(unittest.WithNodeID(participants[0].NodeID)) 376 participants = append(participants, dupeIDIdentity) 377 378 root := unittest.RootSnapshotFixture(participants) 379 bootstrap(t, root, func(state *bprotocol.State, err error) { 380 assert.Error(t, err) 381 }) 382 }) 383 384 t.Run("zero weight", func(t *testing.T) { 385 zeroWeightIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification), unittest.WithWeight(0)) 386 participants := unittest.CompleteIdentitySet(zeroWeightIdentity) 387 root := unittest.RootSnapshotFixture(participants) 388 bootstrap(t, root, func(state *bprotocol.State, err error) { 389 assert.Error(t, err) 390 }) 391 }) 392 393 t.Run("missing role", func(t *testing.T) { 394 requiredRoles := []flow.Role{ 395 flow.RoleConsensus, 396 flow.RoleExecution, 397 flow.RoleVerification, 398 } 399 400 for _, role := range requiredRoles { 401 t.Run(fmt.Sprintf("no %s nodes", role), func(t *testing.T) { 402 participants := unittest.IdentityListFixture(5, unittest.WithAllRolesExcept(role)) 403 root := unittest.RootSnapshotFixture(participants) 404 bootstrap(t, root, func(state *bprotocol.State, err error) { 405 assert.Error(t, err) 406 }) 407 }) 408 } 409 }) 410 411 t.Run("duplicate address", func(t *testing.T) { 412 participants := unittest.CompleteIdentitySet() 413 dupeAddressIdentity := unittest.IdentityFixture(unittest.WithAddress(participants[0].Address)) 414 participants = append(participants, dupeAddressIdentity) 415 416 root := unittest.RootSnapshotFixture(participants) 417 bootstrap(t, root, func(state *bprotocol.State, err error) { 418 assert.Error(t, err) 419 }) 420 }) 421 422 t.Run("non-canonical ordering", func(t *testing.T) { 423 participants := unittest.IdentityListFixture(20, unittest.WithAllRoles()) 424 425 root := unittest.RootSnapshotFixture(participants) 426 // randomly shuffle the identities so they are not canonically ordered 427 encodable := root.Encodable() 428 var err error 429 encodable.Identities, err = participants.Shuffle() 430 require.NoError(t, err) 431 root = inmem.SnapshotFromEncodable(encodable) 432 bootstrap(t, root, func(state *bprotocol.State, err error) { 433 assert.Error(t, err) 434 }) 435 }) 436 } 437 438 func TestBootstrap_DisconnectedSealingSegment(t *testing.T) { 439 rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) 440 // convert to encodable to easily modify snapshot 441 encodable := rootSnapshot.Encodable() 442 // add an un-connected tail block to the sealing segment 443 tail := unittest.BlockFixture() 444 encodable.SealingSegment.Blocks = append([]*flow.Block{&tail}, encodable.SealingSegment.Blocks...) 445 rootSnapshot = inmem.SnapshotFromEncodable(encodable) 446 447 bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { 448 assert.Error(t, err) 449 }) 450 } 451 452 func TestBootstrap_SealingSegmentMissingSeal(t *testing.T) { 453 rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) 454 // convert to encodable to easily modify snapshot 455 encodable := rootSnapshot.Encodable() 456 // we are missing the required first seal 457 encodable.SealingSegment.FirstSeal = nil 458 rootSnapshot = inmem.SnapshotFromEncodable(encodable) 459 460 bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { 461 assert.Error(t, err) 462 }) 463 } 464 465 func TestBootstrap_SealingSegmentMissingResult(t *testing.T) { 466 rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) 467 // convert to encodable to easily modify snapshot 468 encodable := rootSnapshot.Encodable() 469 // we are missing the result referenced by the root seal 470 encodable.SealingSegment.ExecutionResults = nil 471 rootSnapshot = inmem.SnapshotFromEncodable(encodable) 472 473 bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { 474 assert.Error(t, err) 475 }) 476 } 477 478 func TestBootstrap_InvalidQuorumCertificate(t *testing.T) { 479 rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) 480 // convert to encodable to easily modify snapshot 481 encodable := rootSnapshot.Encodable() 482 encodable.QuorumCertificate.BlockID = unittest.IdentifierFixture() 483 rootSnapshot = inmem.SnapshotFromEncodable(encodable) 484 485 bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { 486 assert.Error(t, err) 487 }) 488 } 489 490 func TestBootstrap_SealMismatch(t *testing.T) { 491 t.Run("seal doesn't match tail block", func(t *testing.T) { 492 rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) 493 // convert to encodable to easily modify snapshot 494 encodable := rootSnapshot.Encodable() 495 encodable.LatestSeal.BlockID = unittest.IdentifierFixture() 496 497 bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { 498 assert.Error(t, err) 499 }) 500 }) 501 502 t.Run("result doesn't match tail block", func(t *testing.T) { 503 rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) 504 // convert to encodable to easily modify snapshot 505 encodable := rootSnapshot.Encodable() 506 encodable.LatestResult.BlockID = unittest.IdentifierFixture() 507 508 bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { 509 assert.Error(t, err) 510 }) 511 }) 512 513 t.Run("seal doesn't match result", func(t *testing.T) { 514 rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) 515 // convert to encodable to easily modify snapshot 516 encodable := rootSnapshot.Encodable() 517 encodable.LatestSeal.ResultID = unittest.IdentifierFixture() 518 519 bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { 520 assert.Error(t, err) 521 }) 522 }) 523 } 524 525 // bootstraps protocol state with the given snapshot and invokes the callback 526 // with the result of the constructor 527 func bootstrap(t *testing.T, rootSnapshot protocol.Snapshot, f func(*bprotocol.State, error)) { 528 metrics := metrics.NewNoopCollector() 529 dir := unittest.TempDir(t) 530 defer os.RemoveAll(dir) 531 db := unittest.BadgerDB(t, dir) 532 defer db.Close() 533 all := storutil.StorageLayer(t, db) 534 state, err := bprotocol.Bootstrap( 535 metrics, 536 db, 537 all.Headers, 538 all.Seals, 539 all.Results, 540 all.Blocks, 541 all.QuorumCertificates, 542 all.Setups, 543 all.EpochCommits, 544 all.Statuses, 545 all.VersionBeacons, 546 rootSnapshot, 547 ) 548 f(state, err) 549 } 550 551 // snapshotAfter bootstraps the protocol state from the root snapshot, applies 552 // the state-changing function f, clears the on-disk state, and returns a 553 // memory-backed snapshot corresponding to that returned by f. 554 // 555 // This is used for generating valid snapshots to use when testing bootstrapping 556 // from non-root states. 557 func snapshotAfter(t *testing.T, rootSnapshot protocol.Snapshot, f func(*bprotocol.FollowerState) protocol.Snapshot) protocol.Snapshot { 558 var after protocol.Snapshot 559 protoutil.RunWithFollowerProtocolState(t, rootSnapshot, func(_ *badger.DB, state *bprotocol.FollowerState) { 560 snap := f(state) 561 var err error 562 after, err = inmem.FromSnapshot(snap) 563 require.NoError(t, err) 564 }) 565 return after 566 } 567 568 // buildBlock extends the protocol state by the given block 569 func buildBlock(t *testing.T, state protocol.FollowerState, block *flow.Block) { 570 require.NoError(t, state.ExtendCertified(context.Background(), block, unittest.CertifyBlock(block.Header))) 571 } 572 573 // buildFinalizedBlock extends the protocol state by the given block and marks the block as finalized 574 func buildFinalizedBlock(t *testing.T, state protocol.FollowerState, block *flow.Block) { 575 require.NoError(t, state.ExtendCertified(context.Background(), block, unittest.CertifyBlock(block.Header))) 576 require.NoError(t, state.Finalize(context.Background(), block.ID())) 577 } 578 579 // assertSealingSegmentBlocksQueryable bootstraps the state with the given 580 // snapshot, then verifies that all sealing segment blocks are queryable. 581 func assertSealingSegmentBlocksQueryableAfterBootstrap(t *testing.T, snapshot protocol.Snapshot) { 582 bootstrap(t, snapshot, func(state *bprotocol.State, err error) { 583 require.NoError(t, err) 584 585 segment, err := state.Final().SealingSegment() 586 require.NoError(t, err) 587 588 rootBlock, err := state.Params().FinalizedRoot() 589 require.NoError(t, err) 590 591 // root block should be the highest block from the sealing segment 592 assert.Equal(t, segment.Highest().Header, rootBlock) 593 594 // for each block in the sealing segment we should be able to query: 595 // * Head 596 // * SealedResult 597 // * Commit 598 for _, block := range segment.Blocks { 599 blockID := block.ID() 600 snap := state.AtBlockID(blockID) 601 header, err := snap.Head() 602 assert.NoError(t, err) 603 assert.Equal(t, blockID, header.ID()) 604 _, seal, err := snap.SealedResult() 605 assert.NoError(t, err) 606 assert.Equal(t, segment.LatestSeals[blockID], seal.ID()) 607 commit, err := snap.Commit() 608 assert.NoError(t, err) 609 assert.Equal(t, seal.FinalState, commit) 610 } 611 // for all blocks but the head, we should be unable to query SealingSegment: 612 for _, block := range segment.Blocks[:len(segment.Blocks)-1] { 613 snap := state.AtBlockID(block.ID()) 614 _, err := snap.SealingSegment() 615 assert.ErrorIs(t, err, protocol.ErrSealingSegmentBelowRootBlock) 616 } 617 }) 618 } 619 620 // BenchmarkFinal benchmarks retrieving the latest finalized block from storage. 621 func BenchmarkFinal(b *testing.B) { 622 util.RunWithBootstrapState(b, unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()), func(db *badger.DB, state *bprotocol.State) { 623 b.ResetTimer() 624 for i := 0; i < b.N; i++ { 625 header, err := state.Final().Head() 626 assert.NoError(b, err) 627 assert.NotNil(b, header) 628 } 629 }) 630 } 631 632 // BenchmarkFinal benchmarks retrieving the block by height from storage. 633 func BenchmarkByHeight(b *testing.B) { 634 util.RunWithBootstrapState(b, unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()), func(db *badger.DB, state *bprotocol.State) { 635 b.ResetTimer() 636 for i := 0; i < b.N; i++ { 637 header, err := state.AtHeight(0).Head() 638 assert.NoError(b, err) 639 assert.NotNil(b, header) 640 } 641 }) 642 }