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