github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/state/protocol/badger/mutator.go (about)

     1  package badger
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/dgraph-io/badger/v2"
     9  	"github.com/rs/zerolog"
    10  
    11  	"github.com/onflow/flow-go/engine"
    12  	"github.com/onflow/flow-go/model/flow"
    13  	"github.com/onflow/flow-go/module"
    14  	"github.com/onflow/flow-go/module/irrecoverable"
    15  	"github.com/onflow/flow-go/module/signature"
    16  	"github.com/onflow/flow-go/module/trace"
    17  	"github.com/onflow/flow-go/state"
    18  	"github.com/onflow/flow-go/state/protocol"
    19  	"github.com/onflow/flow-go/storage"
    20  	"github.com/onflow/flow-go/storage/badger/operation"
    21  	"github.com/onflow/flow-go/storage/badger/procedure"
    22  	"github.com/onflow/flow-go/storage/badger/transaction"
    23  )
    24  
    25  // FollowerState implements a lighter version of a mutable protocol state.
    26  // When extending the state, it performs hardly any checks on the block payload.
    27  // Instead, the FollowerState relies on the consensus nodes to run the full
    28  // payload check and uses quorum certificates to prove validity of block payloads.
    29  // Consequently, a block B should only be considered valid, if
    30  // there is a certifying QC for that block QC.View == Block.View && QC.BlockID == Block.ID().
    31  //
    32  // The FollowerState allows non-consensus nodes to execute fork-aware queries
    33  // against the protocol state, while minimizing the amount of payload checks
    34  // the non-consensus nodes have to perform.
    35  type FollowerState struct {
    36  	*State
    37  
    38  	index      storage.Index
    39  	payloads   storage.Payloads
    40  	tracer     module.Tracer
    41  	logger     zerolog.Logger
    42  	consumer   protocol.Consumer
    43  	blockTimer protocol.BlockTimer
    44  }
    45  
    46  var _ protocol.FollowerState = (*FollowerState)(nil)
    47  
    48  // ParticipantState implements a mutable state for consensus participant. It can extend the
    49  // state with a new block, by checking the _entire_ block payload.
    50  type ParticipantState struct {
    51  	*FollowerState
    52  	receiptValidator module.ReceiptValidator
    53  	sealValidator    module.SealValidator
    54  }
    55  
    56  var _ protocol.ParticipantState = (*ParticipantState)(nil)
    57  
    58  // NewFollowerState initializes a light-weight version of a mutable protocol
    59  // state. This implementation is suitable only for NON-Consensus nodes.
    60  func NewFollowerState(
    61  	logger zerolog.Logger,
    62  	tracer module.Tracer,
    63  	consumer protocol.Consumer,
    64  	state *State,
    65  	index storage.Index,
    66  	payloads storage.Payloads,
    67  	blockTimer protocol.BlockTimer,
    68  ) (*FollowerState, error) {
    69  	followerState := &FollowerState{
    70  		State:      state,
    71  		index:      index,
    72  		payloads:   payloads,
    73  		tracer:     tracer,
    74  		logger:     logger,
    75  		consumer:   consumer,
    76  		blockTimer: blockTimer,
    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  	logger zerolog.Logger,
    87  	tracer module.Tracer,
    88  	consumer protocol.Consumer,
    89  	state *State,
    90  	index storage.Index,
    91  	payloads storage.Payloads,
    92  	blockTimer protocol.BlockTimer,
    93  	receiptValidator module.ReceiptValidator,
    94  	sealValidator module.SealValidator,
    95  ) (*ParticipantState, error) {
    96  	followerState, err := NewFollowerState(
    97  		logger,
    98  		tracer,
    99  		consumer,
   100  		state,
   101  		index,
   102  		payloads,
   103  		blockTimer,
   104  	)
   105  	if err != nil {
   106  		return nil, fmt.Errorf("initialization of Mutable Follower State failed: %w", err)
   107  	}
   108  	return &ParticipantState{
   109  		FollowerState:    followerState,
   110  		receiptValidator: receiptValidator,
   111  		sealValidator:    sealValidator,
   112  	}, nil
   113  }
   114  
   115  // ExtendCertified extends the protocol state of a CONSENSUS FOLLOWER. While it checks
   116  // the validity of the header; it does _not_ check the validity of the payload.
   117  // Instead, the consensus follower relies on the consensus participants to
   118  // validate the full payload. Payload validity can be proved by a valid quorum certificate.
   119  // Certifying QC must match candidate block:
   120  //
   121  //	candidate.View == certifyingQC.View && candidate.ID() == certifyingQC.BlockID
   122  //
   123  // Caution:
   124  //   - This function expects that `certifyingQC` has been validated.
   125  //   - The parent block must already be stored.
   126  //
   127  // No errors are expected during normal operations.
   128  func (m *FollowerState) ExtendCertified(ctx context.Context, candidate *flow.Block, certifyingQC *flow.QuorumCertificate) error {
   129  	span, ctx := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorHeaderExtend)
   130  	defer span.End()
   131  
   132  	// check if candidate block has been already processed
   133  	blockID := candidate.ID()
   134  	isDuplicate, err := m.checkBlockAlreadyProcessed(blockID)
   135  	if err != nil || isDuplicate {
   136  		return err
   137  	}
   138  	deferredDbOps := transaction.NewDeferredDbOps()
   139  
   140  	// sanity check if certifyingQC actually certifies candidate block
   141  	if certifyingQC.View != candidate.Header.View {
   142  		return fmt.Errorf("qc doesn't certify candidate block, expect %d view, got %d", candidate.Header.View, certifyingQC.View)
   143  	}
   144  	if certifyingQC.BlockID != blockID {
   145  		return fmt.Errorf("qc doesn't certify candidate block, expect %x blockID, got %x", blockID, certifyingQC.BlockID)
   146  	}
   147  
   148  	// check if the block header is a valid extension of parent block
   149  	err = m.headerExtend(ctx, candidate, certifyingQC, deferredDbOps)
   150  	if err != nil {
   151  		// since we have a QC for this block, it cannot be an invalid extension
   152  		return fmt.Errorf("unexpected invalid block (id=%x) with certifying qc (id=%x): %s",
   153  			candidate.ID(), certifyingQC.ID(), err.Error())
   154  	}
   155  
   156  	// find the last seal at the parent block
   157  	_, err = m.lastSealed(candidate, deferredDbOps)
   158  	if err != nil {
   159  		return fmt.Errorf("failed to determine the lastest sealed block in fork: %w", err)
   160  	}
   161  
   162  	// evolve protocol state and verify consistency with commitment included in
   163  	err = m.evolveProtocolState(ctx, candidate, deferredDbOps)
   164  	if err != nil {
   165  		return fmt.Errorf("evolving protocol state failed: %w", err)
   166  	}
   167  
   168  	// Execute the deferred database operations as one atomic transaction and emit scheduled notifications on success.
   169  	// The `candidate` block _must be valid_ (otherwise, the state will be corrupted)!
   170  	err = operation.RetryOnConflictTx(m.db, transaction.Update, deferredDbOps.Pending()) // No errors are expected during normal operations
   171  	if err != nil {
   172  		return fmt.Errorf("failed to persist candidate block %v and its dependencies: %w", blockID, err)
   173  	}
   174  
   175  	return nil
   176  }
   177  
   178  // Extend extends the protocol state of a CONSENSUS PARTICIPANT. It checks
   179  // the validity of the _entire block_ (header and full payload).
   180  // Expected errors during normal operations:
   181  //   - state.OutdatedExtensionError if the candidate block is outdated (e.g. orphaned)
   182  //   - state.InvalidExtensionError if the candidate block is invalid
   183  func (m *ParticipantState) Extend(ctx context.Context, candidate *flow.Block) error {
   184  	span, ctx := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtend)
   185  	defer span.End()
   186  
   187  	// check if candidate block has been already processed
   188  	isDuplicate, err := m.checkBlockAlreadyProcessed(candidate.ID())
   189  	if err != nil || isDuplicate {
   190  		return err
   191  	}
   192  	deferredDbOps := transaction.NewDeferredDbOps()
   193  
   194  	// check if the block header is a valid extension of parent block
   195  	err = m.headerExtend(ctx, candidate, nil, deferredDbOps)
   196  	if err != nil {
   197  		return fmt.Errorf("header not compliant with chain state: %w", err)
   198  	}
   199  
   200  	// check if the block header is a valid extension of the finalized state
   201  	err = m.checkOutdatedExtension(candidate.Header)
   202  	if err != nil {
   203  		if state.IsOutdatedExtensionError(err) {
   204  			return fmt.Errorf("candidate block is an outdated extension: %w", err)
   205  		}
   206  		return fmt.Errorf("could not check if block is an outdated extension: %w", err)
   207  	}
   208  
   209  	// check if the guarantees in the payload is a valid extension of the finalized state
   210  	err = m.guaranteeExtend(ctx, candidate)
   211  	if err != nil {
   212  		return fmt.Errorf("payload guarantee(s) not compliant with chain state: %w", err)
   213  	}
   214  
   215  	// check if the receipts in the payload are valid
   216  	err = m.receiptExtend(ctx, candidate)
   217  	if err != nil {
   218  		return fmt.Errorf("payload receipt(s) not compliant with chain state: %w", err)
   219  	}
   220  
   221  	// check if the seals in the payload is a valid extension of the finalized state
   222  	_, err = m.sealExtend(ctx, candidate, deferredDbOps)
   223  	if err != nil {
   224  		return fmt.Errorf("payload seal(s) not compliant with chain state: %w", err)
   225  	}
   226  
   227  	// evolve protocol state and verify consistency with commitment included in payload
   228  	err = m.evolveProtocolState(ctx, candidate, deferredDbOps)
   229  	if err != nil {
   230  		return fmt.Errorf("evolving protocol state failed: %w", err)
   231  	}
   232  
   233  	// Execute the deferred database operations and emit scheduled notifications on success.
   234  	// The `candidate` block _must be valid_ (otherwise, the state will be corrupted)!
   235  	err = operation.RetryOnConflictTx(m.db, transaction.Update, deferredDbOps.Pending()) // No errors are expected during normal operations
   236  	if err != nil {
   237  		return fmt.Errorf("failed to persist candiate block %v and its dependencies: %w", candidate.ID(), err)
   238  	}
   239  	return nil
   240  }
   241  
   242  // headerExtend verifies the validity of the block header (excluding verification of the
   243  // consensus rules). Specifically, we check that
   244  //  1. the payload is consistent with the payload hash stated in the header
   245  //  2. candidate header is consistent with its parent:
   246  //     - ChainID is identical
   247  //     - height increases by 1
   248  //     - ParentView stated by the candidate block equals the parent's actual view
   249  //  3. candidate's block time conforms to protocol rules
   250  //  4. If a `certifyingQC` is given (can be nil), we sanity-check that it certifies the candidate block
   251  //
   252  // If all checks pass, this method queues the following operations to persist the candidate block and
   253  // schedules `BlockProcessable` notification to be emitted in order of increasing height:
   254  //
   255  //	5a. store QC embedded into the candidate block and emit `BlockProcessable` notification for the parent
   256  //	5b. store candidate block and index it as a child of its parent (needed for recovery to traverse unfinalized blocks)
   257  //	5c. if we are given a certifyingQC, store it and queue a `BlockProcessable` notification for the candidate block
   258  //
   259  // If `headerExtend` is called by `ParticipantState.Extend` (full consensus participant) then `certifyingQC` will be nil,
   260  // but the block payload will be validated. If `headerExtend` is called by `FollowerState.Extend` (consensus follower),
   261  // then `certifyingQC` must be not nil which proves payload validity.
   262  //
   263  // Expected errors during normal operations:
   264  //   - state.InvalidExtensionError if the candidate block is invalid
   265  func (m *FollowerState) headerExtend(ctx context.Context, candidate *flow.Block, certifyingQC *flow.QuorumCertificate, deferredDbOps *transaction.DeferredDbOps) error {
   266  	span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendCheckHeader)
   267  	defer span.End()
   268  	blockID := candidate.ID()
   269  	header := candidate.Header
   270  
   271  	// STEP 1: Check that the payload is consistent with the payload hash in the header
   272  	if candidate.Payload.Hash() != header.PayloadHash {
   273  		return state.NewInvalidExtensionError("payload integrity check failed")
   274  	}
   275  
   276  	// STEP 2: Next, we can check whether the block is a valid descendant of the
   277  	// parent. It should have the same chain ID and a height that is one bigger.
   278  	parent, err := m.headers.ByBlockID(header.ParentID)
   279  	if err != nil {
   280  		return state.NewInvalidExtensionErrorf("could not retrieve parent: %s", err)
   281  	}
   282  	if header.ChainID != parent.ChainID {
   283  		return state.NewInvalidExtensionErrorf("candidate built for invalid chain (candidate: %s, parent: %s)",
   284  			header.ChainID, parent.ChainID)
   285  	}
   286  	if header.ParentView != parent.View {
   287  		return state.NewInvalidExtensionErrorf("candidate build with inconsistent parent view (candidate: %d, parent %d)",
   288  			header.ParentView, parent.View)
   289  	}
   290  	if header.Height != parent.Height+1 {
   291  		return state.NewInvalidExtensionErrorf("candidate built with invalid height (candidate: %d, parent: %d)",
   292  			header.Height, parent.Height)
   293  	}
   294  
   295  	// STEP 3: check validity of block timestamp using parent's timestamp
   296  	err = m.blockTimer.Validate(parent.Timestamp, header.Timestamp)
   297  	if err != nil {
   298  		if protocol.IsInvalidBlockTimestampError(err) {
   299  			return state.NewInvalidExtensionErrorf("candidate contains invalid timestamp: %w", err)
   300  		}
   301  		return fmt.Errorf("validating block's time stamp failed with unexpected error: %w", err)
   302  	}
   303  
   304  	// STEP 4: if a certifying QC is given (can be nil), sanity-check that it actually certifies the candidate block
   305  	if certifyingQC != nil {
   306  		if certifyingQC.View != header.View {
   307  			return fmt.Errorf("qc doesn't certify candidate block, expect %d view, got %d", header.View, certifyingQC.View)
   308  		}
   309  		if certifyingQC.BlockID != blockID {
   310  			return fmt.Errorf("qc doesn't certify candidate block, expect %x blockID, got %x", blockID, certifyingQC.BlockID)
   311  		}
   312  	}
   313  
   314  	// STEP 5:
   315  	qc := candidate.Header.QuorumCertificate()
   316  	deferredDbOps.AddDbOp(func(tx *transaction.Tx) error {
   317  		// STEP 5a: Store QC for parent block and emit `BlockProcessable` notification if and only if
   318  		//  - the QC for the parent has not been stored before (otherwise, we already emitted the notification) and
   319  		//  - the parent block's height is larger than the finalized root height (the root block is already considered processed)
   320  		// Thereby, we reduce duplicated `BlockProcessable` notifications.
   321  		err := m.qcs.StoreTx(qc)(tx)
   322  		if err != nil {
   323  			if !errors.Is(err, storage.ErrAlreadyExists) {
   324  				return fmt.Errorf("could not store incorporated qc: %w", err)
   325  			}
   326  		} else {
   327  			// trigger BlockProcessable for parent block above root height
   328  			if parent.Height > m.finalizedRootHeight {
   329  				tx.OnSucceed(func() {
   330  					m.consumer.BlockProcessable(parent, qc)
   331  				})
   332  			}
   333  		}
   334  
   335  		// STEP 5b: Store candidate block and index it as a child of its parent (needed for recovery to traverse unfinalized blocks)
   336  		err = m.blocks.StoreTx(candidate)(tx) // insert the block into the database AND cache
   337  		if err != nil {
   338  			return fmt.Errorf("could not store candidate block: %w", err)
   339  		}
   340  		err = transaction.WithTx(procedure.IndexNewBlock(blockID, candidate.Header.ParentID))(tx)
   341  		if err != nil {
   342  			return fmt.Errorf("could not index new block: %w", err)
   343  		}
   344  
   345  		// STEP 5c: if we are given a certifyingQC, store it and queue a `BlockProcessable` notification for the candidate block
   346  		if certifyingQC != nil {
   347  			err = m.qcs.StoreTx(certifyingQC)(tx)
   348  			if err != nil {
   349  				return fmt.Errorf("could not store certifying qc: %w", err)
   350  			}
   351  			tx.OnSucceed(func() { // queue a BlockProcessable event for candidate block, since it is certified
   352  				m.consumer.BlockProcessable(candidate.Header, certifyingQC)
   353  			})
   354  		}
   355  		return nil
   356  	})
   357  
   358  	return nil
   359  }
   360  
   361  // checkBlockAlreadyProcessed checks if block has been added to the protocol state.
   362  // Returns:
   363  // * (true, nil) - block has been already processed.
   364  // * (false, nil) - block has not been processed.
   365  // * (false, error) - unknown error when trying to query protocol state.
   366  // No errors are expected during normal operation.
   367  func (m *FollowerState) checkBlockAlreadyProcessed(blockID flow.Identifier) (bool, error) {
   368  	_, err := m.headers.ByBlockID(blockID)
   369  	if err != nil {
   370  		if errors.Is(err, storage.ErrNotFound) {
   371  			return false, nil
   372  		}
   373  		return false, fmt.Errorf("could not check if candidate block (%x) has been already processed: %w", blockID, err)
   374  	}
   375  	return true, nil
   376  }
   377  
   378  // checkOutdatedExtension checks whether given block is
   379  // valid in the context of the entire state. For this, the block needs to
   380  // directly connect, through its ancestors, to the last finalized block.
   381  // Expected errors during normal operations:
   382  //   - state.OutdatedExtensionError if the candidate block is outdated (e.g. orphaned)
   383  func (m *ParticipantState) checkOutdatedExtension(header *flow.Header) error {
   384  	var finalizedHeight uint64
   385  	err := m.db.View(operation.RetrieveFinalizedHeight(&finalizedHeight))
   386  	if err != nil {
   387  		return fmt.Errorf("could not retrieve finalized height: %w", err)
   388  	}
   389  	var finalID flow.Identifier
   390  	err = m.db.View(operation.LookupBlockHeight(finalizedHeight, &finalID))
   391  	if err != nil {
   392  		return fmt.Errorf("could not lookup finalized block: %w", err)
   393  	}
   394  
   395  	ancestorID := header.ParentID
   396  	for ancestorID != finalID {
   397  		ancestor, err := m.headers.ByBlockID(ancestorID)
   398  		if err != nil {
   399  			return fmt.Errorf("could not retrieve ancestor (%x): %w", ancestorID, err)
   400  		}
   401  		if ancestor.Height < finalizedHeight {
   402  			// this happens when the candidate block is on a fork that does not include all the
   403  			// finalized blocks.
   404  			// for instance:
   405  			// A (Finalized) <- B (Finalized) <- C (Finalized) <- D <- E <- F
   406  			//                  ^- G             ^- H             ^- I
   407  			// block G is not a valid block, because it does not have C (which has been finalized) as an ancestor
   408  			// block H and I are valid, because they do have C as an ancestor
   409  			return state.NewOutdatedExtensionErrorf(
   410  				"candidate block (height: %d) conflicts with finalized state (ancestor: %d final: %d)",
   411  				header.Height, ancestor.Height, finalizedHeight)
   412  		}
   413  		ancestorID = ancestor.ParentID
   414  	}
   415  	return nil
   416  }
   417  
   418  // guaranteeExtend verifies the validity of the collection guarantees that are
   419  // included in the block. Specifically, we check for expired collections and
   420  // duplicated collections (also including ancestor blocks).
   421  // Expected errors during normal operations:
   422  //   - state.InvalidExtensionError if the candidate block contains invalid collection guarantees
   423  func (m *ParticipantState) guaranteeExtend(ctx context.Context, candidate *flow.Block) error {
   424  	span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendCheckGuarantees)
   425  	defer span.End()
   426  
   427  	header := candidate.Header
   428  	payload := candidate.Payload
   429  
   430  	// we only look as far back for duplicates as the transaction expiry limit;
   431  	// if a guarantee was included before that, we will disqualify it on the
   432  	// basis of the reference block anyway
   433  	limit := header.Height - flow.DefaultTransactionExpiry
   434  	if limit > header.Height { // overflow check
   435  		limit = 0
   436  	}
   437  	if limit < m.sporkRootBlockHeight {
   438  		limit = m.sporkRootBlockHeight
   439  	}
   440  
   441  	// build a list of all previously used guarantees on this part of the chain
   442  	ancestorID := header.ParentID
   443  	lookup := make(map[flow.Identifier]struct{})
   444  	for {
   445  		ancestor, err := m.headers.ByBlockID(ancestorID)
   446  		if err != nil {
   447  			return fmt.Errorf("could not retrieve ancestor header (%x): %w", ancestorID, err)
   448  		}
   449  		index, err := m.index.ByBlockID(ancestorID)
   450  		if err != nil {
   451  			return fmt.Errorf("could not retrieve ancestor index (%x): %w", ancestorID, err)
   452  		}
   453  		for _, collID := range index.CollectionIDs {
   454  			lookup[collID] = struct{}{}
   455  		}
   456  		if ancestor.Height <= limit {
   457  			break
   458  		}
   459  		ancestorID = ancestor.ParentID
   460  	}
   461  
   462  	// check each guarantee included in the payload for duplication and expiry
   463  	for _, guarantee := range payload.Guarantees {
   464  
   465  		// if the guarantee was already included before, error
   466  		_, duplicated := lookup[guarantee.ID()]
   467  		if duplicated {
   468  			return state.NewInvalidExtensionErrorf("payload includes duplicate guarantee (%x)", guarantee.ID())
   469  		}
   470  
   471  		// get the reference block to check expiry
   472  		ref, err := m.headers.ByBlockID(guarantee.ReferenceBlockID)
   473  		if err != nil {
   474  			if errors.Is(err, storage.ErrNotFound) {
   475  				return state.NewInvalidExtensionErrorf("could not get reference block %x: %w", guarantee.ReferenceBlockID, err)
   476  			}
   477  			return fmt.Errorf("could not get reference block (%x): %w", guarantee.ReferenceBlockID, err)
   478  		}
   479  
   480  		// if the guarantee references a block with expired height, error
   481  		if ref.Height < limit {
   482  			return state.NewInvalidExtensionErrorf("payload includes expired guarantee (height: %d, limit: %d)",
   483  				ref.Height, limit)
   484  		}
   485  
   486  		// check the guarantors are correct
   487  		_, err = protocol.FindGuarantors(m, guarantee)
   488  		if err != nil {
   489  			if signature.IsInvalidSignerIndicesError(err) ||
   490  				errors.Is(err, protocol.ErrNextEpochNotCommitted) ||
   491  				errors.Is(err, protocol.ErrClusterNotFound) {
   492  				return state.NewInvalidExtensionErrorf("guarantee %v contains invalid guarantors: %w", guarantee.ID(), err)
   493  			}
   494  			return fmt.Errorf("could not find guarantor for guarantee %v: %w", guarantee.ID(), err)
   495  		}
   496  	}
   497  
   498  	return nil
   499  }
   500  
   501  // sealExtend checks the compliance of the payload seals. It queues a deferred database
   502  // operation for indexing the latest seal as of the candidate block and returns the latest seal.
   503  // Expected errors during normal operations:
   504  //   - state.InvalidExtensionError if the candidate block has invalid seals
   505  func (m *ParticipantState) sealExtend(ctx context.Context, candidate *flow.Block, deferredDbOps *transaction.DeferredDbOps) (*flow.Seal, error) {
   506  	span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendCheckSeals)
   507  	defer span.End()
   508  
   509  	lastSeal, err := m.sealValidator.Validate(candidate)
   510  	if err != nil {
   511  		return nil, state.NewInvalidExtensionErrorf("seal validation error: %w", err)
   512  	}
   513  
   514  	deferredDbOps.AddBadgerOp(operation.IndexLatestSealAtBlock(candidate.ID(), lastSeal.ID()))
   515  	return lastSeal, nil
   516  }
   517  
   518  // receiptExtend checks the compliance of the receipt payload.
   519  //   - Receipts should pertain to blocks on the fork
   520  //   - Receipts should not appear more than once on a fork
   521  //   - Receipts should pass the ReceiptValidator check
   522  //   - No seal has been included for the respective block in this particular fork
   523  //
   524  // We require the receipts to be sorted by block height (within a payload).
   525  //
   526  // Expected errors during normal operations:
   527  //   - state.InvalidExtensionError if the candidate block contains invalid receipts
   528  func (m *ParticipantState) receiptExtend(ctx context.Context, candidate *flow.Block) error {
   529  	span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorExtendCheckReceipts)
   530  	defer span.End()
   531  
   532  	err := m.receiptValidator.ValidatePayload(candidate)
   533  	if err != nil {
   534  		// TODO: this might be not an error, potentially it can be solved by requesting more data and processing this receipt again
   535  		if errors.Is(err, storage.ErrNotFound) {
   536  			return state.NewInvalidExtensionErrorf("some entities referenced by receipts are missing: %w", err)
   537  		}
   538  		if engine.IsInvalidInputError(err) {
   539  			return state.NewInvalidExtensionErrorf("payload includes invalid receipts: %w", err)
   540  		}
   541  		return fmt.Errorf("unexpected payload validation error %w", err)
   542  	}
   543  
   544  	return nil
   545  }
   546  
   547  // lastSealed determines the highest sealed block from the fork with head `candidate`.
   548  // It queues a deferred database operation for indexing the latest seal as of the candidate block.
   549  // and returns the latest seal.
   550  //
   551  // For instance, here is the chain state: block 100 is the head, block 97 is finalized,
   552  // and 95 is the last sealed block at the state of block 100.
   553  // 95 (sealed) <- 96 <- 97 (finalized) <- 98 <- 99 <- 100
   554  // Now, if block 101 is extending block 100, and its payload has a seal for 96, then it will
   555  // be the last sealed for block 101.
   556  // No errors are expected during normal operation.
   557  func (m *FollowerState) lastSealed(candidate *flow.Block, deferredDbOps *transaction.DeferredDbOps) (latestSeal *flow.Seal, err error) {
   558  	payload := candidate.Payload
   559  	blockID := candidate.ID()
   560  
   561  	// If the candidate blocks' payload has no seals, the latest seal in this fork remains unchanged, i.e. latest seal as of the
   562  	// parent is also the latest seal as of the candidate block. Otherwise, we take the latest seal included in the candidate block.
   563  	// Note that seals might not be ordered in the block.
   564  	if len(payload.Seals) == 0 {
   565  		latestSeal, err = m.seals.HighestInFork(candidate.Header.ParentID)
   566  		if err != nil {
   567  			return nil, fmt.Errorf("could not retrieve parent seal (%x): %w", candidate.Header.ParentID, err)
   568  		}
   569  	} else {
   570  		ordered, err := protocol.OrderedSeals(payload.Seals, m.headers)
   571  		if err != nil {
   572  			// all errors are unexpected - differentiation is for clearer error messages
   573  			if errors.Is(err, storage.ErrNotFound) {
   574  				return nil, irrecoverable.NewExceptionf("ordering seals: candidate payload contains seals for unknown block: %w", err)
   575  			}
   576  			if errors.Is(err, protocol.ErrDiscontinuousSeals) || errors.Is(err, protocol.ErrMultipleSealsForSameHeight) {
   577  				return nil, irrecoverable.NewExceptionf("ordering seals: candidate payload contains invalid seal set: %w", err)
   578  			}
   579  			return nil, fmt.Errorf("unexpected error ordering seals: %w", err)
   580  		}
   581  		latestSeal = ordered[len(ordered)-1]
   582  	}
   583  
   584  	deferredDbOps.AddBadgerOp(operation.IndexLatestSealAtBlock(blockID, latestSeal.ID()))
   585  	return latestSeal, nil
   586  }
   587  
   588  // evolveProtocolState
   589  //   - instantiates a Protocol State Mutator from the parent block's state
   590  //   - applies any state-changing service events sealed by this block
   591  //   - verifies that the resulting protocol state is consistent with the commitment in the block
   592  //
   593  // Expected errors during normal operations:
   594  //   - state.InvalidExtensionError if the Protocol State commitment in the candidate block does
   595  //     not match the Protocol State we constructed locally
   596  func (m *FollowerState) evolveProtocolState(ctx context.Context, candidate *flow.Block, deferredDbOps *transaction.DeferredDbOps) error {
   597  	span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorEvolveProtocolState)
   598  	defer span.End()
   599  
   600  	// Evolve the Protocol State starting from the parent block's state. Information that may change the state is:
   601  	// the candidate block's view and Service Events from execution results sealed in the candidate block.
   602  	updatedStateID, dbUpdates, err := m.protocolState.EvolveState(candidate.Header.ParentID, candidate.Header.View, candidate.Payload.Seals)
   603  	if err != nil {
   604  		return fmt.Errorf("evolving protocol state failed: %w", err)
   605  	}
   606  
   607  	// verify Protocol State commitment in the candidate block matches the locally-constructed value
   608  	if updatedStateID != candidate.Payload.ProtocolStateID {
   609  		return state.NewInvalidExtensionErrorf("invalid protocol state commitment %x in block, which should be %x", candidate.Payload.ProtocolStateID, updatedStateID)
   610  	}
   611  	deferredDbOps.AddDbOps(dbUpdates.Pending().WithBlock(candidate.ID()))
   612  	return nil
   613  }
   614  
   615  // Finalize marks the specified block as finalized.
   616  // This method only finalizes one block at a time.
   617  // Hence, the parent of `blockID` has to be the last finalized block.
   618  // No errors are expected during normal operations.
   619  func (m *FollowerState) Finalize(ctx context.Context, blockID flow.Identifier) error {
   620  	// preliminaries: start tracer and retrieve full block
   621  	span, _ := m.tracer.StartSpanFromContext(ctx, trace.ProtoStateMutatorFinalize)
   622  	defer span.End()
   623  	block, err := m.blocks.ByID(blockID)
   624  	if err != nil {
   625  		return fmt.Errorf("could not retrieve full block that should be finalized: %w", err)
   626  	}
   627  	header := block.Header
   628  
   629  	// keep track of metrics updates and protocol events to emit:
   630  	//  - metrics are updated after a successful database update
   631  	//  - protocol events are emitted atomically with the database update
   632  	var metrics []func()
   633  	var events []func()
   634  
   635  	// Verify that the parent block is the latest finalized block.
   636  	// this must be the case, as the `Finalize` method only finalizes one block
   637  	// at a time and hence the parent of `blockID` must already be finalized.
   638  	var finalized uint64
   639  	err = m.db.View(operation.RetrieveFinalizedHeight(&finalized))
   640  	if err != nil {
   641  		return fmt.Errorf("could not retrieve finalized height: %w", err)
   642  	}
   643  	var finalID flow.Identifier
   644  	err = m.db.View(operation.LookupBlockHeight(finalized, &finalID))
   645  	if err != nil {
   646  		return fmt.Errorf("could not retrieve final header: %w", err)
   647  	}
   648  	if header.ParentID != finalID {
   649  		return fmt.Errorf("can only finalize child of last finalized block")
   650  	}
   651  
   652  	// We also want to update the last sealed height. Retrieve the block
   653  	// seal indexed for the block and retrieve the block that was sealed by it.
   654  	lastSeal, err := m.seals.HighestInFork(blockID)
   655  	if err != nil {
   656  		return fmt.Errorf("could not look up sealed header: %w", err)
   657  	}
   658  	sealed, err := m.headers.ByBlockID(lastSeal.BlockID)
   659  	if err != nil {
   660  		return fmt.Errorf("could not retrieve sealed header: %w", err)
   661  	}
   662  
   663  	// We update metrics and emit protocol events for epoch state changes when
   664  	// the block corresponding to the state change is finalized
   665  	parentPsSnapshot, err := m.protocolState.AtBlockID(block.Header.ParentID)
   666  	if err != nil {
   667  		return fmt.Errorf("could not retrieve protocol state snapshot for parent: %w", err)
   668  	}
   669  	finalizingPsSnapshot, err := m.protocolState.AtBlockID(blockID)
   670  	if err != nil {
   671  		return fmt.Errorf("could not retrieve protocol state snapshot: %w", err)
   672  	}
   673  	currentEpochSetup := finalizingPsSnapshot.EpochSetup()
   674  	epochFallbackTriggered, err := m.isEpochEmergencyFallbackTriggered()
   675  	if err != nil {
   676  		return fmt.Errorf("could not check persisted epoch emergency fallback flag: %w", err)
   677  	}
   678  
   679  	// if epoch fallback was not previously triggered, check whether this block triggers it
   680  	// TODO(efm-recovery): remove separate global EFM flag
   681  	if !epochFallbackTriggered && finalizingPsSnapshot.InvalidEpochTransitionAttempted() {
   682  		epochFallbackTriggered = true
   683  		// emit the protocol event only the first time epoch fallback is triggered
   684  		events = append(events, m.consumer.EpochEmergencyFallbackTriggered)
   685  		metrics = append(metrics, m.metrics.EpochEmergencyFallbackTriggered)
   686  	}
   687  
   688  	// Determine metric updates and protocol events related to epoch phase changes and epoch transitions.
   689  	epochPhaseMetrics, epochPhaseEvents, err := m.epochMetricsAndEventsOnBlockFinalized(parentPsSnapshot, finalizingPsSnapshot, header)
   690  	if err != nil {
   691  		return fmt.Errorf("could not determine epoch phase metrics/events for finalized block: %w", err)
   692  	}
   693  	metrics = append(metrics, epochPhaseMetrics...)
   694  	events = append(events, epochPhaseEvents...)
   695  
   696  	// Extract and validate version beacon events from the block seals.
   697  	versionBeacons, err := m.versionBeaconOnBlockFinalized(block)
   698  	if err != nil {
   699  		return fmt.Errorf("cannot process version beacon: %w", err)
   700  	}
   701  
   702  	// Persist updates in database
   703  	// * Add this block to the height-indexed set of finalized blocks.
   704  	// * Update the largest finalized height to this block's height.
   705  	// * Update the largest height of sealed and finalized block.
   706  	//   This value could actually stay the same if it has no seals in
   707  	//   its payload, in which case the parent's seal is the same.
   708  	// * set the epoch fallback flag, if it is triggered
   709  	err = operation.RetryOnConflict(m.db.Update, func(tx *badger.Txn) error {
   710  		err = operation.IndexBlockHeight(header.Height, blockID)(tx)
   711  		if err != nil {
   712  			return fmt.Errorf("could not insert number mapping: %w", err)
   713  		}
   714  		err = operation.UpdateFinalizedHeight(header.Height)(tx)
   715  		if err != nil {
   716  			return fmt.Errorf("could not update finalized height: %w", err)
   717  		}
   718  		err = operation.UpdateSealedHeight(sealed.Height)(tx)
   719  		if err != nil {
   720  			return fmt.Errorf("could not update sealed height: %w", err)
   721  		}
   722  		if epochFallbackTriggered {
   723  			err = operation.SetEpochEmergencyFallbackTriggered(blockID)(tx)
   724  			if err != nil {
   725  				return fmt.Errorf("could not set epoch fallback flag: %w", err)
   726  			}
   727  		}
   728  		// TODO(efm-recovery): we should be able to omit the `!epochFallbackTriggered` check here.
   729  		if isFirstBlockOfEpoch(parentPsSnapshot, finalizingPsSnapshot) && !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.cachedLatestFinal.Store(&cachedHeader{blockID, header})
   763  	if len(block.Payload.Seals) > 0 {
   764  		m.State.cachedLatestSealed.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  // isFirstBlockOfEpoch returns true if the given block is the first block of a new epoch
   796  // by comparing the block's Protocol State Snapshot to that of its parent.
   797  // NOTE: There can be multiple (un-finalized) blocks that qualify as the first block of epoch N.
   798  func isFirstBlockOfEpoch(parentPsSnapshot, blockPsSnapshot protocol.DynamicProtocolState) bool {
   799  	return parentPsSnapshot.Epoch() < blockPsSnapshot.Epoch()
   800  }
   801  
   802  // epochMetricsAndEventsOnBlockFinalized determines metrics to update and protocol
   803  // events to emit upon finalizing a block.
   804  //   - We notify about an epoch transition when the first block of the new epoch is finalized
   805  //   - We notify about an epoch phase transition when the first block within the new epoch phase is finalized
   806  //
   807  // This method must be called for each finalized block.
   808  // No errors are expected during normal operation.
   809  func (m *FollowerState) epochMetricsAndEventsOnBlockFinalized(parentPsSnapshot, finalizedPsSnapshot protocol.DynamicProtocolState, finalized *flow.Header) (
   810  	metrics []func(),
   811  	events []func(),
   812  	err error,
   813  ) {
   814  	if finalizedPsSnapshot.InvalidEpochTransitionAttempted() {
   815  		// No epoch state changes to notify on when EFM is triggered
   816  		return nil, nil, nil
   817  	}
   818  
   819  	parentEpochCounter := parentPsSnapshot.Epoch()
   820  	childEpochCounter := finalizedPsSnapshot.Epoch()
   821  	parentEpochPhase := parentPsSnapshot.EpochPhase()
   822  	childEpochPhase := finalizedPsSnapshot.EpochPhase()
   823  
   824  	// Same epoch phase -> nothing to do
   825  	if parentEpochPhase == childEpochPhase {
   826  		return
   827  	}
   828  
   829  	// Different counter -> must be an epoch transition
   830  	if parentEpochCounter != childEpochCounter {
   831  		childEpochSetup := finalizedPsSnapshot.EpochSetup()
   832  		events = append(events, func() { m.consumer.EpochTransition(childEpochSetup.Counter, finalized) })
   833  		// set current epoch counter corresponding to new epoch
   834  		metrics = append(metrics, func() { m.metrics.CurrentEpochCounter(childEpochSetup.Counter) })
   835  		// denote the most recent epoch transition height
   836  		metrics = append(metrics, func() { m.metrics.EpochTransitionHeight(finalized.Height) })
   837  		// set epoch phase - since we are starting a new epoch we begin in the staking phase
   838  		metrics = append(metrics, func() { m.metrics.CurrentEpochPhase(flow.EpochPhaseStaking) })
   839  		// set current epoch view values
   840  		metrics = append(
   841  			metrics,
   842  			func() { m.metrics.CurrentEpochFinalView(childEpochSetup.FinalView) },
   843  			func() { m.metrics.CurrentDKGPhase1FinalView(childEpochSetup.DKGPhase1FinalView) },
   844  			func() { m.metrics.CurrentDKGPhase2FinalView(childEpochSetup.DKGPhase2FinalView) },
   845  			func() { m.metrics.CurrentDKGPhase3FinalView(childEpochSetup.DKGPhase3FinalView) },
   846  		)
   847  		return
   848  	}
   849  	// Transition from Staking phase to Setup phase. `finalized` is first block in Setup phase.
   850  	if parentEpochPhase == flow.EpochPhaseStaking && childEpochPhase == flow.EpochPhaseSetup {
   851  		events = append(events, func() { m.metrics.CurrentEpochPhase(flow.EpochPhaseSetup) })
   852  		events = append(events, func() { m.consumer.EpochSetupPhaseStarted(childEpochCounter, finalized) })
   853  		return
   854  	}
   855  	// Transition from Setup phase to Committed phase. `finalized` is first block in Committed phase.
   856  	if parentEpochPhase == flow.EpochPhaseSetup && childEpochPhase == flow.EpochPhaseCommitted {
   857  		events = append(events, func() { m.metrics.CurrentEpochPhase(flow.EpochPhaseCommitted) })
   858  		events = append(events, func() { m.consumer.EpochCommittedPhaseStarted(childEpochCounter, finalized) })
   859  		return
   860  	}
   861  
   862  	return nil, nil, fmt.Errorf("sanity check failed: invalid subsequent [epoch-phase] [%d-%s]->[%d-%s]",
   863  		parentEpochCounter, parentEpochPhase, childEpochCounter, childEpochPhase)
   864  }
   865  
   866  // versionBeaconOnBlockFinalized extracts and returns the VersionBeacons from the
   867  // finalized block's seals.
   868  // This could return multiple VersionBeacons if the parent block contains multiple Seals.
   869  // The version beacons will be returned in the ascending height order of the seals.
   870  // Technically only the last VersionBeacon is relevant.
   871  func (m *FollowerState) versionBeaconOnBlockFinalized(
   872  	finalized *flow.Block,
   873  ) ([]*flow.SealedVersionBeacon, error) {
   874  	var versionBeacons []*flow.SealedVersionBeacon
   875  
   876  	seals, err := protocol.OrderedSeals(finalized.Payload.Seals, m.headers)
   877  	if err != nil {
   878  		if errors.Is(err, storage.ErrNotFound) {
   879  			return nil, fmt.Errorf(
   880  				"ordering seals: parent payload contains"+
   881  					" seals for unknown block: %w", err)
   882  		}
   883  		return nil, fmt.Errorf("unexpected error ordering seals: %w", err)
   884  	}
   885  
   886  	for _, seal := range seals {
   887  		result, err := m.results.ByID(seal.ResultID)
   888  		if err != nil {
   889  			return nil, fmt.Errorf(
   890  				"could not retrieve result (id=%x) for seal (id=%x): %w",
   891  				seal.ResultID,
   892  				seal.ID(),
   893  				err)
   894  		}
   895  		for _, event := range result.ServiceEvents {
   896  
   897  			ev, ok := event.Event.(*flow.VersionBeacon)
   898  
   899  			if !ok {
   900  				// skip other service event types.
   901  				// validation if this is a known service event type is done elsewhere.
   902  				continue
   903  			}
   904  
   905  			err := ev.Validate()
   906  			if err != nil {
   907  				m.logger.Warn().
   908  					Err(err).
   909  					Str("block_id", finalized.ID().String()).
   910  					Interface("event", ev).
   911  					Msg("invalid VersionBeacon service event")
   912  				continue
   913  			}
   914  
   915  			// The version beacon only becomes actionable/valid/active once the block
   916  			// containing the version beacon has been sealed. That is why we set the
   917  			// Seal height to the current block height.
   918  			versionBeacons = append(versionBeacons, &flow.SealedVersionBeacon{
   919  				VersionBeacon: ev,
   920  				SealHeight:    finalized.Header.Height,
   921  			})
   922  		}
   923  	}
   924  
   925  	return versionBeacons, nil
   926  }