github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/forks/forks_test.go (about) 1 package forks 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/mock" 9 "github.com/stretchr/testify/require" 10 11 "github.com/onflow/flow-go/consensus/hotstuff" 12 "github.com/onflow/flow-go/consensus/hotstuff/mocks" 13 "github.com/onflow/flow-go/consensus/hotstuff/model" 14 "github.com/onflow/flow-go/model/flow" 15 mockmodule "github.com/onflow/flow-go/module/mock" 16 ) 17 18 /***************************************************************************** 19 * NOTATION: * 20 * A block is denoted as [◄(<qc_number>) <block_view_number>]. * 21 * For example, [◄(1) 2] means: a block of view 2 that has a QC for view 1. * 22 *****************************************************************************/ 23 24 // TestInitialization verifies that at initialization, Forks reports: 25 // - the root / genesis block as finalized 26 // - it has no finalization proof for the root / genesis block (block and its finalization is trusted) 27 func TestInitialization(t *testing.T) { 28 forks, _ := newForks(t) 29 requireOnlyGenesisBlockFinalized(t, forks) 30 _, hasProof := forks.FinalityProof() 31 require.False(t, hasProof) 32 } 33 34 // TestFinalize_Direct1Chain tests adding a direct 1-chain on top of the genesis block: 35 // - receives [◄(1) 2] [◄(2) 5] 36 // 37 // Expected behaviour: 38 // - On the one hand, Forks should not finalize any _additional_ blocks, because there is 39 // no finalizable 2-chain for [◄(1) 2]. Hence, finalization no events should be emitted. 40 // - On the other hand, after adding the two blocks, Forks has enough knowledge to construct 41 // a FinalityProof for the genesis block. 42 func TestFinalize_Direct1Chain(t *testing.T) { 43 builder := NewBlockBuilder(). 44 Add(1, 2). 45 Add(2, 3) 46 blocks, err := builder.Blocks() 47 require.Nil(t, err) 48 49 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 50 forks, _ := newForks(t) 51 52 // adding block [◄(1) 2] should not finalize anything 53 // as the genesis block is trusted, there should be no FinalityProof available for it 54 require.NoError(t, forks.AddValidatedBlock(blocks[0])) 55 requireOnlyGenesisBlockFinalized(t, forks) 56 _, hasProof := forks.FinalityProof() 57 require.False(t, hasProof) 58 59 // After adding block [◄(2) 3], Forks has enough knowledge to construct a FinalityProof for the 60 // genesis block. However, finalization remains at the genesis block, so no events should be emitted. 61 expectedFinalityProof := makeFinalityProof(t, builder.GenesisBlock().Block, blocks[0], blocks[1].QC) 62 require.NoError(t, forks.AddValidatedBlock(blocks[1])) 63 requireLatestFinalizedBlock(t, forks, builder.GenesisBlock().Block) 64 requireFinalityProof(t, forks, expectedFinalityProof) 65 }) 66 67 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 68 forks, _ := newForks(t) 69 70 // After adding CertifiedBlock [◄(1) 2] ◄(2), Forks has enough knowledge to construct a FinalityProof for 71 // the genesis block. However, finalization remains at the genesis block, so no events should be emitted. 72 expectedFinalityProof := makeFinalityProof(t, builder.GenesisBlock().Block, blocks[0], blocks[1].QC) 73 c, err := model.NewCertifiedBlock(blocks[0], blocks[1].QC) 74 require.NoError(t, err) 75 76 require.NoError(t, forks.AddCertifiedBlock(&c)) 77 requireLatestFinalizedBlock(t, forks, builder.GenesisBlock().Block) 78 requireFinalityProof(t, forks, expectedFinalityProof) 79 }) 80 } 81 82 // TestFinalize_Direct2Chain tests adding a direct 1-chain on a direct 1-chain (direct 2-chain). 83 // - receives [◄(1) 2] [◄(2) 3] [◄(3) 4] 84 // - Forks should finalize [◄(1) 2] 85 func TestFinalize_Direct2Chain(t *testing.T) { 86 blocks, err := NewBlockBuilder(). 87 Add(1, 2). 88 Add(2, 3). 89 Add(3, 4). 90 Blocks() 91 require.Nil(t, err) 92 expectedFinalityProof := makeFinalityProof(t, blocks[0], blocks[1], blocks[2].QC) 93 94 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 95 forks, _ := newForks(t) 96 require.Nil(t, addValidatedBlockToForks(forks, blocks)) 97 98 requireLatestFinalizedBlock(t, forks, blocks[0]) 99 requireFinalityProof(t, forks, expectedFinalityProof) 100 }) 101 102 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 103 forks, _ := newForks(t) 104 require.Nil(t, addCertifiedBlocksToForks(forks, blocks)) 105 106 requireLatestFinalizedBlock(t, forks, blocks[0]) 107 requireFinalityProof(t, forks, expectedFinalityProof) 108 }) 109 } 110 111 // TestFinalize_DirectIndirect2Chain tests adding an indirect 1-chain on a direct 1-chain. 112 // receives [◄(1) 2] [◄(2) 3] [◄(3) 5] 113 // it should finalize [◄(1) 2] 114 func TestFinalize_DirectIndirect2Chain(t *testing.T) { 115 blocks, err := NewBlockBuilder(). 116 Add(1, 2). 117 Add(2, 3). 118 Add(3, 5). 119 Blocks() 120 require.Nil(t, err) 121 expectedFinalityProof := makeFinalityProof(t, blocks[0], blocks[1], blocks[2].QC) 122 123 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 124 forks, _ := newForks(t) 125 require.Nil(t, addValidatedBlockToForks(forks, blocks)) 126 127 requireLatestFinalizedBlock(t, forks, blocks[0]) 128 requireFinalityProof(t, forks, expectedFinalityProof) 129 }) 130 131 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 132 forks, _ := newForks(t) 133 require.Nil(t, addCertifiedBlocksToForks(forks, blocks)) 134 135 requireLatestFinalizedBlock(t, forks, blocks[0]) 136 requireFinalityProof(t, forks, expectedFinalityProof) 137 }) 138 } 139 140 // TestFinalize_IndirectDirect2Chain tests adding a direct 1-chain on an indirect 1-chain. 141 // - Forks receives [◄(1) 3] [◄(3) 5] [◄(7) 7] 142 // - it should not finalize any blocks because there is no finalizable 2-chain. 143 func TestFinalize_IndirectDirect2Chain(t *testing.T) { 144 blocks, err := NewBlockBuilder(). 145 Add(1, 3). 146 Add(3, 5). 147 Add(5, 7). 148 Blocks() 149 require.Nil(t, err) 150 151 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 152 forks, _ := newForks(t) 153 require.Nil(t, addValidatedBlockToForks(forks, blocks)) 154 155 requireOnlyGenesisBlockFinalized(t, forks) 156 _, hasProof := forks.FinalityProof() 157 require.False(t, hasProof) 158 }) 159 160 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 161 forks, _ := newForks(t) 162 require.Nil(t, addCertifiedBlocksToForks(forks, blocks)) 163 164 requireOnlyGenesisBlockFinalized(t, forks) 165 _, hasProof := forks.FinalityProof() 166 require.False(t, hasProof) 167 }) 168 } 169 170 // TestFinalize_Direct2ChainOnIndirect tests adding a direct 2-chain on an indirect 2-chain: 171 // - ingesting [◄(1) 3] [◄(3) 5] [◄(5) 6] [◄(6) 7] [◄(7) 8] 172 // - should result in finalization of [◄(5) 6] 173 func TestFinalize_Direct2ChainOnIndirect(t *testing.T) { 174 blocks, err := NewBlockBuilder(). 175 Add(1, 3). 176 Add(3, 5). 177 Add(5, 6). 178 Add(6, 7). 179 Add(7, 8). 180 Blocks() 181 require.Nil(t, err) 182 expectedFinalityProof := makeFinalityProof(t, blocks[2], blocks[3], blocks[4].QC) 183 184 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 185 forks, _ := newForks(t) 186 require.Nil(t, addValidatedBlockToForks(forks, blocks)) 187 188 requireLatestFinalizedBlock(t, forks, blocks[2]) 189 requireFinalityProof(t, forks, expectedFinalityProof) 190 }) 191 192 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 193 forks, _ := newForks(t) 194 require.Nil(t, addCertifiedBlocksToForks(forks, blocks)) 195 196 requireLatestFinalizedBlock(t, forks, blocks[2]) 197 requireFinalityProof(t, forks, expectedFinalityProof) 198 }) 199 } 200 201 // TestFinalize_Direct2ChainOnDirect tests adding a sequence of direct 2-chains: 202 // - ingesting [◄(1) 2] [◄(2) 3] [◄(3) 4] [◄(4) 5] [◄(5) 6] 203 // - should result in finalization of [◄(3) 4] 204 func TestFinalize_Direct2ChainOnDirect(t *testing.T) { 205 blocks, err := NewBlockBuilder(). 206 Add(1, 2). 207 Add(2, 3). 208 Add(3, 4). 209 Add(4, 5). 210 Add(5, 6). 211 Blocks() 212 require.Nil(t, err) 213 expectedFinalityProof := makeFinalityProof(t, blocks[2], blocks[3], blocks[4].QC) 214 215 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 216 forks, _ := newForks(t) 217 require.Nil(t, addValidatedBlockToForks(forks, blocks)) 218 219 requireLatestFinalizedBlock(t, forks, blocks[2]) 220 requireFinalityProof(t, forks, expectedFinalityProof) 221 }) 222 223 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 224 forks, _ := newForks(t) 225 require.Nil(t, addCertifiedBlocksToForks(forks, blocks)) 226 227 requireLatestFinalizedBlock(t, forks, blocks[2]) 228 requireFinalityProof(t, forks, expectedFinalityProof) 229 }) 230 } 231 232 // TestFinalize_Multiple2Chains tests the case where a block can be finalized by different 2-chains. 233 // - ingesting [◄(1) 2] [◄(2) 3] [◄(3) 5] [◄(3) 6] [◄(3) 7] 234 // - should result in finalization of [◄(1) 2] 235 func TestFinalize_Multiple2Chains(t *testing.T) { 236 blocks, err := NewBlockBuilder(). 237 Add(1, 2). 238 Add(2, 3). 239 Add(3, 5). 240 Add(3, 6). 241 Add(3, 7). 242 Blocks() 243 require.Nil(t, err) 244 expectedFinalityProof := makeFinalityProof(t, blocks[0], blocks[1], blocks[2].QC) 245 246 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 247 forks, _ := newForks(t) 248 require.Nil(t, addValidatedBlockToForks(forks, blocks)) 249 250 requireLatestFinalizedBlock(t, forks, blocks[0]) 251 requireFinalityProof(t, forks, expectedFinalityProof) 252 }) 253 254 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 255 forks, _ := newForks(t) 256 require.Nil(t, addCertifiedBlocksToForks(forks, blocks)) 257 258 requireLatestFinalizedBlock(t, forks, blocks[0]) 259 requireFinalityProof(t, forks, expectedFinalityProof) 260 }) 261 } 262 263 // TestFinalize_OrphanedFork tests that we can finalize a block which causes a conflicting fork to be orphaned. 264 // We ingest the the following block tree: 265 // 266 // [◄(1) 2] [◄(2) 3] 267 // [◄(2) 4] [◄(4) 5] [◄(5) 6] 268 // 269 // which should result in finalization of [◄(2) 4] and pruning of [◄(2) 3] 270 func TestFinalize_OrphanedFork(t *testing.T) { 271 blocks, err := NewBlockBuilder(). 272 Add(1, 2). // [◄(1) 2] 273 Add(2, 3). // [◄(2) 3], should eventually be pruned 274 Add(2, 4). // [◄(2) 4], should eventually be finalized 275 Add(4, 5). // [◄(4) 5] 276 Add(5, 6). // [◄(5) 6] 277 Blocks() 278 require.Nil(t, err) 279 expectedFinalityProof := makeFinalityProof(t, blocks[2], blocks[3], blocks[4].QC) 280 281 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 282 forks, _ := newForks(t) 283 require.Nil(t, addValidatedBlockToForks(forks, blocks)) 284 285 require.False(t, forks.IsKnownBlock(blocks[1].BlockID)) 286 requireLatestFinalizedBlock(t, forks, blocks[2]) 287 requireFinalityProof(t, forks, expectedFinalityProof) 288 }) 289 290 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 291 forks, _ := newForks(t) 292 require.Nil(t, addCertifiedBlocksToForks(forks, blocks)) 293 294 require.False(t, forks.IsKnownBlock(blocks[1].BlockID)) 295 requireLatestFinalizedBlock(t, forks, blocks[2]) 296 requireFinalityProof(t, forks, expectedFinalityProof) 297 }) 298 } 299 300 // TestDuplication tests that delivering the same block/qc multiple times has 301 // the same end state as delivering the block/qc once. 302 // - Forks receives [◄(1) 2] [◄(2) 3] [◄(2) 3] [◄(3) 4] [◄(3) 4] [◄(4) 5] [◄(4) 5] 303 // - it should finalize [◄(2) 3] 304 func TestDuplication(t *testing.T) { 305 blocks, err := NewBlockBuilder(). 306 Add(1, 2). 307 Add(2, 3). 308 Add(2, 3). 309 Add(3, 4). 310 Add(3, 4). 311 Add(4, 5). 312 Add(4, 5). 313 Blocks() 314 require.Nil(t, err) 315 expectedFinalityProof := makeFinalityProof(t, blocks[1], blocks[3], blocks[5].QC) 316 317 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 318 forks, _ := newForks(t) 319 require.Nil(t, addValidatedBlockToForks(forks, blocks)) 320 321 requireLatestFinalizedBlock(t, forks, blocks[1]) 322 requireFinalityProof(t, forks, expectedFinalityProof) 323 }) 324 325 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 326 forks, _ := newForks(t) 327 require.Nil(t, addCertifiedBlocksToForks(forks, blocks)) 328 329 requireLatestFinalizedBlock(t, forks, blocks[1]) 330 requireFinalityProof(t, forks, expectedFinalityProof) 331 }) 332 } 333 334 // TestIgnoreBlocksBelowFinalizedView tests that blocks below finalized view are ignored. 335 // - Forks receives [◄(1) 2] [◄(2) 3] [◄(3) 4] [◄(1) 5] 336 // - it should finalize [◄(1) 2] 337 func TestIgnoreBlocksBelowFinalizedView(t *testing.T) { 338 builder := NewBlockBuilder(). 339 Add(1, 2). // [◄(1) 2] 340 Add(2, 3). // [◄(2) 3] 341 Add(3, 4). // [◄(3) 4] 342 Add(1, 5) // [◄(1) 5] 343 blocks, err := builder.Blocks() 344 require.Nil(t, err) 345 expectedFinalityProof := makeFinalityProof(t, blocks[0], blocks[1], blocks[2].QC) 346 347 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 348 // initialize forks and add first 3 blocks: 349 // * block [◄(1) 2] should then be finalized 350 // * and block [1] should be pruned 351 forks, _ := newForks(t) 352 require.Nil(t, addValidatedBlockToForks(forks, blocks[:3])) 353 354 // sanity checks to confirm correct test setup 355 requireLatestFinalizedBlock(t, forks, blocks[0]) 356 requireFinalityProof(t, forks, expectedFinalityProof) 357 require.False(t, forks.IsKnownBlock(builder.GenesisBlock().ID())) 358 359 // adding block [◄(1) 5]: note that QC is _below_ the pruning threshold, i.e. cannot resolve the parent 360 // * Forks should store block, despite the parent already being pruned 361 // * finalization should not change 362 orphanedBlock := blocks[3] 363 require.Nil(t, forks.AddValidatedBlock(orphanedBlock)) 364 require.True(t, forks.IsKnownBlock(orphanedBlock.BlockID)) 365 requireLatestFinalizedBlock(t, forks, blocks[0]) 366 requireFinalityProof(t, forks, expectedFinalityProof) 367 }) 368 369 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 370 // initialize forks and add first 3 blocks: 371 // * block [◄(1) 2] should then be finalized 372 // * and block [1] should be pruned 373 forks, _ := newForks(t) 374 require.Nil(t, addCertifiedBlocksToForks(forks, blocks[:3])) 375 // sanity checks to confirm correct test setup 376 requireLatestFinalizedBlock(t, forks, blocks[0]) 377 requireFinalityProof(t, forks, expectedFinalityProof) 378 require.False(t, forks.IsKnownBlock(builder.GenesisBlock().ID())) 379 380 // adding block [◄(1) 5]: note that QC is _below_ the pruning threshold, i.e. cannot resolve the parent 381 // * Forks should store block, despite the parent already being pruned 382 // * finalization should not change 383 certBlockWithUnknownParent := toCertifiedBlock(t, blocks[3]) 384 require.Nil(t, forks.AddCertifiedBlock(certBlockWithUnknownParent)) 385 require.True(t, forks.IsKnownBlock(certBlockWithUnknownParent.Block.BlockID)) 386 requireLatestFinalizedBlock(t, forks, blocks[0]) 387 requireFinalityProof(t, forks, expectedFinalityProof) 388 }) 389 } 390 391 // TestDoubleProposal tests that the DoubleProposal notification is emitted when two different 392 // blocks for the same view are added. We ingest the the following block tree: 393 // 394 // / [◄(1) 2] 395 // [1] 396 // \ [◄(1) 2'] 397 // 398 // which should result in a DoubleProposal event referencing the blocks [◄(1) 2] and [◄(1) 2'] 399 func TestDoubleProposal(t *testing.T) { 400 blocks, err := NewBlockBuilder(). 401 Add(1, 2). // [◄(1) 2] 402 AddVersioned(1, 2, 0, 1). // [◄(1) 2'] 403 Blocks() 404 require.Nil(t, err) 405 406 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 407 forks, notifier := newForks(t) 408 notifier.On("OnDoubleProposeDetected", blocks[1], blocks[0]).Once() 409 410 err = addValidatedBlockToForks(forks, blocks) 411 require.Nil(t, err) 412 }) 413 414 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 415 forks, notifier := newForks(t) 416 notifier.On("OnDoubleProposeDetected", blocks[1], blocks[0]).Once() 417 418 err = forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[0])) // add [◄(1) 2] as certified block 419 require.Nil(t, err) 420 err = forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[1])) // add [◄(1) 2'] as certified block 421 require.Nil(t, err) 422 }) 423 } 424 425 // TestConflictingQCs checks that adding 2 conflicting QCs should return model.ByzantineThresholdExceededError 426 // We ingest the following block tree: 427 // 428 // [◄(1) 2] [◄(2) 3] [◄(3) 4] [◄(4) 6] 429 // [◄(2) 3'] [◄(3') 5] 430 // 431 // which should result in a `ByzantineThresholdExceededError`, because conflicting blocks 3 and 3' both have QCs 432 func TestConflictingQCs(t *testing.T) { 433 blocks, err := NewBlockBuilder(). 434 Add(1, 2). // [◄(1) 2] 435 Add(2, 3). // [◄(2) 3] 436 AddVersioned(2, 3, 0, 1). // [◄(2) 3'] 437 Add(3, 4). // [◄(3) 4] 438 Add(4, 6). // [◄(4) 6] 439 AddVersioned(3, 5, 1, 0). // [◄(3') 5] 440 Blocks() 441 require.Nil(t, err) 442 443 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 444 forks, notifier := newForks(t) 445 notifier.On("OnDoubleProposeDetected", blocks[2], blocks[1]).Return(nil) 446 447 err = addValidatedBlockToForks(forks, blocks) 448 assert.True(t, model.IsByzantineThresholdExceededError(err)) 449 }) 450 451 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 452 forks, notifier := newForks(t) 453 notifier.On("OnDoubleProposeDetected", blocks[2], blocks[1]).Return(nil) 454 455 // As [◄(3') 5] is not certified, it will not be added to Forks. However, its QC ◄(3') is 456 // delivered to Forks as part of the *certified* block [◄(2) 3']. 457 err = addCertifiedBlocksToForks(forks, blocks) 458 assert.True(t, model.IsByzantineThresholdExceededError(err)) 459 }) 460 } 461 462 // TestConflictingFinalizedForks checks that finalizing 2 conflicting forks should return model.ByzantineThresholdExceededError 463 // We ingest the the following block tree: 464 // 465 // [◄(1) 2] [◄(2) 3] [◄(3) 4] [◄(4) 5] 466 // [◄(2) 6] [◄(6) 7] [◄(7) 8] 467 // 468 // Here, both blocks [◄(2) 3] and [◄(2) 6] satisfy the finalization condition, i.e. we have a fork 469 // in the finalized blocks, which should result in a model.ByzantineThresholdExceededError exception. 470 func TestConflictingFinalizedForks(t *testing.T) { 471 blocks, err := NewBlockBuilder(). 472 Add(1, 2). 473 Add(2, 3). 474 Add(3, 4). 475 Add(4, 5). // finalizes [◄(2) 3] 476 Add(2, 6). 477 Add(6, 7). 478 Add(7, 8). // finalizes [◄(2) 6], conflicting with conflicts with [◄(2) 3] 479 Blocks() 480 require.Nil(t, err) 481 482 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 483 forks, _ := newForks(t) 484 err = addValidatedBlockToForks(forks, blocks) 485 assert.True(t, model.IsByzantineThresholdExceededError(err)) 486 }) 487 488 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 489 forks, _ := newForks(t) 490 err = addCertifiedBlocksToForks(forks, blocks) 491 assert.True(t, model.IsByzantineThresholdExceededError(err)) 492 }) 493 } 494 495 // TestAddDisconnectedBlock checks that adding a block which does not connect to the 496 // latest finalized block returns a `model.MissingBlockError` 497 // - receives [◄(2) 3] 498 // - should return `model.MissingBlockError`, because the parent is above the pruning 499 // threshold, but Forks does not know its parent 500 func TestAddDisconnectedBlock(t *testing.T) { 501 blocks, err := NewBlockBuilder(). 502 Add(1, 2). // we will skip this block [◄(1) 2] 503 Add(2, 3). // [◄(2) 3] 504 Blocks() 505 require.Nil(t, err) 506 507 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 508 forks, _ := newForks(t) 509 err := forks.AddValidatedBlock(blocks[1]) 510 require.Error(t, err) 511 assert.True(t, model.IsMissingBlockError(err)) 512 }) 513 514 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 515 forks, _ := newForks(t) 516 err := forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[1])) 517 require.Error(t, err) 518 assert.True(t, model.IsMissingBlockError(err)) 519 }) 520 } 521 522 // TestGetBlock tests that we can retrieve stored blocks. Here, we test that 523 // attempting to retrieve nonexistent or pruned blocks fails without causing an exception. 524 // - Forks receives [◄(1) 2] [◄(2) 3] [◄(3) 4], then [◄(4) 5] 525 // - should finalize [◄(1) 2], then [◄(2) 3] 526 func TestGetBlock(t *testing.T) { 527 blocks, err := NewBlockBuilder(). 528 Add(1, 2). // [◄(1) 2] 529 Add(2, 3). // [◄(2) 3] 530 Add(3, 4). // [◄(3) 4] 531 Add(4, 5). // [◄(4) 5] 532 Blocks() 533 require.Nil(t, err) 534 535 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 536 blocksAddedFirst := blocks[:3] // [◄(1) 2] [◄(2) 3] [◄(3) 4] 537 remainingBlock := blocks[3] // [◄(4) 5] 538 forks, _ := newForks(t) 539 540 // should be unable to retrieve a block before it is added 541 _, ok := forks.GetBlock(blocks[0].BlockID) 542 assert.False(t, ok) 543 544 // add first 3 blocks - should finalize [◄(1) 2] 545 err = addValidatedBlockToForks(forks, blocksAddedFirst) 546 require.Nil(t, err) 547 548 // should be able to retrieve all stored blocks 549 for _, block := range blocksAddedFirst { 550 b, ok := forks.GetBlock(block.BlockID) 551 assert.True(t, ok) 552 assert.Equal(t, block, b) 553 } 554 555 // add remaining block [◄(4) 5] - should finalize [◄(2) 3] and prune [◄(1) 2] 556 require.Nil(t, forks.AddValidatedBlock(remainingBlock)) 557 558 // should be able to retrieve just added block 559 b, ok := forks.GetBlock(remainingBlock.BlockID) 560 assert.True(t, ok) 561 assert.Equal(t, remainingBlock, b) 562 563 // should be unable to retrieve pruned block 564 _, ok = forks.GetBlock(blocksAddedFirst[0].BlockID) 565 assert.False(t, ok) 566 }) 567 568 // Caution: finalization is driven by QCs. Therefore, we include the QC for block 3 569 // in the first batch of blocks that we add. This is analogous to previous test case, 570 // except that we are delivering the QC ◄(3) as part of the certified block of view 2 571 // [◄(2) 3] ◄(3) 572 // while in the previous sub-test, the QC ◄(3) was delivered as part of block [◄(3) 4] 573 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 574 blocksAddedFirst := toCertifiedBlocks(t, blocks[:2]...) // [◄(1) 2] [◄(2) 3] ◄(3) 575 remainingBlock := toCertifiedBlock(t, blocks[2]) // [◄(3) 4] ◄(4) 576 forks, _ := newForks(t) 577 578 // should be unable to retrieve a block before it is added 579 _, ok := forks.GetBlock(blocks[0].BlockID) 580 assert.False(t, ok) 581 582 // add first blocks - should finalize [◄(1) 2] 583 err := forks.AddCertifiedBlock(blocksAddedFirst[0]) 584 require.Nil(t, err) 585 err = forks.AddCertifiedBlock(blocksAddedFirst[1]) 586 require.Nil(t, err) 587 588 // should be able to retrieve all stored blocks 589 for _, block := range blocksAddedFirst { 590 b, ok := forks.GetBlock(block.Block.BlockID) 591 assert.True(t, ok) 592 assert.Equal(t, block.Block, b) 593 } 594 595 // add remaining block [◄(4) 5] - should finalize [◄(2) 3] and prune [◄(1) 2] 596 require.Nil(t, forks.AddCertifiedBlock(remainingBlock)) 597 598 // should be able to retrieve just added block 599 b, ok := forks.GetBlock(remainingBlock.Block.BlockID) 600 assert.True(t, ok) 601 assert.Equal(t, remainingBlock.Block, b) 602 603 // should be unable to retrieve pruned block 604 _, ok = forks.GetBlock(blocksAddedFirst[0].Block.BlockID) 605 assert.False(t, ok) 606 }) 607 } 608 609 // TestGetBlocksForView tests retrieving blocks for a view (also including double proposals). 610 // - Forks receives [◄(1) 2] [◄(2) 4] [◄(2) 4'], 611 // where [◄(2) 4'] is a double proposal, because it has the same view as [◄(2) 4] 612 // 613 // Expected behaviour: 614 // - Forks should store all the blocks 615 // - Forks should emit a `OnDoubleProposeDetected` notification 616 // - we can retrieve all blocks, including the double proposals 617 func TestGetBlocksForView(t *testing.T) { 618 blocks, err := NewBlockBuilder(). 619 Add(1, 2). // [◄(1) 2] 620 Add(2, 4). // [◄(2) 4] 621 AddVersioned(2, 4, 0, 1). // [◄(2) 4'] 622 Blocks() 623 require.Nil(t, err) 624 625 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 626 forks, notifier := newForks(t) 627 notifier.On("OnDoubleProposeDetected", blocks[2], blocks[1]).Once() 628 629 err = addValidatedBlockToForks(forks, blocks) 630 require.Nil(t, err) 631 632 // expect 1 block at view 2 633 storedBlocks := forks.GetBlocksForView(2) 634 assert.Len(t, storedBlocks, 1) 635 assert.Equal(t, blocks[0], storedBlocks[0]) 636 637 // expect 2 blocks at view 4 638 storedBlocks = forks.GetBlocksForView(4) 639 assert.Len(t, storedBlocks, 2) 640 assert.ElementsMatch(t, blocks[1:], storedBlocks) 641 642 // expect 0 blocks at view 3 643 storedBlocks = forks.GetBlocksForView(3) 644 assert.Len(t, storedBlocks, 0) 645 }) 646 647 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 648 forks, notifier := newForks(t) 649 notifier.On("OnDoubleProposeDetected", blocks[2], blocks[1]).Once() 650 651 err := forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[0])) 652 require.Nil(t, err) 653 err = forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[1])) 654 require.Nil(t, err) 655 err = forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[2])) 656 require.Nil(t, err) 657 658 // expect 1 block at view 2 659 storedBlocks := forks.GetBlocksForView(2) 660 assert.Len(t, storedBlocks, 1) 661 assert.Equal(t, blocks[0], storedBlocks[0]) 662 663 // expect 2 blocks at view 4 664 storedBlocks = forks.GetBlocksForView(4) 665 assert.Len(t, storedBlocks, 2) 666 assert.ElementsMatch(t, blocks[1:], storedBlocks) 667 668 // expect 0 blocks at view 3 669 storedBlocks = forks.GetBlocksForView(3) 670 assert.Len(t, storedBlocks, 0) 671 }) 672 } 673 674 // TestNotifications tests that Forks emits the expected events: 675 // - Forks receives [◄(1) 2] [◄(2) 3] [◄(3) 4] 676 // 677 // Expected Behaviour: 678 // - Each of the ingested blocks should result in an `OnBlockIncorporated` notification 679 // - Forks should finalize [◄(1) 2], resulting in a `MakeFinal` event and an `OnFinalizedBlock` event 680 func TestNotifications(t *testing.T) { 681 builder := NewBlockBuilder(). 682 Add(1, 2). 683 Add(2, 3). 684 Add(3, 4) 685 blocks, err := builder.Blocks() 686 require.Nil(t, err) 687 688 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 689 notifier := &mocks.Consumer{} 690 // 4 blocks including the genesis are incorporated 691 notifier.On("OnBlockIncorporated", mock.Anything).Return(nil).Times(4) 692 notifier.On("OnFinalizedBlock", blocks[0]).Once() 693 finalizationCallback := mockmodule.NewFinalizer(t) 694 finalizationCallback.On("MakeFinal", blocks[0].BlockID).Return(nil).Once() 695 696 forks, err := New(builder.GenesisBlock(), finalizationCallback, notifier) 697 require.NoError(t, err) 698 require.NoError(t, addValidatedBlockToForks(forks, blocks)) 699 }) 700 701 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 702 notifier := &mocks.Consumer{} 703 // 4 blocks including the genesis are incorporated 704 notifier.On("OnBlockIncorporated", mock.Anything).Return(nil).Times(4) 705 notifier.On("OnFinalizedBlock", blocks[0]).Once() 706 finalizationCallback := mockmodule.NewFinalizer(t) 707 finalizationCallback.On("MakeFinal", blocks[0].BlockID).Return(nil).Once() 708 709 forks, err := New(builder.GenesisBlock(), finalizationCallback, notifier) 710 require.NoError(t, err) 711 require.NoError(t, addCertifiedBlocksToForks(forks, blocks)) 712 }) 713 } 714 715 // TestFinalizingMultipleBlocks tests that `OnFinalizedBlock` notifications are emitted in correct order 716 // when there are multiple blocks finalized by adding a _single_ block. 717 // - receiving [◄(1) 3] [◄(3) 5] [◄(5) 7] [◄(7) 11] [◄(11) 12] should not finalize any blocks, 718 // because there is no 2-chain with the first chain link being a _direct_ 1-chain 719 // - adding [◄(12) 22] should finalize up to block [◄(6) 11] 720 // 721 // This test verifies the following expected properties: 722 // 1. Safety under reentrancy: 723 // While Forks is single-threaded, there is still the possibility of reentrancy. Specifically, the 724 // consumers of our finalization events are served by the goroutine executing Forks. It is conceivable 725 // that a consumer might access Forks and query the latest finalization proof. This would be legal, if 726 // the component supplying the goroutine to Forks also consumes the notifications. Therefore, for API 727 // safety, we require forks to _first update_ its `FinalityProof()` before it emits _any_ events. 728 // 2. For each finalized block, `finalizationCallback` event is executed _before_ `OnFinalizedBlock` notifications. 729 // 3. Blocks are finalized in order of increasing height (without skipping any blocks). 730 func TestFinalizingMultipleBlocks(t *testing.T) { 731 builder := NewBlockBuilder(). 732 Add(1, 3). // index 0: [◄(1) 2] 733 Add(3, 5). // index 1: [◄(2) 4] 734 Add(5, 7). // index 2: [◄(4) 6] 735 Add(7, 11). // index 3: [◄(6) 11] -- expected to be finalized 736 Add(11, 12). // index 4: [◄(11) 12] 737 Add(12, 22) // index 5: [◄(12) 22] 738 blocks, err := builder.Blocks() 739 require.Nil(t, err) 740 741 // The Finality Proof should right away point to the _latest_ finalized block. Subsequently emitting 742 // Finalization events for lower blocks is fine, because notifications are guaranteed to be 743 // _eventually_ arriving. I.e. consumers expect notifications / events to be potentially lagging behind. 744 expectedFinalityProof := makeFinalityProof(t, blocks[3], blocks[4], blocks[5].QC) 745 746 setupForksAndAssertions := func() (*Forks, *mockmodule.Finalizer, *mocks.Consumer) { 747 // initialize Forks with custom event consumers so we can check order of emitted events 748 notifier := &mocks.Consumer{} 749 finalizationCallback := mockmodule.NewFinalizer(t) 750 notifier.On("OnBlockIncorporated", mock.Anything).Return(nil) 751 forks, err := New(builder.GenesisBlock(), finalizationCallback, notifier) 752 require.NoError(t, err) 753 754 // expecting finalization of [◄(1) 2] [◄(2) 4] [◄(4) 6] [◄(6) 11] in this order 755 blocksAwaitingFinalization := toBlockAwaitingFinalization(blocks[:4]) 756 757 finalizationCallback.On("MakeFinal", mock.Anything).Run(func(args mock.Arguments) { 758 requireFinalityProof(t, forks, expectedFinalityProof) // Requirement 1: forks should _first update_ its `FinalityProof()` before it emits _any_ events 759 760 // Requirement 3: finalized in order of increasing height (without skipping any blocks). 761 expectedNextFinalizationEvents := blocksAwaitingFinalization[0] 762 require.Equal(t, expectedNextFinalizationEvents.Block.BlockID, args[0]) 763 764 // Requirement 2: finalized block, `finalizationCallback` event is executed _before_ `OnFinalizedBlock` notifications. 765 // no duplication of events under normal operations expected 766 require.False(t, expectedNextFinalizationEvents.MakeFinalCalled) 767 require.False(t, expectedNextFinalizationEvents.OnFinalizedBlockEmitted) 768 expectedNextFinalizationEvents.MakeFinalCalled = true 769 }).Return(nil).Times(4) 770 771 notifier.On("OnFinalizedBlock", mock.Anything).Run(func(args mock.Arguments) { 772 requireFinalityProof(t, forks, expectedFinalityProof) // Requirement 1: forks should _first update_ its `FinalityProof()` before it emits _any_ events 773 774 // Requirement 3: finalized in order of increasing height (without skipping any blocks). 775 expectedNextFinalizationEvents := blocksAwaitingFinalization[0] 776 require.Equal(t, expectedNextFinalizationEvents.Block, args[0]) 777 778 // Requirement 2: finalized block, `finalizationCallback` event is executed _before_ `OnFinalizedBlock` notifications. 779 // no duplication of events under normal operations expected 780 require.True(t, expectedNextFinalizationEvents.MakeFinalCalled) 781 require.False(t, expectedNextFinalizationEvents.OnFinalizedBlockEmitted) 782 expectedNextFinalizationEvents.OnFinalizedBlockEmitted = true 783 784 // At this point, `MakeFinal` and `OnFinalizedBlock` have both been emitted for the block, so we are done with it 785 blocksAwaitingFinalization = blocksAwaitingFinalization[1:] 786 }).Times(4) 787 788 return forks, finalizationCallback, notifier 789 } 790 791 t.Run("consensus participant mode: ingest validated blocks", func(t *testing.T) { 792 forks, finalizationCallback, notifier := setupForksAndAssertions() 793 err = addValidatedBlockToForks(forks, blocks[:5]) // adding [◄(1) 2] [◄(2) 4] [◄(4) 6] [◄(6) 11] [◄(11) 12] 794 require.Nil(t, err) 795 requireOnlyGenesisBlockFinalized(t, forks) // finalization should still be at the genesis block 796 797 require.NoError(t, forks.AddValidatedBlock(blocks[5])) // adding [◄(12) 22] should trigger finalization events 798 requireFinalityProof(t, forks, expectedFinalityProof) 799 finalizationCallback.AssertExpectations(t) 800 notifier.AssertExpectations(t) 801 }) 802 803 t.Run("consensus follower mode: ingest certified blocks", func(t *testing.T) { 804 forks, finalizationCallback, notifier := setupForksAndAssertions() 805 // adding [◄(1) 2] [◄(2) 4] [◄(4) 6] [◄(6) 11] ◄(11) 806 require.NoError(t, forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[0]))) 807 require.NoError(t, forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[1]))) 808 require.NoError(t, forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[2]))) 809 require.NoError(t, forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[3]))) 810 require.Nil(t, err) 811 requireOnlyGenesisBlockFinalized(t, forks) // finalization should still be at the genesis block 812 813 // adding certified block [◄(11) 12] ◄(12) should trigger finalization events 814 require.NoError(t, forks.AddCertifiedBlock(toCertifiedBlock(t, blocks[4]))) 815 requireFinalityProof(t, forks, expectedFinalityProof) 816 finalizationCallback.AssertExpectations(t) 817 notifier.AssertExpectations(t) 818 }) 819 } 820 821 //* ************************************* internal functions ************************************* */ 822 823 func newForks(t *testing.T) (*Forks, *mocks.Consumer) { 824 notifier := mocks.NewConsumer(t) 825 notifier.On("OnBlockIncorporated", mock.Anything).Return(nil).Maybe() 826 notifier.On("OnFinalizedBlock", mock.Anything).Maybe() 827 finalizationCallback := mockmodule.NewFinalizer(t) 828 finalizationCallback.On("MakeFinal", mock.Anything).Return(nil).Maybe() 829 830 genesisBQ := makeGenesis() 831 832 forks, err := New(genesisBQ, finalizationCallback, notifier) 833 834 require.Nil(t, err) 835 return forks, notifier 836 } 837 838 // addValidatedBlockToForks adds all the given blocks to Forks, in order. 839 // If any errors occur, returns the first one. 840 func addValidatedBlockToForks(forks *Forks, blocks []*model.Block) error { 841 for _, block := range blocks { 842 err := forks.AddValidatedBlock(block) 843 if err != nil { 844 return fmt.Errorf("test failed to add block for view %d: %w", block.View, err) 845 } 846 } 847 return nil 848 } 849 850 // addCertifiedBlocksToForks iterates over all blocks, caches them locally in a map, 851 // constructs certified blocks whenever possible and adds the certified blocks to forks, 852 // Note: if blocks is a single fork, the _last block_ in the slice will not be added, 853 // 854 // because there is no qc for it 855 // 856 // If any errors occur, returns the first one. 857 func addCertifiedBlocksToForks(forks *Forks, blocks []*model.Block) error { 858 uncertifiedBlocks := make(map[flow.Identifier]*model.Block) 859 for _, b := range blocks { 860 uncertifiedBlocks[b.BlockID] = b 861 parentID := b.QC.BlockID 862 parent, found := uncertifiedBlocks[parentID] 863 if !found { 864 continue 865 } 866 delete(uncertifiedBlocks, parentID) 867 868 certParent, err := model.NewCertifiedBlock(parent, b.QC) 869 if err != nil { 870 return fmt.Errorf("test failed to creat certified block for view %d: %w", certParent.Block.View, err) 871 } 872 err = forks.AddCertifiedBlock(&certParent) 873 if err != nil { 874 return fmt.Errorf("test failed to add certified block for view %d: %w", certParent.Block.View, err) 875 } 876 } 877 878 return nil 879 } 880 881 // requireLatestFinalizedBlock asserts that the latest finalized block has the given view and qc view. 882 func requireLatestFinalizedBlock(t *testing.T, forks *Forks, expectedFinalized *model.Block) { 883 require.Equal(t, expectedFinalized, forks.FinalizedBlock(), "finalized block is not as expected") 884 require.Equal(t, forks.FinalizedView(), expectedFinalized.View, "FinalizedView returned wrong value") 885 } 886 887 // requireOnlyGenesisBlockFinalized asserts that no blocks have been finalized beyond the genesis block. 888 // Caution: does not inspect output of `forks.FinalityProof()` 889 func requireOnlyGenesisBlockFinalized(t *testing.T, forks *Forks) { 890 genesis := makeGenesis() 891 require.Equal(t, forks.FinalizedBlock(), genesis.Block, "finalized block is not the genesis block") 892 require.Equal(t, forks.FinalizedBlock().View, genesis.Block.View) 893 require.Equal(t, forks.FinalizedBlock().View, genesis.CertifyingQC.View) 894 require.Equal(t, forks.FinalizedView(), genesis.Block.View, "finalized block has wrong qc") 895 896 finalityProof, isKnown := forks.FinalityProof() 897 require.Nil(t, finalityProof, "expecting finality proof to be nil for genesis block at initialization") 898 require.False(t, isKnown, "no finality proof should be known for genesis block at initialization") 899 } 900 901 // requireNoBlocksFinalized asserts that no blocks have been finalized (genesis is latest finalized block). 902 func requireFinalityProof(t *testing.T, forks *Forks, expectedFinalityProof *hotstuff.FinalityProof) { 903 finalityProof, isKnown := forks.FinalityProof() 904 require.True(t, isKnown) 905 require.Equal(t, expectedFinalityProof, finalityProof) 906 require.Equal(t, forks.FinalizedBlock(), expectedFinalityProof.Block) 907 require.Equal(t, forks.FinalizedView(), expectedFinalityProof.Block.View) 908 } 909 910 // toCertifiedBlock generates a QC for the given block and returns their combination as a certified block 911 func toCertifiedBlock(t *testing.T, block *model.Block) *model.CertifiedBlock { 912 qc := &flow.QuorumCertificate{ 913 View: block.View, 914 BlockID: block.BlockID, 915 } 916 cb, err := model.NewCertifiedBlock(block, qc) 917 require.Nil(t, err) 918 return &cb 919 } 920 921 // toCertifiedBlocks generates a QC for the given block and returns their combination as a certified blocks 922 func toCertifiedBlocks(t *testing.T, blocks ...*model.Block) []*model.CertifiedBlock { 923 certBlocks := make([]*model.CertifiedBlock, 0, len(blocks)) 924 for _, b := range blocks { 925 certBlocks = append(certBlocks, toCertifiedBlock(t, b)) 926 } 927 return certBlocks 928 } 929 930 func makeFinalityProof(t *testing.T, block *model.Block, directChild *model.Block, qcCertifyingChild *flow.QuorumCertificate) *hotstuff.FinalityProof { 931 c, err := model.NewCertifiedBlock(directChild, qcCertifyingChild) // certified child of FinalizedBlock 932 require.NoError(t, err) 933 return &hotstuff.FinalityProof{Block: block, CertifiedChild: c} 934 } 935 936 // blockAwaitingFinalization is intended for tracking finalization events and their order for a specific block 937 type blockAwaitingFinalization struct { 938 Block *model.Block 939 MakeFinalCalled bool // indicates whether `Finalizer.MakeFinal` was called 940 OnFinalizedBlockEmitted bool // indicates whether `OnFinalizedBlockCalled` notification was emitted 941 } 942 943 // toBlockAwaitingFinalization creates a `blockAwaitingFinalization` tracker for each input block 944 func toBlockAwaitingFinalization(blocks []*model.Block) []*blockAwaitingFinalization { 945 trackers := make([]*blockAwaitingFinalization, 0, len(blocks)) 946 for _, b := range blocks { 947 tracker := &blockAwaitingFinalization{b, false, false} 948 trackers = append(trackers, tracker) 949 } 950 return trackers 951 }