@@ -664,7 +664,7 @@ func (m *FollowerState) Finalize(ctx context.Context, blockID flow.Identifier) e
664664 // If epoch emergency fallback is triggered, the current epoch continues until
665665 // the next spork - so skip these updates.
666666 if ! epochFallbackTriggered {
667- epochPhaseMetrics , epochPhaseEvents , err := m .epochPhaseMetricsAndEventsOnBlockFinalized (header , epochStatus )
667+ epochPhaseMetrics , epochPhaseEvents , err := m .epochPhaseMetricsAndEventsOnBlockFinalized (block , epochStatus )
668668 if err != nil {
669669 return fmt .Errorf ("could not determine epoch phase metrics/events for finalized block: %w" , err )
670670 }
@@ -848,41 +848,38 @@ func (m *FollowerState) epochTransitionMetricsAndEventsOnBlockFinalized(block *f
848848 return
849849}
850850
851- // epochPhaseMetricsAndEventsOnBlockFinalized determines metrics to update
852- // and protocol events to emit, if this block is the first of a new epoch phase.
853- //
854- // Protocol events and metric updates happen when we finalize the block at
855- // which a service event causing an epoch phase change comes into effect.
856- // See handleEpochServiceEvents for details.
851+ // epochPhaseMetricsAndEventsOnBlockFinalized determines metrics to update and protocol
852+ // events to emit. Service Events embedded into an execution result take effect, when the
853+ // execution result's _seal is finalized_ (i.e. when the block holding a seal for the
854+ // result is finalized). See also handleEpochServiceEvents for further details. Example:
857855//
858856// Convention:
859857//
860- // A <-- ... <-- P(Seal_A) <----- B
861- // ↑ ↑
862- // block sealing service event first block of new Epoch phase
863- // for epoch-phase transition (e.g. EpochSetup phase)
864- // (e.g. EpochSetup event)
858+ // A <-- ... <-- C(Seal_A)
865859//
866- // Per convention, protocol events for epoch phase changes are emitted when
867- // the first block of the new phase (eg. EpochSetup phase) is _finalized_.
868- // Meaning that the new phase has started .
860+ // Suppose an EpochSetup service event is emitted during execution of block A. C seals A, therefore
861+ // we apply the metrics/events when C is finalized. The first block of the EpochSetup
862+ // phase is block C .
869863//
870864// This function should only be called when epoch fallback *has not already been triggered*.
871865// No errors are expected during normal operation.
872- func (m * FollowerState ) epochPhaseMetricsAndEventsOnBlockFinalized (block * flow.Header , epochStatus * flow.EpochStatus ) (
866+ func (m * FollowerState ) epochPhaseMetricsAndEventsOnBlockFinalized (block * flow.Block , epochStatus * flow.EpochStatus ) (
873867 metrics []func (),
874868 events []func (),
875869 err error ,
876870) {
877871
878- parent , err := m .blocks .ByID (block .ParentID )
872+ // block payload may not specify seals in order, so order them by block height before processing
873+ orderedSeals , err := protocol .OrderedSeals (block .Payload , m .headers )
879874 if err != nil {
880- return nil , nil , fmt .Errorf ("could not get parent (id=%x): %w" , block .ParentID , err )
875+ if errors .Is (err , storage .ErrNotFound ) {
876+ return nil , nil , fmt .Errorf ("ordering seals: parent payload contains seals for unknown block: %s" , err .Error ())
877+ }
878+ return nil , nil , fmt .Errorf ("unexpected error ordering seals: %w" , err )
881879 }
882880
883881 // track service event driven metrics and protocol events that should be emitted
884- for _ , seal := range parent .Payload .Seals {
885-
882+ for _ , seal := range orderedSeals {
886883 result , err := m .results .ByID (seal .ResultID )
887884 if err != nil {
888885 return nil , nil , fmt .Errorf ("could not retrieve result (id=%x) for seal (id=%x): %w" , seal .ResultID , seal .ID (), err )
@@ -893,12 +890,12 @@ func (m *FollowerState) epochPhaseMetricsAndEventsOnBlockFinalized(block *flow.H
893890 // update current epoch phase
894891 events = append (events , func () { m .metrics .CurrentEpochPhase (flow .EpochPhaseSetup ) })
895892 // track epoch phase transition (staking->setup)
896- events = append (events , func () { m .consumer .EpochSetupPhaseStarted (ev .Counter - 1 , block ) })
893+ events = append (events , func () { m .consumer .EpochSetupPhaseStarted (ev .Counter - 1 , block . Header ) })
897894 case * flow.EpochCommit :
898895 // update current epoch phase
899896 events = append (events , func () { m .metrics .CurrentEpochPhase (flow .EpochPhaseCommitted ) })
900897 // track epoch phase transition (setup->committed)
901- events = append (events , func () { m .consumer .EpochCommittedPhaseStarted (ev .Counter - 1 , block ) })
898+ events = append (events , func () { m .consumer .EpochCommittedPhaseStarted (ev .Counter - 1 , block . Header ) })
902899 // track final view of committed epoch
903900 nextEpochSetup , err := m .epoch .setups .ByID (epochStatus .NextEpoch .SetupID )
904901 if err != nil {
@@ -971,27 +968,26 @@ func (m *FollowerState) epochStatus(block *flow.Header, epochFallbackTriggered b
971968
972969// handleEpochServiceEvents handles applying state changes which occur as a result
973970// of service events being included in a block payload:
974- // * inserting incorporated service events
975- // * updating EpochStatus for the candidate block
971+ // - inserting incorporated service events
972+ // - updating EpochStatus for the candidate block
976973//
977974// Consider a chain where a service event is emitted during execution of block A.
978- // Block B contains a receipt for A. Block C contains a seal for block A. Block
979- // D contains a QC for C.
975+ // Block B contains a receipt for A. Block C contains a seal for block A.
980976//
981- // A <- B(RA) <- C(SA) <- D
977+ // A <- .. <- B(RA) <- .. <- C(SA)
982978//
983979// Service events are included within execution results, which are stored
984980// opaquely as part of the block payload in block B. We only validate and insert
985- // the typed service event to storage once we have received a valid QC for the
986- // block containing the seal for A. This occurs once we mark block D as valid
987- // with MarkValid. Because of this, any change to the protocol state introduced
988- // by a service event emitted in A would only become visible when querying D or
989- // later (D's children).
990- // TODO(active-pacemaker) update docs here (remove reference to MarkValid) https://github.com/dapperlabs/flow-go/issues/6254
981+ // the typed service event to storage once we process C, the block containing the
982+ // seal for block A. This is because we rely on the sealing subsystem to validate
983+ // correctness of the service event before processing it.
984+ // Consequently, any change to the protocol state introduced by a service event
985+ // emitted during execution of block A would only become visible when querying
986+ // C or its descendants.
991987//
992988// This method will only apply service-event-induced state changes when the
993- // input block has the form of block D (ie. has a parent, which contains a seal
994- // for a block in which a service event was emitted).
989+ // input block has the form of block C (ie. contains a seal for a block in
990+ // which a service event was emitted).
995991//
996992// Return values:
997993// - dbUpdates - If the service events are valid, or there are no service events,
@@ -1025,20 +1021,16 @@ func (m *FollowerState) handleEpochServiceEvents(candidate *flow.Block) (dbUpdat
10251021 return dbUpdates , nil
10261022 }
10271023
1028- // We apply service events from blocks which are sealed by this block's PARENT .
1029- // The parent 's payload might contain epoch preparation service events for the next
1024+ // We apply service events from blocks which are sealed by this candidate block .
1025+ // The block 's payload might contain epoch preparation service events for the next
10301026 // epoch. In this case, we need to update the tentative protocol state.
10311027 // We need to validate whether all information is available in the protocol
10321028 // state to go to the next epoch when needed. In cases where there is a bug
10331029 // in the smart contract, it could be that this happens too late and the
10341030 // chain finalization should halt.
1035- parent , err := m .blocks .ByID (candidate .Header .ParentID )
1036- if err != nil {
1037- return nil , fmt .Errorf ("could not get parent (id=%x): %w" , candidate .Header .ParentID , err )
1038- }
10391031
10401032 // block payload may not specify seals in order, so order them by block height before processing
1041- orderedSeals , err := protocol .OrderedSeals (parent .Payload , m .headers )
1033+ orderedSeals , err := protocol .OrderedSeals (candidate .Payload , m .headers )
10421034 if err != nil {
10431035 if errors .Is (err , storage .ErrNotFound ) {
10441036 return nil , fmt .Errorf ("ordering seals: parent payload contains seals for unknown block: %s" , err .Error ())
0 commit comments