github.com/koko1123/flow-go-1@v0.29.6/state/protocol/badger/mutator_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 "sync" 10 "testing" 11 "time" 12 13 "github.com/dgraph-io/badger/v3" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/mock" 16 "github.com/stretchr/testify/require" 17 18 "github.com/koko1123/flow-go-1/engine" 19 "github.com/koko1123/flow-go-1/model/flow" 20 "github.com/koko1123/flow-go-1/model/flow/filter" 21 "github.com/koko1123/flow-go-1/model/flow/order" 22 "github.com/koko1123/flow-go-1/module/metrics" 23 mockmodule "github.com/koko1123/flow-go-1/module/mock" 24 "github.com/koko1123/flow-go-1/module/signature" 25 "github.com/koko1123/flow-go-1/module/trace" 26 st "github.com/koko1123/flow-go-1/state" 27 realprotocol "github.com/koko1123/flow-go-1/state/protocol" 28 protocol "github.com/koko1123/flow-go-1/state/protocol/badger" 29 "github.com/koko1123/flow-go-1/state/protocol/events" 30 "github.com/koko1123/flow-go-1/state/protocol/inmem" 31 mockprotocol "github.com/koko1123/flow-go-1/state/protocol/mock" 32 "github.com/koko1123/flow-go-1/state/protocol/util" 33 "github.com/koko1123/flow-go-1/storage" 34 stoerr "github.com/koko1123/flow-go-1/storage" 35 bstorage "github.com/koko1123/flow-go-1/storage/badger" 36 "github.com/koko1123/flow-go-1/storage/badger/operation" 37 storeutil "github.com/koko1123/flow-go-1/storage/util" 38 "github.com/koko1123/flow-go-1/utils/unittest" 39 "github.com/onflow/flow-go/crypto" 40 ) 41 42 func init() { 43 rand.Seed(time.Now().UnixNano()) 44 } 45 46 var participants = unittest.IdentityListFixture(5, unittest.WithAllRoles()) 47 48 func TestBootstrapValid(t *testing.T) { 49 rootSnapshot := unittest.RootSnapshotFixture(participants) 50 util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *protocol.State) { 51 var finalized uint64 52 err := db.View(operation.RetrieveFinalizedHeight(&finalized)) 53 require.NoError(t, err) 54 55 var sealed uint64 56 err = db.View(operation.RetrieveSealedHeight(&sealed)) 57 require.NoError(t, err) 58 59 var genesisID flow.Identifier 60 err = db.View(operation.LookupBlockHeight(0, &genesisID)) 61 require.NoError(t, err) 62 63 var header flow.Header 64 err = db.View(operation.RetrieveHeader(genesisID, &header)) 65 require.NoError(t, err) 66 67 var sealID flow.Identifier 68 err = db.View(operation.LookupLatestSealAtBlock(genesisID, &sealID)) 69 require.NoError(t, err) 70 71 _, seal, err := rootSnapshot.SealedResult() 72 require.NoError(t, err) 73 err = db.View(operation.RetrieveSeal(sealID, seal)) 74 require.NoError(t, err) 75 76 block, err := rootSnapshot.Head() 77 require.NoError(t, err) 78 require.Equal(t, block.Height, finalized) 79 require.Equal(t, block.Height, sealed) 80 require.Equal(t, block.ID(), genesisID) 81 require.Equal(t, block.ID(), seal.BlockID) 82 require.Equal(t, block, &header) 83 }) 84 } 85 86 func TestExtendValid(t *testing.T) { 87 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 88 metrics := metrics.NewNoopCollector() 89 tracer := trace.NewNoopTracer() 90 headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := storeutil.StorageLayer(t, db) 91 92 // create a event consumer to test epoch transition events 93 distributor := events.NewDistributor() 94 consumer := new(mockprotocol.Consumer) 95 distributor.AddConsumer(consumer) 96 97 block, result, seal := unittest.BootstrapFixture(participants) 98 qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(block.ID())) 99 rootSnapshot, err := inmem.SnapshotFromBootstrapState(block, result, seal, qc) 100 require.NoError(t, err) 101 102 state, err := protocol.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) 103 require.NoError(t, err) 104 105 fullState, err := protocol.NewFullConsensusState(state, index, payloads, tracer, consumer, util.MockBlockTimer(), 106 util.MockReceiptValidator(), util.MockSealValidator(seals)) 107 require.NoError(t, err) 108 109 extend := unittest.BlockWithParentFixture(block.Header) 110 extend.Payload.Guarantees = nil 111 extend.Header.PayloadHash = extend.Payload.Hash() 112 113 err = fullState.Extend(context.Background(), extend) 114 require.NoError(t, err) 115 116 finalCommit, err := state.Final().Commit() 117 require.NoError(t, err) 118 require.Equal(t, seal.FinalState, finalCommit) 119 120 consumer.On("BlockFinalized", extend.Header).Once() 121 err = fullState.Finalize(context.Background(), extend.ID()) 122 require.NoError(t, err) 123 consumer.AssertExpectations(t) 124 }) 125 } 126 127 func TestSealedIndex(t *testing.T) { 128 rootSnapshot := unittest.RootSnapshotFixture(participants) 129 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 130 rootHeader, err := rootSnapshot.Head() 131 require.NoError(t, err) 132 133 // build a chain: 134 // G <- B1 <- B2 (resultB1) <- B3 <- B4 (resultB2, resultB3) <- B5 (sealB1) <- B6 (sealB2, sealB3) <- B7 135 // test that when B4 is finalized, can only find seal for G 136 // when B5 is finalized, can find seal for B1 137 // when B7 is finalized, can find seals for B2, B3 138 139 // block 1 140 b1 := unittest.BlockWithParentFixture(rootHeader) 141 b1.SetPayload(flow.EmptyPayload()) 142 err = state.Extend(context.Background(), b1) 143 require.NoError(t, err) 144 145 // block 2(result B1) 146 b1Receipt := unittest.ReceiptForBlockFixture(b1) 147 b2 := unittest.BlockWithParentFixture(b1.Header) 148 b2.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(b1Receipt))) 149 err = state.Extend(context.Background(), b2) 150 require.NoError(t, err) 151 152 // block 3 153 b3 := unittest.BlockWithParentFixture(b2.Header) 154 b3.SetPayload(flow.EmptyPayload()) 155 err = state.Extend(context.Background(), b3) 156 require.NoError(t, err) 157 158 // block 4 (resultB2, resultB3) 159 b2Receipt := unittest.ReceiptForBlockFixture(b2) 160 b3Receipt := unittest.ReceiptForBlockFixture(b3) 161 b4 := unittest.BlockWithParentFixture(b3.Header) 162 b4.SetPayload(flow.Payload{ 163 Receipts: []*flow.ExecutionReceiptMeta{b2Receipt.Meta(), b3Receipt.Meta()}, 164 Results: []*flow.ExecutionResult{&b2Receipt.ExecutionResult, &b3Receipt.ExecutionResult}, 165 }) 166 err = state.Extend(context.Background(), b4) 167 require.NoError(t, err) 168 169 // block 5 (sealB1) 170 b1Seal := unittest.Seal.Fixture(unittest.Seal.WithResult(&b1Receipt.ExecutionResult)) 171 b5 := unittest.BlockWithParentFixture(b4.Header) 172 b5.SetPayload(flow.Payload{ 173 Seals: []*flow.Seal{b1Seal}, 174 }) 175 err = state.Extend(context.Background(), b5) 176 require.NoError(t, err) 177 178 // block 6 (sealB2, sealB3) 179 b2Seal := unittest.Seal.Fixture(unittest.Seal.WithResult(&b2Receipt.ExecutionResult)) 180 b3Seal := unittest.Seal.Fixture(unittest.Seal.WithResult(&b3Receipt.ExecutionResult)) 181 b6 := unittest.BlockWithParentFixture(b5.Header) 182 b6.SetPayload(flow.Payload{ 183 Seals: []*flow.Seal{b2Seal, b3Seal}, 184 }) 185 err = state.Extend(context.Background(), b6) 186 require.NoError(t, err) 187 188 // block 7 189 b7 := unittest.BlockWithParentFixture(b6.Header) 190 b7.SetPayload(flow.EmptyPayload()) 191 err = state.Extend(context.Background(), b7) 192 require.NoError(t, err) 193 194 // finalizing b1 - b4 195 // when B4 is finalized, can only find seal for G 196 err = state.Finalize(context.Background(), b1.ID()) 197 require.NoError(t, err) 198 err = state.Finalize(context.Background(), b2.ID()) 199 require.NoError(t, err) 200 err = state.Finalize(context.Background(), b3.ID()) 201 require.NoError(t, err) 202 err = state.Finalize(context.Background(), b4.ID()) 203 require.NoError(t, err) 204 205 metrics := metrics.NewNoopCollector() 206 seals := bstorage.NewSeals(metrics, db) 207 208 // can only find seal for G 209 _, err = seals.FinalizedSealForBlock(rootHeader.ID()) 210 require.NoError(t, err) 211 212 _, err = seals.FinalizedSealForBlock(b1.ID()) 213 require.Error(t, err) 214 require.ErrorIs(t, err, storage.ErrNotFound) 215 216 // when B5 is finalized, can find seal for B1 217 err = state.Finalize(context.Background(), b5.ID()) 218 require.NoError(t, err) 219 220 s1, err := seals.FinalizedSealForBlock(b1.ID()) 221 require.NoError(t, err) 222 require.Equal(t, b1Seal, s1) 223 224 _, err = seals.FinalizedSealForBlock(b2.ID()) 225 require.Error(t, err) 226 require.ErrorIs(t, err, storage.ErrNotFound) 227 228 // when B7 is finalized, can find seals for B2, B3 229 err = state.Finalize(context.Background(), b6.ID()) 230 require.NoError(t, err) 231 232 err = state.Finalize(context.Background(), b7.ID()) 233 require.NoError(t, err) 234 235 s2, err := seals.FinalizedSealForBlock(b2.ID()) 236 require.NoError(t, err) 237 require.Equal(t, b2Seal, s2) 238 239 s3, err := seals.FinalizedSealForBlock(b3.ID()) 240 require.NoError(t, err) 241 require.Equal(t, b3Seal, s3) 242 }) 243 244 } 245 246 func TestExtendSealedBoundary(t *testing.T) { 247 rootSnapshot := unittest.RootSnapshotFixture(participants) 248 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 249 head, err := rootSnapshot.Head() 250 require.NoError(t, err) 251 _, seal, err := rootSnapshot.SealedResult() 252 require.NoError(t, err) 253 finalCommit, err := state.Final().Commit() 254 require.NoError(t, err) 255 require.Equal(t, seal.FinalState, finalCommit, "original commit should be root commit") 256 257 // Create a first block on top of the snapshot 258 block1 := unittest.BlockWithParentFixture(head) 259 block1.SetPayload(flow.EmptyPayload()) 260 err = state.Extend(context.Background(), block1) 261 require.NoError(t, err) 262 263 // Add a second block containing a receipt committing to the first block 264 block1Receipt := unittest.ReceiptForBlockFixture(block1) 265 block2 := unittest.BlockWithParentFixture(block1.Header) 266 block2.SetPayload(flow.Payload{ 267 Receipts: []*flow.ExecutionReceiptMeta{block1Receipt.Meta()}, 268 Results: []*flow.ExecutionResult{&block1Receipt.ExecutionResult}, 269 }) 270 err = state.Extend(context.Background(), block2) 271 require.NoError(t, err) 272 273 // Add a third block containing a seal for the first block 274 block1Seal := unittest.Seal.Fixture(unittest.Seal.WithResult(&block1Receipt.ExecutionResult)) 275 block3 := unittest.BlockWithParentFixture(block2.Header) 276 block3.SetPayload(flow.Payload{ 277 Seals: []*flow.Seal{block1Seal}, 278 }) 279 err = state.Extend(context.Background(), block3) 280 require.NoError(t, err) 281 282 finalCommit, err = state.Final().Commit() 283 require.NoError(t, err) 284 require.Equal(t, seal.FinalState, finalCommit, "commit should not change before finalizing") 285 286 err = state.Finalize(context.Background(), block1.ID()) 287 require.NoError(t, err) 288 289 finalCommit, err = state.Final().Commit() 290 require.NoError(t, err) 291 require.Equal(t, seal.FinalState, finalCommit, "commit should not change after finalizing non-sealing block") 292 293 err = state.Finalize(context.Background(), block2.ID()) 294 require.NoError(t, err) 295 296 finalCommit, err = state.Final().Commit() 297 require.NoError(t, err) 298 require.Equal(t, seal.FinalState, finalCommit, "commit should not change after finalizing non-sealing block") 299 300 err = state.Finalize(context.Background(), block3.ID()) 301 require.NoError(t, err) 302 303 finalCommit, err = state.Final().Commit() 304 require.NoError(t, err) 305 require.Equal(t, block1Seal.FinalState, finalCommit, "commit should change after finalizing sealing block") 306 }) 307 } 308 309 func TestExtendMissingParent(t *testing.T) { 310 rootSnapshot := unittest.RootSnapshotFixture(participants) 311 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 312 extend := unittest.BlockFixture() 313 extend.Payload.Guarantees = nil 314 extend.Payload.Seals = nil 315 extend.Header.Height = 2 316 extend.Header.View = 2 317 extend.Header.ParentID = unittest.BlockFixture().ID() 318 extend.Header.PayloadHash = extend.Payload.Hash() 319 320 err := state.Extend(context.Background(), &extend) 321 require.Error(t, err) 322 require.True(t, st.IsInvalidExtensionError(err), err) 323 324 // verify seal not indexed 325 var sealID flow.Identifier 326 err = db.View(operation.LookupLatestSealAtBlock(extend.ID(), &sealID)) 327 require.Error(t, err) 328 require.True(t, errors.Is(err, stoerr.ErrNotFound), err) 329 }) 330 } 331 332 func TestExtendHeightTooSmall(t *testing.T) { 333 rootSnapshot := unittest.RootSnapshotFixture(participants) 334 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 335 head, err := rootSnapshot.Head() 336 require.NoError(t, err) 337 338 extend := unittest.BlockFixture() 339 extend.SetPayload(flow.EmptyPayload()) 340 extend.Header.Height = 1 341 extend.Header.View = 1 342 extend.Header.ParentID = head.ID() 343 344 err = state.Extend(context.Background(), &extend) 345 require.NoError(t, err) 346 347 // create another block with the same height and view, that is coming after 348 extend.Header.ParentID = extend.Header.ID() 349 extend.Header.Height = 1 350 extend.Header.View = 2 351 352 err = state.Extend(context.Background(), &extend) 353 require.Error(t, err) 354 355 // verify seal not indexed 356 var sealID flow.Identifier 357 err = db.View(operation.LookupLatestSealAtBlock(extend.ID(), &sealID)) 358 require.Error(t, err) 359 require.True(t, errors.Is(err, stoerr.ErrNotFound), err) 360 }) 361 } 362 363 func TestExtendHeightTooLarge(t *testing.T) { 364 rootSnapshot := unittest.RootSnapshotFixture(participants) 365 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 366 367 head, err := rootSnapshot.Head() 368 require.NoError(t, err) 369 370 block := unittest.BlockWithParentFixture(head) 371 block.SetPayload(flow.EmptyPayload()) 372 // set an invalid height 373 block.Header.Height = head.Height + 2 374 375 err = state.Extend(context.Background(), block) 376 require.Error(t, err) 377 }) 378 } 379 380 func TestExtendBlockNotConnected(t *testing.T) { 381 rootSnapshot := unittest.RootSnapshotFixture(participants) 382 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 383 384 head, err := rootSnapshot.Head() 385 require.NoError(t, err) 386 387 // add 2 blocks, the second finalizing/sealing the state of the first 388 extend := unittest.BlockWithParentFixture(head) 389 extend.SetPayload(flow.EmptyPayload()) 390 391 err = state.Extend(context.Background(), extend) 392 require.NoError(t, err) 393 394 err = state.Finalize(context.Background(), extend.ID()) 395 require.NoError(t, err) 396 397 // create a fork at view/height 1 and try to connect it to root 398 extend.Header.Timestamp = extend.Header.Timestamp.Add(time.Second) 399 extend.Header.ParentID = head.ID() 400 401 err = state.Extend(context.Background(), extend) 402 require.Error(t, err) 403 404 // verify seal not indexed 405 var sealID flow.Identifier 406 err = db.View(operation.LookupLatestSealAtBlock(extend.ID(), &sealID)) 407 require.Error(t, err) 408 require.True(t, errors.Is(err, stoerr.ErrNotFound), err) 409 }) 410 } 411 412 func TestExtendInvalidChainID(t *testing.T) { 413 rootSnapshot := unittest.RootSnapshotFixture(participants) 414 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 415 head, err := rootSnapshot.Head() 416 require.NoError(t, err) 417 418 block := unittest.BlockWithParentFixture(head) 419 block.SetPayload(flow.EmptyPayload()) 420 // use an invalid chain ID 421 block.Header.ChainID = head.ChainID + "-invalid" 422 423 err = state.Extend(context.Background(), block) 424 require.Error(t, err) 425 require.True(t, st.IsInvalidExtensionError(err), err) 426 }) 427 } 428 429 func TestExtendReceiptsNotSorted(t *testing.T) { 430 // TODO: this test needs to be updated: 431 // We don't require the receipts to be sorted by height anymore 432 // We could require an "parent first" ordering, which is less strict than 433 // a full ordering by height 434 unittest.SkipUnless(t, unittest.TEST_TODO, "needs update") 435 436 rootSnapshot := unittest.RootSnapshotFixture(participants) 437 head, err := rootSnapshot.Head() 438 require.NoError(t, err) 439 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 440 // create block2 and block3 441 block2 := unittest.BlockWithParentFixture(head) 442 block2.Payload.Guarantees = nil 443 block2.Header.PayloadHash = block2.Payload.Hash() 444 err := state.Extend(context.Background(), block2) 445 require.NoError(t, err) 446 447 block3 := unittest.BlockWithParentFixture(block2.Header) 448 block3.Payload.Guarantees = nil 449 block3.Header.PayloadHash = block3.Payload.Hash() 450 err = state.Extend(context.Background(), block3) 451 require.NoError(t, err) 452 453 receiptA := unittest.ReceiptForBlockFixture(block3) 454 receiptB := unittest.ReceiptForBlockFixture(block2) 455 456 // insert a block with payload receipts not sorted by block height. 457 block4 := unittest.BlockWithParentFixture(block3.Header) 458 block4.Payload = &flow.Payload{ 459 Receipts: []*flow.ExecutionReceiptMeta{receiptA.Meta(), receiptB.Meta()}, 460 Results: []*flow.ExecutionResult{&receiptA.ExecutionResult, &receiptB.ExecutionResult}, 461 } 462 block4.Header.PayloadHash = block4.Payload.Hash() 463 err = state.Extend(context.Background(), block4) 464 require.Error(t, err) 465 require.True(t, st.IsInvalidExtensionError(err), err) 466 }) 467 } 468 469 func TestExtendReceiptsInvalid(t *testing.T) { 470 validator := &mockmodule.ReceiptValidator{} 471 472 rootSnapshot := unittest.RootSnapshotFixture(participants) 473 util.RunWithFullProtocolStateAndValidator(t, rootSnapshot, validator, func(db *badger.DB, state *protocol.MutableState) { 474 head, err := rootSnapshot.Head() 475 require.NoError(t, err) 476 477 validator.On("ValidatePayload", mock.Anything).Return(nil).Once() 478 479 // create block2 and block3 480 block2 := unittest.BlockWithParentFixture(head) 481 block2.SetPayload(flow.EmptyPayload()) 482 err = state.Extend(context.Background(), block2) 483 require.NoError(t, err) 484 485 // Add a receipt for block 2 486 receipt := unittest.ExecutionReceiptFixture() 487 488 block3 := unittest.BlockWithParentFixture(block2.Header) 489 block3.SetPayload(flow.Payload{ 490 Receipts: []*flow.ExecutionReceiptMeta{receipt.Meta()}, 491 Results: []*flow.ExecutionResult{&receipt.ExecutionResult}, 492 }) 493 494 // force the receipt validator to refuse this payload 495 validator.On("ValidatePayload", block3).Return(engine.NewInvalidInputError("")).Once() 496 497 err = state.Extend(context.Background(), block3) 498 require.Error(t, err) 499 require.True(t, st.IsInvalidExtensionError(err), err) 500 validator.AssertExpectations(t) 501 }) 502 } 503 504 func TestExtendReceiptsValid(t *testing.T) { 505 rootSnapshot := unittest.RootSnapshotFixture(participants) 506 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 507 head, err := rootSnapshot.Head() 508 require.NoError(t, err) 509 block2 := unittest.BlockWithParentFixture(head) 510 block2.SetPayload(flow.EmptyPayload()) 511 err = state.Extend(context.Background(), block2) 512 require.NoError(t, err) 513 514 block3 := unittest.BlockWithParentFixture(block2.Header) 515 block3.SetPayload(flow.EmptyPayload()) 516 err = state.Extend(context.Background(), block3) 517 require.NoError(t, err) 518 519 block4 := unittest.BlockWithParentFixture(block3.Header) 520 block4.SetPayload(flow.EmptyPayload()) 521 err = state.Extend(context.Background(), block4) 522 require.NoError(t, err) 523 524 receipt3a := unittest.ReceiptForBlockFixture(block3) 525 receipt3b := unittest.ReceiptForBlockFixture(block3) 526 receipt3c := unittest.ReceiptForBlockFixture(block4) 527 528 block5 := unittest.BlockWithParentFixture(block4.Header) 529 block5.SetPayload(flow.Payload{ 530 Receipts: []*flow.ExecutionReceiptMeta{ 531 receipt3a.Meta(), 532 receipt3b.Meta(), 533 receipt3c.Meta(), 534 }, 535 Results: []*flow.ExecutionResult{ 536 &receipt3a.ExecutionResult, 537 &receipt3b.ExecutionResult, 538 &receipt3c.ExecutionResult, 539 }, 540 }) 541 err = state.Extend(context.Background(), block5) 542 require.NoError(t, err) 543 }) 544 } 545 546 // Tests the full flow of transitioning between epochs by finalizing a setup 547 // event, then a commit event, then finalizing the first block of the next epoch. 548 // Also tests that appropriate epoch transition events are fired. 549 // 550 // Epoch information becomes available in the protocol state in the block AFTER 551 // the block sealing the relevant service event. This is because the block after 552 // the sealing block contains a QC certifying validity of the payload of the 553 // sealing block. 554 // 555 // ROOT <- B1 <- B2(R1) <- B3(S1) <- B4 <- B5(R2) <- B6(S2) <- B7 <- B8 <-|- B9 556 // 557 // B4 contains a QC for B3, which seals B1, in which EpochSetup is emitted. 558 // * we can query the EpochSetup beginning with B4 559 // * EpochSetupPhaseStarted triggered when B4 is finalized 560 // 561 // B7 contains a QC for B6, which seals B2, in which EpochCommitted is emitted. 562 // * we can query the EpochCommit beginning with B7 563 // * EpochSetupPhaseStarted triggered when B7 is finalized 564 // 565 // B8 is the final block of the epoch. 566 // B9 is the first block of the NEXT epoch. 567 func TestExtendEpochTransitionValid(t *testing.T) { 568 // create a event consumer to test epoch transition events 569 consumer := new(mockprotocol.Consumer) 570 consumer.On("BlockFinalized", mock.Anything) 571 rootSnapshot := unittest.RootSnapshotFixture(participants) 572 573 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 574 575 // set up state and mock ComplianceMetrics object 576 metrics := new(mockmodule.ComplianceMetrics) 577 metrics.On("BlockSealed", mock.Anything) 578 metrics.On("SealedHeight", mock.Anything) 579 metrics.On("FinalizedHeight", mock.Anything) 580 metrics.On("BlockFinalized", mock.Anything) 581 582 // expect epoch metric calls on bootstrap 583 initialCurrentEpoch := rootSnapshot.Epochs().Current() 584 counter, err := initialCurrentEpoch.Counter() 585 require.NoError(t, err) 586 finalView, err := initialCurrentEpoch.FinalView() 587 require.NoError(t, err) 588 initialPhase, err := rootSnapshot.Phase() 589 require.NoError(t, err) 590 metrics.On("CurrentEpochCounter", counter).Once() 591 metrics.On("CurrentEpochPhase", initialPhase).Once() 592 metrics.On("CommittedEpochFinalView", finalView).Once() 593 594 metrics.On("CurrentEpochFinalView", finalView).Once() 595 596 dkgPhase1FinalView, dkgPhase2FinalView, dkgPhase3FinalView, err := realprotocol.DKGPhaseViews(initialCurrentEpoch) 597 require.NoError(t, err) 598 metrics.On("CurrentDKGPhase1FinalView", dkgPhase1FinalView).Once() 599 metrics.On("CurrentDKGPhase2FinalView", dkgPhase2FinalView).Once() 600 metrics.On("CurrentDKGPhase3FinalView", dkgPhase3FinalView).Once() 601 602 tracer := trace.NewNoopTracer() 603 headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := storeutil.StorageLayer(t, db) 604 protoState, err := protocol.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) 605 require.NoError(t, err) 606 receiptValidator := util.MockReceiptValidator() 607 sealValidator := util.MockSealValidator(seals) 608 state, err := protocol.NewFullConsensusState(protoState, index, payloads, tracer, consumer, 609 util.MockBlockTimer(), receiptValidator, sealValidator) 610 require.NoError(t, err) 611 612 head, err := rootSnapshot.Head() 613 require.NoError(t, err) 614 result, _, err := rootSnapshot.SealedResult() 615 require.NoError(t, err) 616 617 // we should begin the epoch in the staking phase 618 phase, err := state.AtBlockID(head.ID()).Phase() 619 assert.NoError(t, err) 620 require.Equal(t, flow.EpochPhaseStaking, phase) 621 622 // add a block for the first seal to reference 623 block1 := unittest.BlockWithParentFixture(head) 624 block1.SetPayload(flow.EmptyPayload()) 625 err = state.Extend(context.Background(), block1) 626 require.NoError(t, err) 627 err = state.Finalize(context.Background(), block1.ID()) 628 require.NoError(t, err) 629 630 epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 631 epoch1FinalView := epoch1Setup.FinalView 632 633 // add a participant for the next epoch 634 epoch2NewParticipant := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 635 epoch2Participants := append(participants, epoch2NewParticipant).Sort(order.Canonical) 636 637 // create the epoch setup event for the second epoch 638 epoch2Setup := unittest.EpochSetupFixture( 639 unittest.WithParticipants(epoch2Participants), 640 unittest.SetupWithCounter(epoch1Setup.Counter+1), 641 unittest.WithFinalView(epoch1FinalView+1000), 642 unittest.WithFirstView(epoch1FinalView+1), 643 ) 644 645 // create a receipt for block 1 containing the EpochSetup event 646 receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1) 647 receipt1.ExecutionResult.ServiceEvents = []flow.ServiceEvent{epoch2Setup.ServiceEvent()} 648 seal1.ResultID = receipt1.ExecutionResult.ID() 649 650 // add a second block with the receipt for block 1 651 block2 := unittest.BlockWithParentFixture(block1.Header) 652 block2.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt1))) 653 654 err = state.Extend(context.Background(), block2) 655 require.NoError(t, err) 656 err = state.Finalize(context.Background(), block2.ID()) 657 require.NoError(t, err) 658 659 // block 3 contains the seal for block 1 660 block3 := unittest.BlockWithParentFixture(block2.Header) 661 block3.SetPayload(flow.Payload{ 662 Seals: []*flow.Seal{seal1}, 663 }) 664 665 // insert the block sealing the EpochSetup event 666 err = state.Extend(context.Background(), block3) 667 require.NoError(t, err) 668 669 // insert a block with a QC pointing to block 3 670 block4 := unittest.BlockWithParentFixture(block3.Header) 671 err = state.Extend(context.Background(), block4) 672 require.NoError(t, err) 673 674 // now that the setup event has been emitted, we should be in the setup phase 675 phase, err = state.AtBlockID(block4.ID()).Phase() 676 assert.NoError(t, err) 677 require.Equal(t, flow.EpochPhaseSetup, phase) 678 679 // we should NOT be able to query epoch 2 wrt blocks before 4 680 for _, blockID := range []flow.Identifier{block1.ID(), block2.ID(), block3.ID()} { 681 _, err = state.AtBlockID(blockID).Epochs().Next().InitialIdentities() 682 require.Error(t, err) 683 _, err = state.AtBlockID(blockID).Epochs().Next().Clustering() 684 require.Error(t, err) 685 } 686 687 // we should be able to query epoch 2 wrt block 4 688 _, err = state.AtBlockID(block4.ID()).Epochs().Next().InitialIdentities() 689 assert.NoError(t, err) 690 _, err = state.AtBlockID(block4.ID()).Epochs().Next().Clustering() 691 assert.NoError(t, err) 692 693 // only setup event is finalized, not commit, so shouldn't be able to get certain info 694 _, err = state.AtBlockID(block4.ID()).Epochs().Next().DKG() 695 require.Error(t, err) 696 697 // finalize block 3 so we can finalize subsequent blocks 698 err = state.Finalize(context.Background(), block3.ID()) 699 require.NoError(t, err) 700 701 // finalize block 4 so we can finalize subsequent blocks 702 // ensure an epoch phase transition when we finalize block 4 703 consumer.On("EpochSetupPhaseStarted", epoch2Setup.Counter-1, block4.Header).Once() 704 metrics.On("CurrentEpochPhase", flow.EpochPhaseSetup).Once() 705 err = state.Finalize(context.Background(), block4.ID()) 706 require.NoError(t, err) 707 consumer.AssertCalled(t, "EpochSetupPhaseStarted", epoch2Setup.Counter-1, block4.Header) 708 metrics.AssertCalled(t, "CurrentEpochPhase", flow.EpochPhaseSetup) 709 710 // now that the setup event has been emitted, we should be in the setup phase 711 phase, err = state.AtBlockID(block4.ID()).Phase() 712 assert.NoError(t, err) 713 require.Equal(t, flow.EpochPhaseSetup, phase) 714 715 epoch2Commit := unittest.EpochCommitFixture( 716 unittest.CommitWithCounter(epoch2Setup.Counter), 717 unittest.WithClusterQCsFromAssignments(epoch2Setup.Assignments), 718 unittest.WithDKGFromParticipants(epoch2Participants), 719 ) 720 721 // create receipt and seal for block 2 722 // the receipt for block 2 contains the EpochCommit event 723 receipt2, seal2 := unittest.ReceiptAndSealForBlock(block2) 724 receipt2.ExecutionResult.ServiceEvents = []flow.ServiceEvent{epoch2Commit.ServiceEvent()} 725 seal2.ResultID = receipt2.ExecutionResult.ID() 726 727 // block 5 contains the receipt for block 2 728 block5 := unittest.BlockWithParentFixture(block4.Header) 729 block5.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt2))) 730 731 err = state.Extend(context.Background(), block5) 732 require.NoError(t, err) 733 err = state.Finalize(context.Background(), block5.ID()) 734 require.NoError(t, err) 735 736 // block 6 contains the seal for block 2 737 block6 := unittest.BlockWithParentFixture(block5.Header) 738 block6.SetPayload(flow.Payload{ 739 Seals: []*flow.Seal{seal2}, 740 }) 741 742 err = state.Extend(context.Background(), block6) 743 require.NoError(t, err) 744 745 // insert a block with a QC pointing to block 6 746 block7 := unittest.BlockWithParentFixture(block6.Header) 747 err = state.Extend(context.Background(), block7) 748 require.NoError(t, err) 749 750 // we should NOT be able to query epoch 2 commit info wrt blocks before 7 751 for _, blockID := range []flow.Identifier{block4.ID(), block5.ID(), block6.ID()} { 752 _, err = state.AtBlockID(blockID).Epochs().Next().DKG() 753 require.Error(t, err) 754 } 755 756 // now epoch 2 is fully ready, we can query anything we want about it wrt block 7 (or later) 757 _, err = state.AtBlockID(block7.ID()).Epochs().Next().InitialIdentities() 758 require.NoError(t, err) 759 _, err = state.AtBlockID(block7.ID()).Epochs().Next().Clustering() 760 require.NoError(t, err) 761 _, err = state.AtBlockID(block7.ID()).Epochs().Next().DKG() 762 assert.NoError(t, err) 763 764 // now that the commit event has been emitted, we should be in the committed phase 765 phase, err = state.AtBlockID(block7.ID()).Phase() 766 assert.NoError(t, err) 767 require.Equal(t, flow.EpochPhaseCommitted, phase) 768 769 err = state.Finalize(context.Background(), block6.ID()) 770 require.NoError(t, err) 771 772 // expect epoch phase transition once we finalize block 7 773 consumer.On("EpochCommittedPhaseStarted", epoch2Setup.Counter-1, block7.Header).Once() 774 // expect committed final view to be updated, since we are committing epoch 2 775 metrics.On("CommittedEpochFinalView", epoch2Setup.FinalView).Once() 776 metrics.On("CurrentEpochPhase", flow.EpochPhaseCommitted).Once() 777 err = state.Finalize(context.Background(), block7.ID()) 778 779 require.NoError(t, err) 780 consumer.AssertCalled(t, "EpochCommittedPhaseStarted", epoch2Setup.Counter-1, block7.Header) 781 metrics.AssertCalled(t, "CommittedEpochFinalView", epoch2Setup.FinalView) 782 metrics.AssertCalled(t, "CurrentEpochPhase", flow.EpochPhaseCommitted) 783 784 // we should still be in epoch 1 785 epochCounter, err := state.AtBlockID(block4.ID()).Epochs().Current().Counter() 786 require.NoError(t, err) 787 require.Equal(t, epoch1Setup.Counter, epochCounter) 788 789 // block 8 has the final view of the epoch 790 block8 := unittest.BlockWithParentFixture(block7.Header) 791 block8.SetPayload(flow.EmptyPayload()) 792 block8.Header.View = epoch1FinalView 793 794 err = state.Extend(context.Background(), block8) 795 require.NoError(t, err) 796 797 // we should still be in epoch 1, since epochs are inclusive of final view 798 epochCounter, err = state.AtBlockID(block8.ID()).Epochs().Current().Counter() 799 require.NoError(t, err) 800 require.Equal(t, epoch1Setup.Counter, epochCounter) 801 802 // block 9 has a view > final view of epoch 1, it will be considered the first block of epoch 2 803 block9 := unittest.BlockWithParentFixture(block8.Header) 804 block9.SetPayload(flow.EmptyPayload()) 805 // we should handle views that aren't exactly the first valid view of the epoch 806 block9.Header.View = epoch1FinalView + uint64(1+rand.Intn(10)) 807 808 err = state.Extend(context.Background(), block9) 809 require.NoError(t, err) 810 811 // now, at long last, we are in epoch 2 812 epochCounter, err = state.AtBlockID(block9.ID()).Epochs().Current().Counter() 813 require.NoError(t, err) 814 require.Equal(t, epoch2Setup.Counter, epochCounter) 815 816 // we should begin epoch 2 in staking phase 817 // how that the commit event has been emitted, we should be in the committed phase 818 phase, err = state.AtBlockID(block9.ID()).Phase() 819 assert.NoError(t, err) 820 require.Equal(t, flow.EpochPhaseStaking, phase) 821 822 // expect epoch transition once we finalize block 9 823 consumer.On("EpochTransition", epoch2Setup.Counter, block9.Header).Once() 824 metrics.On("CurrentEpochCounter", epoch2Setup.Counter).Once() 825 metrics.On("CurrentEpochPhase", flow.EpochPhaseStaking).Once() 826 metrics.On("CurrentEpochFinalView", epoch2Setup.FinalView).Once() 827 metrics.On("CurrentDKGPhase1FinalView", epoch2Setup.DKGPhase1FinalView).Once() 828 metrics.On("CurrentDKGPhase2FinalView", epoch2Setup.DKGPhase2FinalView).Once() 829 metrics.On("CurrentDKGPhase3FinalView", epoch2Setup.DKGPhase3FinalView).Once() 830 831 err = state.Finalize(context.Background(), block8.ID()) 832 require.NoError(t, err) 833 err = state.Finalize(context.Background(), block9.ID()) 834 require.NoError(t, err) 835 consumer.AssertCalled(t, "EpochTransition", epoch2Setup.Counter, block9.Header) 836 metrics.AssertCalled(t, "CurrentEpochCounter", epoch2Setup.Counter) 837 metrics.AssertCalled(t, "CurrentEpochPhase", flow.EpochPhaseStaking) 838 metrics.AssertCalled(t, "CurrentEpochFinalView", epoch2Setup.FinalView) 839 metrics.AssertCalled(t, "CurrentDKGPhase1FinalView", epoch2Setup.DKGPhase1FinalView) 840 metrics.AssertCalled(t, "CurrentDKGPhase2FinalView", epoch2Setup.DKGPhase2FinalView) 841 metrics.AssertCalled(t, "CurrentDKGPhase3FinalView", epoch2Setup.DKGPhase3FinalView) 842 843 metrics.AssertExpectations(t) 844 }) 845 } 846 847 // we should be able to have conflicting forks with two different instances of 848 // the same service event for the same epoch 849 // 850 // /--B1<--B3(R1)<--B5(S1)<--B7 851 // ROOT <--+ 852 // \--B2<--B4(R2)<--B6(S2)<--B8 853 func TestExtendConflictingEpochEvents(t *testing.T) { 854 rootSnapshot := unittest.RootSnapshotFixture(participants) 855 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 856 857 head, err := rootSnapshot.Head() 858 require.NoError(t, err) 859 result, _, err := rootSnapshot.SealedResult() 860 require.NoError(t, err) 861 862 // add two conflicting blocks for each service event to reference 863 block1 := unittest.BlockWithParentFixture(head) 864 block1.SetPayload(flow.EmptyPayload()) 865 err = state.Extend(context.Background(), block1) 866 require.NoError(t, err) 867 868 block2 := unittest.BlockWithParentFixture(head) 869 block2.SetPayload(flow.EmptyPayload()) 870 err = state.Extend(context.Background(), block2) 871 require.NoError(t, err) 872 873 rootSetup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 874 875 // create two conflicting epoch setup events for the next epoch (final view differs) 876 nextEpochSetup1 := unittest.EpochSetupFixture( 877 unittest.WithParticipants(rootSetup.Participants), 878 unittest.SetupWithCounter(rootSetup.Counter+1), 879 unittest.WithFinalView(rootSetup.FinalView+1000), 880 unittest.WithFirstView(rootSetup.FinalView+1), 881 ) 882 nextEpochSetup2 := unittest.EpochSetupFixture( 883 unittest.WithParticipants(rootSetup.Participants), 884 unittest.SetupWithCounter(rootSetup.Counter+1), 885 unittest.WithFinalView(rootSetup.FinalView+2000), // final view differs 886 unittest.WithFirstView(rootSetup.FinalView+1), 887 ) 888 889 // add blocks containing receipts for block1 and block2 (necessary for sealing) 890 // block 1 receipt contains nextEpochSetup1 891 block1Receipt := unittest.ReceiptForBlockFixture(block1) 892 block1Receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{nextEpochSetup1.ServiceEvent()} 893 894 // add block 1 receipt to block 3 payload 895 block3 := unittest.BlockWithParentFixture(block1.Header) 896 block3.SetPayload(flow.Payload{ 897 Receipts: []*flow.ExecutionReceiptMeta{block1Receipt.Meta()}, 898 Results: []*flow.ExecutionResult{&block1Receipt.ExecutionResult}, 899 }) 900 err = state.Extend(context.Background(), block3) 901 require.NoError(t, err) 902 903 // block 2 receipt contains nextEpochSetup2 904 block2Receipt := unittest.ReceiptForBlockFixture(block2) 905 block2Receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{nextEpochSetup2.ServiceEvent()} 906 907 // add block 2 receipt to block 4 payload 908 block4 := unittest.BlockWithParentFixture(block2.Header) 909 block4.SetPayload(flow.Payload{ 910 Receipts: []*flow.ExecutionReceiptMeta{block2Receipt.Meta()}, 911 Results: []*flow.ExecutionResult{&block2Receipt.ExecutionResult}, 912 }) 913 err = state.Extend(context.Background(), block4) 914 require.NoError(t, err) 915 916 // seal for block 1 917 seal1 := unittest.Seal.Fixture(unittest.Seal.WithResult(&block1Receipt.ExecutionResult)) 918 919 // seal for block 2 920 seal2 := unittest.Seal.Fixture(unittest.Seal.WithResult(&block2Receipt.ExecutionResult)) 921 922 // block 5 builds on block 3, contains seal for block 1 923 block5 := unittest.BlockWithParentFixture(block3.Header) 924 block5.SetPayload(flow.Payload{ 925 Seals: []*flow.Seal{seal1}, 926 }) 927 err = state.Extend(context.Background(), block5) 928 require.NoError(t, err) 929 930 // block 6 builds on block 4, contains seal for block 2 931 block6 := unittest.BlockWithParentFixture(block4.Header) 932 block6.SetPayload(flow.Payload{ 933 Seals: []*flow.Seal{seal2}, 934 }) 935 err = state.Extend(context.Background(), block6) 936 require.NoError(t, err) 937 938 // block 7 builds on block 5, contains QC for block 7 939 block7 := unittest.BlockWithParentFixture(block5.Header) 940 err = state.Extend(context.Background(), block7) 941 require.NoError(t, err) 942 943 // block 8 builds on block 6, contains QC for block 6 944 block8 := unittest.BlockWithParentFixture(block6.Header) 945 err = state.Extend(context.Background(), block8) 946 require.NoError(t, err) 947 948 // should be able query each epoch from the appropriate reference block 949 setup1FinalView, err := state.AtBlockID(block7.ID()).Epochs().Next().FinalView() 950 assert.NoError(t, err) 951 require.Equal(t, nextEpochSetup1.FinalView, setup1FinalView) 952 953 setup2FinalView, err := state.AtBlockID(block8.ID()).Epochs().Next().FinalView() 954 assert.NoError(t, err) 955 require.Equal(t, nextEpochSetup2.FinalView, setup2FinalView) 956 }) 957 } 958 959 // we should be able to have conflicting forks with two DUPLICATE instances of 960 // the same service event for the same epoch 961 // 962 // /--B1<--B3(R1)<--B5(S1)<--B7 963 // ROOT <--+ 964 // \--B2<--B4(R2)<--B6(S2)<--B8 965 func TestExtendDuplicateEpochEvents(t *testing.T) { 966 rootSnapshot := unittest.RootSnapshotFixture(participants) 967 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 968 969 head, err := rootSnapshot.Head() 970 require.NoError(t, err) 971 result, _, err := rootSnapshot.SealedResult() 972 require.NoError(t, err) 973 974 // add two conflicting blocks for each service event to reference 975 block1 := unittest.BlockWithParentFixture(head) 976 block1.SetPayload(flow.EmptyPayload()) 977 err = state.Extend(context.Background(), block1) 978 require.NoError(t, err) 979 980 block2 := unittest.BlockWithParentFixture(head) 981 block2.SetPayload(flow.EmptyPayload()) 982 err = state.Extend(context.Background(), block2) 983 require.NoError(t, err) 984 985 rootSetup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 986 987 // create an epoch setup event to insert to BOTH forks 988 nextEpochSetup := unittest.EpochSetupFixture( 989 unittest.WithParticipants(rootSetup.Participants), 990 unittest.SetupWithCounter(rootSetup.Counter+1), 991 unittest.WithFinalView(rootSetup.FinalView+1000), 992 unittest.WithFirstView(rootSetup.FinalView+1), 993 ) 994 995 // add blocks containing receipts for block1 and block2 (necessary for sealing) 996 // block 1 receipt contains nextEpochSetup1 997 block1Receipt := unittest.ReceiptForBlockFixture(block1) 998 block1Receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{nextEpochSetup.ServiceEvent()} 999 1000 // add block 1 receipt to block 3 payload 1001 block3 := unittest.BlockWithParentFixture(block1.Header) 1002 block3.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(block1Receipt))) 1003 err = state.Extend(context.Background(), block3) 1004 require.NoError(t, err) 1005 1006 // block 2 receipt contains nextEpochSetup2 1007 block2Receipt := unittest.ReceiptForBlockFixture(block2) 1008 block2Receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{nextEpochSetup.ServiceEvent()} 1009 1010 // add block 2 receipt to block 4 payload 1011 block4 := unittest.BlockWithParentFixture(block2.Header) 1012 block4.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(block2Receipt))) 1013 err = state.Extend(context.Background(), block4) 1014 require.NoError(t, err) 1015 1016 // seal for block 1 1017 seal1 := unittest.Seal.Fixture(unittest.Seal.WithResult(&block1Receipt.ExecutionResult)) 1018 1019 // seal for block 2 1020 seal2 := unittest.Seal.Fixture(unittest.Seal.WithResult(&block2Receipt.ExecutionResult)) 1021 1022 // block 5 builds on block 3, contains seal for block 1 1023 block5 := unittest.BlockWithParentFixture(block3.Header) 1024 block5.SetPayload(flow.Payload{ 1025 Seals: []*flow.Seal{seal1}, 1026 }) 1027 err = state.Extend(context.Background(), block5) 1028 require.NoError(t, err) 1029 1030 // block 6 builds on block 4, contains seal for block 2 1031 block6 := unittest.BlockWithParentFixture(block4.Header) 1032 block6.SetPayload(flow.Payload{ 1033 Seals: []*flow.Seal{seal2}, 1034 }) 1035 err = state.Extend(context.Background(), block6) 1036 require.NoError(t, err) 1037 1038 // block 7 builds on block 5, contains QC for block 7 1039 block7 := unittest.BlockWithParentFixture(block5.Header) 1040 err = state.Extend(context.Background(), block7) 1041 require.NoError(t, err) 1042 1043 // block 8 builds on block 6, contains QC for block 6 1044 // at this point we are inserting the duplicate EpochSetup, should not error 1045 block8 := unittest.BlockWithParentFixture(block6.Header) 1046 err = state.Extend(context.Background(), block8) 1047 require.NoError(t, err) 1048 1049 // should be able query each epoch from the appropriate reference block 1050 finalView, err := state.AtBlockID(block7.ID()).Epochs().Next().FinalView() 1051 assert.NoError(t, err) 1052 require.Equal(t, nextEpochSetup.FinalView, finalView) 1053 1054 finalView, err = state.AtBlockID(block8.ID()).Epochs().Next().FinalView() 1055 assert.NoError(t, err) 1056 require.Equal(t, nextEpochSetup.FinalView, finalView) 1057 }) 1058 } 1059 1060 // extending protocol state with an invalid epoch setup service event should cause an error 1061 func TestExtendEpochSetupInvalid(t *testing.T) { 1062 rootSnapshot := unittest.RootSnapshotFixture(participants) 1063 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 1064 head, err := rootSnapshot.Head() 1065 require.NoError(t, err) 1066 result, _, err := rootSnapshot.SealedResult() 1067 require.NoError(t, err) 1068 1069 // add a block for the first seal to reference 1070 block1 := unittest.BlockWithParentFixture(head) 1071 block1.SetPayload(flow.EmptyPayload()) 1072 err = state.Extend(context.Background(), block1) 1073 require.NoError(t, err) 1074 1075 epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 1076 1077 // add a participant for the next epoch 1078 epoch2NewParticipant := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 1079 epoch2Participants := append(participants, epoch2NewParticipant).Sort(order.Canonical) 1080 1081 // this function will return a VALID setup event and seal, we will modify 1082 // in different ways in each test case 1083 createSetup := func(opts ...func(*flow.EpochSetup)) (*flow.EpochSetup, *flow.ExecutionReceipt, *flow.Seal) { 1084 setup := unittest.EpochSetupFixture( 1085 unittest.WithParticipants(epoch2Participants), 1086 unittest.SetupWithCounter(epoch1Setup.Counter+1), 1087 unittest.WithFinalView(epoch1Setup.FinalView+1000), 1088 unittest.WithFirstView(epoch1Setup.FinalView+1), 1089 ) 1090 for _, apply := range opts { 1091 apply(setup) 1092 } 1093 receipt, seal := unittest.ReceiptAndSealForBlock(block1) 1094 receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{setup.ServiceEvent()} 1095 seal.ResultID = receipt.ExecutionResult.ID() 1096 return setup, receipt, seal 1097 } 1098 1099 // expect a setup event with wrong counter to trigger EECC without error 1100 t.Run("wrong counter (EECC)", func(t *testing.T) { 1101 _, receipt, seal := createSetup(func(setup *flow.EpochSetup) { 1102 setup.Counter = epoch1Setup.Counter 1103 }) 1104 1105 sealingBlock := unittest.SealBlock(t, state, block1, receipt, seal) 1106 1107 qcBlock := unittest.BlockWithParentFixture(sealingBlock) 1108 err = state.Extend(context.Background(), qcBlock) 1109 require.NoError(t, err) 1110 assertEpochEmergencyFallbackTriggered(t, db) 1111 }) 1112 1113 // expect a setup event with wrong final view to trigger EECC without error 1114 t.Run("invalid final view (EECC)", func(t *testing.T) { 1115 _, receipt, seal := createSetup(func(setup *flow.EpochSetup) { 1116 setup.FinalView = block1.Header.View 1117 }) 1118 1119 sealingBlock := unittest.SealBlock(t, state, block1, receipt, seal) 1120 1121 qcBlock := unittest.BlockWithParentFixture(sealingBlock) 1122 err = state.Extend(context.Background(), qcBlock) 1123 require.NoError(t, err) 1124 assertEpochEmergencyFallbackTriggered(t, db) 1125 }) 1126 1127 // expect a setup event with empty seed to trigger EECC without error 1128 t.Run("empty seed (EECC)", func(t *testing.T) { 1129 _, receipt, seal := createSetup(func(setup *flow.EpochSetup) { 1130 setup.RandomSource = nil 1131 }) 1132 1133 sealingBlock := unittest.SealBlock(t, state, block1, receipt, seal) 1134 1135 qcBlock := unittest.BlockWithParentFixture(sealingBlock) 1136 err = state.Extend(context.Background(), qcBlock) 1137 require.NoError(t, err) 1138 assertEpochEmergencyFallbackTriggered(t, db) 1139 }) 1140 }) 1141 } 1142 1143 // extending protocol state with an invalid epoch commit service event should cause an error 1144 func TestExtendEpochCommitInvalid(t *testing.T) { 1145 rootSnapshot := unittest.RootSnapshotFixture(participants) 1146 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 1147 1148 head, err := rootSnapshot.Head() 1149 require.NoError(t, err) 1150 result, _, err := rootSnapshot.SealedResult() 1151 require.NoError(t, err) 1152 1153 // add a block for the first seal to reference 1154 block1 := unittest.BlockWithParentFixture(head) 1155 block1.SetPayload(flow.EmptyPayload()) 1156 err = state.Extend(context.Background(), block1) 1157 require.NoError(t, err) 1158 1159 epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 1160 1161 // swap consensus node for a new one for epoch 2 1162 epoch2NewParticipant := unittest.IdentityFixture(unittest.WithRole(flow.RoleConsensus)) 1163 epoch2Participants := append( 1164 participants.Filter(filter.Not(filter.HasRole(flow.RoleConsensus))), 1165 epoch2NewParticipant, 1166 ).Sort(order.Canonical) 1167 1168 createSetup := func(block *flow.Block) (*flow.EpochSetup, *flow.ExecutionReceipt, *flow.Seal) { 1169 setup := unittest.EpochSetupFixture( 1170 unittest.WithParticipants(epoch2Participants), 1171 unittest.SetupWithCounter(epoch1Setup.Counter+1), 1172 unittest.WithFinalView(epoch1Setup.FinalView+1000), 1173 unittest.WithFirstView(epoch1Setup.FinalView+1), 1174 ) 1175 1176 receipt, seal := unittest.ReceiptAndSealForBlock(block) 1177 receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{setup.ServiceEvent()} 1178 seal.ResultID = receipt.ExecutionResult.ID() 1179 return setup, receipt, seal 1180 } 1181 1182 createCommit := func(block *flow.Block, opts ...func(*flow.EpochCommit)) (*flow.EpochCommit, *flow.ExecutionReceipt, *flow.Seal) { 1183 commit := unittest.EpochCommitFixture( 1184 unittest.CommitWithCounter(epoch1Setup.Counter+1), 1185 unittest.WithDKGFromParticipants(epoch2Participants), 1186 ) 1187 for _, apply := range opts { 1188 apply(commit) 1189 } 1190 receipt, seal := unittest.ReceiptAndSealForBlock(block) 1191 receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{commit.ServiceEvent()} 1192 seal.ResultID = receipt.ExecutionResult.ID() 1193 return commit, receipt, seal 1194 } 1195 1196 t.Run("without setup", func(t *testing.T) { 1197 _, receipt, seal := createCommit(block1) 1198 1199 sealingBlock := unittest.SealBlock(t, state, block1, receipt, seal) 1200 1201 qcBlock := unittest.BlockWithParentFixture(sealingBlock) 1202 err = state.Extend(context.Background(), qcBlock) 1203 require.Error(t, err) 1204 require.True(t, st.IsInvalidExtensionError(err), err) 1205 }) 1206 1207 // seal block 1, in which EpochSetup was emitted 1208 epoch2Setup, setupReceipt, setupSeal := createSetup(block1) 1209 block2 := unittest.SealBlock(t, state, block1, setupReceipt, setupSeal) 1210 1211 // insert a block with a QC for block 2 1212 block3 := unittest.BlockWithParentFixture(block2) 1213 err = state.Extend(context.Background(), block3) 1214 require.NoError(t, err) 1215 1216 // expect a commit event with wrong counter to trigger EECC without error 1217 t.Run("inconsistent counter (EECC)", func(t *testing.T) { 1218 _, receipt, seal := createCommit(block3, func(commit *flow.EpochCommit) { 1219 commit.Counter = epoch2Setup.Counter + 1 1220 }) 1221 1222 sealingBlock := unittest.SealBlock(t, state, block3, receipt, seal) 1223 1224 qcBlock := unittest.BlockWithParentFixture(sealingBlock) 1225 err = state.Extend(context.Background(), qcBlock) 1226 require.NoError(t, err) 1227 assertEpochEmergencyFallbackTriggered(t, db) 1228 }) 1229 1230 // expect a commit event with wrong cluster QCs to trigger EECC without error 1231 t.Run("inconsistent cluster QCs (EECC)", func(t *testing.T) { 1232 _, receipt, seal := createCommit(block3, func(commit *flow.EpochCommit) { 1233 commit.ClusterQCs = append(commit.ClusterQCs, flow.ClusterQCVoteDataFromQC(unittest.QuorumCertificateWithSignerIDsFixture())) 1234 }) 1235 1236 sealingBlock := unittest.SealBlock(t, state, block3, receipt, seal) 1237 1238 qcBlock := unittest.BlockWithParentFixture(sealingBlock) 1239 err = state.Extend(context.Background(), qcBlock) 1240 require.NoError(t, err) 1241 assertEpochEmergencyFallbackTriggered(t, db) 1242 }) 1243 1244 // expect a commit event with wrong dkg participants to trigger EECC without error 1245 t.Run("inconsistent DKG participants (EECC)", func(t *testing.T) { 1246 _, receipt, seal := createCommit(block3, func(commit *flow.EpochCommit) { 1247 // add an extra dkg key 1248 commit.DKGParticipantKeys = append(commit.DKGParticipantKeys, unittest.KeyFixture(crypto.BLSBLS12381).PublicKey()) 1249 }) 1250 1251 sealingBlock := unittest.SealBlock(t, state, block3, receipt, seal) 1252 1253 qcBlock := unittest.BlockWithParentFixture(sealingBlock) 1254 err = state.Extend(context.Background(), qcBlock) 1255 require.NoError(t, err) 1256 assertEpochEmergencyFallbackTriggered(t, db) 1257 }) 1258 }) 1259 } 1260 1261 // if we reach the first block of the next epoch before both setup and commit 1262 // service events are finalized, the chain should halt 1263 // 1264 // ROOT <- B1 <- B2(R1) <- B3(S1) <- B4 1265 func TestExtendEpochTransitionWithoutCommit(t *testing.T) { 1266 1267 // skipping because this case will now result in emergency epoch continuation kicking in 1268 unittest.SkipUnless(t, unittest.TEST_TODO, "disabled as the current implementation uses a temporary fallback measure in this case (triggers EECC), rather than returning an error") 1269 1270 rootSnapshot := unittest.RootSnapshotFixture(participants) 1271 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 1272 head, err := rootSnapshot.Head() 1273 require.NoError(t, err) 1274 result, _, err := rootSnapshot.SealedResult() 1275 require.NoError(t, err) 1276 1277 // add a block for the first seal to reference 1278 block1 := unittest.BlockWithParentFixture(head) 1279 block1.SetPayload(flow.EmptyPayload()) 1280 err = state.Extend(context.Background(), block1) 1281 require.NoError(t, err) 1282 err = state.Finalize(context.Background(), block1.ID()) 1283 require.NoError(t, err) 1284 1285 epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 1286 epoch1FinalView := epoch1Setup.FinalView 1287 1288 // add a participant for the next epoch 1289 epoch2NewParticipant := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 1290 epoch2Participants := append(participants, epoch2NewParticipant).Sort(order.Canonical) 1291 1292 // create the epoch setup event for the second epoch 1293 epoch2Setup := unittest.EpochSetupFixture( 1294 unittest.WithParticipants(epoch2Participants), 1295 unittest.SetupWithCounter(epoch1Setup.Counter+1), 1296 unittest.WithFinalView(epoch1FinalView+1000), 1297 unittest.WithFirstView(epoch1FinalView+1), 1298 ) 1299 1300 receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1) 1301 receipt1.ExecutionResult.ServiceEvents = []flow.ServiceEvent{epoch2Setup.ServiceEvent()} 1302 1303 // add a block containing a receipt for block 1 1304 block2 := unittest.BlockWithParentFixture(block1.Header) 1305 block2.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt1))) 1306 err = state.Extend(context.Background(), block2) 1307 require.NoError(t, err) 1308 err = state.Finalize(context.Background(), block2.ID()) 1309 require.NoError(t, err) 1310 1311 // block 3 seals block 1 1312 block3 := unittest.BlockWithParentFixture(block2.Header) 1313 block3.SetPayload(flow.Payload{ 1314 Seals: []*flow.Seal{seal1}, 1315 }) 1316 err = state.Extend(context.Background(), block3) 1317 require.NoError(t, err) 1318 1319 // block 4 will be the first block for epoch 2 1320 block4 := unittest.BlockWithParentFixture(block3.Header) 1321 block4.Header.View = epoch1Setup.FinalView + 1 1322 1323 err = state.Extend(context.Background(), block4) 1324 require.Error(t, err) 1325 }) 1326 } 1327 1328 func TestEmergencyEpochChainContinuation(t *testing.T) { 1329 1330 // if we reach the first block of the next epoch before both setup and commit 1331 // service events are finalized, the chain should continue with the previous epoch. 1332 // 1333 // ROOT <- B1 <- B2(R1) <- B3(S1) <- B4 1334 t.Run("epoch transition without commit event - should continue with fallback epoch", func(t *testing.T) { 1335 1336 rootSnapshot := unittest.RootSnapshotFixture(participants) 1337 metricsMock := new(mockmodule.ComplianceMetrics) 1338 mockMetricsForRootSnapshot(metricsMock, rootSnapshot) 1339 1340 util.RunWithFullProtocolStateAndMetrics(t, rootSnapshot, metricsMock, func(db *badger.DB, state *protocol.MutableState) { 1341 head, err := rootSnapshot.Head() 1342 require.NoError(t, err) 1343 result, _, err := rootSnapshot.SealedResult() 1344 require.NoError(t, err) 1345 1346 // add a block for the first seal to reference 1347 block1 := unittest.BlockWithParentFixture(head) 1348 block1.SetPayload(flow.EmptyPayload()) 1349 err = state.Extend(context.Background(), block1) 1350 require.NoError(t, err) 1351 err = state.Finalize(context.Background(), block1.ID()) 1352 require.NoError(t, err) 1353 1354 epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 1355 epoch1FinalView := epoch1Setup.FinalView 1356 1357 // add a participant for the next epoch 1358 epoch2NewParticipant := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 1359 epoch2Participants := append(participants, epoch2NewParticipant).Sort(order.Canonical) 1360 1361 // create the epoch setup event for the second epoch 1362 epoch2Setup := unittest.EpochSetupFixture( 1363 unittest.WithParticipants(epoch2Participants), 1364 unittest.SetupWithCounter(epoch1Setup.Counter+1), 1365 unittest.WithFinalView(epoch1FinalView+1000), 1366 unittest.WithFirstView(epoch1FinalView+1), 1367 ) 1368 1369 receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1) 1370 receipt1.ExecutionResult.ServiceEvents = []flow.ServiceEvent{epoch2Setup.ServiceEvent()} 1371 1372 // add a block containing a receipt for block 1 1373 block2 := unittest.BlockWithParentFixture(block1.Header) 1374 block2.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt1))) 1375 err = state.Extend(context.Background(), block2) 1376 require.NoError(t, err) 1377 err = state.Finalize(context.Background(), block2.ID()) 1378 require.NoError(t, err) 1379 1380 // block 3 seals block 1 1381 block3 := unittest.BlockWithParentFixture(block2.Header) 1382 block3.SetPayload(flow.Payload{ 1383 Seals: []*flow.Seal{seal1}, 1384 }) 1385 err = state.Extend(context.Background(), block3) 1386 require.NoError(t, err) 1387 1388 // block 4 will be the first block for epoch 2 1389 block4 := unittest.BlockWithParentFixture(block3.Header) 1390 block4.Header.View = epoch1Setup.FinalView + 1 1391 1392 // inserting block 4 should trigger EECC 1393 metricsMock.On("EpochEmergencyFallbackTriggered").Once() 1394 1395 err = state.Extend(context.Background(), block4) 1396 require.NoError(t, err) 1397 1398 assertEpochEmergencyFallbackTriggered(t, db) 1399 1400 // epoch metrics should not be emitted 1401 metricsMock.AssertNotCalled(t, "EpochTransition", epoch2Setup.Counter, mock.Anything) 1402 metricsMock.AssertNotCalled(t, "CurrentEpochCounter", epoch2Setup.Counter) 1403 metricsMock.AssertExpectations(t) 1404 }) 1405 }) 1406 1407 // if we reach the first block of the next epoch before either setup and commit 1408 // service events are finalized, the chain should continue with the previous epoch. 1409 // 1410 // ROOT <- B1 <- B2(R1) <- B3(S1) <- B4 1411 t.Run("epoch transition without setup event - should continue with fallback epoch", func(t *testing.T) { 1412 1413 rootSnapshot := unittest.RootSnapshotFixture(participants) 1414 metricsMock := new(mockmodule.ComplianceMetrics) 1415 mockMetricsForRootSnapshot(metricsMock, rootSnapshot) 1416 1417 util.RunWithFullProtocolStateAndMetrics(t, rootSnapshot, metricsMock, func(db *badger.DB, state *protocol.MutableState) { 1418 head, err := rootSnapshot.Head() 1419 require.NoError(t, err) 1420 result, _, err := rootSnapshot.SealedResult() 1421 require.NoError(t, err) 1422 1423 // add a block for the first seal to reference 1424 block1 := unittest.BlockWithParentFixture(head) 1425 block1.SetPayload(flow.EmptyPayload()) 1426 err = state.Extend(context.Background(), block1) 1427 require.NoError(t, err) 1428 err = state.Finalize(context.Background(), block1.ID()) 1429 require.NoError(t, err) 1430 1431 epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 1432 receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1) 1433 1434 // add a block containing a receipt for block 1 1435 block2 := unittest.BlockWithParentFixture(block1.Header) 1436 block2.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt1))) 1437 err = state.Extend(context.Background(), block2) 1438 require.NoError(t, err) 1439 err = state.Finalize(context.Background(), block2.ID()) 1440 require.NoError(t, err) 1441 1442 // block 3 seals block 1 1443 block3 := unittest.BlockWithParentFixture(block2.Header) 1444 block3.SetPayload(flow.Payload{ 1445 Seals: []*flow.Seal{seal1}, 1446 }) 1447 err = state.Extend(context.Background(), block3) 1448 require.NoError(t, err) 1449 1450 // block 4 will be the first block for epoch 2 1451 block4 := unittest.BlockWithParentFixture(block3.Header) 1452 block4.Header.View = epoch1Setup.FinalView + 1 1453 1454 // inserting block 4 should trigger EECC 1455 metricsMock.On("EpochEmergencyFallbackTriggered").Once() 1456 1457 err = state.Extend(context.Background(), block4) 1458 require.NoError(t, err) 1459 1460 assertEpochEmergencyFallbackTriggered(t, db) 1461 1462 // epoch metrics should not be emitted 1463 metricsMock.AssertNotCalled(t, "EpochTransition", epoch1Setup.Counter+1, mock.Anything) 1464 metricsMock.AssertNotCalled(t, "CurrentEpochCounter", epoch1Setup.Counter+1) 1465 metricsMock.AssertExpectations(t) 1466 }) 1467 }) 1468 1469 // if an invalid epoch service event is incorporated, we should: 1470 // * not apply the phase transition corresponding to the invalid service event 1471 // * trigger EECC 1472 // 1473 // ROOT <- B1 <- B2(R1) <- B3(S1) <- B4 1474 t.Run("epoch transition with invalid service event - should continue with fallback epoch", func(t *testing.T) { 1475 1476 rootSnapshot := unittest.RootSnapshotFixture(participants) 1477 metricsMock := new(mockmodule.ComplianceMetrics) 1478 mockMetricsForRootSnapshot(metricsMock, rootSnapshot) 1479 1480 util.RunWithFullProtocolStateAndMetrics(t, rootSnapshot, metricsMock, func(db *badger.DB, state *protocol.MutableState) { 1481 head, err := rootSnapshot.Head() 1482 require.NoError(t, err) 1483 result, _, err := rootSnapshot.SealedResult() 1484 require.NoError(t, err) 1485 1486 // add a block for the first seal to reference 1487 block1 := unittest.BlockWithParentFixture(head) 1488 block1.SetPayload(flow.EmptyPayload()) 1489 err = state.Extend(context.Background(), block1) 1490 require.NoError(t, err) 1491 err = state.Finalize(context.Background(), block1.ID()) 1492 require.NoError(t, err) 1493 1494 epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 1495 epoch1FinalView := epoch1Setup.FinalView 1496 1497 // add a participant for the next epoch 1498 epoch2NewParticipant := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 1499 epoch2Participants := append(participants, epoch2NewParticipant).Sort(order.Canonical) 1500 1501 // create the epoch setup event for the second epoch 1502 // this event is invalid because it used a non-contiguous first view 1503 epoch2Setup := unittest.EpochSetupFixture( 1504 unittest.WithParticipants(epoch2Participants), 1505 unittest.SetupWithCounter(epoch1Setup.Counter+1), 1506 unittest.WithFinalView(epoch1FinalView+1000), 1507 unittest.WithFirstView(epoch1FinalView+10), // invalid first view 1508 ) 1509 1510 receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1) 1511 receipt1.ExecutionResult.ServiceEvents = []flow.ServiceEvent{epoch2Setup.ServiceEvent()} 1512 1513 // incorporating the service event should trigger EECC 1514 metricsMock.On("EpochEmergencyFallbackTriggered").Once() 1515 1516 // add a block containing a receipt for block 1 1517 block2 := unittest.BlockWithParentFixture(block1.Header) 1518 block2.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt1))) 1519 err = state.Extend(context.Background(), block2) 1520 require.NoError(t, err) 1521 err = state.Finalize(context.Background(), block2.ID()) 1522 require.NoError(t, err) 1523 1524 // block 3 seals block 1 1525 block3 := unittest.BlockWithParentFixture(block2.Header) 1526 block3.SetPayload(flow.Payload{ 1527 Seals: []*flow.Seal{seal1}, 1528 }) 1529 err = state.Extend(context.Background(), block3) 1530 require.NoError(t, err) 1531 1532 // block 4 will be the first block for epoch 2 1533 block4 := unittest.BlockWithParentFixture(block3.Header) 1534 block4.Header.View = epoch1Setup.FinalView + 1 1535 1536 err = state.Extend(context.Background(), block4) 1537 require.NoError(t, err) 1538 1539 assertEpochEmergencyFallbackTriggered(t, db) 1540 1541 // epoch metrics should not be emitted 1542 metricsMock.AssertNotCalled(t, "EpochTransition", epoch2Setup.Counter, mock.Anything) 1543 metricsMock.AssertNotCalled(t, "CurrentEpochCounter", epoch2Setup.Counter) 1544 metricsMock.AssertExpectations(t) 1545 }) 1546 }) 1547 } 1548 1549 func TestExtendInvalidSealsInBlock(t *testing.T) { 1550 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 1551 metrics := metrics.NewNoopCollector() 1552 tracer := trace.NewNoopTracer() 1553 headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := storeutil.StorageLayer(t, db) 1554 1555 // create a event consumer to test epoch transition events 1556 distributor := events.NewDistributor() 1557 consumer := new(mockprotocol.Consumer) 1558 distributor.AddConsumer(consumer) 1559 1560 rootSnapshot := unittest.RootSnapshotFixture(participants) 1561 1562 state, err := protocol.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) 1563 require.NoError(t, err) 1564 1565 head, err := rootSnapshot.Head() 1566 require.NoError(t, err) 1567 1568 block1 := unittest.BlockWithParentFixture(head) 1569 block1.Payload.Guarantees = nil 1570 block1.Header.PayloadHash = block1.Payload.Hash() 1571 1572 block1Receipt := unittest.ReceiptForBlockFixture(block1) 1573 block2 := unittest.BlockWithParentFixture(block1.Header) 1574 block2.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(block1Receipt))) 1575 1576 block1Seal := unittest.Seal.Fixture(unittest.Seal.WithResult(&block1Receipt.ExecutionResult)) 1577 block3 := unittest.BlockWithParentFixture(block2.Header) 1578 block3.SetPayload(flow.Payload{ 1579 Seals: []*flow.Seal{block1Seal}, 1580 }) 1581 1582 sealValidator := &mockmodule.SealValidator{} 1583 sealValidator.On("Validate", mock.Anything). 1584 Return(func(candidate *flow.Block) *flow.Seal { 1585 if candidate.ID() == block3.ID() { 1586 return nil 1587 } 1588 seal, _ := seals.HighestInFork(candidate.Header.ParentID) 1589 return seal 1590 }, func(candidate *flow.Block) error { 1591 if candidate.ID() == block3.ID() { 1592 return engine.NewInvalidInputError("") 1593 } 1594 _, err := seals.HighestInFork(candidate.Header.ParentID) 1595 return err 1596 }). 1597 Times(3) 1598 1599 fullState, err := protocol.NewFullConsensusState(state, index, payloads, tracer, consumer, 1600 util.MockBlockTimer(), util.MockReceiptValidator(), sealValidator) 1601 require.NoError(t, err) 1602 1603 err = fullState.Extend(context.Background(), block1) 1604 require.NoError(t, err) 1605 err = fullState.Extend(context.Background(), block2) 1606 require.NoError(t, err) 1607 err = fullState.Extend(context.Background(), block3) 1608 1609 sealValidator.AssertExpectations(t) 1610 require.Error(t, err) 1611 require.True(t, st.IsInvalidExtensionError(err)) 1612 }) 1613 } 1614 1615 func TestHeaderExtendValid(t *testing.T) { 1616 rootSnapshot := unittest.RootSnapshotFixture(participants) 1617 util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { 1618 head, err := rootSnapshot.Head() 1619 require.NoError(t, err) 1620 _, seal, err := rootSnapshot.SealedResult() 1621 require.NoError(t, err) 1622 1623 extend := unittest.BlockWithParentFixture(head) 1624 extend.SetPayload(flow.EmptyPayload()) 1625 1626 err = state.Extend(context.Background(), extend) 1627 require.NoError(t, err) 1628 1629 finalCommit, err := state.Final().Commit() 1630 require.NoError(t, err) 1631 require.Equal(t, seal.FinalState, finalCommit) 1632 }) 1633 } 1634 1635 func TestHeaderExtendMissingParent(t *testing.T) { 1636 rootSnapshot := unittest.RootSnapshotFixture(participants) 1637 util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { 1638 extend := unittest.BlockFixture() 1639 extend.Payload.Guarantees = nil 1640 extend.Payload.Seals = nil 1641 extend.Header.Height = 2 1642 extend.Header.View = 2 1643 extend.Header.ParentID = unittest.BlockFixture().ID() 1644 extend.Header.PayloadHash = extend.Payload.Hash() 1645 1646 err := state.Extend(context.Background(), &extend) 1647 require.Error(t, err) 1648 require.True(t, st.IsInvalidExtensionError(err), err) 1649 1650 // verify seal not indexed 1651 var sealID flow.Identifier 1652 err = db.View(operation.LookupLatestSealAtBlock(extend.ID(), &sealID)) 1653 require.Error(t, err) 1654 require.True(t, errors.Is(err, stoerr.ErrNotFound), err) 1655 }) 1656 } 1657 1658 func TestHeaderExtendHeightTooSmall(t *testing.T) { 1659 rootSnapshot := unittest.RootSnapshotFixture(participants) 1660 util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { 1661 head, err := rootSnapshot.Head() 1662 require.NoError(t, err) 1663 1664 block1 := unittest.BlockWithParentFixture(head) 1665 err = state.Extend(context.Background(), block1) 1666 require.NoError(t, err) 1667 1668 // create another block that points to the previous block `extend` as parent 1669 // but has _same_ height as parent. This violates the condition that a child's 1670 // height must increment the parent's height by one, i.e. it should be rejected 1671 // by the follower right away 1672 block2 := unittest.BlockWithParentFixture(block1.Header) 1673 block2.Header.Height = block1.Header.Height 1674 1675 err = state.Extend(context.Background(), block2) 1676 require.Error(t, err) 1677 1678 // verify seal not indexed 1679 var sealID flow.Identifier 1680 err = db.View(operation.LookupLatestSealAtBlock(block2.ID(), &sealID)) 1681 require.Error(t, err) 1682 require.True(t, errors.Is(err, stoerr.ErrNotFound), err) 1683 }) 1684 } 1685 1686 func TestHeaderExtendHeightTooLarge(t *testing.T) { 1687 rootSnapshot := unittest.RootSnapshotFixture(participants) 1688 util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { 1689 head, err := rootSnapshot.Head() 1690 require.NoError(t, err) 1691 1692 block := unittest.BlockWithParentFixture(head) 1693 block.SetPayload(flow.EmptyPayload()) 1694 // set an invalid height 1695 block.Header.Height = head.Height + 2 1696 1697 err = state.Extend(context.Background(), block) 1698 require.Error(t, err) 1699 }) 1700 } 1701 1702 func TestHeaderExtendBlockNotConnected(t *testing.T) { 1703 rootSnapshot := unittest.RootSnapshotFixture(participants) 1704 util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { 1705 head, err := rootSnapshot.Head() 1706 require.NoError(t, err) 1707 1708 // add 2 blocks, where: 1709 // first block is added and then finalized; 1710 // second block is a sibling to the finalized block 1711 // The Follower should reject this block as an outdated chain extension 1712 block1 := unittest.BlockWithParentFixture(head) 1713 err = state.Extend(context.Background(), block1) 1714 require.NoError(t, err) 1715 1716 err = state.Finalize(context.Background(), block1.ID()) 1717 require.NoError(t, err) 1718 1719 // create a fork at view/height 1 and try to connect it to root 1720 block2 := unittest.BlockWithParentFixture(head) 1721 err = state.Extend(context.Background(), block2) 1722 require.Error(t, err) 1723 require.True(t, st.IsOutdatedExtensionError(err), err) 1724 1725 // verify seal not indexed 1726 var sealID flow.Identifier 1727 err = db.View(operation.LookupLatestSealAtBlock(block2.ID(), &sealID)) 1728 require.Error(t, err) 1729 require.True(t, errors.Is(err, stoerr.ErrNotFound), err) 1730 }) 1731 } 1732 1733 func TestHeaderExtendHighestSeal(t *testing.T) { 1734 rootSnapshot := unittest.RootSnapshotFixture(participants) 1735 head, err := rootSnapshot.Head() 1736 require.NoError(t, err) 1737 util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { 1738 // create block2 and block3 1739 block2 := unittest.BlockWithParentFixture(head) 1740 block2.SetPayload(flow.EmptyPayload()) 1741 err := state.Extend(context.Background(), block2) 1742 require.NoError(t, err) 1743 1744 block3 := unittest.BlockWithParentFixture(block2.Header) 1745 block3.SetPayload(flow.EmptyPayload()) 1746 err = state.Extend(context.Background(), block3) 1747 require.NoError(t, err) 1748 1749 // create seals for block2 and block3 1750 seal2 := unittest.Seal.Fixture( 1751 unittest.Seal.WithBlockID(block2.ID()), 1752 ) 1753 seal3 := unittest.Seal.Fixture( 1754 unittest.Seal.WithBlockID(block3.ID()), 1755 ) 1756 1757 // include the seals in block4 1758 block4 := unittest.BlockWithParentFixture(block3.Header) 1759 block4.SetPayload(flow.Payload{ 1760 // placing seals in the reversed order to test 1761 // Extend will pick the highest sealed block 1762 Seals: []*flow.Seal{seal3, seal2}, 1763 Guarantees: nil, 1764 }) 1765 err = state.Extend(context.Background(), block4) 1766 require.NoError(t, err) 1767 1768 finalCommit, err := state.AtBlockID(block4.ID()).Commit() 1769 require.NoError(t, err) 1770 require.Equal(t, seal3.FinalState, finalCommit) 1771 }) 1772 } 1773 1774 // TestExtendInvalidGuarantee checks if Extend method will reject invalid blocks that contain 1775 // guarantees with invalid guarantors 1776 func TestExtendInvalidGuarantee(t *testing.T) { 1777 rootSnapshot := unittest.RootSnapshotFixture(participants) 1778 util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { 1779 // create a valid block 1780 head, err := rootSnapshot.Head() 1781 require.NoError(t, err) 1782 1783 cluster, err := unittest.SnapshotClusterByIndex(rootSnapshot, 0) 1784 require.NoError(t, err) 1785 1786 // prepare for a valid guarantor signer indices to be used in the valid block 1787 all := cluster.Members().NodeIDs() 1788 validSignerIndices, err := signature.EncodeSignersToIndices(all, all) 1789 require.NoError(t, err) 1790 1791 block := unittest.BlockWithParentFixture(head) 1792 payload := flow.EmptyPayload() 1793 payload.Guarantees = []*flow.CollectionGuarantee{ 1794 { 1795 ChainID: cluster.ChainID(), 1796 ReferenceBlockID: head.ID(), 1797 SignerIndices: validSignerIndices, 1798 }, 1799 } 1800 1801 // now the valid block has a guarantee in the payload with valid signer indices. 1802 block.SetPayload(payload) 1803 1804 // check Extend should accept this valid block 1805 err = state.Extend(context.Background(), block) 1806 require.NoError(t, err) 1807 1808 // now the guarantee has invalid signer indices: the checksum should have 4 bytes, but it only has 1 1809 payload.Guarantees[0].SignerIndices = []byte{byte(1)} 1810 err = state.Extend(context.Background(), block) 1811 require.Error(t, err) 1812 require.True(t, signature.IsInvalidSignerIndicesError(err), err) 1813 require.True(t, errors.Is(err, signature.ErrInvalidChecksum), err) 1814 require.True(t, st.IsInvalidExtensionError(err), err) 1815 1816 // now the guarantee has invalid signer indices: the checksum should have 4 bytes, but it only has 1 1817 checksumMismatch := make([]byte, len(validSignerIndices)) 1818 copy(checksumMismatch, validSignerIndices) 1819 checksumMismatch[0] = byte(1) 1820 if checksumMismatch[0] == validSignerIndices[0] { 1821 checksumMismatch[0] = byte(2) 1822 } 1823 payload.Guarantees[0].SignerIndices = checksumMismatch 1824 err = state.Extend(context.Background(), block) 1825 require.Error(t, err) 1826 require.True(t, signature.IsInvalidSignerIndicesError(err), err) 1827 require.True(t, errors.Is(err, signature.ErrInvalidChecksum), err) 1828 require.True(t, st.IsInvalidExtensionError(err), err) 1829 1830 // let's test even if the checksum is correct, but signer indices is still wrong because the tailing are not 0, 1831 // then the block should still be rejected. 1832 wrongTailing := make([]byte, len(validSignerIndices)) 1833 copy(wrongTailing, validSignerIndices) 1834 wrongTailing[len(wrongTailing)-1] = byte(255) 1835 1836 payload.Guarantees[0].SignerIndices = wrongTailing 1837 err = state.Extend(context.Background(), block) 1838 require.Error(t, err) 1839 require.True(t, signature.IsInvalidSignerIndicesError(err), err) 1840 require.True(t, errors.Is(err, signature.ErrIllegallyPaddedBitVector), err) 1841 require.True(t, st.IsInvalidExtensionError(err), err) 1842 1843 // test imcompatible bit vector length 1844 wrongbitVectorLength := validSignerIndices[0 : len(validSignerIndices)-1] 1845 payload.Guarantees[0].SignerIndices = wrongbitVectorLength 1846 err = state.Extend(context.Background(), block) 1847 require.Error(t, err) 1848 require.True(t, signature.IsInvalidSignerIndicesError(err), err) 1849 require.True(t, errors.Is(err, signature.ErrIncompatibleBitVectorLength), err) 1850 require.True(t, st.IsInvalidExtensionError(err), err) 1851 1852 // revert back to good value 1853 payload.Guarantees[0].SignerIndices = validSignerIndices 1854 1855 // test the ReferenceBlockID is not found 1856 payload.Guarantees[0].ReferenceBlockID = flow.ZeroID 1857 err = state.Extend(context.Background(), block) 1858 require.Error(t, err) 1859 require.True(t, errors.Is(err, storage.ErrNotFound), err) 1860 require.True(t, st.IsInvalidExtensionError(err), err) 1861 1862 // revert back to good value 1863 payload.Guarantees[0].ReferenceBlockID = head.ID() 1864 1865 // TODO: test the guarantee has bad reference block ID that would return ErrEpochNotCommitted 1866 // this case is not easy to create, since the test case has no such block yet. 1867 // we need to refactor the MutableState to add a guaranteeValidator, so that we can mock it and 1868 // return the ErrEpochNotCommitted for testing 1869 1870 // test the guarantee has wrong chain ID, and should return ErrClusterNotFound 1871 payload.Guarantees[0].ChainID = flow.ChainID("some_bad_chain_ID") 1872 err = state.Extend(context.Background(), block) 1873 require.Error(t, err) 1874 require.True(t, errors.Is(err, realprotocol.ErrClusterNotFound), err) 1875 require.True(t, st.IsInvalidExtensionError(err), err) 1876 }) 1877 } 1878 1879 func TestMakeValid(t *testing.T) { 1880 t.Run("should trigger BlockProcessable with parent block", func(t *testing.T) { 1881 consumer := &mockprotocol.Consumer{} 1882 rootSnapshot := unittest.RootSnapshotFixture(participants) 1883 head, err := rootSnapshot.Head() 1884 require.NoError(t, err) 1885 util.RunWithFullProtocolStateAndConsumer(t, rootSnapshot, consumer, func(db *badger.DB, state *protocol.MutableState) { 1886 // create block2 and block3 1887 block2 := unittest.BlockWithParentFixture(head) 1888 block2.SetPayload(flow.EmptyPayload()) 1889 err := state.Extend(context.Background(), block2) 1890 require.NoError(t, err) 1891 1892 block3 := unittest.BlockWithParentFixture(block2.Header) 1893 block3.SetPayload(flow.EmptyPayload()) 1894 err = state.Extend(context.Background(), block3) 1895 require.NoError(t, err) 1896 1897 consumer.On("BlockProcessable", mock.Anything).Return() 1898 1899 // make valid on block2 1900 err = state.MarkValid(block2.ID()) 1901 require.NoError(t, err) 1902 1903 // because the parent block is the root block, 1904 // BlockProcessable is not triggered on root block. 1905 consumer.AssertNotCalled(t, "BlockProcessable") 1906 1907 err = state.MarkValid(block3.ID()) 1908 require.NoError(t, err) 1909 1910 // because the parent is not a root block, BlockProcessable event should be emitted 1911 // block3's parent is block2 1912 consumer.AssertCalled(t, "BlockProcessable", block2.Header) 1913 }) 1914 }) 1915 } 1916 1917 // If block B is finalized and contains a seal for block A, then A is the last sealed block 1918 func TestSealed(t *testing.T) { 1919 rootSnapshot := unittest.RootSnapshotFixture(participants) 1920 util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { 1921 head, err := rootSnapshot.Head() 1922 require.NoError(t, err) 1923 1924 // block 1 will be sealed 1925 block1 := unittest.BlockWithParentFixture(head) 1926 err = state.Extend(context.Background(), block1) 1927 require.NoError(t, err) 1928 err = state.Finalize(context.Background(), block1.ID()) 1929 require.NoError(t, err) 1930 1931 receipt1, seal1 := unittest.ReceiptAndSealForBlock(block1) 1932 1933 // block 2 contains receipt for block 1 1934 block2 := unittest.BlockWithParentFixture(block1.Header) 1935 block2.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt1))) 1936 err = state.Extend(context.Background(), block2) 1937 require.NoError(t, err) 1938 err = state.Finalize(context.Background(), block2.ID()) 1939 require.NoError(t, err) 1940 1941 // block 3 contains seal for block 1 1942 block3 := unittest.BlockWithParentFixture(block2.Header) 1943 block3.SetPayload(flow.Payload{ 1944 Seals: []*flow.Seal{seal1}, 1945 }) 1946 err = state.Extend(context.Background(), block3) 1947 require.NoError(t, err) 1948 err = state.Finalize(context.Background(), block3.ID()) 1949 require.NoError(t, err) 1950 1951 sealed, err := state.Sealed().Head() 1952 require.NoError(t, err) 1953 require.Equal(t, block1.ID(), sealed.ID()) 1954 }) 1955 } 1956 1957 // Test that when adding a block to database, there are only two cases at any point of time: 1958 // 1) neither the block header, nor the payload index exist in database 1959 // 2) both the block header and the payload index can be found in database 1960 // A non atomic bug would be: header is found in DB, but payload index is not found 1961 func TestCacheAtomicity(t *testing.T) { 1962 rootSnapshot := unittest.RootSnapshotFixture(participants) 1963 util.RunWithFollowerProtocolStateAndHeaders(t, rootSnapshot, 1964 func(db *badger.DB, state *protocol.FollowerState, headers storage.Headers, index storage.Index) { 1965 head, err := rootSnapshot.Head() 1966 require.NoError(t, err) 1967 1968 block := unittest.BlockWithParentFixture(head) 1969 blockID := block.ID() 1970 1971 // check 100 times to see if either 1) or 2) satisfies 1972 var wg sync.WaitGroup 1973 wg.Add(1) 1974 go func(blockID flow.Identifier) { 1975 for i := 0; i < 100; i++ { 1976 _, err := headers.ByBlockID(blockID) 1977 if errors.Is(err, stoerr.ErrNotFound) { 1978 continue 1979 } 1980 require.NoError(t, err) 1981 1982 _, err = index.ByBlockID(blockID) 1983 require.NoError(t, err, "found block ID, but index is missing, DB updates is non-atomic") 1984 } 1985 wg.Done() 1986 }(blockID) 1987 1988 // storing the block to database, which supposed to be atomic updates to headers and index, 1989 // both to badger database and the cache. 1990 err = state.Extend(context.Background(), block) 1991 require.NoError(t, err) 1992 wg.Wait() 1993 }) 1994 } 1995 1996 // TestHeaderInvalidTimestamp tests that extending header with invalid timestamp results in sentinel error 1997 func TestHeaderInvalidTimestamp(t *testing.T) { 1998 unittest.RunWithBadgerDB(t, func(db *badger.DB) { 1999 metrics := metrics.NewNoopCollector() 2000 tracer := trace.NewNoopTracer() 2001 headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := storeutil.StorageLayer(t, db) 2002 2003 // create a event consumer to test epoch transition events 2004 distributor := events.NewDistributor() 2005 consumer := new(mockprotocol.Consumer) 2006 distributor.AddConsumer(consumer) 2007 2008 block, result, seal := unittest.BootstrapFixture(participants) 2009 qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(block.ID())) 2010 rootSnapshot, err := inmem.SnapshotFromBootstrapState(block, result, seal, qc) 2011 require.NoError(t, err) 2012 2013 state, err := protocol.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) 2014 require.NoError(t, err) 2015 2016 blockTimer := &mockprotocol.BlockTimer{} 2017 blockTimer.On("Validate", mock.Anything, mock.Anything).Return(realprotocol.NewInvalidBlockTimestamp("")) 2018 2019 fullState, err := protocol.NewFullConsensusState(state, index, payloads, tracer, consumer, blockTimer, 2020 util.MockReceiptValidator(), util.MockSealValidator(seals)) 2021 require.NoError(t, err) 2022 2023 extend := unittest.BlockWithParentFixture(block.Header) 2024 extend.Payload.Guarantees = nil 2025 extend.Header.PayloadHash = extend.Payload.Hash() 2026 2027 err = fullState.Extend(context.Background(), extend) 2028 assert.Error(t, err, "a proposal with invalid timestamp has to be rejected") 2029 assert.True(t, st.IsInvalidExtensionError(err), "if timestamp is invalid it should return invalid block error") 2030 }) 2031 } 2032 2033 func assertEpochEmergencyFallbackTriggered(t *testing.T, db *badger.DB) { 2034 var triggered bool 2035 err := db.View(operation.CheckEpochEmergencyFallbackTriggered(&triggered)) 2036 require.NoError(t, err) 2037 assert.True(t, triggered) 2038 } 2039 2040 // mockMetricsForRootSnapshot mocks the given metrics mock object to expect all 2041 // metrics which are set during bootstrapping and building blocks. 2042 func mockMetricsForRootSnapshot(metricsMock *mockmodule.ComplianceMetrics, rootSnapshot *inmem.Snapshot) { 2043 metricsMock.On("CurrentEpochCounter", rootSnapshot.Encodable().Epochs.Current.Counter) 2044 metricsMock.On("CurrentEpochPhase", rootSnapshot.Encodable().Phase) 2045 metricsMock.On("CurrentEpochFinalView", rootSnapshot.Encodable().Epochs.Current.FinalView) 2046 metricsMock.On("CommittedEpochFinalView", rootSnapshot.Encodable().Epochs.Current.FinalView) 2047 metricsMock.On("CurrentDKGPhase1FinalView", rootSnapshot.Encodable().Epochs.Current.DKGPhase1FinalView) 2048 metricsMock.On("CurrentDKGPhase2FinalView", rootSnapshot.Encodable().Epochs.Current.DKGPhase2FinalView) 2049 metricsMock.On("CurrentDKGPhase3FinalView", rootSnapshot.Encodable().Epochs.Current.DKGPhase3FinalView) 2050 metricsMock.On("BlockSealed", mock.Anything) 2051 metricsMock.On("BlockFinalized", mock.Anything) 2052 metricsMock.On("FinalizedHeight", mock.Anything) 2053 metricsMock.On("SealedHeight", mock.Anything) 2054 2055 }