github.com/onflow/flow-go@v0.33.17/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/v2" 11 "github.com/rs/zerolog" 12 13 "github.com/onflow/flow-go/engine" 14 "github.com/onflow/flow-go/model/flow" 15 "github.com/onflow/flow-go/module" 16 "github.com/onflow/flow-go/module/irrecoverable" 17 "github.com/onflow/flow-go/module/signature" 18 "github.com/onflow/flow-go/module/trace" 19 "github.com/onflow/flow-go/state" 20 "github.com/onflow/flow-go/state/protocol" 21 "github.com/onflow/flow-go/storage" 22 "github.com/onflow/flow-go/storage/badger/operation" 23 "github.com/onflow/flow-go/storage/badger/procedure" 24 "github.com/onflow/flow-go/storage/badger/transaction" 25 ) 26 27 // FollowerState implements a lighter version of a mutable protocol state. 28 // When extending the state, it performs hardly any checks on the block payload. 29 // Instead, the FollowerState relies on the consensus nodes to run the full 30 // payload check and uses quorum certificates to prove validity of block payloads. 31 // Consequently, a block B should only be considered valid, if 32 // there is a certifying QC for that block QC.View == Block.View && QC.BlockID == Block.ID(). 33 // 34 // The FollowerState allows non-consensus nodes to execute fork-aware queries 35 // against the protocol state, while minimizing the amount of payload checks 36 // the non-consensus nodes have to perform. 37 type FollowerState struct { 38 *State 39 40 index storage.Index 41 payloads storage.Payloads 42 tracer module.Tracer 43 logger zerolog.Logger 44 consumer protocol.Consumer 45 blockTimer protocol.BlockTimer 46 } 47 48 var _ protocol.FollowerState = (*FollowerState)(nil) 49 50 // ParticipantState implements a mutable state for consensus participant. It can extend the 51 // state with a new block, by checking the _entire_ block payload. 52 type ParticipantState struct { 53 *FollowerState 54 receiptValidator module.ReceiptValidator 55 sealValidator module.SealValidator 56 } 57 58 var _ protocol.ParticipantState = (*ParticipantState)(nil) 59 60 // NewFollowerState initializes a light-weight version of a mutable protocol 61 // state. This implementation is suitable only for NON-Consensus nodes. 62 func NewFollowerState( 63 logger zerolog.Logger, 64 tracer module.Tracer, 65 consumer protocol.Consumer, 66 state *State, 67 index storage.Index, 68 payloads storage.Payloads, 69 blockTimer protocol.BlockTimer, 70 ) (*FollowerState, error) { 71 followerState := &FollowerState{ 72 State: state, 73 index: index, 74 payloads: payloads, 75 tracer: tracer, 76 logger: logger, 77 consumer: consumer, 78 blockTimer: blockTimer, 79 } 80 return followerState, nil 81 } 82 83 // NewFullConsensusState initializes a new mutable protocol state backed by a 84 // badger database. When extending the state with a new block, it checks the 85 // _entire_ block payload. Consensus nodes should use the FullConsensusState, 86 // while other node roles can use the lighter FollowerState. 87 func NewFullConsensusState( 88 logger zerolog.Logger, 89 tracer module.Tracer, 90 consumer protocol.Consumer, 91 state *State, 92 index storage.Index, 93 payloads storage.Payloads, 94 blockTimer protocol.BlockTimer, 95 receiptValidator module.ReceiptValidator, 96 sealValidator module.SealValidator, 97 ) (*ParticipantState, error) { 98 followerState, err := NewFollowerState( 99 logger, 100 tracer, 101 consumer, 102 state, 103 index, 104 payloads, 105 blockTimer, 106 ) 107 if err != nil { 108 return nil, fmt.Errorf("initialization of Mutable Follower State failed: %w", err) 109 } 110 return &ParticipantState{ 111 FollowerState: followerState, 112 receiptValidator: receiptValidator, 113 sealValidator: sealValidator, 114 }, nil 115 } 116 117 // ExtendCertified extends the protocol state of a CONSENSUS FOLLOWER. While it checks 118 // the validity of the header; it does _not_ check the validity of the payload. 119 // Instead, the consensus follower relies on the consensus participants to 120 // validate the full payload. Payload validity can be proved by a valid quorum certificate. 121 // Certifying QC must match candidate block: 122 // 123 // candidate.View == certifyingQC.View && candidate.ID() == certifyingQC.BlockID 124 // 125 // Caution: 126 // - This function expects that `certifyingQC` has been validated. 127 // - The parent block must already be stored. 128 // 129 // No errors are expected during normal operations. 130 func (m *FollowerState) ExtendCertified(ctx context.Context, candidate *flow.Block, certifyingQC *flow.QuorumCertificate) error { 131 span, ctx := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorHeaderExtend) 132 defer span.End() 133 134 // check if candidate block has been already processed 135 blockID := candidate.ID() 136 isDuplicate, err := m.checkBlockAlreadyProcessed(blockID) 137 if err != nil || isDuplicate { 138 return err 139 } 140 141 // sanity check if certifyingQC actually certifies candidate block 142 if certifyingQC.View != candidate.Header.View { 143 return fmt.Errorf("qc doesn't certify candidate block, expect %d view, got %d", candidate.Header.View, certifyingQC.View) 144 } 145 if certifyingQC.BlockID != blockID { 146 return fmt.Errorf("qc doesn't certify candidate block, expect %x blockID, got %x", blockID, certifyingQC.BlockID) 147 } 148 149 // check if the block header is a valid extension of parent block 150 err = m.headerExtend(candidate) 151 if err != nil { 152 // since we have a QC for this block, it cannot be an invalid extension 153 return fmt.Errorf("unexpected invalid block (id=%x) with certifying qc (id=%x): %s", 154 candidate.ID(), certifyingQC.ID(), err.Error()) 155 } 156 157 // find the last seal at the parent block 158 last, err := m.lastSealed(candidate) 159 if err != nil { 160 return fmt.Errorf("payload seal(s) not compliant with chain state: %w", err) 161 } 162 163 // insert the block, certifying QC and index the last seal for the block 164 err = m.insert(ctx, candidate, certifyingQC, last) 165 if err != nil { 166 return fmt.Errorf("failed to insert the block: %w", err) 167 } 168 169 return nil 170 } 171 172 // Extend extends the protocol state of a CONSENSUS PARTICIPANT. It checks 173 // the validity of the _entire block_ (header and full payload). 174 // Expected errors during normal operations: 175 // - state.OutdatedExtensionError if the candidate block is outdated (e.g. orphaned) 176 // - state.InvalidExtensionError if the candidate block is invalid 177 func (m *ParticipantState) Extend(ctx context.Context, candidate *flow.Block) error { 178 span, ctx := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtend) 179 defer span.End() 180 181 // check if candidate block has been already processed 182 isDuplicate, err := m.checkBlockAlreadyProcessed(candidate.ID()) 183 if err != nil || isDuplicate { 184 return err 185 } 186 187 // check if the block header is a valid extension of parent block 188 err = m.headerExtend(candidate) 189 if err != nil { 190 return fmt.Errorf("header not compliant with chain state: %w", err) 191 } 192 193 // check if the block header is a valid extension of the finalized state 194 err = m.checkOutdatedExtension(candidate.Header) 195 if err != nil { 196 if state.IsOutdatedExtensionError(err) { 197 return fmt.Errorf("candidate block is an outdated extension: %w", err) 198 } 199 return fmt.Errorf("could not check if block is an outdated extension: %w", err) 200 } 201 202 // check if the guarantees in the payload is a valid extension of the finalized state 203 err = m.guaranteeExtend(ctx, candidate) 204 if err != nil { 205 return fmt.Errorf("payload guarantee(s) not compliant with chain state: %w", err) 206 } 207 208 // check if the receipts in the payload are valid 209 err = m.receiptExtend(ctx, candidate) 210 if err != nil { 211 return fmt.Errorf("payload receipt(s) not compliant with chain state: %w", err) 212 } 213 214 // check if the seals in the payload is a valid extension of the finalized state 215 lastSeal, err := m.sealExtend(ctx, candidate) 216 if err != nil { 217 return fmt.Errorf("payload seal(s) not compliant with chain state: %w", err) 218 } 219 220 // insert the block and index the last seal for the block 221 err = m.insert(ctx, candidate, nil, lastSeal) 222 if err != nil { 223 return fmt.Errorf("failed to insert the block: %w", err) 224 } 225 226 return nil 227 } 228 229 // headerExtend verifies the validity of the block header (excluding verification of the 230 // consensus rules). Specifically, we check that the block connects to the last finalized block. 231 // Expected errors during normal operations: 232 // - state.InvalidExtensionError if the candidate block is invalid 233 func (m *FollowerState) headerExtend(candidate *flow.Block) error { 234 // FIRST: We do some initial cheap sanity checks, like checking the payload 235 // hash is consistent 236 237 header := candidate.Header 238 payload := candidate.Payload 239 if payload.Hash() != header.PayloadHash { 240 return state.NewInvalidExtensionError("payload integrity check failed") 241 } 242 243 // SECOND: Next, we can check whether the block is a valid descendant of the 244 // parent. It should have the same chain ID and a height that is one bigger. 245 246 parent, err := m.headers.ByBlockID(header.ParentID) 247 if err != nil { 248 return state.NewInvalidExtensionErrorf("could not retrieve parent: %s", err) 249 } 250 if header.ChainID != parent.ChainID { 251 return state.NewInvalidExtensionErrorf("candidate built for invalid chain (candidate: %s, parent: %s)", 252 header.ChainID, parent.ChainID) 253 } 254 if header.ParentView != parent.View { 255 return state.NewInvalidExtensionErrorf("candidate build with inconsistent parent view (candidate: %d, parent %d)", 256 header.ParentView, parent.View) 257 } 258 if header.Height != parent.Height+1 { 259 return state.NewInvalidExtensionErrorf("candidate built with invalid height (candidate: %d, parent: %d)", 260 header.Height, parent.Height) 261 } 262 263 // check validity of block timestamp using parent's timestamp 264 err = m.blockTimer.Validate(parent.Timestamp, candidate.Header.Timestamp) 265 if err != nil { 266 if protocol.IsInvalidBlockTimestampError(err) { 267 return state.NewInvalidExtensionErrorf("candidate contains invalid timestamp: %w", err) 268 } 269 return fmt.Errorf("validating block's time stamp failed with unexpected error: %w", err) 270 } 271 272 return nil 273 } 274 275 // checkBlockAlreadyProcessed checks if block has been added to the protocol state. 276 // Returns: 277 // * (true, nil) - block has been already processed. 278 // * (false, nil) - block has not been processed. 279 // * (false, error) - unknown error when trying to query protocol state. 280 // No errors are expected during normal operation. 281 func (m *FollowerState) checkBlockAlreadyProcessed(blockID flow.Identifier) (bool, error) { 282 _, err := m.headers.ByBlockID(blockID) 283 if err != nil { 284 if errors.Is(err, storage.ErrNotFound) { 285 return false, nil 286 } 287 return false, fmt.Errorf("could not check if candidate block (%x) has been already processed: %w", blockID, err) 288 } 289 return true, nil 290 } 291 292 // checkOutdatedExtension checks whether given block is 293 // valid in the context of the entire state. For this, the block needs to 294 // directly connect, through its ancestors, to the last finalized block. 295 // Expected errors during normal operations: 296 // - state.OutdatedExtensionError if the candidate block is outdated (e.g. orphaned) 297 func (m *ParticipantState) checkOutdatedExtension(header *flow.Header) error { 298 var finalizedHeight uint64 299 err := m.db.View(operation.RetrieveFinalizedHeight(&finalizedHeight)) 300 if err != nil { 301 return fmt.Errorf("could not retrieve finalized height: %w", err) 302 } 303 var finalID flow.Identifier 304 err = m.db.View(operation.LookupBlockHeight(finalizedHeight, &finalID)) 305 if err != nil { 306 return fmt.Errorf("could not lookup finalized block: %w", err) 307 } 308 309 ancestorID := header.ParentID 310 for ancestorID != finalID { 311 ancestor, err := m.headers.ByBlockID(ancestorID) 312 if err != nil { 313 return fmt.Errorf("could not retrieve ancestor (%x): %w", ancestorID, err) 314 } 315 if ancestor.Height < finalizedHeight { 316 // this happens when the candidate block is on a fork that does not include all the 317 // finalized blocks. 318 // for instance: 319 // A (Finalized) <- B (Finalized) <- C (Finalized) <- D <- E <- F 320 // ^- G ^- H ^- I 321 // block G is not a valid block, because it does not have C (which has been finalized) as an ancestor 322 // block H and I are valid, because they do have C as an ancestor 323 return state.NewOutdatedExtensionErrorf( 324 "candidate block (height: %d) conflicts with finalized state (ancestor: %d final: %d)", 325 header.Height, ancestor.Height, finalizedHeight) 326 } 327 ancestorID = ancestor.ParentID 328 } 329 return nil 330 } 331 332 // guaranteeExtend verifies the validity of the collection guarantees that are 333 // included in the block. Specifically, we check for expired collections and 334 // duplicated collections (also including ancestor blocks). 335 func (m *ParticipantState) guaranteeExtend(ctx context.Context, candidate *flow.Block) error { 336 337 span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendCheckGuarantees) 338 defer span.End() 339 340 header := candidate.Header 341 payload := candidate.Payload 342 343 // we only look as far back for duplicates as the transaction expiry limit; 344 // if a guarantee was included before that, we will disqualify it on the 345 // basis of the reference block anyway 346 limit := header.Height - flow.DefaultTransactionExpiry 347 if limit > header.Height { // overflow check 348 limit = 0 349 } 350 if limit < m.sporkRootBlockHeight { 351 limit = m.sporkRootBlockHeight 352 } 353 354 // build a list of all previously used guarantees on this part of the chain 355 ancestorID := header.ParentID 356 lookup := make(map[flow.Identifier]struct{}) 357 for { 358 ancestor, err := m.headers.ByBlockID(ancestorID) 359 if err != nil { 360 return fmt.Errorf("could not retrieve ancestor header (%x): %w", ancestorID, err) 361 } 362 index, err := m.index.ByBlockID(ancestorID) 363 if err != nil { 364 return fmt.Errorf("could not retrieve ancestor index (%x): %w", ancestorID, err) 365 } 366 for _, collID := range index.CollectionIDs { 367 lookup[collID] = struct{}{} 368 } 369 if ancestor.Height <= limit { 370 break 371 } 372 ancestorID = ancestor.ParentID 373 } 374 375 // check each guarantee included in the payload for duplication and expiry 376 for _, guarantee := range payload.Guarantees { 377 378 // if the guarantee was already included before, error 379 _, duplicated := lookup[guarantee.ID()] 380 if duplicated { 381 return state.NewInvalidExtensionErrorf("payload includes duplicate guarantee (%x)", guarantee.ID()) 382 } 383 384 // get the reference block to check expiry 385 ref, err := m.headers.ByBlockID(guarantee.ReferenceBlockID) 386 if err != nil { 387 if errors.Is(err, storage.ErrNotFound) { 388 return state.NewInvalidExtensionErrorf("could not get reference block %x: %w", guarantee.ReferenceBlockID, err) 389 } 390 return fmt.Errorf("could not get reference block (%x): %w", guarantee.ReferenceBlockID, err) 391 } 392 393 // if the guarantee references a block with expired height, error 394 if ref.Height < limit { 395 return state.NewInvalidExtensionErrorf("payload includes expired guarantee (height: %d, limit: %d)", 396 ref.Height, limit) 397 } 398 399 // check the guarantors are correct 400 _, err = protocol.FindGuarantors(m, guarantee) 401 if err != nil { 402 if signature.IsInvalidSignerIndicesError(err) || 403 errors.Is(err, protocol.ErrNextEpochNotCommitted) || 404 errors.Is(err, protocol.ErrClusterNotFound) { 405 return state.NewInvalidExtensionErrorf("guarantee %v contains invalid guarantors: %w", guarantee.ID(), err) 406 } 407 return fmt.Errorf("could not find guarantor for guarantee %v: %w", guarantee.ID(), err) 408 } 409 } 410 411 return nil 412 } 413 414 // sealExtend checks the compliance of the payload seals. Returns last seal that form a chain for 415 // candidate block. 416 func (m *ParticipantState) sealExtend(ctx context.Context, candidate *flow.Block) (*flow.Seal, error) { 417 418 span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendCheckSeals) 419 defer span.End() 420 421 lastSeal, err := m.sealValidator.Validate(candidate) 422 if err != nil { 423 return nil, state.NewInvalidExtensionErrorf("seal validation error: %w", err) 424 } 425 426 return lastSeal, nil 427 } 428 429 // receiptExtend checks the compliance of the receipt payload. 430 // - Receipts should pertain to blocks on the fork 431 // - Receipts should not appear more than once on a fork 432 // - Receipts should pass the ReceiptValidator check 433 // - No seal has been included for the respective block in this particular fork 434 // 435 // We require the receipts to be sorted by block height (within a payload). 436 func (m *ParticipantState) receiptExtend(ctx context.Context, candidate *flow.Block) error { 437 438 span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendCheckReceipts) 439 defer span.End() 440 441 err := m.receiptValidator.ValidatePayload(candidate) 442 if err != nil { 443 // TODO: this might be not an error, potentially it can be solved by requesting more data and processing this receipt again 444 if errors.Is(err, storage.ErrNotFound) { 445 return state.NewInvalidExtensionErrorf("some entities referenced by receipts are missing: %w", err) 446 } 447 if engine.IsInvalidInputError(err) { 448 return state.NewInvalidExtensionErrorf("payload includes invalid receipts: %w", err) 449 } 450 return fmt.Errorf("unexpected payload validation error %w", err) 451 } 452 453 return nil 454 } 455 456 // lastSealed returns the highest sealed block from the fork with head `candidate`. 457 // For instance, here is the chain state: block 100 is the head, block 97 is finalized, 458 // and 95 is the last sealed block at the state of block 100. 459 // 95 (sealed) <- 96 <- 97 (finalized) <- 98 <- 99 <- 100 460 // Now, if block 101 is extending block 100, and its payload has a seal for 96, then it will 461 // be the last sealed for block 101. 462 // No errors are expected during normal operation. 463 func (m *FollowerState) lastSealed(candidate *flow.Block) (*flow.Seal, error) { 464 header := candidate.Header 465 payload := candidate.Payload 466 467 // getting the last sealed block 468 last, err := m.seals.HighestInFork(header.ParentID) 469 if err != nil { 470 return nil, fmt.Errorf("could not retrieve parent seal (%x): %w", header.ParentID, err) 471 } 472 473 // if the payload of the block has no seals, then the last seal is the seal for the highest block 474 if len(payload.Seals) == 0 { 475 return last, nil 476 } 477 478 ordered, err := protocol.OrderedSeals(payload, m.headers) 479 if err != nil { 480 // all errors are unexpected - differentiation is for clearer error messages 481 if errors.Is(err, storage.ErrNotFound) { 482 return nil, fmt.Errorf("ordering seals: candidate payload contains seals for unknown block: %s", err.Error()) 483 } 484 if errors.Is(err, protocol.ErrDiscontinuousSeals) || errors.Is(err, protocol.ErrMultipleSealsForSameHeight) { 485 return nil, fmt.Errorf("ordering seals: candidate payload contains invalid seal set: %s", err.Error()) 486 } 487 return nil, fmt.Errorf("unexpected error ordering seals: %w", err) 488 } 489 return ordered[len(ordered)-1], nil 490 } 491 492 // insert stores the candidate block in the database. 493 // The `candidate` block _must be valid_ (otherwise, the state will be corrupted). 494 // dbUpdates contains other database operations which must be applied atomically 495 // with inserting the block. 496 // Caller is responsible for ensuring block validity. 497 // If insert is called from Extend(by consensus participant) then certifyingQC will be nil but the block payload will be validated. 498 // If insert is called from ExtendCertified(by consensus follower) then certifyingQC must be not nil which proves payload validity. 499 // No errors are expected during normal operations. 500 func (m *FollowerState) insert(ctx context.Context, candidate *flow.Block, certifyingQC *flow.QuorumCertificate, last *flow.Seal) error { 501 span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendDBInsert) 502 defer span.End() 503 504 blockID := candidate.ID() 505 parentID := candidate.Header.ParentID 506 latestSealID := last.ID() 507 508 parent, err := m.headers.ByBlockID(parentID) 509 if err != nil { 510 return fmt.Errorf("could not retrieve block header for %x: %w", parentID, err) 511 } 512 513 // apply any state changes from service events sealed by this block's parent 514 dbUpdates, err := m.handleEpochServiceEvents(candidate) 515 if err != nil { 516 return fmt.Errorf("could not process service events: %w", err) 517 } 518 519 qc := candidate.Header.QuorumCertificate() 520 521 var events []func() 522 523 // Both the header itself and its payload are in compliance with the protocol state. 524 // We can now store the candidate block, as well as adding its final seal 525 // to the seal index and initializing its children index. 526 err = operation.RetryOnConflictTx(m.db, transaction.Update, func(tx *transaction.Tx) error { 527 // insert the block into the database AND cache 528 err := m.blocks.StoreTx(candidate)(tx) 529 if err != nil { 530 return fmt.Errorf("could not store candidate block: %w", err) 531 } 532 533 err = m.qcs.StoreTx(qc)(tx) 534 if err != nil { 535 if !errors.Is(err, storage.ErrAlreadyExists) { 536 return fmt.Errorf("could not store incorporated qc: %w", err) 537 } 538 } else { 539 // trigger BlockProcessable for parent blocks above root height 540 if parent.Height > m.finalizedRootHeight { 541 events = append(events, func() { 542 m.consumer.BlockProcessable(parent, qc) 543 }) 544 } 545 } 546 547 if certifyingQC != nil { 548 err = m.qcs.StoreTx(certifyingQC)(tx) 549 if err != nil { 550 return fmt.Errorf("could not store certifying qc: %w", err) 551 } 552 553 // trigger BlockProcessable for candidate block if it's certified 554 events = append(events, func() { 555 m.consumer.BlockProcessable(candidate.Header, certifyingQC) 556 }) 557 } 558 559 // index the latest sealed block in this fork 560 err = transaction.WithTx(operation.IndexLatestSealAtBlock(blockID, latestSealID))(tx) 561 if err != nil { 562 return fmt.Errorf("could not index candidate seal: %w", err) 563 } 564 565 // index the child block for recovery 566 err = transaction.WithTx(procedure.IndexNewBlock(blockID, candidate.Header.ParentID))(tx) 567 if err != nil { 568 return fmt.Errorf("could not index new block: %w", err) 569 } 570 571 // apply any optional DB operations from service events 572 for _, apply := range dbUpdates { 573 err := apply(tx) 574 if err != nil { 575 return fmt.Errorf("could not apply operation: %w", err) 576 } 577 } 578 579 return nil 580 }) 581 if err != nil { 582 return fmt.Errorf("could not execute state extension: %w", err) 583 } 584 585 // execute scheduled events 586 for _, event := range events { 587 event() 588 } 589 590 return nil 591 } 592 593 // Finalize marks the specified block as finalized. 594 // This method only finalizes one block at a time. 595 // Hence, the parent of `blockID` has to be the last finalized block. 596 // No errors are expected during normal operations. 597 func (m *FollowerState) Finalize(ctx context.Context, blockID flow.Identifier) error { 598 599 // preliminaries: start tracer and retrieve full block 600 span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorFinalize) 601 defer span.End() 602 block, err := m.blocks.ByID(blockID) 603 if err != nil { 604 return fmt.Errorf("could not retrieve full block that should be finalized: %w", err) 605 } 606 header := block.Header 607 608 // keep track of metrics updates and protocol events to emit: 609 // * metrics are updated after a successful database update 610 // * protocol events are emitted atomically with the database update 611 var metrics []func() 612 var events []func() 613 614 // Verify that the parent block is the latest finalized block. 615 // this must be the case, as the `Finalize` method only finalizes one block 616 // at a time and hence the parent of `blockID` must already be finalized. 617 var finalized uint64 618 err = m.db.View(operation.RetrieveFinalizedHeight(&finalized)) 619 if err != nil { 620 return fmt.Errorf("could not retrieve finalized height: %w", err) 621 } 622 var finalID flow.Identifier 623 err = m.db.View(operation.LookupBlockHeight(finalized, &finalID)) 624 if err != nil { 625 return fmt.Errorf("could not retrieve final header: %w", err) 626 } 627 if header.ParentID != finalID { 628 return fmt.Errorf("can only finalize child of last finalized block") 629 } 630 631 // We also want to update the last sealed height. Retrieve the block 632 // seal indexed for the block and retrieve the block that was sealed by it. 633 lastSeal, err := m.seals.HighestInFork(blockID) 634 if err != nil { 635 return fmt.Errorf("could not look up sealed header: %w", err) 636 } 637 sealed, err := m.headers.ByBlockID(lastSeal.BlockID) 638 if err != nil { 639 return fmt.Errorf("could not retrieve sealed header: %w", err) 640 } 641 642 // We update metrics and emit protocol events for epoch state changes when 643 // the block corresponding to the state change is finalized 644 epochStatus, err := m.epoch.statuses.ByBlockID(blockID) 645 if err != nil { 646 return fmt.Errorf("could not retrieve epoch state: %w", err) 647 } 648 currentEpochSetup, err := m.epoch.setups.ByID(epochStatus.CurrentEpoch.SetupID) 649 if err != nil { 650 return fmt.Errorf("could not retrieve setup event for current epoch: %w", err) 651 } 652 epochFallbackTriggered, err := m.isEpochEmergencyFallbackTriggered() 653 if err != nil { 654 return fmt.Errorf("could not check persisted epoch emergency fallback flag: %w", err) 655 } 656 657 // if epoch fallback was not previously triggered, check whether this block triggers it 658 if !epochFallbackTriggered { 659 epochFallbackTriggered, err = m.epochFallbackTriggeredByFinalizedBlock(header, epochStatus, currentEpochSetup) 660 if err != nil { 661 return fmt.Errorf("could not check whether finalized block triggers epoch fallback: %w", err) 662 } 663 if epochFallbackTriggered { 664 // emit the protocol event only the first time epoch fallback is triggered 665 events = append(events, m.consumer.EpochEmergencyFallbackTriggered) 666 metrics = append(metrics, m.metrics.EpochEmergencyFallbackTriggered) 667 } 668 } 669 670 isFirstBlockOfEpoch, err := m.isFirstBlockOfEpoch(header, currentEpochSetup) 671 if err != nil { 672 return fmt.Errorf("could not check if block is first of epoch: %w", err) 673 } 674 675 // Determine metric updates and protocol events related to epoch phase 676 // changes and epoch transitions. 677 // If epoch emergency fallback is triggered, the current epoch continues until 678 // the next spork - so skip these updates. 679 if !epochFallbackTriggered { 680 epochPhaseMetrics, epochPhaseEvents, err := m.epochPhaseMetricsAndEventsOnBlockFinalized(block, epochStatus) 681 if err != nil { 682 return fmt.Errorf("could not determine epoch phase metrics/events for finalized block: %w", err) 683 } 684 metrics = append(metrics, epochPhaseMetrics...) 685 events = append(events, epochPhaseEvents...) 686 687 if isFirstBlockOfEpoch { 688 epochTransitionMetrics, epochTransitionEvents := m.epochTransitionMetricsAndEventsOnBlockFinalized(header, currentEpochSetup) 689 if err != nil { 690 return fmt.Errorf("could not determine epoch transition metrics/events for finalized block: %w", err) 691 } 692 metrics = append(metrics, epochTransitionMetrics...) 693 events = append(events, epochTransitionEvents...) 694 } 695 } 696 697 // Extract and validate version beacon events from the block seals. 698 versionBeacons, err := m.versionBeaconOnBlockFinalized(block) 699 if err != nil { 700 return fmt.Errorf("cannot process version beacon: %w", err) 701 } 702 703 // Persist updates in database 704 // * Add this block to the height-indexed set of finalized blocks. 705 // * Update the largest finalized height to this block's height. 706 // * Update the largest height of sealed and finalized block. 707 // This value could actually stay the same if it has no seals in 708 // its payload, in which case the parent's seal is the same. 709 // * set the epoch fallback flag, if it is triggered 710 err = operation.RetryOnConflict(m.db.Update, func(tx *badger.Txn) error { 711 err = operation.IndexBlockHeight(header.Height, blockID)(tx) 712 if err != nil { 713 return fmt.Errorf("could not insert number mapping: %w", err) 714 } 715 err = operation.UpdateFinalizedHeight(header.Height)(tx) 716 if err != nil { 717 return fmt.Errorf("could not update finalized height: %w", err) 718 } 719 err = operation.UpdateSealedHeight(sealed.Height)(tx) 720 if err != nil { 721 return fmt.Errorf("could not update sealed height: %w", err) 722 } 723 if epochFallbackTriggered { 724 err = operation.SetEpochEmergencyFallbackTriggered(blockID)(tx) 725 if err != nil { 726 return fmt.Errorf("could not set epoch fallback flag: %w", err) 727 } 728 } 729 if isFirstBlockOfEpoch && !epochFallbackTriggered { 730 err = operation.InsertEpochFirstHeight(currentEpochSetup.Counter, header.Height)(tx) 731 if err != nil { 732 return fmt.Errorf("could not insert epoch first block height: %w", err) 733 } 734 } 735 736 // When a block is finalized, we commit the result for each seal it contains. The sealing logic 737 // guarantees that only a single, continuous execution fork is sealed. Here, we index for 738 // each block ID the ID of its _finalized_ seal. 739 for _, seal := range block.Payload.Seals { 740 err = operation.IndexFinalizedSealByBlockID(seal.BlockID, seal.ID())(tx) 741 if err != nil { 742 return fmt.Errorf("could not index the seal by the sealed block ID: %w", err) 743 } 744 } 745 746 if len(versionBeacons) > 0 { 747 // only index the last version beacon as that is the relevant one. 748 // TODO: The other version beacons can be used for validation. 749 err := operation.IndexVersionBeaconByHeight(versionBeacons[len(versionBeacons)-1])(tx) 750 if err != nil { 751 return fmt.Errorf("could not index version beacon or height (%d): %w", header.Height, err) 752 } 753 } 754 755 return nil 756 }) 757 if err != nil { 758 return fmt.Errorf("could not persist finalization operations for block (%x): %w", blockID, err) 759 } 760 761 // update the cache 762 m.State.cachedFinal.Store(&cachedHeader{blockID, header}) 763 if len(block.Payload.Seals) > 0 { 764 m.State.cachedSealed.Store(&cachedHeader{lastSeal.BlockID, sealed}) 765 } 766 767 // Emit protocol events after database transaction succeeds. Event delivery is guaranteed, 768 // _except_ in case of a crash. Hence, when recovering from a crash, consumers need to deduce 769 // from the state whether they have missed events and re-execute the respective actions. 770 m.consumer.BlockFinalized(header) 771 for _, emit := range events { 772 emit() 773 } 774 775 // update sealed/finalized block metrics 776 m.metrics.FinalizedHeight(header.Height) 777 m.metrics.SealedHeight(sealed.Height) 778 m.metrics.BlockFinalized(block) 779 for _, seal := range block.Payload.Seals { 780 sealedBlock, err := m.blocks.ByID(seal.BlockID) 781 if err != nil { 782 return fmt.Errorf("could not retrieve sealed block (%x): %w", seal.BlockID, err) 783 } 784 m.metrics.BlockSealed(sealedBlock) 785 } 786 787 // apply all queued metrics 788 for _, updateMetric := range metrics { 789 updateMetric() 790 } 791 792 return nil 793 } 794 795 // epochFallbackTriggeredByFinalizedBlock checks whether finalizing the input block 796 // would trigger epoch emergency fallback mode. In particular, we trigger epoch 797 // fallback mode while finalizing block B in either of the following cases: 798 // 1. B is the head of a fork in which epoch fallback was tentatively triggered, 799 // due to incorporating an invalid service event. 800 // 2. (a) B is the first finalized block with view greater than or equal to the epoch 801 // commitment deadline for the current epoch AND 802 // (b) the next epoch has not been committed as of B. 803 // 804 // This function should only be called when epoch fallback *has not already been triggered*. 805 // See protocol.Params for more details on the epoch commitment deadline. 806 // 807 // No errors are expected during normal operation. 808 func (m *FollowerState) epochFallbackTriggeredByFinalizedBlock(block *flow.Header, epochStatus *flow.EpochStatus, currentEpochSetup *flow.EpochSetup) (bool, error) { 809 // 1. Epoch fallback is tentatively triggered on this fork 810 if epochStatus.InvalidServiceEventIncorporated { 811 return true, nil 812 } 813 814 // 2.(a) determine whether block B is past the epoch commitment deadline 815 safetyThreshold, err := m.Params().EpochCommitSafetyThreshold() 816 if err != nil { 817 return false, fmt.Errorf("could not get epoch commit safety threshold: %w", err) 818 } 819 blockExceedsDeadline := block.View+safetyThreshold >= currentEpochSetup.FinalView 820 821 // 2.(b) determine whether the next epoch is committed w.r.t. block B 822 currentEpochPhase, err := epochStatus.Phase() 823 if err != nil { 824 return false, fmt.Errorf("could not get current epoch phase: %w", err) 825 } 826 isNextEpochCommitted := currentEpochPhase == flow.EpochPhaseCommitted 827 828 blockTriggersEpochFallback := blockExceedsDeadline && !isNextEpochCommitted 829 return blockTriggersEpochFallback, nil 830 } 831 832 // isFirstBlockOfEpoch returns true if the given block is the first block of a new epoch. 833 // We accept the EpochSetup event for the current epoch (w.r.t. input block B) which contains 834 // the FirstView for the epoch (denoted W). By construction, B.View >= W. 835 // Definition: B is the first block of the epoch if and only if B.parent.View < W 836 // 837 // NOTE: There can be multiple (un-finalized) blocks that qualify as the first block of epoch N. 838 // No errors are expected during normal operation. 839 func (m *FollowerState) isFirstBlockOfEpoch(block *flow.Header, currentEpochSetup *flow.EpochSetup) (bool, error) { 840 currentEpochFirstView := currentEpochSetup.FirstView 841 // sanity check: B.View >= W 842 if block.View < currentEpochFirstView { 843 return false, irrecoverable.NewExceptionf("data inconsistency: block (id=%x, view=%d) is below its epoch first view %d", block.ID(), block.View, currentEpochFirstView) 844 } 845 846 parent, err := m.headers.ByBlockID(block.ParentID) 847 if err != nil { 848 return false, irrecoverable.NewExceptionf("could not retrieve parent (id=%s): %w", block.ParentID, err) 849 } 850 851 return parent.View < currentEpochFirstView, nil 852 } 853 854 // epochTransitionMetricsAndEventsOnBlockFinalized determines metrics to update 855 // and protocol events to emit for blocks which are the first block of a new epoch. 856 // Protocol events and updating metrics happen once when we finalize the _first_ 857 // block of the new Epoch (same convention as for Epoch-Phase-Changes). 858 // 859 // NOTE: This function must only be called when input `block` is the first block 860 // of the epoch denoted by `currentEpochSetup`. 861 func (m *FollowerState) epochTransitionMetricsAndEventsOnBlockFinalized(block *flow.Header, currentEpochSetup *flow.EpochSetup) ( 862 metrics []func(), 863 events []func(), 864 ) { 865 866 events = append(events, func() { m.consumer.EpochTransition(currentEpochSetup.Counter, block) }) 867 // set current epoch counter corresponding to new epoch 868 metrics = append(metrics, func() { m.metrics.CurrentEpochCounter(currentEpochSetup.Counter) }) 869 // denote the most recent epoch transition height 870 metrics = append(metrics, func() { m.metrics.EpochTransitionHeight(block.Height) }) 871 // set epoch phase - since we are starting a new epoch we begin in the staking phase 872 metrics = append(metrics, func() { m.metrics.CurrentEpochPhase(flow.EpochPhaseStaking) }) 873 // set current epoch view values 874 metrics = append( 875 metrics, 876 func() { m.metrics.CurrentEpochFinalView(currentEpochSetup.FinalView) }, 877 func() { m.metrics.CurrentDKGPhase1FinalView(currentEpochSetup.DKGPhase1FinalView) }, 878 func() { m.metrics.CurrentDKGPhase2FinalView(currentEpochSetup.DKGPhase2FinalView) }, 879 func() { m.metrics.CurrentDKGPhase3FinalView(currentEpochSetup.DKGPhase3FinalView) }, 880 ) 881 882 return 883 } 884 885 // epochPhaseMetricsAndEventsOnBlockFinalized determines metrics to update and protocol 886 // events to emit. Service Events embedded into an execution result take effect, when the 887 // execution result's _seal is finalized_ (i.e. when the block holding a seal for the 888 // result is finalized). See also handleEpochServiceEvents for further details. Example: 889 // 890 // Convention: 891 // 892 // A <-- ... <-- C(Seal_A) 893 // 894 // Suppose an EpochSetup service event is emitted during execution of block A. C seals A, therefore 895 // we apply the metrics/events when C is finalized. The first block of the EpochSetup 896 // phase is block C. 897 // 898 // This function should only be called when epoch fallback *has not already been triggered*. 899 // No errors are expected during normal operation. 900 func (m *FollowerState) epochPhaseMetricsAndEventsOnBlockFinalized(block *flow.Block, epochStatus *flow.EpochStatus) ( 901 metrics []func(), 902 events []func(), 903 err error, 904 ) { 905 906 // block payload may not specify seals in order, so order them by block height before processing 907 orderedSeals, err := protocol.OrderedSeals(block.Payload, m.headers) 908 if err != nil { 909 if errors.Is(err, storage.ErrNotFound) { 910 return nil, nil, fmt.Errorf("ordering seals: parent payload contains seals for unknown block: %s", err.Error()) 911 } 912 return nil, nil, fmt.Errorf("unexpected error ordering seals: %w", err) 913 } 914 915 // track service event driven metrics and protocol events that should be emitted 916 for _, seal := range orderedSeals { 917 result, err := m.results.ByID(seal.ResultID) 918 if err != nil { 919 return nil, nil, fmt.Errorf("could not retrieve result (id=%x) for seal (id=%x): %w", seal.ResultID, seal.ID(), err) 920 } 921 for _, event := range result.ServiceEvents { 922 switch ev := event.Event.(type) { 923 case *flow.EpochSetup: 924 // update current epoch phase 925 events = append(events, func() { m.metrics.CurrentEpochPhase(flow.EpochPhaseSetup) }) 926 // track epoch phase transition (staking->setup) 927 events = append(events, func() { m.consumer.EpochSetupPhaseStarted(ev.Counter-1, block.Header) }) 928 case *flow.EpochCommit: 929 // update current epoch phase 930 events = append(events, func() { m.metrics.CurrentEpochPhase(flow.EpochPhaseCommitted) }) 931 // track epoch phase transition (setup->committed) 932 events = append(events, func() { m.consumer.EpochCommittedPhaseStarted(ev.Counter-1, block.Header) }) 933 // track final view of committed epoch 934 nextEpochSetup, err := m.epoch.setups.ByID(epochStatus.NextEpoch.SetupID) 935 if err != nil { 936 return nil, nil, fmt.Errorf("could not retrieve setup event for next epoch: %w", err) 937 } 938 events = append(events, func() { m.metrics.CommittedEpochFinalView(nextEpochSetup.FinalView) }) 939 case *flow.VersionBeacon: 940 // do nothing for now 941 default: 942 return nil, nil, fmt.Errorf("invalid service event type in payload (%T)", ev) 943 } 944 } 945 } 946 947 return 948 } 949 950 // epochStatus computes the EpochStatus for the given block *before* applying 951 // any service event state changes which come into effect with this block. 952 // 953 // Specifically, we must determine whether block is the first block of a new 954 // epoch in its respective fork. We do this by comparing the block's view to 955 // the Epoch data from its parent. If the block's view is _larger_ than the 956 // final View of the parent's epoch, the block starts a new Epoch. 957 // 958 // Possible outcomes: 959 // 1. Block is in same Epoch as parent (block.View < epoch.FinalView) 960 // -> the parent's EpochStatus.CurrentEpoch also applies for the current block 961 // 2. Block enters the next Epoch (block.View ≥ epoch.FinalView) 962 // a) HAPPY PATH: Epoch fallback is not triggered, we enter the next epoch: 963 // -> the parent's EpochStatus.NextEpoch is the current block's EpochStatus.CurrentEpoch 964 // b) FALLBACK PATH: Epoch fallback is triggered, we continue the current epoch: 965 // -> the parent's EpochStatus.CurrentEpoch also applies for the current block 966 // 967 // As the parent was a valid extension of the chain, by induction, the parent 968 // satisfies all consistency requirements of the protocol. 969 // 970 // Returns the EpochStatus for the input block. 971 // No error returns are expected under normal operations 972 func (m *FollowerState) epochStatus(block *flow.Header, epochFallbackTriggered bool) (*flow.EpochStatus, error) { 973 parentStatus, err := m.epoch.statuses.ByBlockID(block.ParentID) 974 if err != nil { 975 return nil, fmt.Errorf("could not retrieve epoch state for parent: %w", err) 976 } 977 parentSetup, err := m.epoch.setups.ByID(parentStatus.CurrentEpoch.SetupID) 978 if err != nil { 979 return nil, fmt.Errorf("could not retrieve EpochSetup event for parent: %w", err) 980 } 981 982 // Case 1 or 2b (still in parent block's epoch or epoch fallback triggered): 983 if block.View <= parentSetup.FinalView || epochFallbackTriggered { 984 // IMPORTANT: copy the status to avoid modifying the parent status in the cache 985 return parentStatus.Copy(), nil 986 } 987 988 // Case 2a (first block of new epoch): 989 // sanity check: parent's epoch Preparation should be completed and have EpochSetup and EpochCommit events 990 if parentStatus.NextEpoch.SetupID == flow.ZeroID { 991 return nil, fmt.Errorf("missing setup event for starting next epoch") 992 } 993 if parentStatus.NextEpoch.CommitID == flow.ZeroID { 994 return nil, fmt.Errorf("missing commit event for starting next epoch") 995 } 996 epochStatus, err := flow.NewEpochStatus( 997 parentStatus.CurrentEpoch.SetupID, parentStatus.CurrentEpoch.CommitID, 998 parentStatus.NextEpoch.SetupID, parentStatus.NextEpoch.CommitID, 999 flow.ZeroID, flow.ZeroID, 1000 ) 1001 return epochStatus, err 1002 1003 } 1004 1005 // versionBeaconOnBlockFinalized extracts and returns the VersionBeacons from the 1006 // finalized block's seals. 1007 // This could return multiple VersionBeacons if the parent block contains multiple Seals. 1008 // The version beacons will be returned in the ascending height order of the seals. 1009 // Technically only the last VersionBeacon is relevant. 1010 func (m *FollowerState) versionBeaconOnBlockFinalized( 1011 finalized *flow.Block, 1012 ) ([]*flow.SealedVersionBeacon, error) { 1013 var versionBeacons []*flow.SealedVersionBeacon 1014 1015 seals, err := protocol.OrderedSeals(finalized.Payload, m.headers) 1016 if err != nil { 1017 if errors.Is(err, storage.ErrNotFound) { 1018 return nil, fmt.Errorf( 1019 "ordering seals: parent payload contains"+ 1020 " seals for unknown block: %w", err) 1021 } 1022 return nil, fmt.Errorf("unexpected error ordering seals: %w", err) 1023 } 1024 1025 for _, seal := range seals { 1026 result, err := m.results.ByID(seal.ResultID) 1027 if err != nil { 1028 return nil, fmt.Errorf( 1029 "could not retrieve result (id=%x) for seal (id=%x): %w", 1030 seal.ResultID, 1031 seal.ID(), 1032 err) 1033 } 1034 for _, event := range result.ServiceEvents { 1035 1036 ev, ok := event.Event.(*flow.VersionBeacon) 1037 1038 if !ok { 1039 // skip other service event types. 1040 // validation if this is a known service event type is done elsewhere. 1041 continue 1042 } 1043 1044 err := ev.Validate() 1045 if err != nil { 1046 m.logger.Warn(). 1047 Err(err). 1048 Str("block_id", finalized.ID().String()). 1049 Interface("event", ev). 1050 Msg("invalid VersionBeacon service event") 1051 continue 1052 } 1053 1054 // The version beacon only becomes actionable/valid/active once the block 1055 // containing the version beacon has been sealed. That is why we set the 1056 // Seal height to the current block height. 1057 versionBeacons = append(versionBeacons, &flow.SealedVersionBeacon{ 1058 VersionBeacon: ev, 1059 SealHeight: finalized.Header.Height, 1060 }) 1061 } 1062 } 1063 1064 return versionBeacons, nil 1065 } 1066 1067 // handleEpochServiceEvents handles applying state changes which occur as a result 1068 // of service events being included in a block payload: 1069 // - inserting incorporated service events 1070 // - updating EpochStatus for the candidate block 1071 // 1072 // Consider a chain where a service event is emitted during execution of block A. 1073 // Block B contains a receipt for A. Block C contains a seal for block A. 1074 // 1075 // A <- .. <- B(RA) <- .. <- C(SA) 1076 // 1077 // Service events are included within execution results, which are stored 1078 // opaquely as part of the block payload in block B. We only validate and insert 1079 // the typed service event to storage once we process C, the block containing the 1080 // seal for block A. This is because we rely on the sealing subsystem to validate 1081 // correctness of the service event before processing it. 1082 // Consequently, any change to the protocol state introduced by a service event 1083 // emitted during execution of block A would only become visible when querying 1084 // C or its descendants. 1085 // 1086 // This method will only apply service-event-induced state changes when the 1087 // input block has the form of block C (ie. contains a seal for a block in 1088 // which a service event was emitted). 1089 // 1090 // Return values: 1091 // - dbUpdates - If the service events are valid, or there are no service events, 1092 // this method returns a slice of Badger operations to apply while storing the block. 1093 // This includes an operation to index the epoch status for every block, and 1094 // operations to insert service events for blocks that include them. 1095 // 1096 // No errors are expected during normal operation. 1097 func (m *FollowerState) handleEpochServiceEvents(candidate *flow.Block) (dbUpdates []func(*transaction.Tx) error, err error) { 1098 epochFallbackTriggered, err := m.isEpochEmergencyFallbackTriggered() 1099 if err != nil { 1100 return nil, fmt.Errorf("could not retrieve epoch fallback status: %w", err) 1101 } 1102 epochStatus, err := m.epochStatus(candidate.Header, epochFallbackTriggered) 1103 if err != nil { 1104 return nil, fmt.Errorf("could not determine epoch status for candidate block: %w", err) 1105 } 1106 activeSetup, err := m.epoch.setups.ByID(epochStatus.CurrentEpoch.SetupID) 1107 if err != nil { 1108 return nil, fmt.Errorf("could not retrieve current epoch setup event: %w", err) 1109 } 1110 1111 // always persist the candidate's epoch status 1112 // note: We are scheduling the operation to store the Epoch status using the _pointer_ variable `epochStatus`. 1113 // The struct `epochStatus` points to will still be modified below. 1114 blockID := candidate.ID() 1115 dbUpdates = append(dbUpdates, m.epoch.statuses.StoreTx(blockID, epochStatus)) 1116 1117 // never process service events after epoch fallback is triggered 1118 if epochStatus.InvalidServiceEventIncorporated || epochFallbackTriggered { 1119 return dbUpdates, nil 1120 } 1121 1122 // We apply service events from blocks which are sealed by this candidate block. 1123 // The block's payload might contain epoch preparation service events for the next 1124 // epoch. In this case, we need to update the tentative protocol state. 1125 // We need to validate whether all information is available in the protocol 1126 // state to go to the next epoch when needed. In cases where there is a bug 1127 // in the smart contract, it could be that this happens too late and the 1128 // chain finalization should halt. 1129 1130 // block payload may not specify seals in order, so order them by block height before processing 1131 orderedSeals, err := protocol.OrderedSeals(candidate.Payload, m.headers) 1132 if err != nil { 1133 if errors.Is(err, storage.ErrNotFound) { 1134 return nil, fmt.Errorf("ordering seals: parent payload contains seals for unknown block: %s", err.Error()) 1135 } 1136 return nil, fmt.Errorf("unexpected error ordering seals: %w", err) 1137 } 1138 for _, seal := range orderedSeals { 1139 result, err := m.results.ByID(seal.ResultID) 1140 if err != nil { 1141 return nil, fmt.Errorf("could not get result (id=%x) for seal (id=%x): %w", seal.ResultID, seal.ID(), err) 1142 } 1143 1144 for _, event := range result.ServiceEvents { 1145 1146 switch ev := event.Event.(type) { 1147 case *flow.EpochSetup: 1148 // validate the service event 1149 err := isValidExtendingEpochSetup(ev, activeSetup, epochStatus) 1150 if err != nil { 1151 if protocol.IsInvalidServiceEventError(err) { 1152 // we have observed an invalid service event, which triggers epoch fallback mode 1153 epochStatus.InvalidServiceEventIncorporated = true 1154 return dbUpdates, nil 1155 } 1156 return nil, fmt.Errorf("unexpected error validating EpochSetup service event: %w", err) 1157 } 1158 1159 // prevents multiple setup events for same Epoch (including multiple setup events in payload of same block) 1160 epochStatus.NextEpoch.SetupID = ev.ID() 1161 1162 // we'll insert the setup event when we insert the block 1163 dbUpdates = append(dbUpdates, m.epoch.setups.StoreTx(ev)) 1164 1165 case *flow.EpochCommit: 1166 // if we receive an EpochCommit event, we must have already observed an EpochSetup event 1167 // => otherwise, we have observed an EpochCommit without corresponding EpochSetup, which triggers epoch fallback mode 1168 if epochStatus.NextEpoch.SetupID == flow.ZeroID { 1169 epochStatus.InvalidServiceEventIncorporated = true 1170 return dbUpdates, nil 1171 } 1172 1173 // if we have observed an EpochSetup event, we must be able to retrieve it from the database 1174 // => otherwise, this is a symptom of bug or data corruption since this component sets the SetupID field 1175 extendingSetup, err := m.epoch.setups.ByID(epochStatus.NextEpoch.SetupID) 1176 if err != nil { 1177 if errors.Is(err, storage.ErrNotFound) { 1178 return nil, irrecoverable.NewExceptionf("could not retrieve EpochSetup (id=%x) stored in EpochStatus for block %x: %w", 1179 epochStatus.NextEpoch.SetupID, blockID, err) 1180 } 1181 return nil, fmt.Errorf("unexpected error retrieving next epoch setup: %w", err) 1182 } 1183 1184 // validate the service event 1185 err = isValidExtendingEpochCommit(ev, extendingSetup, activeSetup, epochStatus) 1186 if err != nil { 1187 if protocol.IsInvalidServiceEventError(err) { 1188 // we have observed an invalid service event, which triggers epoch fallback mode 1189 epochStatus.InvalidServiceEventIncorporated = true 1190 return dbUpdates, nil 1191 } 1192 return nil, fmt.Errorf("unexpected error validating EpochCommit service event: %w", err) 1193 } 1194 1195 // prevents multiple setup events for same Epoch (including multiple setup events in payload of same block) 1196 epochStatus.NextEpoch.CommitID = ev.ID() 1197 1198 // we'll insert the commit event when we insert the block 1199 dbUpdates = append(dbUpdates, m.epoch.commits.StoreTx(ev)) 1200 case *flow.VersionBeacon: 1201 // do nothing for now 1202 default: 1203 return nil, fmt.Errorf("invalid service event type (type_name=%s, go_type=%T)", event.Type, ev) 1204 } 1205 } 1206 } 1207 return 1208 }