github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/forks/forks.go (about) 1 package forks 2 3 import ( 4 "fmt" 5 6 "github.com/onflow/flow-go/consensus/hotstuff" 7 "github.com/onflow/flow-go/consensus/hotstuff/model" 8 "github.com/onflow/flow-go/model/flow" 9 "github.com/onflow/flow-go/module" 10 "github.com/onflow/flow-go/module/forest" 11 ) 12 13 // Forks enforces structural validity of the consensus state and implements 14 // finalization rules as defined in Jolteon consensus https://arxiv.org/abs/2106.10362 15 // The same approach has later been adopted by the Diem team resulting in DiemBFT v4: 16 // https://developers.diem.com/papers/diem-consensus-state-machine-replication-in-the-diem-blockchain/2021-08-17.pdf 17 // Forks is NOT safe for concurrent use by multiple goroutines. 18 type Forks struct { 19 finalizationCallback module.Finalizer 20 notifier hotstuff.FollowerConsumer 21 forest forest.LevelledForest 22 trustedRoot *model.CertifiedBlock 23 24 // finalityProof holds the latest finalized block including the certified child as proof of finality. 25 // CAUTION: is nil, when Forks has not yet finalized any blocks beyond the finalized root block it was initialized with 26 finalityProof *hotstuff.FinalityProof 27 } 28 29 var _ hotstuff.Forks = (*Forks)(nil) 30 31 func New(trustedRoot *model.CertifiedBlock, finalizationCallback module.Finalizer, notifier hotstuff.FollowerConsumer) (*Forks, error) { 32 if (trustedRoot.Block.BlockID != trustedRoot.CertifyingQC.BlockID) || (trustedRoot.Block.View != trustedRoot.CertifyingQC.View) { 33 return nil, model.NewConfigurationErrorf("invalid root: root QC is not pointing to root block") 34 } 35 36 forks := Forks{ 37 finalizationCallback: finalizationCallback, 38 notifier: notifier, 39 forest: *forest.NewLevelledForest(trustedRoot.Block.View), 40 trustedRoot: trustedRoot, 41 finalityProof: nil, 42 } 43 44 // verify and add root block to levelled forest 45 err := forks.EnsureBlockIsValidExtension(trustedRoot.Block) 46 if err != nil { 47 return nil, fmt.Errorf("invalid root block %v: %w", trustedRoot.ID(), err) 48 } 49 forks.forest.AddVertex(ToBlockContainer2(trustedRoot.Block)) 50 return &forks, nil 51 } 52 53 // FinalizedView returns the largest view number where a finalized block is known 54 func (f *Forks) FinalizedView() uint64 { 55 if f.finalityProof == nil { 56 return f.trustedRoot.Block.View 57 } 58 return f.finalityProof.Block.View 59 } 60 61 // FinalizedBlock returns the finalized block with the largest view number 62 func (f *Forks) FinalizedBlock() *model.Block { 63 if f.finalityProof == nil { 64 return f.trustedRoot.Block 65 } 66 return f.finalityProof.Block 67 } 68 69 // FinalityProof returns the latest finalized block and a certified child from 70 // the subsequent view, which proves finality. 71 // CAUTION: method returns (nil, false), when Forks has not yet finalized any 72 // blocks beyond the finalized root block it was initialized with. 73 func (f *Forks) FinalityProof() (*hotstuff.FinalityProof, bool) { 74 return f.finalityProof, f.finalityProof != nil 75 } 76 77 // GetBlock returns (BlockProposal, true) if the block with the specified 78 // id was found and (nil, false) otherwise. 79 func (f *Forks) GetBlock(blockID flow.Identifier) (*model.Block, bool) { 80 blockContainer, hasBlock := f.forest.GetVertex(blockID) 81 if !hasBlock { 82 return nil, false 83 } 84 return blockContainer.(*BlockContainer).Block(), true 85 } 86 87 // GetBlocksForView returns all known blocks for the given view 88 func (f *Forks) GetBlocksForView(view uint64) []*model.Block { 89 vertexIterator := f.forest.GetVerticesAtLevel(view) 90 blocks := make([]*model.Block, 0, 1) // in the vast majority of cases, there will only be one proposal for a particular view 91 for vertexIterator.HasNext() { 92 v := vertexIterator.NextVertex() 93 blocks = append(blocks, v.(*BlockContainer).Block()) 94 } 95 return blocks 96 } 97 98 // IsKnownBlock checks whether block is known. 99 func (f *Forks) IsKnownBlock(blockID flow.Identifier) bool { 100 _, hasBlock := f.forest.GetVertex(blockID) 101 return hasBlock 102 } 103 104 // IsProcessingNeeded determines whether the given block needs processing, 105 // based on the block's view and hash. 106 // Returns false if any of the following conditions applies 107 // - block view is _below_ the most recently finalized block 108 // - the block already exists in the consensus state 109 // 110 // UNVALIDATED: expects block to pass Forks.EnsureBlockIsValidExtension(block) 111 func (f *Forks) IsProcessingNeeded(block *model.Block) bool { 112 if block.View < f.FinalizedView() || f.IsKnownBlock(block.BlockID) { 113 return false 114 } 115 return true 116 } 117 118 // EnsureBlockIsValidExtension checks that the given block is a valid extension to the tree 119 // of blocks already stored (no state modifications). Specifically, the following conditions 120 // are enforced, which are critical to the correctness of Forks: 121 // 122 // 1. If a block with the same ID is already stored, their views must be identical. 123 // 2. The block's view must be strictly larger than the view of its parent. 124 // 3. The parent must already be stored (or below the pruning height). 125 // 126 // Exclusions to these rules (by design): 127 // Let W denote the view of block's parent (i.e. W := block.QC.View) and F the latest 128 // finalized view. 129 // 130 // (i) If block.View < F, adding the block would be a no-op. Such blocks are considered 131 // compatible (principle of vacuous truth), i.e. we skip checking 1, 2, 3. 132 // (ii) If block.View == F, we do not inspect the QC / parent at all (skip 2 and 3). 133 // This exception is important for compatability with genesis or spork-root blocks, 134 // which do not contain a QC. 135 // (iii) If block.View > F, but block.QC.View < F the parent has already been pruned. In 136 // this case, we omit rule 3. (principle of vacuous truth applied to the parent) 137 // 138 // We assume that all blocks are fully verified. A valid block must satisfy all consistency 139 // requirements; otherwise we have a bug in the compliance layer. 140 // 141 // Error returns: 142 // - model.MissingBlockError if the parent of the input proposal does not exist in the 143 // forest (but is above the pruned view). Represents violation of condition 3. 144 // - model.InvalidBlockError if the block violates condition 1. or 2. 145 // - generic error in case of unexpected bug or internal state corruption 146 func (f *Forks) EnsureBlockIsValidExtension(block *model.Block) error { 147 if block.View < f.forest.LowestLevel { // exclusion (i) 148 return nil 149 } 150 151 // LevelledForest enforces conditions 1. and 2. including the respective exclusions (ii) and (iii). 152 blockContainer := ToBlockContainer2(block) 153 err := f.forest.VerifyVertex(blockContainer) 154 if err != nil { 155 if forest.IsInvalidVertexError(err) { 156 return model.NewInvalidBlockErrorf(block, "not a valid vertex for block tree: %w", err) 157 } 158 return fmt.Errorf("block tree generated unexpected error validating vertex: %w", err) 159 } 160 161 // Condition 3: 162 // LevelledForest implements a more generalized algorithm that also works for disjoint graphs. 163 // Therefore, LevelledForest _not_ enforce condition 3. Here, we additionally require that the 164 // pending blocks form a tree (connected graph), i.e. we need to enforce condition 3 165 if (block.View == f.forest.LowestLevel) || (block.QC.View < f.forest.LowestLevel) { // exclusion (ii) and (iii) 166 return nil 167 } 168 // For a block whose parent is _not_ below the pruning height, we expect the parent to be known. 169 if _, isParentKnown := f.forest.GetVertex(block.QC.BlockID); !isParentKnown { // missing parent 170 return model.MissingBlockError{ 171 View: block.QC.View, 172 BlockID: block.QC.BlockID, 173 } 174 } 175 return nil 176 } 177 178 // AddCertifiedBlock appends the given certified block to the tree of pending 179 // blocks and updates the latest finalized block (if finalization progressed). 180 // Unless the parent is below the pruning threshold (latest finalized view), we 181 // require that the parent is already stored in Forks. Calling this method with 182 // previously processed blocks leaves the consensus state invariant (though, 183 // it will potentially cause some duplicate processing). 184 // 185 // Possible error returns: 186 // - model.MissingBlockError if the parent does not exist in the forest (but is above 187 // the pruned view). From the perspective of Forks, this error is benign (no-op). 188 // - model.InvalidBlockError if the block is invalid (see `Forks.EnsureBlockIsValidExtension` 189 // for details). From the perspective of Forks, this error is benign (no-op). However, we 190 // assume all blocks are fully verified, i.e. they should satisfy all consistency 191 // requirements. Hence, this error is likely an indicator of a bug in the compliance layer. 192 // - model.ByzantineThresholdExceededError if conflicting QCs or conflicting finalized 193 // blocks have been detected (violating a foundational consensus guarantees). This 194 // indicates that there are 1/3+ Byzantine nodes (weighted by stake) in the network, 195 // breaking the safety guarantees of HotStuff (or there is a critical bug / data 196 // corruption). Forks cannot recover from this exception. 197 // - All other errors are potential symptoms of bugs or state corruption. 198 func (f *Forks) AddCertifiedBlock(certifiedBlock *model.CertifiedBlock) error { 199 if !f.IsProcessingNeeded(certifiedBlock.Block) { 200 return nil 201 } 202 203 // Check proposal for byzantine evidence, store it and emit `OnBlockIncorporated` notification. 204 // Note: `checkForByzantineEvidence` only inspects the block, but _not_ its certifying QC. Hence, 205 // we have to additionally check here, whether the certifying QC conflicts with any known QCs. 206 err := f.checkForByzantineEvidence(certifiedBlock.Block) 207 if err != nil { 208 return fmt.Errorf("cannot check for Byzantine evidence in certified block %v: %w", certifiedBlock.Block.BlockID, err) 209 } 210 err = f.checkForConflictingQCs(certifiedBlock.CertifyingQC) 211 if err != nil { 212 return fmt.Errorf("certifying QC for block %v failed check for conflicts: %w", certifiedBlock.Block.BlockID, err) 213 } 214 f.forest.AddVertex(ToBlockContainer2(certifiedBlock.Block)) 215 f.notifier.OnBlockIncorporated(certifiedBlock.Block) 216 217 // Update finality status: 218 err = f.checkForAdvancingFinalization(certifiedBlock) 219 if err != nil { 220 return fmt.Errorf("updating finalization failed: %w", err) 221 } 222 return nil 223 } 224 225 // AddValidatedBlock appends the validated block to the tree of pending 226 // blocks and updates the latest finalized block (if applicable). Unless the parent is 227 // below the pruning threshold (latest finalized view), we require that the parent is 228 // already stored in Forks. Calling this method with previously processed blocks 229 // leaves the consensus state invariant (though, it will potentially cause some 230 // duplicate processing). 231 // Notes: 232 // - Method `AddCertifiedBlock(..)` should be used preferably, if a QC certifying 233 // `block` is already known. This is generally the case for the consensus follower. 234 // Method `AddValidatedBlock` is intended for active consensus participants, which fully 235 // validate blocks (incl. payload), i.e. QCs are processed as part of validated proposals. 236 // 237 // Possible error returns: 238 // - model.MissingBlockError if the parent does not exist in the forest (but is above 239 // the pruned view). From the perspective of Forks, this error is benign (no-op). 240 // - model.InvalidBlockError if the block is invalid (see `Forks.EnsureBlockIsValidExtension` 241 // for details). From the perspective of Forks, this error is benign (no-op). However, we 242 // assume all blocks are fully verified, i.e. they should satisfy all consistency 243 // requirements. Hence, this error is likely an indicator of a bug in the compliance layer. 244 // - model.ByzantineThresholdExceededError if conflicting QCs or conflicting finalized 245 // blocks have been detected (violating a foundational consensus guarantees). This 246 // indicates that there are 1/3+ Byzantine nodes (weighted by stake) in the network, 247 // breaking the safety guarantees of HotStuff (or there is a critical bug / data 248 // corruption). Forks cannot recover from this exception. 249 // - All other errors are potential symptoms of bugs or state corruption. 250 func (f *Forks) AddValidatedBlock(proposal *model.Block) error { 251 if !f.IsProcessingNeeded(proposal) { 252 return nil 253 } 254 255 // Check proposal for byzantine evidence, store it and emit `OnBlockIncorporated` notification: 256 err := f.checkForByzantineEvidence(proposal) 257 if err != nil { 258 return fmt.Errorf("cannot check Byzantine evidence for block %v: %w", proposal.BlockID, err) 259 } 260 f.forest.AddVertex(ToBlockContainer2(proposal)) 261 f.notifier.OnBlockIncorporated(proposal) 262 263 // Update finality status: In the implementation, our notion of finality is based on certified blocks. 264 // The certified parent essentially combines the parent, with the QC contained in block, to drive finalization. 265 parent, found := f.GetBlock(proposal.QC.BlockID) 266 if !found { 267 // Not finding the parent means it is already pruned; hence this block does not change the finalization state. 268 return nil 269 } 270 certifiedParent, err := model.NewCertifiedBlock(parent, proposal.QC) 271 if err != nil { 272 return fmt.Errorf("mismatching QC with parent (corrupted Forks state):%w", err) 273 } 274 err = f.checkForAdvancingFinalization(&certifiedParent) 275 if err != nil { 276 return fmt.Errorf("updating finalization failed: %w", err) 277 } 278 return nil 279 } 280 281 // checkForByzantineEvidence inspects whether the given `block` together with the already 282 // known information yields evidence of byzantine behaviour. Furthermore, the method enforces 283 // that `block` is a valid extension of the tree of pending blocks. If the block is a double 284 // proposal, we emit an `OnBlockIncorporated` notification. Though, provided the block is a 285 // valid extension of the block tree by itself, it passes this method without an error. 286 // 287 // Possible error returns: 288 // - model.MissingBlockError if the parent does not exist in the forest (but is above 289 // the pruned view). From the perspective of Forks, this error is benign (no-op). 290 // - model.InvalidBlockError if the block is invalid (see `Forks.EnsureBlockIsValidExtension` 291 // for details). From the perspective of Forks, this error is benign (no-op). However, we 292 // assume all blocks are fully verified, i.e. they should satisfy all consistency 293 // requirements. Hence, this error is likely an indicator of a bug in the compliance layer. 294 // - model.ByzantineThresholdExceededError if conflicting QCs have been detected. 295 // Forks cannot recover from this exception. 296 // - All other errors are potential symptoms of bugs or state corruption. 297 func (f *Forks) checkForByzantineEvidence(block *model.Block) error { 298 err := f.EnsureBlockIsValidExtension(block) 299 if err != nil { 300 return fmt.Errorf("consistency check on block failed: %w", err) 301 } 302 err = f.checkForConflictingQCs(block.QC) 303 if err != nil { 304 return fmt.Errorf("checking QC for conflicts failed: %w", err) 305 } 306 f.checkForDoubleProposal(block) 307 return nil 308 } 309 310 // checkForConflictingQCs checks if QC conflicts with a stored Quorum Certificate. 311 // In case a conflicting QC is found, an ByzantineThresholdExceededError is returned. 312 // Two Quorum Certificates q1 and q2 are defined as conflicting iff: 313 // 314 // q1.View == q2.View AND q1.BlockID ≠ q2.BlockID 315 // 316 // This means there are two Quorums for conflicting blocks at the same view. 317 // Per 'Observation 1' from the Jolteon paper https://arxiv.org/pdf/2106.10362v1.pdf, 318 // two conflicting QCs can exist if and only if the Byzantine threshold is exceeded. 319 // Error returns: 320 // - model.ByzantineThresholdExceededError if conflicting QCs have been detected. 321 // Forks cannot recover from this exception. 322 // - All other errors are potential symptoms of bugs or state corruption. 323 func (f *Forks) checkForConflictingQCs(qc *flow.QuorumCertificate) error { 324 it := f.forest.GetVerticesAtLevel(qc.View) 325 for it.HasNext() { 326 otherBlock := it.NextVertex() // by construction, must have same view as qc.View 327 if qc.BlockID != otherBlock.VertexID() { 328 // * we have just found another block at the same view number as qc.View but with different hash 329 // * if this block has a child c, this child will have 330 // c.qc.view = parentView 331 // c.qc.ID != parentBlockID 332 // => conflicting qc 333 otherChildren := f.forest.GetChildren(otherBlock.VertexID()) 334 if otherChildren.HasNext() { 335 otherChild := otherChildren.NextVertex().(*BlockContainer).Block() 336 conflictingQC := otherChild.QC 337 return model.ByzantineThresholdExceededError{Evidence: fmt.Sprintf( 338 "conflicting QCs at view %d: %v and %v", 339 qc.View, qc.BlockID, conflictingQC.BlockID, 340 )} 341 } 342 } 343 } 344 return nil 345 } 346 347 // checkForDoubleProposal checks if the input proposal is a double proposal. 348 // A double proposal occurs when two proposals with the same view exist in Forks. 349 // If there is a double proposal, notifier.OnDoubleProposeDetected is triggered. 350 func (f *Forks) checkForDoubleProposal(block *model.Block) { 351 it := f.forest.GetVerticesAtLevel(block.View) 352 for it.HasNext() { 353 otherVertex := it.NextVertex() // by construction, must have same view as block 354 otherBlock := otherVertex.(*BlockContainer).Block() 355 if block.BlockID != otherBlock.BlockID { 356 f.notifier.OnDoubleProposeDetected(block, otherBlock) 357 } 358 } 359 } 360 361 // checkForAdvancingFinalization checks whether observing certifiedBlock leads to progress of 362 // finalization. This function should be called every time a new block is added to Forks. If the new 363 // block is the head of a 2-chain satisfying the finalization rule, we update `Forks.finalityProof` to 364 // the new latest finalized block. Calling this method with previously-processed blocks leaves the 365 // consensus state invariant. 366 // UNVALIDATED: assumes that relevant block properties are consistent with previous blocks 367 // Error returns: 368 // - model.MissingBlockError if the parent does not exist in the forest (but is above 369 // the pruned view). From the perspective of Forks, this error is benign (no-op). 370 // - model.ByzantineThresholdExceededError in case we detect a finalization fork (violating 371 // a foundational consensus guarantee). This indicates that there are 1/3+ Byzantine nodes 372 // (weighted by stake) in the network, breaking the safety guarantees of HotStuff (or there 373 // is a critical bug / data corruption). Forks cannot recover from this exception. 374 // - generic error in case of unexpected bug or internal state corruption 375 func (f *Forks) checkForAdvancingFinalization(certifiedBlock *model.CertifiedBlock) error { 376 // We prune all blocks in forest which are below the most recently finalized block. 377 // Hence, we have a pruned ancestry if and only if either of the following conditions applies: 378 // (a) If a block's parent view (i.e. block.QC.View) is below the most recently finalized block. 379 // (b) If a block's view is equal to the most recently finalized block. 380 // Caution: 381 // * Under normal operation, case (b) is covered by the logic for case (a) 382 // * However, the existence of a genesis block requires handling case (b) explicitly: 383 // The root block is specified and trusted by the node operator. If the root block is the 384 // genesis block, it might not contain a QC pointing to a parent (as there is no parent). 385 // In this case, condition (a) cannot be evaluated. 386 lastFinalizedView := f.FinalizedView() 387 if (certifiedBlock.View() <= lastFinalizedView) || (certifiedBlock.Block.QC.View < lastFinalizedView) { 388 // Repeated blocks are expected during normal operations. We enter this code block if and only 389 // if the parent's view is _below_ the last finalized block. It is straight forward to show: 390 // Lemma: Let B be a block whose 2-chain reaches beyond the last finalized block 391 // => B will not update the locked or finalized block 392 return nil 393 } 394 395 // retrieve parent; always expected to succeed, because we passed the checks above 396 qcForParent := certifiedBlock.Block.QC 397 parentVertex, parentBlockKnown := f.forest.GetVertex(qcForParent.BlockID) 398 if !parentBlockKnown { 399 return model.MissingBlockError{View: qcForParent.View, BlockID: qcForParent.BlockID} 400 } 401 parentBlock := parentVertex.(*BlockContainer).Block() 402 403 // Note: we assume that all stored blocks pass Forks.EnsureBlockIsValidExtension(block); 404 // specifically, that Proposal's ViewNumber is strictly monotonically 405 // increasing which is enforced by LevelledForest.VerifyVertex(...) 406 // We denote: 407 // * a DIRECT 1-chain as '<-' 408 // * a general 1-chain as '<~' (direct or indirect) 409 // Jolteon's rule for finalizing `parentBlock` is 410 // parentBlock <- Block <~ certifyingQC (i.e. a DIRECT 1-chain PLUS any 1-chain) 411 // ╰─────────────────────╯ 412 // certifiedBlock 413 // Hence, we can finalize `parentBlock` as head of a 2-chain, 414 // if and only if `Block.View` is exactly 1 higher than the view of `parentBlock` 415 if parentBlock.View+1 != certifiedBlock.View() { 416 return nil 417 } 418 419 // `parentBlock` is now finalized: 420 // * While Forks is single-threaded, there is still the possibility of reentrancy. Specifically, the 421 // consumers of our finalization events are served by the goroutine executing Forks. It is conceivable 422 // that a consumer might access Forks and query the latest finalization proof. This would be legal, if 423 // the component supplying the goroutine to Forks also consumes the notifications. 424 // * Therefore, for API safety, we want to first update Fork's `finalityProof` before we emit any notifications. 425 426 // Advancing finalization step (i): we collect all blocks for finalization (no notifications are emitted) 427 blocksToBeFinalized, err := f.collectBlocksForFinalization(qcForParent) 428 if err != nil { 429 return fmt.Errorf("advancing finalization to block %v from view %d failed: %w", qcForParent.BlockID, qcForParent.View, err) 430 } 431 432 // Advancing finalization step (ii): update `finalityProof` and prune `LevelledForest` 433 f.finalityProof = &hotstuff.FinalityProof{Block: parentBlock, CertifiedChild: *certifiedBlock} 434 err = f.forest.PruneUpToLevel(f.FinalizedView()) 435 if err != nil { 436 return fmt.Errorf("pruning levelled forest failed unexpectedly: %w", err) 437 } 438 439 // Advancing finalization step (iii): iterate over the blocks from (i) and emit finalization events 440 for _, b := range blocksToBeFinalized { 441 // first notify other critical components about finalized block - all errors returned here are fatal exceptions 442 err = f.finalizationCallback.MakeFinal(b.BlockID) 443 if err != nil { 444 return fmt.Errorf("finalization error in other component: %w", err) 445 } 446 447 // notify less important components about finalized block 448 f.notifier.OnFinalizedBlock(b) 449 } 450 return nil 451 } 452 453 // collectBlocksForFinalization collects and returns all newly finalized blocks up to (and including) 454 // the block pointed to by `qc`. The blocks are listed in order of increasing height. 455 // Error returns: 456 // - model.ByzantineThresholdExceededError in case we detect a finalization fork (violating 457 // a foundational consensus guarantee). This indicates that there are 1/3+ Byzantine nodes 458 // (weighted by stake) in the network, breaking the safety guarantees of HotStuff (or there 459 // is a critical bug / data corruption). Forks cannot recover from this exception. 460 // - generic error in case of bug or internal state corruption 461 func (f *Forks) collectBlocksForFinalization(qc *flow.QuorumCertificate) ([]*model.Block, error) { 462 lastFinalized := f.FinalizedBlock() 463 if qc.View < lastFinalized.View { 464 return nil, model.ByzantineThresholdExceededError{Evidence: fmt.Sprintf( 465 "finalizing block with view %d which is lower than previously finalized block at view %d", 466 qc.View, lastFinalized.View, 467 )} 468 } 469 if qc.View == lastFinalized.View { // no new blocks to be finalized 470 return nil, nil 471 } 472 473 // Collect all blocks that are pending finalization in slice. While we crawl the blocks starting 474 // from the newest finalized block backwards (decreasing views), we would like to return them in 475 // order of _increasing_ view. Therefore, we fill the slice starting with the highest index. 476 l := qc.View - lastFinalized.View // l is an upper limit to the number of blocks that can be maximally finalized 477 blocksToBeFinalized := make([]*model.Block, l) 478 for qc.View > lastFinalized.View { 479 b, ok := f.GetBlock(qc.BlockID) 480 if !ok { 481 return nil, fmt.Errorf("failed to get block (view=%d, blockID=%x) for finalization", qc.View, qc.BlockID) 482 } 483 l-- 484 blocksToBeFinalized[l] = b 485 qc = b.QC // move to parent 486 } 487 // Now, `l` is the index where we stored the oldest block that should be finalized. Note that `l` 488 // might be larger than zero, if some views have no finalized blocks. Hence, `blocksToBeFinalized` 489 // might start with nil entries, which we remove: 490 blocksToBeFinalized = blocksToBeFinalized[l:] 491 492 // qc should now point to the latest finalized block. Otherwise, the 493 // consensus committee is compromised (or we have a critical internal bug). 494 if qc.View < lastFinalized.View { 495 return nil, model.ByzantineThresholdExceededError{Evidence: fmt.Sprintf( 496 "finalizing block with view %d which is lower than previously finalized block at view %d", 497 qc.View, lastFinalized.View, 498 )} 499 } 500 if qc.View == lastFinalized.View && lastFinalized.BlockID != qc.BlockID { 501 return nil, model.ByzantineThresholdExceededError{Evidence: fmt.Sprintf( 502 "finalizing blocks with view %d at conflicting forks: %x and %x", 503 qc.View, qc.BlockID, lastFinalized.BlockID, 504 )} 505 } 506 507 return blocksToBeFinalized, nil 508 }