github.com/koko1123/flow-go-1@v0.29.6/state/protocol/badger/mutator.go (about) 1 // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED 2 3 package badger 4 5 import ( 6 "context" 7 "errors" 8 "fmt" 9 10 "github.com/dgraph-io/badger/v3" 11 12 "github.com/koko1123/flow-go-1/engine" 13 "github.com/koko1123/flow-go-1/model/flow" 14 "github.com/koko1123/flow-go-1/module" 15 "github.com/koko1123/flow-go-1/module/signature" 16 "github.com/koko1123/flow-go-1/module/trace" 17 "github.com/koko1123/flow-go-1/state" 18 "github.com/koko1123/flow-go-1/state/protocol" 19 "github.com/koko1123/flow-go-1/storage" 20 "github.com/koko1123/flow-go-1/storage/badger/operation" 21 "github.com/koko1123/flow-go-1/storage/badger/procedure" 22 "github.com/koko1123/flow-go-1/storage/badger/transaction" 23 ) 24 25 // errIncompleteEpochConfiguration is a sentinel error returned when there are 26 // still epoch service events missing and the new epoch can't be constructed. 27 var errIncompleteEpochConfiguration = errors.New("block beyond epoch boundary") 28 29 // FollowerState implements a lighter version of a mutable protocol state. 30 // When extending the state, it performs hardly any checks on the block payload. 31 // Instead, the FollowerState relies on the consensus nodes to run the full 32 // payload check. Consequently, a block B should only be considered valid, if 33 // a child block with a valid header is known. The child block's header 34 // includes quorum certificate, which proves that a super-majority of consensus 35 // nodes consider block B as valid. 36 // 37 // The FollowerState allows non-consensus nodes to execute fork-aware queries 38 // against the protocol state, while minimizing the amount of payload checks 39 // the non-consensus nodes have to perform. 40 type FollowerState struct { 41 *State 42 43 index storage.Index 44 payloads storage.Payloads 45 tracer module.Tracer 46 consumer protocol.Consumer 47 blockTimer protocol.BlockTimer 48 cfg Config 49 } 50 51 // MutableState implements a mutable protocol state. When extending the 52 // state with a new block, it checks the _entire_ block payload. 53 type MutableState struct { 54 *FollowerState 55 receiptValidator module.ReceiptValidator 56 sealValidator module.SealValidator 57 } 58 59 // NewFollowerState initializes a light-weight version of a mutable protocol 60 // state. This implementation is suitable only for NON-Consensus nodes. 61 func NewFollowerState( 62 state *State, 63 index storage.Index, 64 payloads storage.Payloads, 65 tracer module.Tracer, 66 consumer protocol.Consumer, 67 blockTimer protocol.BlockTimer, 68 ) (*FollowerState, error) { 69 followerState := &FollowerState{ 70 State: state, 71 index: index, 72 payloads: payloads, 73 tracer: tracer, 74 consumer: consumer, 75 blockTimer: blockTimer, 76 cfg: DefaultConfig(), 77 } 78 return followerState, nil 79 } 80 81 // NewFullConsensusState initializes a new mutable protocol state backed by a 82 // badger database. When extending the state with a new block, it checks the 83 // _entire_ block payload. Consensus nodes should use the FullConsensusState, 84 // while other node roles can use the lighter FollowerState. 85 func NewFullConsensusState( 86 state *State, 87 index storage.Index, 88 payloads storage.Payloads, 89 tracer module.Tracer, 90 consumer protocol.Consumer, 91 blockTimer protocol.BlockTimer, 92 receiptValidator module.ReceiptValidator, 93 sealValidator module.SealValidator, 94 ) (*MutableState, error) { 95 followerState, err := NewFollowerState(state, index, payloads, tracer, consumer, blockTimer) 96 if err != nil { 97 return nil, fmt.Errorf("initialization of Mutable Follower State failed: %w", err) 98 } 99 return &MutableState{ 100 FollowerState: followerState, 101 receiptValidator: receiptValidator, 102 sealValidator: sealValidator, 103 }, nil 104 } 105 106 // Extend extends the protocol state of a CONSENSUS FOLLOWER. While it checks 107 // the validity of the header; it does _not_ check the validity of the payload. 108 // Instead, the consensus follower relies on the consensus participants to 109 // validate the full payload. Therefore, a follower a QC (i.e. a child block) as 110 // proof that a block is valid. 111 func (m *FollowerState) Extend(ctx context.Context, candidate *flow.Block) error { 112 113 span, ctx := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorHeaderExtend) 114 defer span.End() 115 116 // check if the block header is a valid extension of the finalized state 117 err := m.headerExtend(candidate) 118 if err != nil { 119 return fmt.Errorf("header not compliant with chain state: %w", err) 120 } 121 122 // find the last seal at the parent block 123 last, err := m.lastSealed(candidate) 124 if err != nil { 125 return fmt.Errorf("payload seal(s) not compliant with chain state: %w", err) 126 } 127 128 // insert the block and index the last seal for the block 129 err = m.insert(ctx, candidate, last) 130 if err != nil { 131 return fmt.Errorf("failed to insert the block: %w", err) 132 } 133 134 return nil 135 } 136 137 // Extend extends the protocol state of a CONSENSUS PARTICIPANT. It checks 138 // the validity of the _entire block_ (header and full payload). 139 func (m *MutableState) Extend(ctx context.Context, candidate *flow.Block) error { 140 141 span, ctx := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtend) 142 defer span.End() 143 144 // check if the block header is a valid extension of the finalized state 145 err := m.headerExtend(candidate) 146 if err != nil { 147 return fmt.Errorf("header not compliant with chain state: %w", err) 148 } 149 150 // check if the guarantees in the payload is a valid extension of the finalized state 151 err = m.guaranteeExtend(ctx, candidate) 152 if err != nil { 153 return fmt.Errorf("payload guarantee(s) not compliant with chain state: %w", err) 154 } 155 156 // check if the receipts in the payload are valid 157 err = m.receiptExtend(ctx, candidate) 158 if err != nil { 159 return fmt.Errorf("payload receipt(s) not compliant with chain state: %w", err) 160 } 161 162 // check if the seals in the payload is a valid extension of the finalized 163 // state 164 lastSeal, err := m.sealExtend(ctx, candidate) 165 if err != nil { 166 return fmt.Errorf("payload seal(s) not compliant with chain state: %w", err) 167 } 168 169 // insert the block and index the last seal for the block 170 err = m.insert(ctx, candidate, lastSeal) 171 if err != nil { 172 return fmt.Errorf("failed to insert the block: %w", err) 173 } 174 175 return nil 176 } 177 178 // headerExtend verifies the validity of the block header (excluding verification of the 179 // consensus rules). Specifically, we check that the block connects to the last finalized block. 180 func (m *FollowerState) headerExtend(candidate *flow.Block) error { 181 // FIRST: We do some initial cheap sanity checks, like checking the payload 182 // hash is consistent 183 184 header := candidate.Header 185 payload := candidate.Payload 186 if payload.Hash() != header.PayloadHash { 187 return state.NewInvalidExtensionError("payload integrity check failed") 188 } 189 190 // SECOND: Next, we can check whether the block is a valid descendant of the 191 // parent. It should have the same chain ID and a height that is one bigger. 192 193 parent, err := m.headers.ByBlockID(header.ParentID) 194 if err != nil { 195 return state.NewInvalidExtensionErrorf("could not retrieve parent: %s", err) 196 } 197 if header.ChainID != parent.ChainID { 198 return state.NewInvalidExtensionErrorf("candidate built for invalid chain (candidate: %s, parent: %s)", 199 header.ChainID, parent.ChainID) 200 } 201 if header.Height != parent.Height+1 { 202 return state.NewInvalidExtensionErrorf("candidate built with invalid height (candidate: %d, parent: %d)", 203 header.Height, parent.Height) 204 } 205 206 // check validity of block timestamp using parent's timestamp 207 err = m.blockTimer.Validate(parent.Timestamp, candidate.Header.Timestamp) 208 if err != nil { 209 if protocol.IsInvalidBlockTimestampError(err) { 210 return state.NewInvalidExtensionErrorf("candidate contains invalid timestamp: %w", err) 211 } 212 return fmt.Errorf("validating block's time stamp failed with unexpected error: %w", err) 213 } 214 215 // THIRD: Once we have established the block is valid within itself, and the 216 // block is valid in relation to its parent, we can check whether it is 217 // valid in the context of the entire state. For this, the block needs to 218 // directly connect, through its ancestors, to the last finalized block. 219 220 var finalizedHeight uint64 221 err = m.db.View(operation.RetrieveFinalizedHeight(&finalizedHeight)) 222 if err != nil { 223 return fmt.Errorf("could not retrieve finalized height: %w", err) 224 } 225 var finalID flow.Identifier 226 err = m.db.View(operation.LookupBlockHeight(finalizedHeight, &finalID)) 227 if err != nil { 228 return fmt.Errorf("could not lookup finalized block: %w", err) 229 } 230 231 ancestorID := header.ParentID 232 for ancestorID != finalID { 233 ancestor, err := m.headers.ByBlockID(ancestorID) 234 if err != nil { 235 return fmt.Errorf("could not retrieve ancestor (%x): %w", ancestorID, err) 236 } 237 if ancestor.Height < finalizedHeight { 238 // this happens when the candidate block is on a fork that does not include all the 239 // finalized blocks. 240 // for instance: 241 // A (Finalized) <- B (Finalized) <- C (Finalized) <- D <- E <- F 242 // ^- G ^- H ^- I 243 // block G is not a valid block, because it does not have C (which has been finalized) as an ancestor 244 // block H and I are valid, because they do have C as an ancestor 245 return state.NewOutdatedExtensionErrorf( 246 "candidate block (height: %d) conflicts with finalized state (ancestor: %d final: %d)", 247 header.Height, ancestor.Height, finalizedHeight) 248 } 249 ancestorID = ancestor.ParentID 250 } 251 252 return nil 253 } 254 255 // guaranteeExtend verifies the validity of the collection guarantees that are 256 // included in the block. Specifically, we check for expired collections and 257 // duplicated collections (also including ancestor blocks). 258 func (m *MutableState) guaranteeExtend(ctx context.Context, candidate *flow.Block) error { 259 260 span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendCheckGuarantees) 261 defer span.End() 262 263 header := candidate.Header 264 payload := candidate.Payload 265 266 // we only look as far back for duplicates as the transaction expiry limit; 267 // if a guarantee was included before that, we will disqualify it on the 268 // basis of the reference block anyway 269 limit := header.Height - m.cfg.transactionExpiry 270 if limit > header.Height { // overflow check 271 limit = 0 272 } 273 274 // look up the root height so we don't look too far back 275 // initially this is the genesis block height (aka 0). 276 var rootHeight uint64 277 err := m.db.View(operation.RetrieveRootHeight(&rootHeight)) 278 if err != nil { 279 return fmt.Errorf("could not retrieve root block height: %w", err) 280 } 281 if limit < rootHeight { 282 limit = rootHeight 283 } 284 285 // build a list of all previously used guarantees on this part of the chain 286 ancestorID := header.ParentID 287 lookup := make(map[flow.Identifier]struct{}) 288 for { 289 ancestor, err := m.headers.ByBlockID(ancestorID) 290 if err != nil { 291 return fmt.Errorf("could not retrieve ancestor header (%x): %w", ancestorID, err) 292 } 293 index, err := m.index.ByBlockID(ancestorID) 294 if err != nil { 295 return fmt.Errorf("could not retrieve ancestor index (%x): %w", ancestorID, err) 296 } 297 for _, collID := range index.CollectionIDs { 298 lookup[collID] = struct{}{} 299 } 300 if ancestor.Height <= limit { 301 break 302 } 303 ancestorID = ancestor.ParentID 304 } 305 306 // check each guarantee included in the payload for duplication and expiry 307 for _, guarantee := range payload.Guarantees { 308 309 // if the guarantee was already included before, error 310 _, duplicated := lookup[guarantee.ID()] 311 if duplicated { 312 return state.NewInvalidExtensionErrorf("payload includes duplicate guarantee (%x)", guarantee.ID()) 313 } 314 315 // get the reference block to check expiry 316 ref, err := m.headers.ByBlockID(guarantee.ReferenceBlockID) 317 if err != nil { 318 if errors.Is(err, storage.ErrNotFound) { 319 return state.NewInvalidExtensionErrorf("could not get reference block %x: %w", guarantee.ReferenceBlockID, err) 320 } 321 return fmt.Errorf("could not get reference block (%x): %w", guarantee.ReferenceBlockID, err) 322 } 323 324 // if the guarantee references a block with expired height, error 325 if ref.Height < limit { 326 return state.NewInvalidExtensionErrorf("payload includes expired guarantee (height: %d, limit: %d)", 327 ref.Height, limit) 328 } 329 330 // check the guarantors are correct 331 _, err = protocol.FindGuarantors(m, guarantee) 332 if err != nil { 333 if signature.IsInvalidSignerIndicesError(err) || 334 errors.Is(err, protocol.ErrEpochNotCommitted) || 335 errors.Is(err, protocol.ErrClusterNotFound) { 336 return state.NewInvalidExtensionErrorf("guarantee %v contains invalid guarantors: %w", guarantee.ID(), err) 337 } 338 return fmt.Errorf("could not find guarantor for guarantee %v: %w", guarantee.ID(), err) 339 } 340 } 341 342 return nil 343 } 344 345 // sealExtend checks the compliance of the payload seals. Returns last seal that form a chain for 346 // candidate block. 347 func (m *MutableState) sealExtend(ctx context.Context, candidate *flow.Block) (*flow.Seal, error) { 348 349 span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendCheckSeals) 350 defer span.End() 351 352 lastSeal, err := m.sealValidator.Validate(candidate) 353 if err != nil { 354 return nil, state.NewInvalidExtensionErrorf("seal validation error: %w", err) 355 } 356 357 return lastSeal, nil 358 } 359 360 // receiptExtend checks the compliance of the receipt payload. 361 // - Receipts should pertain to blocks on the fork 362 // - Receipts should not appear more than once on a fork 363 // - Receipts should pass the ReceiptValidator check 364 // - No seal has been included for the respective block in this particular fork 365 // 366 // We require the receipts to be sorted by block height (within a payload). 367 func (m *MutableState) receiptExtend(ctx context.Context, candidate *flow.Block) error { 368 369 span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendCheckReceipts) 370 defer span.End() 371 372 err := m.receiptValidator.ValidatePayload(candidate) 373 if err != nil { 374 // TODO: this might be not an error, potentially it can be solved by requesting more data and processing this receipt again 375 if errors.Is(err, storage.ErrNotFound) { 376 return state.NewInvalidExtensionErrorf("some entities referenced by receipts are missing: %w", err) 377 } 378 if engine.IsInvalidInputError(err) { 379 return state.NewInvalidExtensionErrorf("payload includes invalid receipts: %w", err) 380 } 381 return fmt.Errorf("unexpected payload validation error %w", err) 382 } 383 384 return nil 385 } 386 387 // lastSealed returns the highest sealed block from the fork with head `candidate`. 388 // For instance, here is the chain state: block 100 is the head, block 97 is finalized, 389 // and 95 is the last sealed block at the state of block 100. 390 // 95 (sealed) <- 96 <- 97 (finalized) <- 98 <- 99 <- 100 391 // Now, if block 101 is extending block 100, and its payload has a seal for 96, then it will 392 // be the last sealed for block 101. 393 func (m *FollowerState) lastSealed(candidate *flow.Block) (*flow.Seal, error) { 394 header := candidate.Header 395 payload := candidate.Payload 396 397 // getting the last sealed block 398 last, err := m.seals.HighestInFork(header.ParentID) 399 if err != nil { 400 return nil, fmt.Errorf("could not retrieve parent seal (%x): %w", header.ParentID, err) 401 } 402 403 // if the payload of the block has seals, then the last seal is the seal for the highest 404 // block 405 if len(payload.Seals) > 0 { 406 var highestHeader *flow.Header 407 for i, seal := range payload.Seals { 408 header, err := m.headers.ByBlockID(seal.BlockID) 409 if err != nil { 410 return nil, state.NewInvalidExtensionErrorf("could not retrieve the header %v for seal: %w", seal.BlockID, err) 411 } 412 413 if i == 0 || header.Height > highestHeader.Height { 414 highestHeader = header 415 last = seal 416 } 417 } 418 } 419 420 return last, nil 421 } 422 423 // insert stores the candidate block in the data base. The 424 // `candidate` block _must be valid_ (otherwise, the state will be corrupted). 425 func (m *FollowerState) insert(ctx context.Context, candidate *flow.Block, last *flow.Seal) error { 426 427 span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendDBInsert) 428 defer span.End() 429 430 blockID := candidate.ID() 431 432 // SIXTH: epoch transitions and service events 433 // (i) Determine protocol state for block's _current_ Epoch. 434 // As we don't have slashing yet, the protocol state is fully 435 // determined by the Epoch Preparation events. 436 // (ii) Determine protocol state for block's _next_ Epoch. 437 // In case any of the payload seals includes system events, 438 // we need to check if they are valid and must apply them 439 // to the protocol state as needed. 440 ops, err := m.handleServiceEvents(candidate) 441 if err != nil { 442 return fmt.Errorf("could not handle service events: %w", err) 443 } 444 445 // FINALLY: Both the header itself and its payload are in compliance with the 446 // protocol state. We can now store the candidate block, as well as adding 447 // its final seal to the seal index and initializing its children index. 448 449 err = operation.RetryOnConflictTx(m.db, transaction.Update, func(tx *transaction.Tx) error { 450 // insert the block into the database AND cache 451 err := m.blocks.StoreTx(candidate)(tx) 452 if err != nil { 453 return fmt.Errorf("could not store candidate block: %w", err) 454 } 455 456 // index the latest sealed block in this fork 457 err = transaction.WithTx(operation.IndexLatestSealAtBlock(blockID, last.ID()))(tx) 458 if err != nil { 459 return fmt.Errorf("could not index candidate seal: %w", err) 460 } 461 462 // index the child block for recovery 463 err = transaction.WithTx(procedure.IndexNewBlock(blockID, candidate.Header.ParentID))(tx) 464 if err != nil { 465 return fmt.Errorf("could not index new block: %w", err) 466 } 467 468 // apply any optional DB operations from service events 469 for _, apply := range ops { 470 err := apply(tx) 471 if err != nil { 472 return fmt.Errorf("could not apply operation: %w", err) 473 } 474 } 475 476 return nil 477 }) 478 479 if err != nil { 480 return fmt.Errorf("could not execute state extension: %w", err) 481 } 482 483 return nil 484 } 485 486 // Finalize marks the specified block as finalized. This method only 487 // finalizes one block at a time. Hence, the parent of `blockID` 488 // has to be the last finalized block. 489 func (m *FollowerState) Finalize(ctx context.Context, blockID flow.Identifier) error { 490 // preliminaries: start tracer and retrieve full block 491 span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorFinalize) 492 defer span.End() 493 block, err := m.blocks.ByID(blockID) 494 if err != nil { 495 return fmt.Errorf("could not retrieve full block that should be finalized: %w", err) 496 } 497 header := block.Header 498 499 // FIRST: verify that the parent block is the latest finalized block. This 500 // must be the case, as the `Finalize(..)` method only finalizes one block 501 // at a time and hence the parent of `blockID` must already be finalized. 502 var finalized uint64 503 err = m.db.View(operation.RetrieveFinalizedHeight(&finalized)) 504 if err != nil { 505 return fmt.Errorf("could not retrieve finalized height: %w", err) 506 } 507 var finalID flow.Identifier 508 err = m.db.View(operation.LookupBlockHeight(finalized, &finalID)) 509 if err != nil { 510 return fmt.Errorf("could not retrieve final header: %w", err) 511 } 512 if header.ParentID != finalID { 513 return fmt.Errorf("can only finalize child of last finalized block") 514 } 515 516 // SECOND: We also want to update the last sealed height. Retrieve the block 517 // seal indexed for the block and retrieve the block that was sealed by it. 518 last, err := m.seals.HighestInFork(blockID) 519 if err != nil { 520 return fmt.Errorf("could not look up sealed header: %w", err) 521 } 522 sealed, err := m.headers.ByBlockID(last.BlockID) 523 if err != nil { 524 return fmt.Errorf("could not retrieve sealed header: %w", err) 525 } 526 527 // THIRD: preparing Epoch-Phase-Change service notifications and metrics updates. 528 // Convention: 529 // .. <--- P <----- B 530 // ↑ ↑ 531 // block sealing service event first block of new 532 // for epoch-phase transition Epoch phase (e.g. 533 // (e.g. EpochSetup event) (EpochSetup phase) 534 // Per convention, service notifications for Epoch-Phase-Changes are emitted, when 535 // the first block of the new phase (EpochSetup phase) is _finalized_. Meaning 536 // that the new phase has started. 537 epochStatus, err := m.epoch.statuses.ByBlockID(blockID) 538 if err != nil { 539 return fmt.Errorf("could not retrieve epoch state: %w", err) 540 } 541 currentEpochSetup, err := m.epoch.setups.ByID(epochStatus.CurrentEpoch.SetupID) 542 if err != nil { 543 return fmt.Errorf("could not retrieve setup event for current epoch: %w", err) 544 } 545 parent, err := m.blocks.ByID(header.ParentID) 546 if err != nil { 547 return fmt.Errorf("could not get parent (id=%x): %w", header.ParentID, err) 548 } 549 550 // EECC - check whether the epoch emergency fallback flag has been set 551 // in the database. If so, skip updating any epoch-related metrics. 552 epochFallbackTriggered, err := m.isEpochEmergencyFallbackTriggered() 553 if err != nil { 554 return fmt.Errorf("could not check epoch emergency fallback flag: %w", err) 555 } 556 557 // track service event driven metrics and protocol events that should be emitted 558 var events []func() 559 for _, seal := range parent.Payload.Seals { 560 // skip updating epoch-related metrics if EECC is triggered 561 if epochFallbackTriggered { 562 break 563 } 564 565 result, err := m.results.ByID(seal.ResultID) 566 if err != nil { 567 return fmt.Errorf("could not retrieve result (id=%x) for seal (id=%x): %w", seal.ResultID, seal.ID(), err) 568 } 569 for _, event := range result.ServiceEvents { 570 switch ev := event.Event.(type) { 571 case *flow.EpochSetup: 572 // update current epoch phase 573 events = append(events, func() { m.metrics.CurrentEpochPhase(flow.EpochPhaseSetup) }) 574 // track epoch phase transition (staking->setup) 575 events = append(events, func() { m.consumer.EpochSetupPhaseStarted(ev.Counter-1, header) }) 576 case *flow.EpochCommit: 577 // update current epoch phase 578 events = append(events, func() { m.metrics.CurrentEpochPhase(flow.EpochPhaseCommitted) }) 579 // track epoch phase transition (setup->committed) 580 events = append(events, func() { m.consumer.EpochCommittedPhaseStarted(ev.Counter-1, header) }) 581 // track final view of committed epoch 582 nextEpochSetup, err := m.epoch.setups.ByID(epochStatus.NextEpoch.SetupID) 583 if err != nil { 584 return fmt.Errorf("could not retrieve setup event for next epoch: %w", err) 585 } 586 events = append(events, func() { m.metrics.CommittedEpochFinalView(nextEpochSetup.FinalView) }) 587 default: 588 return fmt.Errorf("invalid service event type in payload (%T)", event) 589 } 590 } 591 } 592 593 // FOURTH: preparing Epoch-Change service notifications and metrics updates. 594 // Convention: 595 // Service notifications and updating metrics happen when we finalize the _first_ 596 // block of the new Epoch (same convention as for Epoch-Phase-Changes) 597 // Approach: We retrieve the parent block's epoch information. If this block's view 598 // exceeds the final view of its parent's current epoch, this block begins the next epoch. 599 parentBlocksEpoch := m.AtBlockID(header.ParentID).Epochs().Current() 600 parentEpochFinalView, err := parentBlocksEpoch.FinalView() 601 if err != nil { 602 return fmt.Errorf("could not get parent epoch final view: %w", err) 603 } 604 605 // When this block's view exceeds the parent epoch's final view, this block 606 // represents the first block of the next epoch. Therefore we update metrics 607 // related to the epoch transition here. 608 // 609 // We skip updating these metrics when EECC has been triggered 610 if header.View > parentEpochFinalView && !epochFallbackTriggered { 611 events = append(events, func() { m.consumer.EpochTransition(currentEpochSetup.Counter, header) }) 612 613 // set current epoch counter corresponding to new epoch 614 events = append(events, func() { m.metrics.CurrentEpochCounter(currentEpochSetup.Counter) }) 615 // set epoch phase - since we are starting a new epoch we begin in the staking phase 616 events = append(events, func() { m.metrics.CurrentEpochPhase(flow.EpochPhaseStaking) }) 617 // set current epoch view values 618 events = append( 619 events, 620 func() { m.metrics.CurrentEpochFinalView(currentEpochSetup.FinalView) }, 621 func() { m.metrics.CurrentDKGPhase1FinalView(currentEpochSetup.DKGPhase1FinalView) }, 622 func() { m.metrics.CurrentDKGPhase2FinalView(currentEpochSetup.DKGPhase2FinalView) }, 623 func() { m.metrics.CurrentDKGPhase3FinalView(currentEpochSetup.DKGPhase3FinalView) }, 624 ) 625 } 626 627 // if EECC is triggered, update metric 628 if epochFallbackTriggered { 629 m.metrics.EpochEmergencyFallbackTriggered() 630 } 631 632 // FIFTH: Persist updates in database 633 // * Add this block to the height-indexed set of finalized blocks. 634 // * Update the largest finalized height to this block's height. 635 // * Update the largest height of sealed and finalized block. 636 // This value could actually stay the same if it has no seals in 637 // its payload, in which case the parent's seal is the same. 638 err = operation.RetryOnConflict(m.db.Update, func(tx *badger.Txn) error { 639 err = operation.IndexBlockHeight(header.Height, blockID)(tx) 640 if err != nil { 641 return fmt.Errorf("could not insert number mapping: %w", err) 642 } 643 err = operation.UpdateFinalizedHeight(header.Height)(tx) 644 if err != nil { 645 return fmt.Errorf("could not update finalized height: %w", err) 646 } 647 err = operation.UpdateSealedHeight(sealed.Height)(tx) 648 if err != nil { 649 return fmt.Errorf("could not update sealed height: %w", err) 650 } 651 652 // When a block is finalized, we commit the result for each seal it contains. The sealing logic 653 // guarantees that only a single, continuous execution fork is sealed. Here, we index for 654 // each block ID the ID of its _finalized_ seal. 655 for _, seal := range block.Payload.Seals { 656 err = operation.IndexFinalizedSealByBlockID(seal.BlockID, seal.ID())(tx) 657 if err != nil { 658 return fmt.Errorf("could not index the seal by the sealed block ID: %w", err) 659 } 660 } 661 662 // emit protocol events within the scope of the Badger transaction to 663 // guarantee at-least-once delivery 664 m.consumer.BlockFinalized(header) 665 for _, emit := range events { 666 emit() 667 } 668 return nil 669 }) 670 if err != nil { 671 return fmt.Errorf("could not execute finalization: %w", err) 672 } 673 674 // FINALLY: update metrics 675 m.metrics.FinalizedHeight(header.Height) 676 m.metrics.SealedHeight(sealed.Height) 677 m.metrics.BlockFinalized(block) 678 679 for _, seal := range block.Payload.Seals { 680 sealedBlock, err := m.blocks.ByID(seal.BlockID) 681 if err != nil { 682 return fmt.Errorf("could not retrieve sealed block (%x): %w", seal.BlockID, err) 683 } 684 m.metrics.BlockSealed(sealedBlock) 685 } 686 687 return nil 688 } 689 690 // epochStatus computes the EpochStatus for the given block 691 // BEFORE applying the block payload itself 692 // Specifically, we must determine whether block is the first block of a new 693 // epoch in its respective fork. We do this by comparing the block's view to 694 // the Epoch data from its parent. If the block's view is _larger_ than the 695 // final View of the parent's epoch, the block starts a new Epoch. 696 // - case (a): block is in same Epoch as parent. 697 // the parent's EpochStatus.CurrentEpoch also applies for the current block 698 // - case (b): block starts new Epoch in its respective fork. 699 // the parent's EpochStatus.NextEpoch is the current block's EpochStatus.CurrentEpoch 700 // 701 // As the parent was a valid extension of the chain, by induction, the parent satisfies all 702 // consistency requirements of the protocol. 703 // 704 // Returns: 705 // - errIncompleteEpochConfiguration if the epoch has ended before processing 706 // both an EpochSetup and EpochCommit event; so the new epoch can't be constructed. 707 func (m *FollowerState) epochStatus(block *flow.Header) (*flow.EpochStatus, error) { 708 709 parentStatus, err := m.epoch.statuses.ByBlockID(block.ParentID) 710 if err != nil { 711 return nil, fmt.Errorf("could not retrieve epoch state for parent: %w", err) 712 } 713 714 // Retrieve EpochSetup and EpochCommit event for parent block's Epoch 715 parentSetup, err := m.epoch.setups.ByID(parentStatus.CurrentEpoch.SetupID) 716 if err != nil { 717 return nil, fmt.Errorf("could not retrieve EpochSetup event for parent: %w", err) 718 } 719 720 if parentSetup.FinalView < block.View { // first block of a new epoch 721 // sanity check: parent's epoch Preparation should be completed and have EpochSetup and EpochCommit events 722 if parentStatus.NextEpoch.SetupID == flow.ZeroID { 723 return nil, fmt.Errorf("missing setup event for starting next epoch: %w", errIncompleteEpochConfiguration) 724 } 725 if parentStatus.NextEpoch.CommitID == flow.ZeroID { 726 return nil, fmt.Errorf("missing commit event for starting next epoch: %w", errIncompleteEpochConfiguration) 727 } 728 status, err := flow.NewEpochStatus( 729 parentStatus.CurrentEpoch.SetupID, parentStatus.CurrentEpoch.CommitID, 730 parentStatus.NextEpoch.SetupID, parentStatus.NextEpoch.CommitID, 731 flow.ZeroID, flow.ZeroID, 732 ) 733 return status, err 734 } 735 736 // Block is in the same epoch as its parent, re-use the same epoch status 737 // IMPORTANT: copy the status to avoid modifying the parent status in the cache 738 currentStatus := parentStatus.Copy() 739 return currentStatus, err 740 } 741 742 // handleServiceEvents handles applying state changes which occur as a result 743 // of service events being included in a block payload. 744 // 745 // Consider a chain where a service event is emitted during execution of block A. 746 // Block B contains a receipt for A. Block C contains a seal for block A. Block 747 // D contains a QC for C. 748 // 749 // A <- B(RA) <- C(SA) <- D 750 // 751 // Service events are included within execution results, which are stored 752 // opaquely as part of the block payload in block B. We only validate and insert 753 // the typed service event to storage once we have received a valid QC for the 754 // block containing the seal for A. This occurs once we mark block D as valid 755 // with MarkValid. Because of this, any change to the protocol state introduced 756 // by a service event emitted in A would only become visible when querying D or 757 // later (D's children). 758 // 759 // This method will only apply service-event-induced state changes when the 760 // input block has the form of block D (ie. has a parent, which contains a seal 761 // for a block in which a service event was emitted). 762 // 763 // If the service events are valid, or there are no service events, this method 764 // returns a slice of Badger operations to apply while storing the block. This 765 // includes an operation to index the epoch status for every block, and 766 // operations to insert service events for blocks that include them. 767 // 768 // Return values: 769 // - ops: pending database operations to persist this processing step 770 // - error: no errors expected during normal operations 771 func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*transaction.Tx) error, error) { 772 var ops []func(*transaction.Tx) error 773 blockID := block.ID() 774 775 // Determine epoch status for block's CURRENT epoch. 776 // 777 // This yields the tentative protocol state BEFORE applying the block payload. 778 // As we don't have slashing yet, there is nothing in the payload which could 779 // modify the protocol state for the current epoch. 780 781 epochStatus, err := m.epochStatus(block.Header) 782 if errors.Is(err, errIncompleteEpochConfiguration) { 783 // TMP: EMERGENCY EPOCH CHAIN CONTINUATION 784 // 785 // We are proposing or processing the first block of the next epoch, 786 // but that epoch has not been setup. Rather than returning an error 787 // which prevents further block production, we store the block with 788 // the same epoch status as its parent, resulting in it being considered 789 // by the protocol state to fall in the same epoch as its parent. 790 // 791 // CAUTION: this is inconsistent with the FinalView value specified in the epoch. 792 parentStatus, err := m.epoch.statuses.ByBlockID(block.Header.ParentID) 793 if err != nil { 794 return nil, fmt.Errorf("internal error constructing EECC from parent's epoch status: %w", err) 795 } 796 ops = append(ops, m.epoch.statuses.StoreTx(blockID, parentStatus.Copy())) 797 ops = append(ops, transaction.WithTx(operation.SetEpochEmergencyFallbackTriggered(blockID))) 798 ops = append(ops, func(tx *transaction.Tx) error { 799 tx.OnSucceed(m.metrics.EpochEmergencyFallbackTriggered) 800 return nil 801 }) 802 return ops, nil 803 } else if err != nil { 804 return nil, fmt.Errorf("could not determine epoch status: %w", err) 805 } 806 807 activeSetup, err := m.epoch.setups.ByID(epochStatus.CurrentEpoch.SetupID) 808 if err != nil { 809 return nil, fmt.Errorf("could not retrieve current epoch setup event: %w", err) 810 } 811 812 // we will apply service events from blocks which are sealed by this block's PARENT 813 parent, err := m.blocks.ByID(block.Header.ParentID) 814 if err != nil { 815 return nil, fmt.Errorf("could not get parent (id=%x): %w", block.Header.ParentID, err) 816 } 817 818 // The payload might contain epoch preparation service events for the next 819 // epoch. In this case, we need to update the tentative protocol state. 820 // We need to validate whether all information is available in the protocol 821 // state to go to the next epoch when needed. In cases where there is a bug 822 // in the smart contract, it could be that this happens too late and the 823 // chain finalization should halt. 824 SealLoop: 825 for _, seal := range parent.Payload.Seals { 826 result, err := m.results.ByID(seal.ResultID) 827 if err != nil { 828 return nil, fmt.Errorf("could not get result (id=%x) for seal (id=%x): %w", seal.ResultID, seal.ID(), err) 829 } 830 831 for _, event := range result.ServiceEvents { 832 833 switch ev := event.Event.(type) { 834 case *flow.EpochSetup: 835 836 // validate the service event 837 err := isValidExtendingEpochSetup(ev, activeSetup, epochStatus) 838 if protocol.IsInvalidServiceEventError(err) { 839 // EECC - we have observed an invalid service event, which is 840 // an unrecoverable failure. Flag this in the DB and exit 841 ops = append(ops, transaction.WithTx(operation.SetEpochEmergencyFallbackTriggered(blockID))) 842 break SealLoop 843 } 844 845 // prevents multiple setup events for same Epoch (including multiple setup events in payload of same block) 846 epochStatus.NextEpoch.SetupID = ev.ID() 847 848 // we'll insert the setup event when we insert the block 849 ops = append(ops, m.epoch.setups.StoreTx(ev)) 850 851 case *flow.EpochCommit: 852 853 extendingSetup, err := m.epoch.setups.ByID(epochStatus.NextEpoch.SetupID) 854 if err != nil { 855 return nil, state.NewInvalidExtensionErrorf("could not retrieve next epoch setup: %s", err) 856 } 857 // validate the service event 858 err = isValidExtendingEpochCommit(ev, extendingSetup, activeSetup, epochStatus) 859 if protocol.IsInvalidServiceEventError(err) { 860 // EECC - we have observed an invalid service event, which is 861 // an unrecoverable failure. Flag this in the DB and exit 862 ops = append(ops, transaction.WithTx(operation.SetEpochEmergencyFallbackTriggered(blockID))) 863 break SealLoop 864 } 865 866 // prevents multiple setup events for same Epoch (including multiple setup events in payload of same block) 867 epochStatus.NextEpoch.CommitID = ev.ID() 868 869 // we'll insert the commit event when we insert the block 870 ops = append(ops, m.epoch.commits.StoreTx(ev)) 871 872 default: 873 return nil, fmt.Errorf("invalid service event type: %s", event.Type) 874 } 875 } 876 } 877 878 // we always index the epoch status, even when there are no service events 879 ops = append(ops, m.epoch.statuses.StoreTx(block.ID(), epochStatus)) 880 881 return ops, nil 882 } 883 884 // MarkValid marks the block as valid in protocol state, and triggers 885 // `BlockProcessable` event to notify that its parent block is processable. 886 // 887 // Why is the parent block processable, not the block itself? 888 // because a block having a child block means it has been verified 889 // by the majority of consensus participants. 890 // Hence, if a block has passed the header validity check, its parent block 891 // must have passed both the header validity check and the body validity check. 892 // So that consensus followers can skip the block body validity checks and wait 893 // for its child to arrive, and if the child passes the header validity check, it means 894 // the consensus participants have done a complete check on its parent block, 895 // so consensus followers can trust consensus nodes did the right job, and start 896 // processing the parent block. 897 // 898 // NOTE: since a parent can have multiple children, `BlockProcessable` event 899 // could be triggered multiple times for the same block. 900 // NOTE: BlockProcessable should not be blocking, otherwise, it will block the follower 901 func (m *FollowerState) MarkValid(blockID flow.Identifier) error { 902 header, err := m.headers.ByBlockID(blockID) 903 if err != nil { 904 return fmt.Errorf("could not retrieve block header for %x: %w", blockID, err) 905 } 906 parentID := header.ParentID 907 var isParentValid bool 908 err = m.db.View(operation.RetrieveBlockValidity(parentID, &isParentValid)) 909 if err != nil { 910 return fmt.Errorf("could not retrieve validity of parent block (%x): %w", parentID, err) 911 } 912 if !isParentValid { 913 return fmt.Errorf("can only mark block as valid whose parent is valid") 914 } 915 916 parent, err := m.headers.ByBlockID(parentID) 917 if err != nil { 918 return fmt.Errorf("could not retrieve block header for %x: %w", parentID, err) 919 } 920 // root blocks and blocks below the root block are considered as "processed", 921 // so we don't want to trigger `BlockProcessable` event for them. 922 var rootHeight uint64 923 err = m.db.View(operation.RetrieveRootHeight(&rootHeight)) 924 if err != nil { 925 return fmt.Errorf("could not retrieve root block's height: %w", err) 926 } 927 928 err = operation.RetryOnConflict(m.db.Update, func(tx *badger.Txn) error { 929 // insert block validity for this block 930 err = operation.SkipDuplicates(operation.InsertBlockValidity(blockID, true))(tx) 931 if err != nil { 932 return fmt.Errorf("could not insert validity for block (id=%x, height=%d): %w", blockID, header.Height, err) 933 } 934 935 // trigger BlockProcessable for parent blocks above root height 936 if parent.Height > rootHeight { 937 // emit protocol event within the scope of the Badger transaction to 938 // guarantee at-least-once delivery 939 m.consumer.BlockProcessable(parent) 940 } 941 return nil 942 }) 943 if err != nil { 944 return fmt.Errorf("could not mark block as valid (%x): %w", blockID, err) 945 } 946 947 return nil 948 }