-
Notifications
You must be signed in to change notification settings - Fork 177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[EFM] Adjust Block Time Controller #5732
Comments
2 tasks
jordanschalm
added a commit
that referenced
this issue
Jun 7, 2024
address conflicts, including re-generating mocks Text of conflicts below: diff --cc model/flow/protocol_state.go index 4c8bba49bb,53798a1353..0000000000 --- a/model/flow/protocol_state.go +++ b/model/flow/protocol_state.go @@@ -162,20 -147,13 +162,28 @@@ func NewRichEpochProtocolStateEntry NextEpochIdentityTable: IdentityList{}, } - // If previous epoch is specified: ensure respective epoch service events are not nil and consistent with commitments in `ProtocolStateEntry.PreviousEpoch` + // If previous epoch is specified: ensure respective epoch service events are not nil and consistent with commitments in `EpochProtocolStateEntry.PreviousEpoch` if protocolState.PreviousEpoch != nil { ++<<<<<<< HEAD + if protocolState.PreviousEpoch.SetupID != previousEpochSetup.ID() { // calling ID() will panic if EpochSetup event is nil + return nil, fmt.Errorf("supplied previous epoch's setup event (%x) does not match commitment (%x) in ProtocolStateEntry", previousEpochSetup.ID(), protocolState.PreviousEpoch.SetupID) + } + if protocolState.PreviousEpoch.CommitID != previousEpochCommit.ID() { // calling ID() will panic if EpochCommit event is nil + return nil, fmt.Errorf("supplied previous epoch's commit event (%x) does not match commitment (%x) in ProtocolStateEntry", previousEpochCommit.ID(), protocolState.PreviousEpoch.CommitID) ++======= + if protocolState.PreviousEpoch.SetupID != previousEpochSetup.ID() { // calling ID() will panic is EpochSetup event is nil + return nil, fmt.Errorf("supplied previous epoch's setup event (%x) does not match commitment (%x) in EpochProtocolStateEntry", previousEpochSetup.ID(), protocolState.PreviousEpoch.SetupID) + } + if protocolState.PreviousEpoch.CommitID != previousEpochCommit.ID() { // calling ID() will panic is EpochCommit event is nil + return nil, fmt.Errorf("supplied previous epoch's commit event (%x) does not match commitment (%x) in EpochProtocolStateEntry", previousEpochCommit.ID(), protocolState.PreviousEpoch.CommitID) ++>>>>>>> master + } + } else { + if previousEpochSetup != nil { + return nil, fmt.Errorf("no previous epoch but gotten non-nil EpochSetup event") + } + if previousEpochCommit != nil { + return nil, fmt.Errorf("no previous epoch but gotten non-nil EpochCommit event") } } @@@ -227,12 -198,8 +235,12 @@@ } if nextEpoch.CommitID != ZeroID { if nextEpoch.CommitID != nextEpochCommit.ID() { - return nil, fmt.Errorf("supplied next epoch's commit event (%x) does not match commitment (%x) in ProtocolStateEntry", nextEpoch.CommitID, nextEpochCommit.ID()) + return nil, fmt.Errorf("supplied next epoch's commit event (%x) does not match commitment (%x) in EpochProtocolStateEntry", nextEpoch.CommitID, nextEpochCommit.ID()) } + } else { + if nextEpochCommit != nil { + return nil, fmt.Errorf("next epoch not yet committed but got EpochCommit event") + } } result.CurrentEpochIdentityTable, err = BuildIdentityTable( @@@ -285,11 -252,11 +293,19 @@@ func (e *EpochProtocolStateEntry) Copy( if e == nil { return nil } ++<<<<<<< HEAD + return &ProtocolStateEntry{ + PreviousEpoch: e.PreviousEpoch.Copy(), + CurrentEpoch: *e.CurrentEpoch.Copy(), + NextEpoch: e.NextEpoch.Copy(), + EpochFallbackTriggered: e.EpochFallbackTriggered, ++======= + return &EpochProtocolStateEntry{ + PreviousEpoch: e.PreviousEpoch.Copy(), + CurrentEpoch: *e.CurrentEpoch.Copy(), + NextEpoch: e.NextEpoch.Copy(), + InvalidEpochTransitionAttempted: e.InvalidEpochTransitionAttempted, ++>>>>>>> master } } @@@ -313,20 -280,9 +329,20 @@@ func (e *RichEpochProtocolStateEntry) C } } +// CurrentEpochFinalView returns the final view of the current epoch, taking into account possible epoch extensions. +// If there are no epoch extensions, the final view is the final view of the current epoch setup, +// otherwise it is the final view of the last epoch extension. +func (e *RichProtocolStateEntry) CurrentEpochFinalView() uint64 { + l := len(e.CurrentEpoch.EpochExtensions) + if l > 0 { + return e.CurrentEpoch.EpochExtensions[l-1].FinalView + } + return e.CurrentEpochSetup.FinalView +} + // EpochPhase returns the current epoch phase. - // The receiver ProtocolStateEntry must be properly constructed. - func (e *ProtocolStateEntry) EpochPhase() EpochPhase { + // The receiver EpochProtocolStateEntry must be properly constructed. + func (e *EpochProtocolStateEntry) EpochPhase() EpochPhase { // The epoch phase is determined by how much information we have about the next epoch if e.NextEpoch == nil { return EpochPhaseStaking // if no information about the next epoch is known, we are in the Staking Phase diff --cc state/protocol/badger/mutator.go index 230bd73227,395aa197ce..0000000000 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@@ -662,49 -691,36 +691,77 @@@ func (m *FollowerState) Finalize(ctx co // We update metrics and emit protocol events for epoch state changes when // the block corresponding to the state change is finalized ++<<<<<<< HEAD + parentEpochStateSnapshot, err := m.protocolState.AtBlockID(header.ParentID) + if err != nil { + return fmt.Errorf("could not retrieve parent protocol state snapshot: %w", err) + } + epochStateSnapshot, err := m.protocolState.AtBlockID(blockID) + if err != nil { + return fmt.Errorf("could not retrieve protocol state snapshot: %w", err) + } + currentEpochSetup := epochStateSnapshot.EpochSetup() + epochFallbackTriggered := epochStateSnapshot.EpochFallbackTriggered() + + // if epoch fallback was not previously triggered, check whether this block triggers it + if epochFallbackTriggered && !parentEpochStateSnapshot.EpochFallbackTriggered() { ++======= + parentEpochState, err := m.protocolState.EpochStateAtBlockID(block.Header.ParentID) + if err != nil { + return fmt.Errorf("could not retrieve protocol state snapshot for parent: %w", err) + } + finalizingEpochState, err := m.protocolState.EpochStateAtBlockID(blockID) + if err != nil { + return fmt.Errorf("could not retrieve protocol state snapshot: %w", err) + } + currentEpochSetup := finalizingEpochState.EpochSetup() + epochFallbackTriggered, err := m.isEpochEmergencyFallbackTriggered() + if err != nil { + return fmt.Errorf("could not check persisted epoch emergency fallback flag: %w", err) + } + + // if epoch fallback was not previously triggered, check whether this block triggers it + // TODO(efm-recovery): remove separate global EFM flag + if !epochFallbackTriggered && finalizingEpochState.InvalidEpochTransitionAttempted() { + epochFallbackTriggered = true ++>>>>>>> master // emit the protocol event only the first time epoch fallback is triggered events = append(events, m.consumer.EpochEmergencyFallbackTriggered) metrics = append(metrics, m.metrics.EpochEmergencyFallbackTriggered) } - isFirstBlockOfEpoch, err := m.isFirstBlockOfEpoch(header, currentEpochSetup) + // Determine metric updates and protocol events related to epoch phase changes and epoch transitions. + epochPhaseMetrics, epochPhaseEvents, err := m.epochMetricsAndEventsOnBlockFinalized(parentEpochState, finalizingEpochState, header) if err != nil { ++<<<<<<< HEAD + return fmt.Errorf("could not check if block is first of epoch: %w", err) + } + if isFirstBlockOfEpoch { + epochTransitionMetrics, epochTransitionEvents := m.epochTransitionMetricsAndEventsOnBlockFinalized(header, currentEpochSetup) + if err != nil { + return fmt.Errorf("could not determine epoch transition metrics/events for finalized block: %w", err) + } + metrics = append(metrics, epochTransitionMetrics...) + events = append(events, epochTransitionEvents...) + } + + // Determine metric updates and protocol events related to epoch phase changes and epoch transitions. + // If epoch emergency fallback is triggered, the current epoch continues until + // the next spork - so skip these updates. + // TODO(EFM, #5732, #6013): needs update for EFM recovery + if !epochFallbackTriggered { + epochPhaseMetrics, epochPhaseEvents, err := m.epochPhaseMetricsAndEventsOnBlockFinalized(block) + if err != nil { + return fmt.Errorf("could not determine epoch phase metrics/events for finalized block: %w", err) + } + metrics = append(metrics, epochPhaseMetrics...) + events = append(events, epochPhaseEvents...) ++======= + return fmt.Errorf("could not determine epoch phase metrics/events for finalized block: %w", err) ++>>>>>>> master } + metrics = append(metrics, epochPhaseMetrics...) + events = append(events, epochPhaseEvents...) // Extract and validate version beacon events from the block seals. versionBeacons, err := m.versionBeaconOnBlockFinalized(block) @@@ -732,7 -748,14 +789,18 @@@ if err != nil { return fmt.Errorf("could not update sealed height: %w", err) } ++<<<<<<< HEAD + if isFirstBlockOfEpoch { ++======= + if epochFallbackTriggered { + err = operation.SetEpochEmergencyFallbackTriggered(blockID)(tx) + if err != nil { + return fmt.Errorf("could not set epoch fallback flag: %w", err) + } + } + // TODO(efm-recovery): we should be able to omit the `!epochFallbackTriggered` check here. + if isFirstBlockOfEpoch(parentEpochState, finalizingEpochState) && !epochFallbackTriggered { ++>>>>>>> master err = operation.InsertEpochFirstHeight(currentEpochSetup.Counter, header.Height)(tx) if err != nil { return fmt.Errorf("could not insert epoch first block height: %w", err) diff --cc state/protocol/badger/state.go index beef6343ec,298f0f5c03..0000000000 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@@ -968,3 -969,16 +968,19 @@@ func (state *State) populateCache() err return nil } ++<<<<<<< HEAD ++======= + + // isEpochEmergencyFallbackTriggered checks whether epoch fallback has been globally triggered. + // TODO(efm-recovery): Stop storing a global EFM flag, use parentState.EFMTriggered instead + // + // Returns: + // * (true, nil) if epoch fallback is triggered + // * (false, nil) if epoch fallback is not triggered (including if the flag is not set) + // * (false, err) if an unexpected error occurs + func (state *State) isEpochEmergencyFallbackTriggered() (bool, error) { + var triggered bool + err := state.db.View(operation.CheckEpochEmergencyFallbackTriggered(&triggered)) + return triggered, err + } ++>>>>>>> master diff --cc state/protocol/mock/epoch_protocol_state.go index a8685f222f,2ad2840d11..0000000000 --- a/state/protocol/mock/epoch_protocol_state.go +++ b/state/protocol/mock/epoch_protocol_state.go @@@ -112,24 -132,14 +132,28 @@@ func (_m *EpochProtocolState) EpochComm return r0 } +// EpochFallbackTriggered provides a mock function with given fields: +func (_m *DynamicProtocolState) EpochFallbackTriggered() bool { + ret := _m.Called() + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + // EpochPhase provides a mock function with given fields: - func (_m *DynamicProtocolState) EpochPhase() flow.EpochPhase { + func (_m *EpochProtocolState) EpochPhase() flow.EpochPhase { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EpochPhase") + } + var r0 flow.EpochPhase if rf, ok := ret.Get(0).(func() flow.EpochPhase); ok { r0 = rf() @@@ -188,10 -210,32 +224,35 @@@ func (_m *EpochProtocolState) Identitie return r0 } ++<<<<<<< HEAD:state/protocol/mock/dynamic_protocol_state.go ++======= + // InvalidEpochTransitionAttempted provides a mock function with given fields: + func (_m *EpochProtocolState) InvalidEpochTransitionAttempted() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for InvalidEpochTransitionAttempted") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 + } + ++>>>>>>> master:state/protocol/mock/epoch_protocol_state.go // PreviousEpochExists provides a mock function with given fields: - func (_m *DynamicProtocolState) PreviousEpochExists() bool { + func (_m *EpochProtocolState) PreviousEpochExists() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for PreviousEpochExists") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() diff --cc state/protocol/mock/instance_params.go index 7b42147d2d,0ca3db5c8c..0000000000 --- a/state/protocol/mock/instance_params.go +++ b/state/protocol/mock/instance_params.go @@@ -12,6 -12,34 +12,37 @@@ type InstanceParams struct mock.Mock } ++<<<<<<< HEAD ++======= + // EpochFallbackTriggered provides a mock function with given fields: + func (_m *InstanceParams) EpochFallbackTriggered() (bool, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for EpochFallbackTriggered") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func() (bool, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 + } + ++>>>>>>> master // FinalizedRoot provides a mock function with given fields: func (_m *InstanceParams) FinalizedRoot() *flow.Header { ret := _m.Called() diff --cc state/protocol/mock/params.go index 677ba3d9ff,56cad5a925..0000000000 --- a/state/protocol/mock/params.go +++ b/state/protocol/mock/params.go @@@ -40,6 -48,34 +48,37 @@@ func (_m *Params) EpochCommitSafetyThre return r0 } ++<<<<<<< HEAD ++======= + // EpochFallbackTriggered provides a mock function with given fields: + func (_m *Params) EpochFallbackTriggered() (bool, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for EpochFallbackTriggered") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func() (bool, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 + } + ++>>>>>>> master // FinalizedRoot provides a mock function with given fields: func (_m *Params) FinalizedRoot() *flow.Header { ret := _m.Called() diff --cc state/protocol/protocol_state.go index 8dd9877b24,2dbbb457ab..0000000000 --- a/state/protocol/protocol_state.go +++ b/state/protocol/protocol_state.go @@@ -5,37 -5,46 +5,54 @@@ import "github.com/onflow/flow-go/storage/badger/transaction" ) - // InitialProtocolState returns constant data for given epoch. - // This interface can be only obtained for epochs that have progressed to epoch commit event. - type InitialProtocolState interface { - // Epoch returns counter of epoch. + // EpochProtocolState represents the subset of the Protocol State KVStore related to epochs: + // the Identity Table, DKG, cluster assignment, etc. + // EpochProtocolState is fork-aware and can change on a block-by-block basis. + // Each EpochProtocolState instance refers to the state with respect to some reference block. + type EpochProtocolState interface { + // Epoch returns the current epoch counter. Epoch() uint64 + // Clustering returns initial clustering from epoch setup. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! // No errors are expected during normal operations. Clustering() (flow.ClusterList, error) + // EpochSetup returns original epoch setup event that was used to initialize the protocol state. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! EpochSetup() *flow.EpochSetup + // EpochCommit returns original epoch commit event that was used to update the protocol state. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! EpochCommit() *flow.EpochCommit + // DKG returns information about DKG that was obtained from EpochCommit event. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! // No errors are expected during normal operations. DKG() (DKG, error) - // Entry Returns low-level protocol state entry that was used to initialize this object. - // It shouldn't be used by high-level logic, it is useful for some cases such as bootstrapping. - // Prefer using other methods to access protocol state. - Entry() *flow.RichProtocolStateEntry - } - - // DynamicProtocolState extends the InitialProtocolState with data that can change from block to block. - // It can be used to access the identity table at given block. - type DynamicProtocolState interface { - InitialProtocolState ++<<<<<<< HEAD + // EpochFallbackTriggered denotes whether an invalid epoch state transition was attempted + // on the fork ending in this block. Once the first block where this flag is true is finalized, epoch + // fallback mode is triggered. This flag is reset to false when finalizing a block that seals + // a valid EpochRecover service event. + EpochFallbackTriggered() bool ++======= + // InvalidEpochTransitionAttempted denotes whether an invalid epoch state transition was attempted + // on the fork ending this block. Once the first block where this flag is true is finalized, epoch + // fallback mode is triggered. + // TODO for 'leaving Epoch Fallback via special service event': at the moment, this is a one-way transition and requires a spork to recover - need to revisit for sporkless EFM recovery + InvalidEpochTransitionAttempted() bool + ++>>>>>>> master // PreviousEpochExists returns true if a previous epoch exists. This is true for all epoch // except those immediately following a spork. PreviousEpochExists() bool diff --cc state/protocol/protocol_state/epochs/factory.go index 6dd9884e85,4abce702ad..0000000000 --- a/state/protocol/protocol_state/epochs/factory.go +++ b/state/protocol/protocol_state/epochs/factory.go @@@ -44,11 -44,11 +44,16 @@@ func (f *EpochStateMachineFactory) Crea f.epochProtocolStateDB, parentState, mutator, - func(candidateView uint64, parentState *flow.RichProtocolStateEntry) (StateMachine, error) { + func(candidateView uint64, parentState *flow.RichEpochProtocolStateEntry) (StateMachine, error) { return NewHappyPathStateMachine(candidateView, parentState) }, ++<<<<<<< HEAD + func(candidateView uint64, parentState *flow.RichProtocolStateEntry) (StateMachine, error) { + return NewFallbackStateMachine(f.params, candidateView, parentState) ++======= + func(candidateView uint64, parentState *flow.RichEpochProtocolStateEntry) (StateMachine, error) { + return NewFallbackStateMachine(candidateView, parentState), nil ++>>>>>>> master }, ) } diff --cc state/protocol/protocol_state/epochs/fallback_statemachine.go index 8237488a90,e8719615c5..0000000000 --- a/state/protocol/protocol_state/epochs/fallback_statemachine.go +++ b/state/protocol/protocol_state/epochs/fallback_statemachine.go @@@ -24,22 -16,12 +24,31 @@@ type FallbackStateMachine struct var _ StateMachine = (*FallbackStateMachine)(nil) ++<<<<<<< HEAD +// NewFallbackStateMachine constructs a state machine for epoch fallback. It automatically sets +// EpochFallbackTriggered to true, thereby recording that we have entered epoch fallback mode. +// No errors are expected during normal operations. +func NewFallbackStateMachine(params protocol.GlobalParams, view uint64, parentState *flow.RichProtocolStateEntry) (*FallbackStateMachine, error) { + state := parentState.ProtocolStateEntry.Copy() + nextEpochCommitted := state.EpochPhase() == flow.EpochPhaseCommitted + // we are entering fallback mode, this logic needs to be executed only once + if !state.EpochFallbackTriggered { + // the next epoch has not been committed, but possibly setup, make sure it is cleared + if !nextEpochCommitted { + state.NextEpoch = nil + } + state.EpochFallbackTriggered = true + } + + sm := &FallbackStateMachine{ ++======= + // NewFallbackStateMachine constructs a state machine for epoch fallback, it automatically sets + // InvalidEpochTransitionAttempted to true, thereby recording that we have entered epoch fallback mode. + func NewFallbackStateMachine(view uint64, parentState *flow.RichEpochProtocolStateEntry) *FallbackStateMachine { + state := parentState.EpochProtocolStateEntry.Copy() + state.InvalidEpochTransitionAttempted = true + return &FallbackStateMachine{ ++>>>>>>> master baseStateMachine: baseStateMachine{ parentState: parentState, state: state, diff --cc state/protocol/protocol_state/epochs/happy_path_statemachine.go index 00467d518e,fdd2518338..0000000000 --- a/state/protocol/protocol_state/epochs/happy_path_statemachine.go +++ b/state/protocol/protocol_state/epochs/happy_path_statemachine.go @@@ -29,10 -28,10 +29,15 @@@ type HappyPathStateMachine struct var _ StateMachine = (*HappyPathStateMachine)(nil) // NewHappyPathStateMachine creates a new HappyPathStateMachine. -// An exception is returned in case the `InvalidEpochTransitionAttempted` flag is set in the `parentState`. This means that +// An exception is returned in case the `EpochFallbackTriggered` flag is set in the `parentState`. This means that // the protocol state evolution has reached an undefined state from the perspective of the happy path state machine. ++<<<<<<< HEAD +func NewHappyPathStateMachine(view uint64, parentState *flow.RichProtocolStateEntry) (*HappyPathStateMachine, error) { + if parentState.EpochFallbackTriggered { ++======= + func NewHappyPathStateMachine(view uint64, parentState *flow.RichEpochProtocolStateEntry) (*HappyPathStateMachine, error) { + if parentState.InvalidEpochTransitionAttempted { ++>>>>>>> master return nil, irrecoverable.NewExceptionf("cannot create happy path protocol state machine at view (%d) for a parent state"+ "which is in Epoch Fallback Mode", view) } @@@ -58,7 -57,7 +63,11 @@@ // CAUTION: the HappyPathStateMachine is left with a potentially dysfunctional state when this error occurs. Do NOT call the Build method // after such error and discard the HappyPathStateMachine! func (u *HappyPathStateMachine) ProcessEpochSetup(epochSetup *flow.EpochSetup) (bool, error) { ++<<<<<<< HEAD + err := protocol.IsValidExtendingEpochSetup(epochSetup, u.parentState) ++======= + err := protocol.IsValidExtendingEpochSetup(epochSetup, u.parentState.EpochProtocolStateEntry, u.parentState.CurrentEpochSetup) ++>>>>>>> master if err != nil { return false, fmt.Errorf("invalid epoch setup event: %w", err) } @@@ -139,60 -153,32 +148,91 @@@ func (u *HappyPathStateMachine) Process return true, nil } ++<<<<<<< HEAD +// ProcessEpochRecover returns the sentinel error `protocol.InvalidServiceEventError`, which +// indicates that `EpochRecover` are not expected on the happy path of epoch lifecycle. +func (u *HappyPathStateMachine) ProcessEpochRecover(*flow.EpochRecover) (bool, error) { + return false, protocol.NewInvalidServiceEventErrorf("epoch recover event received while on happy path") +} + +// When observing setup event for subsequent epoch, construct the EpochStateContainer for `ProtocolStateEntry.NextEpoch`. +// Context: +// Note that the `EpochStateContainer.ActiveIdentities` only contains the nodes that are *active* in the next epoch. Active means +// that these nodes are authorized to contribute to extending the chain. Nodes are listed in `ActiveIdentities` if and only if +// they are part of the EpochSetup event for the respective epoch. +// +// sanity checking SAFETY-CRITICAL INVARIANT (I): +// - Per convention, the `flow.EpochSetup` event should list the IdentitySkeletons in canonical order. This is useful +// for most efficient construction of the full active Identities for an epoch. We enforce this here at the gateway +// to the protocol state, when we incorporate new information from the EpochSetup event. +// - Note that the system smart contracts manage the identity table as an unordered set! For the protocol state, we desire a fixed +// ordering to simplify various implementation details, like the DKG. Therefore, we order identities in `flow.EpochSetup` during +// conversion from cadence to Go in the function `convert.ServiceEvent(flow.ChainID, flow.Event)` in package `model/convert` +// sanity checking SAFETY-CRITICAL INVARIANT (II): +// While ejection status and dynamic weight are not part of the EpochSetup event, we can supplement this information as follows: +// - Per convention, service events are delivered (asynchronously) in an *order-preserving* manner. Furthermore, weight changes or +// node ejection is entirely mediated by system smart contracts and delivered via service events. +// - Therefore, the EpochSetup event contains the up-to-date snapshot of the epoch participants. Any weight changes or node ejection +// that happened before should be reflected in the EpochSetup event. Specifically, the initial weight should be reduced and ejected +// nodes should be no longer listed in the EpochSetup event. +// - Hence, the following invariant must be satisfied by the system smart contracts for all active nodes in the upcoming epoch: +// (i) The Ejected flag is false. Node X being ejected in epoch N (necessarily via a service event emitted by the system +// smart contracts earlier) but also being listed in the setup event for the subsequent epoch (service event emitted by +// the system smart contracts later) is illegal. +// (ii) When the EpochSetup event is emitted / processed, the weight of all active nodes equals their InitialWeight and + +// For collector clusters, we rely on invariants (I) and (II) holding. See `committees.Cluster` for details, specifically function +// `constructInitialClusterIdentities(..)`. While the system smart contract must satisfy this invariant, we run a sanity check below. +func buildNextEpochActiveParticipants(activeIdentitiesLookup map[flow.Identifier]*flow.DynamicIdentityEntry, currentEpochSetup, nextEpochSetup *flow.EpochSetup) (flow.DynamicIdentityEntryList, error) { + nextEpochActiveIdentities := make(flow.DynamicIdentityEntryList, 0, len(nextEpochSetup.Participants)) + prevNodeID := nextEpochSetup.Participants[0].NodeID + for idx, nextEpochIdentitySkeleton := range nextEpochSetup.Participants { + // sanity checking invariant (I): + if idx > 0 && !flow.IsIdentifierCanonical(prevNodeID, nextEpochIdentitySkeleton.NodeID) { + return nil, protocol.NewInvalidServiceEventErrorf("epoch setup event lists active participants not in canonical ordering") + } + prevNodeID = nextEpochIdentitySkeleton.NodeID + + // sanity checking invariant (II.i): + currentEpochDynamicProperties, found := activeIdentitiesLookup[nextEpochIdentitySkeleton.NodeID] + if found && currentEpochDynamicProperties.Ejected { // invariant violated + return nil, protocol.NewInvalidServiceEventErrorf("node %v is ejected in current epoch %d but readmitted by EpochSetup event for epoch %d", nextEpochIdentitySkeleton.NodeID, currentEpochSetup.Counter, nextEpochSetup.Counter) + } + + nextEpochActiveIdentities = append(nextEpochActiveIdentities, &flow.DynamicIdentityEntry{ + NodeID: nextEpochIdentitySkeleton.NodeID, + Ejected: false, // according to invariant (II.i) + }) + } + return nextEpochActiveIdentities, nil ++======= + // TransitionToNextEpoch updates the notion of 'current epoch', 'previous' and 'next epoch' in the protocol + // state. An epoch transition is only allowed when: + // - next epoch has been set up, + // - next epoch has been committed, + // - invalid state transition has not been attempted (this is ensured by constructor), + // - candidate block is in the next epoch. + // No errors are expected during normal operations. + func (u *HappyPathStateMachine) TransitionToNextEpoch() error { + nextEpoch := u.state.NextEpoch + // Check if there is next epoch protocol state + if nextEpoch == nil { + return fmt.Errorf("protocol state has not been setup yet") + } + // Check if there is a commit event for next epoch + if nextEpoch.CommitID == flow.ZeroID { + return fmt.Errorf("protocol state has not been committed yet") + } + // Check if we are at the next epoch, only then a transition is allowed + if u.view < u.parentState.NextEpochSetup.FirstView { + return fmt.Errorf("protocol state transition is only allowed when enterring next epoch") + } + u.state = &flow.EpochProtocolStateEntry{ + PreviousEpoch: &u.state.CurrentEpoch, + CurrentEpoch: *u.state.NextEpoch, + InvalidEpochTransitionAttempted: false, + } + u.rebuildIdentityLookup() + return nil ++>>>>>>> master } diff --cc state/protocol/protocol_state/epochs/statemachine_test.go index 3e708f485a,386e8d17a9..0000000000 --- a/state/protocol/protocol_state/epochs/statemachine_test.go +++ b/state/protocol/protocol_state/epochs/statemachine_test.go @@@ -515,7 -515,7 +515,11 @@@ func (s *EpochStateMachineSuite) TestEv // TestEvolveStateTransitionToNextEpoch_WithInvalidStateTransition tests that EpochStateMachine transitions to the next epoch // if an invalid state transition has been detected in a block which triggers transitioning to the next epoch. // In such situation, we still need to enter the next epoch (because it has already been committed), but persist in the ++<<<<<<< HEAD +// state that we have entered Epoch fallback mode (`flow.ProtocolStateEntry.EpochFallbackTriggered` is set to `true`). ++======= + // state that we have entered Epoch fallback mode (`flow.EpochProtocolStateEntry.InvalidEpochTransitionAttempted` is set to `true`). ++>>>>>>> master // This test ensures that we don't drop previously committed next epoch. func (s *EpochStateMachineSuite) TestEvolveStateTransitionToNextEpoch_WithInvalidStateTransition() { unittest.SkipUnless(s.T(), unittest.TEST_TODO, @@@ -539,11 -539,11 +543,19 @@@ indexTxDeferredUpdate.On("Execute", mocks.Anything).Return(nil).Once() s.epochStateDB.On("Index", s.candidate.ID(), mocks.Anything).Return(indexTxDeferredUpdate.Execute, nil).Once() ++<<<<<<< HEAD + expectedEpochState := &flow.ProtocolStateEntry{ + PreviousEpoch: s.parentEpochState.CurrentEpoch.Copy(), + CurrentEpoch: *s.parentEpochState.NextEpoch.Copy(), + NextEpoch: nil, + EpochFallbackTriggered: true, ++======= + expectedEpochState := &flow.EpochProtocolStateEntry{ + PreviousEpoch: s.parentEpochState.CurrentEpoch.Copy(), + CurrentEpoch: *s.parentEpochState.NextEpoch.Copy(), + NextEpoch: nil, + InvalidEpochTransitionAttempted: true, ++>>>>>>> master } storeTxDeferredUpdate := storagemock.NewDeferredDBUpdate(s.T()) diff --cc state/protocol/validity.go index 98c302d697,bdaf06ff89..0000000000 --- a/state/protocol/validity.go +++ b/state/protocol/validity.go @@@ -14,8 -14,7 +14,12 @@@ import // CAUTION: This function assumes that all inputs besides extendingCommit are already validated. // Expected errors during normal operations: // * protocol.InvalidServiceEventError if the input service event is invalid to extend the currently active epoch status ++<<<<<<< HEAD +// TODO(EFM, #6019): This function has to be refactored to stop using RichProtocolStateEntry +func IsValidExtendingEpochSetup(extendingSetup *flow.EpochSetup, protocolStateEntry *flow.RichProtocolStateEntry) error { ++======= + func IsValidExtendingEpochSetup(extendingSetup *flow.EpochSetup, protocolStateEntry *flow.EpochProtocolStateEntry, currentEpochSetupEvent *flow.EpochSetup) error { ++>>>>>>> master // Enforce EpochSetup is valid w.r.t to current epoch state if protocolStateEntry.NextEpoch != nil { // We should only have a single epoch setup event per epoch. // true iff EpochSetup event for NEXT epoch was already included before diff --cc state/protocol/validity_test.go index 2dccd23685,a12f53e6b9..0000000000 --- a/state/protocol/validity_test.go +++ b/state/protocol/validity_test.go @@@ -148,7 -148,7 +148,11 @@@ func TestIsValidExtendingEpochSetup(t * unittest.SetupWithCounter(currentEpochSetup.Counter+1), unittest.WithParticipants(participants.ToSkeleton()), ) ++<<<<<<< HEAD + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState) ++======= + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.EpochProtocolStateEntry, currentEpochSetup) ++>>>>>>> master require.NoError(t, err) }) t.Run("(a) We should only have a single epoch setup event per epoch.", func(t *testing.T) { @@@ -160,7 -160,7 +164,11 @@@ unittest.SetupWithCounter(currentEpochSetup.Counter+1), unittest.WithParticipants(participants.ToSkeleton()), ) ++<<<<<<< HEAD + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState) ++======= + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.EpochProtocolStateEntry, currentEpochSetup) ++>>>>>>> master require.Error(t, err) }) t.Run("(b) The setup event should have the counter increased by one", func(t *testing.T) { @@@ -172,7 -172,7 +180,11 @@@ unittest.SetupWithCounter(currentEpochSetup.Counter+2), unittest.WithParticipants(participants.ToSkeleton()), ) ++<<<<<<< HEAD + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState) ++======= + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.EpochProtocolStateEntry, currentEpochSetup) ++>>>>>>> master require.Error(t, err) }) t.Run("(c) The first view needs to be exactly one greater than the current epoch final view", func(t *testing.T) { @@@ -184,7 -184,7 +196,11 @@@ unittest.SetupWithCounter(currentEpochSetup.Counter+1), unittest.WithParticipants(participants.ToSkeleton()), ) ++<<<<<<< HEAD + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState) ++======= + err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.EpochProtocolStateEntry, currentEpochSetup) ++>>>>>>> master require.Error(t, err) }) } * Unmerged path state/protocol/inmem/dynamic_protocol_state.go * Unmerged path state/protocol/inmem/dynamic_protocol_state_test.go
kc1116
added a commit
that referenced
this issue
Jun 25, 2024
commit 45f80fe022a645b6247564a177883ecd8c954d8b Merge: 08929bba55 c5123c9487 Author: Jordan Schalm <jordan.schalm@flowfoundation.org> Date: Thu Jun 20 07:57:31 2024 -0700 Merge pull request #6062 from onflow/jord/6013-events-metrics Add `EpochExtended` and `EpochFallbackModeExited` events commit c5123c9487e7bcdeaf26a04fe7cda87813fc3c18 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Thu Jun 20 07:28:43 2024 -0700 specify precise expectations for epoch extensions in tests commit 5fc589bdd3252e4e8a4863ab77e55474b325637d Author: Jordan Schalm <jordan@dapperlabs.com> Date: Thu Jun 20 07:07:38 2024 -0700 update tests for interface change commit 5d65a0773c0cc1b53310aba888a5ccb92d5634fb Author: Jordan Schalm <jordan@dapperlabs.com> Date: Thu Jun 20 06:58:33 2024 -0700 update mocks commit 5d91f06665a6aa4055398df8addc5b823a1164c0 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Thu Jun 20 06:52:56 2024 -0700 add epoch/header context to new events commit 19ccd4e9389f13320aa069f543bfef9826af728f Author: Jordan Schalm <jordan@dapperlabs.com> Date: Thu Jun 20 06:40:02 2024 -0700 update phase/finalview metrics on epoch transition commit 0f0022e1608768d78c16532da881fda4f923feb5 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Thu Jun 20 06:29:42 2024 -0700 fix typo commit 8a36120af51ea7e40330b10807740e5b3d268427 Author: Jordan Schalm <jordan.schalm@flowfoundation.org> Date: Thu Jun 20 06:29:03 2024 -0700 Apply suggestions from code review Co-authored-by: Alexander Hentschel <alex.hentschel@flowfoundation.org> commit a9b2df064dae419aa562b1b640c9e83567f8b22d Author: Jordan Schalm <jordan@dapperlabs.com> Date: Wed Jun 19 14:34:32 2024 -0700 add todos for issue #6123 commit b30dc2dfc68541308d7779d1e2c642e89f5a44e9 Merge: 02e9d9b6a5 08929bba55 Author: Jordan Schalm <jordan.schalm@flowfoundation.org> Date: Mon Jun 17 11:41:00 2024 -0700 Merge branch 'feature/efm-recovery' into jord/6013-events-metrics commit 08929bba5582b5ad874f141e0178d86a4e7ca1c6 Merge: 29a79d06ed 6fd2ec4b44 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 12:05:24 2024 -0400 Merge pull request #5991 from onflow/khalil/5733-qc-stop-voting Stop in progress QC voting when network goes into EFM commit 6fd2ec4b443211dd422543009e5f23d8653e1173 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 11:26:06 2024 -0400 Update qc_voter_test.go commit 01d41d01b24c416878cde4d346649c21c3936166 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 11:19:33 2024 -0400 fix for loop commit a3679318c2c66c0a10a08b7a67cf8075f03a95ae Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:56:19 2024 -0400 update godoc commit baf45b3e997f525382dec90d2a4e3e32b19bdf04 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:53:31 2024 -0400 store cancel func directly commit f086cc9c0d5e1a89e9847d67d7a6c92f06d78288 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:43:48 2024 -0400 defer cancel commit 87ddec46fb8c724e1254203f45fe78eea2a34379 Merge: 089c141bc8 61e0eb1b27 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:34:51 2024 -0400 Merge branch 'khalil/5733-qc-stop-voting' of github.com:onflow/flow-go into khalil/5733-qc-stop-voting commit 61e0eb1b27385147e2cb200776bcf6a36c839f38 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:16:07 2024 -0400 Update module/epochs/qc_voter.go Co-authored-by: Jordan Schalm <jordan.schalm@flowfoundation.org> commit 91e024b99f774372df0792ed842b168ef449f575 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:15:49 2024 -0400 Update module/epochs/qc_voter_test.go Co-authored-by: Jordan Schalm <jordan.schalm@flowfoundation.org> commit 3c22d4e3a1a6a87b8a40279d12263a0e6acd9d5f Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:15:39 2024 -0400 Update module/epochs/qc_voter_test.go Co-authored-by: Jordan Schalm <jordan.schalm@flowfoundation.org> commit 9af389626883432148797752b20766d4430c8607 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:15:17 2024 -0400 Update engine/collection/epochmgr/engine_test.go Co-authored-by: Jordan Schalm <jordan.schalm@flowfoundation.org> commit 75cb96af54d9537c5d426ac73bf67b98a35a650c Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:14:33 2024 -0400 Update module/epochs/qc_voter.go Co-authored-by: Jordan Schalm <jordan.schalm@flowfoundation.org> commit ab10efd8d7ada1db2f3bf81a915c50d4b9fa7494 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:14:24 2024 -0400 Update engine/collection/epochmgr/engine_test.go Co-authored-by: Jordan Schalm <jordan.schalm@flowfoundation.org> commit 6ced75809460e03c41a894370797b2e8b5938335 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:14:12 2024 -0400 Update engine/collection/epochmgr/engine_test.go Co-authored-by: Jordan Schalm <jordan.schalm@flowfoundation.org> commit a7ae53057733913978ee7f390977e5d7b4fe4ce9 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:13:08 2024 -0400 Update engine/collection/epochmgr/engine_test.go Co-authored-by: Jordan Schalm <jordan.schalm@flowfoundation.org> commit 1c0e395e14b4f37e0cf4962fc38d7a776c74554e Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:12:16 2024 -0400 Update engine/collection/epochmgr/engine.go Co-authored-by: Jordan Schalm <jordan.schalm@flowfoundation.org> commit cd0dfcd7e4728d2712266323529a30d49802332e Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:11:24 2024 -0400 Update engine/collection/epochmgr/engine.go Co-authored-by: Jordan Schalm <jordan.schalm@flowfoundation.org> commit d71ef84e9c1bb21d6eb1393f83ce13c1be002fa7 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Mon Jun 17 10:10:35 2024 -0400 Update engine/collection/epochmgr/engine.go Co-authored-by: Jordan Schalm <jordan.schalm@flowfoundation.org> commit 8a21a541c541cdf738b5f45f01970334b397d9f9 Merge: 535ed31535 29a79d06ed Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Fri Jun 14 16:26:43 2024 -0400 Merge branch 'feature/efm-recovery' into khalil/5733-qc-stop-voting commit 535ed31535da8555e7dca9f225ffc859e16efc4e Merge: 091264c110 fda365e6ee Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Fri Jun 14 16:26:17 2024 -0400 Merge branch 'khalil/5733-qc-stop-voting' of github.com:onflow/flow-go into khalil/5733-qc-stop-voting commit 091264c110f69ab77396c226cf45b98e9df02ba0 Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Fri Jun 14 16:25:57 2024 -0400 use atomic pointer commit 02e9d9b6a5ab0686a47cf2d7d5cae997ec0c54aa Author: Jordan Schalm <jordan@dapperlabs.com> Date: Fri Jun 14 13:23:41 2024 -0700 consolidate DKG phase metrics, update godoc commit f9748fbf1a0a4c7ae12ef1223531e4db71d52fec Merge: b03776b5cd 97f16db05d Author: Jordan Schalm <jordan@dapperlabs.com> Date: Fri Jun 14 13:07:01 2024 -0700 Merge branch 'jord/6013-events-metrics' of github.com:onflow/flow-go into jord/6013-events-metrics commit b03776b5cdae581a3adf021d49413642472f5413 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Fri Jun 14 13:06:26 2024 -0700 tolerate multiple epoch extensions in a block commit 97f16db05dec08dee472523bed9d667726145cd3 Author: Jordan Schalm <jordan.schalm@flowfoundation.org> Date: Fri Jun 14 13:02:06 2024 -0700 Update state/protocol/badger/mutator.go commit fda365e6ee90883431bf17c2b966627fc50ed1fd Author: Khalil Claybon <khalil.claybon@dapperlabs.com> Date: Fri Jun 14 16:01:39 2024 -0400 Update engine/collection/epochmgr/engine.go Co-authored-by: Alexander Hentschel <alex.hentschel@flowfoundation.org> commit 5af958363ad8a9b6635fe283743c57fb028384e9 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Fri Jun 14 13:01:28 2024 -0700 update metric/event godocs commit 176b7a7313fbaf3b522d71fb3b3a8ecbce2f5136 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Fri Jun 14 12:55:36 2024 -0700 lint imports commit 7d315a481144bd67de8f96cfb0deb8c6da56d984 Merge: f015fd1329 29a79d06ed Author: Jordan Schalm <jordan@dapperlabs.com> Date: Fri Jun 14 10:35:47 2024 -0700 Merge branch 'feature/efm-recovery' into jord/6013-events-metrics commit 29a79d06ed9ec0287863da752fc40ec94de33cbd Merge: 3cccef6d14 42426f1416 Author: Jordan Schalm <jordan.schalm@flowfoundation.org> Date: Fri Jun 14 10:35:11 2024 -0700 Merge pull request #6101 from onflow/jord/sync-master-efm-2024-06-14 Sync master/efm-recovery 2024 06 14 commit 42426f1416ba3a769e5dab525cfe02d7c50c6527 Merge: 339e9ef3ee d32b10ebda Author: Jordan Schalm <jordan@dapperlabs.com> Date: Fri Jun 14 09:44:18 2024 -0700 Merge branch 'master' into jord/sync-master-efm-2024-06-14 commit 339e9ef3ee115780be1527faa24be443ba40651c Merge: 3cccef6d14 b4e2ee254a Author: Jordan Schalm <jordan@dapperlabs.com> Date: Fri Jun 14 09:39:29 2024 -0700 Merge branch 'jord/sync-master-efm' into jord/sync-master-efm-2024-06-14 commit f015fd1329aa619e25f211fd0690977a03f1a695 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Thu Jun 13 12:14:26 2024 -0700 consistent efm terminology commit d32b10ebdaea67c0c42a069b7e78f1efe64c87a3 Merge: 2818724f3c eb76ea497f Author: Ramtin M. Seraj <ramtinms@users.noreply.github.com> Date: Thu Jun 13 18:05:14 2024 +0000 Merge pull request #6089 from onflow/ramtin/fix-the-issue-with-the-tx-executed-error-message [Flow EVM] Add error message to the Tx executed event (bug fix) commit eb76ea497f3938b2ea8292082b641cf75e5c6c08 Author: ramtinms <ramtin.seraj@dapperlabs.com> Date: Thu Jun 13 09:20:07 2024 -0700 update state commitment commit dfbb133c6fce06c480615f32e5b556837e47b28e Author: Jordan Schalm <jordan@dapperlabs.com> Date: Thu Jun 13 08:52:42 2024 -0700 add efm-exited events commit 02feb0fabd65896fa931b0398e93e9fee8e07cc3 Author: ramtinms <ramtin.seraj@dapperlabs.com> Date: Wed Jun 12 23:42:01 2024 -0700 fix typo commit 44fc4dc1b9227df3e49fdc7a51f677caa2182c58 Author: ramtinms <ramtin.seraj@dapperlabs.com> Date: Wed Jun 12 23:22:15 2024 -0700 update doc commit 11bdad354cdca6c0a75b1d23fd26d46896b5ec72 Author: ramtinms <ramtin.seraj@dapperlabs.com> Date: Wed Jun 12 23:21:53 2024 -0700 . commit 6b328a1d42c2653edd938678b997f1d39e53dcae Author: ramtinms <ramtin.seraj@dapperlabs.com> Date: Wed Jun 12 23:21:04 2024 -0700 hot fix commit 341b455ede9355f5a8d7b6ef1015a702983e3317 Author: ramtinms <ramtin.seraj@dapperlabs.com> Date: Wed Jun 12 18:35:51 2024 -0700 add returned data commit b89580eb53939039875d1219d16db5568e427175 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Wed Jun 12 16:46:29 2024 -0700 update mocks commit 019049b1ed4d8d60c6149f3710e387f80ddd1c5d Author: Jordan Schalm <jordan@dapperlabs.com> Date: Wed Jun 12 16:45:07 2024 -0700 add event for exiting efm commit c89e77fb8292d6358d325f6c4290571dd5426ed7 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Wed Jun 12 16:37:14 2024 -0700 rename: epoch emergency fallback -> epoch fallback mode commit 80c8557206a5913ca632142f62676ec7dee98828 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Wed Jun 12 16:34:57 2024 -0700 remove skipped test TestExtendEpochTransitionWithoutCommit it is covered by new tests under TestEmergencyEpochFallback commit 4e9a95299931aa37af279d11baa2c806f570570d Author: Jordan Schalm <jordan@dapperlabs.com> Date: Wed Jun 12 16:32:02 2024 -0700 re-enable skipped tests commit 0e5e254fa589c1a60e6807ec01fca8b08729e355 Author: ramtinms <ramtin.seraj@dapperlabs.com> Date: Wed Jun 12 14:58:23 2024 -0700 move error message closer to error code commit a48d27a824f8c9e959d18c59adc22591ef9c504f Author: ramtinms <ramtin.seraj@dapperlabs.com> Date: Wed Jun 12 14:56:36 2024 -0700 Add error message to the Tx executed event commit 2818724f3c61deccb4e2edf1da9e13422ddbf1f3 Merge: be5509d515 e5632072b2 Author: Joshua Hannan <joshua.hannan@flowfoundation.org> Date: Wed Jun 12 21:02:38 2024 +0000 Merge pull request #6085 from onflow/update-core-contracts Updates core contracts commit e5632072b290ed7dc690ae52494da79e11b5a8ca Author: Bastian Müller <bastian@turbolent.com> Date: Wed Jun 12 13:42:27 2024 -0700 go mod tidy commit c9c1de2e8fe6cd4b8be3fd84d1d46f17e7d370f5 Author: Josh Hannan <hannanjoshua19@gmail.com> Date: Wed Jun 12 14:14:27 2024 -0500 update core contracts versions commit be5509d5151a0f9ec1805e469020ee66b4c287ef Merge: 53c970a0ef 058e3c4e76 Author: Jordan Schalm <jordan.schalm@gmail.com> Date: Wed Jun 12 20:13:59 2024 +0000 Merge pull request #6061 from onflow/jord/integration-cruise-ctl-config [Flaky Tests] Testing adjusting safety threshold and block time variables commit 53c970a0efc93fad07a45d0e78b2d860e5ecdcca Merge: 27bb2d1bc6 053e77ccfd Author: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Wed Jun 12 19:39:44 2024 +0000 Merge pull request #6081 from onflow/auto-update-onflow-cadence-v1.0.0-preview.34 Update to Cadence v1.0.0-preview.34 commit 27bb2d1bc6c9cca4e9e0a3721a440692ed046c3d Merge: ab907b9b44 b86ca1c87b Author: Bastian Müller <bastian@turbolent.com> Date: Wed Jun 12 19:08:21 2024 +0000 Merge pull request #6082 from onflow/petera/fix-flaky-access-tests [CI] Remove cruise control overrides in Access integration tests commit b86ca1c87b0a63b0a41a9a8260bb5f4efc14c65b Author: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Wed Jun 12 10:41:09 2024 -0700 [CI] Remove cruise control overrides in Access integration tests commit 053e77ccfd828e4592a164c569c7bab2540e9555 Author: Bastian Müller <bastian@turbolent.com> Date: Wed Jun 12 10:27:37 2024 -0700 adjust tests: add new visits, account for them in metrics commit e5cc4766de0ee7967b35a0ad88489387bdedea82 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Wed Jun 12 10:14:14 2024 -0700 update tests for metric changes commit 6bd2e01996777d10c3b61c61bbd652d63776af62 Author: Bastian Müller <bastian@turbolent.com> Date: Wed Jun 12 09:55:57 2024 -0700 Update to Cadence v1.0.0-preview.34 commit ab907b9b44892b94c7739208d8908b929eb37dc7 Merge: 7a6d8a7807 22daffdb72 Author: Leo Zhang <zhangchiqing@gmail.com> Date: Wed Jun 12 16:24:44 2024 +0000 Merge pull request #6042 from onflow/leo/pebble-chunk-data-packs Pebble based chunk data packs storage commit 7a6d8a7807a4511698fcbc7622229b829a7efa5d Merge: da4f52ccf1 6a430397fe Author: Gregor G <75445744+sideninja@users.noreply.github.com> Date: Wed Jun 12 15:05:41 2024 +0000 Merge pull request #6059 from m-Peter/evm-dry-run-compute-gas-refunds [EVM] Take into account gas refunds in `EVM.dryRun` commit cd852c62833da0c9ba51a7f2f0b5ae22b938ff6c Author: Jordan Schalm <jordan@dapperlabs.com> Date: Wed Jun 12 08:00:12 2024 -0700 update event godoc commit 3a2c757b3b998594129718bbb192e37e14cbd897 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Wed Jun 12 07:55:45 2024 -0700 rename EFM event and metric commit da4f52ccf188e73da5fb2be18b13524a30377c95 Merge: 09b0870897 9e5913c020 Author: Janez Podhostnik <67895329+janezpodhostnik@users.noreply.github.com> Date: Wed Jun 12 14:32:53 2024 +0000 Merge pull request #6044 from onflow/janez/change-metrics-collection-in-computer Change metrics collection in computer commit 09b08708974c04bec3e5287dac28c0a41826baf7 Merge: 216b3ce086 f1a3a3999d Author: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue Jun 11 22:48:23 2024 +0000 Merge pull request #6070 from onflow/fxamacker/fix-migration-missing-path-cap-domain Fix Cadence 1.0 migration missing PathCapabilityStorageDomain commit 216b3ce08691d396d2cd746051e20a25f1ee7374 Merge: 36575f8a38 b145fe1be8 Author: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue Jun 11 22:33:26 2024 +0000 Merge pull request #6063 from onflow/fxamacker/add-util-check-atree-inlined-status Add atree-inlined-status command to util program to check migration results commit 118ffebb52923e1cd9c02764d36a31947b835ae8 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Tue Jun 11 14:57:36 2024 -0700 use Noop in FinalizedReader commit f6a249e6accfb691f5c1b2200cca47a8a52a5562 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Tue Jun 11 14:52:15 2024 -0700 update tests commit f1a3a3999db9bc0bdb94f5ef05f465cfbf7513c5 Merge: 3075a06413 bbae923116 Author: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue Jun 11 16:35:39 2024 -0500 Merge pull request #6074 from onflow/fxamacker/fix-migration-missing-acc-cap-domain Fix migrations missing AccountCapabilityStorageDomain commit bbae9231164254d52b485de7b1906302b1040bdc Author: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue Jun 11 16:15:19 2024 -0500 Fix migrations missing AccountCapabilityStorageDomain Currently, migrations don't include AccountCapabilityStorageDomain during traversal and storage health check. This causes payloads in the domain to not be migrated. This affects atree migration and maybe also Cadence 1.0 migration. This commit adds AccountCapabilityStorageDomain to migrations. commit 22daffdb72b8fbd1f7434d0e1aa44a1b5e909f13 Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Tue Jun 11 11:50:24 2024 -0700 address review comments commit cde5e81fa433856afaee118f8cda6e4115dd4eee Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Mon Jun 10 16:35:24 2024 -0700 add comments commit 466f9a6e1cac5b3f8650cea8651aca4f1f181557 Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Mon Jun 10 09:30:10 2024 -0700 fix chunk data pack codec commit 7e8d0d6bde956ed33a36bd6839ce8667759a573c Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Mon Jun 10 09:06:53 2024 -0700 update mock commit ad06637cee71a5fe41f6c1121fe78238c24b2636 Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Fri Jun 7 15:02:51 2024 -0700 fix lint commit 03fdb3924837fa73e282c680d96a950ad9cb535b Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Fri Jun 7 12:57:59 2024 -0700 extract batch commit 86e6267666475ca12bac231c47bfe9a39591fceb Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Fri Jun 7 11:46:57 2024 -0700 move modules to operations package commit 7efc768e95421a810a7a89ce2dd5a204d5e0f2c7 Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Fri Jun 7 11:41:23 2024 -0700 implement pebble chunk data pack with cache commit b511b0a6f1097139d138ad706c040cfb4ccd326c Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Thu Jun 6 15:44:48 2024 -0700 tmp commit fc774476f32d8d095cbc74faa33dd7bb07035a00 Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Wed Jun 5 16:13:48 2024 -0700 use pebble based chunk data pack storage commit 14dcad5e8f45044d75cfc410462ccaf98846dd07 Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Wed Jun 5 15:55:16 2024 -0700 implement pebble chunk data pack commit d907833726e22b27bbf234231163362f427b8eda Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Wed Jun 5 11:59:46 2024 -0700 move StoredChunkDataPack to storage package commit a96af388b551e3b7211d7d0d3d79bd839798d6fb Author: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Date: Wed Jun 5 10:56:48 2024 -0700 update chunk data pack storage interface commit 3075a064135d80d2ce5a81dd95b89b8223d8548f Author: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue Jun 11 14:45:16 2024 -0500 Fix migrations missing PathCapabilityStorageDomain Currently, migrations don't include PathCapabilityStorageDomain during traversal and storage health check. This causes payloads in the domain to not be migrated. This commit adds PathCapabilityStorageDomain to migrations. commit b145fe1be83e48739360ecc8d97606cfb953998d Author: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Tue Jun 11 12:18:49 2024 -0500 Extract readTrie and parseStateCommitment funcs commit 6a430397fe191d5a0eb5c8c89cb5004cfb693aef Merge: b6fc9e1ea3 36575f8a38 Author: Gregor G <75445744+sideninja@users.noreply.github.com> Date: Tue Jun 11 14:53:45 2024 +0200 Merge branch 'master' into evm-dry-run-compute-gas-refunds commit b6fc9e1ea349062b7fbbb9d575421f9d213518e2 Author: Ardit Marku <markoupetr@gmail.com> Date: Tue Jun 11 13:00:52 2024 +0300 Add the Successful() method on Result and improve tests for EVM.dryRun commit 8d9b45ab91d9fac1b0c4a1bad8774bcb8fec4538 Merge: 8c59995dc8 36575f8a38 Author: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon Jun 10 20:41:16 2024 -0500 Merge branch 'master' into fxamacker/add-util-check-atree-inlined-status commit 8c59995dc8f4317c320964a52a63abf82255026e Author: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon Jun 10 20:14:08 2024 -0500 Add atree-inlined-status command to util program atree-inlined-status command examines atree payloads and outputs number of payloads inlined, not inlined, etc. in JSON format. Among several other flags, the program accepts - n-workers (default=8) - n-payloads (default=all, number of payloads to sample) This can be used for testing and debugging migration results. commit d9b9d3a8a083b43b9fa0ba60a50b67d652369e44 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Mon Jun 10 17:21:45 2024 -0700 mock new metrics/events commit 5aa5d343fde943b81dad27531fea2e30284ed454 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Mon Jun 10 17:04:06 2024 -0700 update mocks commit 8760e36b834eafd57fe69b16f536eafb215416ef Author: Jordan Schalm <jordan@dapperlabs.com> Date: Mon Jun 10 16:58:34 2024 -0700 add EpochExtended event commit 36575f8a38b579a9f08e6a7855523080a78476df Merge: 952cbe49c1 2d76ad360e Author: Peter Argue <89119817+peterargue@users.noreply.github.com> Date: Mon Jun 10 21:57:40 2024 +0000 Merge pull request #5969 from AndriiDiachuk/choose-execution-nodes-preferred-EN-ids-fix [Access] chooseExecutionNodes fix commit e2460c7b51853deef2b01b8a673269f5c1af3a25 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Mon Jun 10 14:28:16 2024 -0700 add godoc to ComplianceMetrics commit 786a7733f2bf7d79844270bf86d3f1150e92431f Merge: 5ed22e7ae2 3cccef6d14 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Mon Jun 10 14:14:37 2024 -0700 Merge branch 'feature/efm-recovery' into jord/6013-events-metrics commit 3cccef6d143f7a45e1540dd4c9efcdd8cc7507dd Author: Jordan Schalm <jordan@dapperlabs.com> Date: Mon Jun 10 13:55:40 2024 -0700 Sync `master` into `feature/efm-recovery` (#6058) * Update cmd/util/cmd/diff-states/cmd.go * adjust test * merge cleanup * Update engine/consensus/matching/core.go Co-authored-by: Jordan Schalm <jordan@dapperlabs.com> * added logging key for byzantine protocol violation * Update cmd/util/ledger/migrations/cadence_values_migration.go Co-authored-by: Faye Amacker <33205765+fxamacker@users.noreply.github.com> * fix suggestion * fix error handling / logging * Removed replace * Added check for uninitialized error * Apply suggestions from code review Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> * set HighestIndexedHeight to 0 on index not initialize error. * mod tidy * temp tracer * test for tracer * use errgroup for concurrent account migration * Apply suggestions from code review Co-authored-by: Alexander Hentschel <alex.hentschel@flowfoundation.org> * rename RichEpochProtocolStateEntry comments, constructor, etc. * EpochProtocolState interface updates * upgrade emulator * rename EpochProtocolStateFromServiceEvents * rename RootEpochProtocolStateFixture * Add migration metrics collection * update epoch protocol state naming in storage layer * EpochProtocolStateEntries godoc * rename EpochProtocolStateEntries in all.go * Collect total values/objects for each contract * add get full collection api * add access fetcher * fix assigning collection requester * add log * add retry * fix retry * use tls * convert port * go mod tidy * add validation * Update to Cadence v1.0.0-preview.30 * remove caching, it is no longer available * adjust tests to fixed String() * Add flag for reporting metrics * update mocks * update mock * Update storage/badger/epoch_protocol_state.go * Also include attachments * add weight for new memory kind * Fix tests * lint * fix lint * handle panics while traversing * Read-lock metric generation * change batch size * TPS fixes * reduce evm batc size * change evm batch size * adjust batch size * experiment with reseting tps after setup * fix adjuster * test * fix set tps bug * test 2 * getting there * 50 per batch * batch size of 100 * next measurement * next measurement * next measurement * next measurement * Update state/protocol/inmem/epoch_protocol_state.go Co-authored-by: Yurii Oleksyshyn <yuraolex@gmail.com> * fixes * fixes * lint fix * disable script checking by default * Report storage traversing errors during metrics collection * add transaction tracer * add context evm debug params * create a tracer and trace with upload * add with transaction tracer option in config * add evm debug params in fvm context * add tracer on block context * wip test for trying out tracer * uploader first draft * Updated TestOnFinalizedBlockSeveralBlocksAhead and TestOnFinalizedBlockSingle tests * Removed unnecessary mock call * refactor to evm tracer * refactor to usage of interface tracer * implement evm tracer and nop tracer * add basic uploader * use evm tracer * rename of tracer tx * not needed * rename * add default client * add tracer test * pass in transaction id * add import for side-effects * add simple tracer test * add nop tracer test * move needed const * add gcp upload test * rename * Apply suggestions from code review Co-authored-by: Yurii Oleksyshyn <yuraolex@gmail.com> * Fixed remarks * log fetching collection * update buffer * add debug log * add uploader test * add integration upload tracer test * use logger * update test logger * remove local replace * revert emulator test * change nop tracer * update handler with nop tracer * Updated test by adding seals to block correctly * mock uploader * mock uploader reuse * test collection of contract call trace * Add the GetErrorForCode helper method This method performs is the inverse mapping of ExecutionErrorCode and ValidationErrorCode. It returns the corresponding error object, given an ErrorCode. * Rename GetErrorForCode to ErrorFromCode * Calculate TotalGasUsed for Tx batch runs and COA calls * add more tests for batch run and run tx * add collection of metrics in all the methods * Update test assertions for TotalGasUsed * add contract deploy at test * Updated test * Linted * add test for handler batch run with trace * remove check due to cd * bucket name configurable * configuring of execution node evm tracing * change logger name * Report additional logs * improve error handling of coa proof verification * add test for invalid proofs * add tracer to deploy at * add trace failure execution test * add retry logic * move collect at the bottom * extract constant values * Fix error message * don't trace scripts * Update to Cadence v1.0.0-preview.32 * add new type key migration * add comment * wip finalized block queue * remove unneeded code * remove map * wip changes on the subscription to the finalized blocks * Revert "wip changes on the subscription to the finalized blocks" This reverts commit 1b737bfe31e075d40a237f6fb746c9e7be1796fb. * Revert "remove map" This reverts commit 38ac28d5ea3ac82e5e30a0b270d28d33e5d0ec06. * Revert "remove unneeded code" This reverts commit 646bc47432d22fde0a90b734ea14fd1fcbc1cb45. * Revert "wip finalized block queue" This reverts commit eaa022bb0a92dfad081ff1cada694c168f0b0ab5. * remove retry complexity * limit to 5 min upload * add block id to evm setup * collect with block id * update tests * exposing evm error msg as part of event * update event decoding * update to Go 1.22 * run Go generate on all packages * go mod tidy * update mockery * change block id name * use nop tracer * tidy * update tests * add collector panic test * fix tests * expose error message to EVM.Result * update state commitments * more state commitment updates * remove unused * check checkpoint has only 1 trie before importing * fix tests * add default nop tracer * update error message name * make helper for trace id * set block id * fix tests with block id * commitment change due to evm contract change * update state commitments * fix lint * disable cruisectl by default in integration tests also remove unneeded hotstuff startup time flag * add test for custom error * capture ret data when tx is failed (evm revert) * add returned value to tx events * emit ret data as part of tx event * rename * unify return data name * update state commitments * Update to Cadence v1.0.0-preview.33 * update new instances of changed naming * update metrics to accomodate current state of EFM --------- Co-authored-by: Bastian Müller <bastian@turbolent.com> Co-authored-by: Alexander Hentschel <alex.hentschel@axiomzen.co> Co-authored-by: Alexander Hentschel <alex.hentschel@flowfoundation.org> Co-authored-by: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Co-authored-by: Andrii Slisarchuk <Guitarheroua@users.noreply.github.com> Co-authored-by: Andrii Slisarchuk <andriyslisarchuk@gmail.com> Co-authored-by: Andrii Diachuk <andriy.dyachuk95@gmail.com> Co-authored-by: Peter Argue <89119817+peterargue@users.noreply.github.com> Co-authored-by: sideninja <75445744+sideninja@users.noreply.github.com> Co-authored-by: Leo Zhang (zhangchiqing) <zhangchiqing@gmail.com> Co-authored-by: Supun Setunga <supun.setunga@gmail.com> Co-authored-by: Janez Podhostnik <67895329+janezpodhostnik@users.noreply.github.com> Co-authored-by: Janez Podhostnik <janez.podhostnik@gmail.com> Co-authored-by: Yurii Oleksyshyn <yuraolex@gmail.com> Co-authored-by: UlyanaAndrukhiv <u.andrukhiv@gmail.com> Co-authored-by: j pimmel <frankly.watson@gmail.com> Co-authored-by: Ardit Marku <markoupetr@gmail.com> Co-authored-by: j pimmel <jerome.pimmel@flowfoundation.org> Co-authored-by: ramtinms <ramtin.seraj@dapperlabs.com> Co-authored-by: Ramtin M. Seraj <ramtinms@users.noreply.github.com> commit 058e3c4e76f292c060957b7a6111fa2a25f3dc57 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Mon Jun 10 13:03:22 2024 -0700 use 250ms block time across all epoch tests commit 5ed22e7ae2e7c1fe7ca377b7f0529f5d825f0c60 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Mon Jun 10 08:45:42 2024 -0700 sketch new events location commit d3af3c6cbde79df90abae09a66197bccb133b050 Author: Ardit Marku <markoupetr@gmail.com> Date: Mon Jun 10 15:24:52 2024 +0300 Take into account gas refunds in EVM.dryRun To correctly compute the gas estimation with EVM.dryRun, we need to add any gas refunds to the used gas. Any potential gas refunds are required during transaction execution, and only after they are refunded to the transaction author. commit 2d76ad360ea8c00a7d00206260523e143f7b8657 Author: Andrii <andriy.dyachuk95@gmail.com> Date: Mon Jun 10 13:32:10 2024 +0300 Fixed tests commit 1c62819191b14de48c521f0d2f38efc7583c5dae Merge: ef71002708 8e6ef660a5 Author: Andrii <andriy.dyachuk95@gmail.com> Date: Mon Jun 10 12:59:15 2024 +0300 Merge branch 'choose-execution-nodes-preferred-EN-ids-fix' of github.com:AndriiDiachuk/flow-go into choose-execution-nodes-preferred-EN-ids-fix commit ef71002708c2ccc75aa18b226764cc6a9001cd94 Merge: 6df9278ae5 952cbe49c1 Author: Andrii <andriy.dyachuk95@gmail.com> Date: Mon Jun 10 12:59:00 2024 +0300 Merge branch 'master' of github.com:AndriiDiachuk/flow-go into choose-execution-nodes-preferred-EN-ids-fix commit b4e2ee254ac6ebfc0a0ea0b292f0eae5f6bd75a4 Author: Jordan Schalm <jordan@dapperlabs.com> Date: Fri Jun 7 15:58:01 2024 -0700 update metrics to accomodate current state of EFM commit 5347f36d5d5c0fadd4b19850ec789c486b08a5dc Author: Jordan Schalm <jordan@dapperlabs.com> Date: Fri Jun 7 15:09:00 2024 -0700 update new instances of changed naming commit 9501352ee877db7865902a460406b3533d6d0b22 Merge: 0dddc9e10f 61b1f2679a Author: Jordan Schalm <jordan@dapperlabs.com> Date: Fri Jun 7 14:59:33 2024 -0700 Merge branch 'master' into jord/sync-master-efm address conflicts, including re-generating mocks Text of conflicts below: diff --cc model/flow/protocol_state.go index 4c8bba49bb,53798a1353..0000000000 --- a/model/flow/protocol_state.go +++ b/model/flow/protocol_state.go @@@ -162,20 -147,13 +162,28 @@@ func NewRichEpochProtocolStateEntry NextEpochIdentityTable: IdentityList{}, } - // If previous epoch is specified: ensure respective epoch service events are not nil and consistent with commitments in `ProtocolStateEntry.PreviousEpoch` + // If previous epoch is specified: ensure respective epoch service events are not nil and consistent with commitments in `EpochProtocolStateEntry.PreviousEpoch` if protocolState.PreviousEpoch != nil { ++<<<<<<< HEAD + if protocolState.PreviousEpoch.SetupID != previousEpochSetup.ID() { // calling ID() will panic if EpochSetup event is nil + return nil, fmt.Errorf("supplied previous epoch's setup event (%x) does not match commitment (%x) in ProtocolStateEntry", previousEpochSetup.ID(), protocolState.PreviousEpoch.SetupID) + } + if protocolState.PreviousEpoch.CommitID != previousEpochCommit.ID() { // calling ID() will panic if EpochCommit event is nil + return nil, fmt.Errorf("supplied previous epoch's commit event (%x) does not match commitment (%x) in ProtocolStateEntry", previousEpochCommit.ID(), protocolState.PreviousEpoch.CommitID) ++======= + if protocolState.PreviousEpoch.SetupID != previousEpochSetup.ID() { // calling ID() will panic is EpochSetup event is nil + return nil, fmt.Errorf("supplied previous epoch's setup event (%x) does not match commitment (%x) in EpochProtocolStateEntry", previousEpochSetup.ID(), protocolState.PreviousEpoch.SetupID) + } + if protocolState.PreviousEpoch.CommitID != previousEpochCommit.ID() { // calling ID() will panic is EpochCommit event is nil + return nil, fmt.Errorf("supplied previous epoch's commit event (%x) does not match commitment (%x) in EpochProtocolStateEntry", previousEpochCommit.ID(), protocolState.PreviousEpoch.CommitID) ++>>>>>>> master + } + } else { + if previousEpochSetup != nil { + return nil, fmt.Errorf("no previous epoch but gotten non-nil EpochSetup event") + } + if previousEpochCommit != nil { + return nil, fmt.Errorf("no previous epoch but gotten non-nil EpochCommit event") } } @@@ -227,12 -198,8 +235,12 @@@ } if nextEpoch.CommitID != ZeroID { if nextEpoch.CommitID != nextEpochCommit.ID() { - return nil, fmt.Errorf("supplied next epoch's commit event (%x) does not match commitment (%x) in ProtocolStateEntry", nextEpoch.CommitID, nextEpochCommit.ID()) + return nil, fmt.Errorf("supplied next epoch's commit event (%x) does not match commitment (%x) in EpochProtocolStateEntry", nextEpoch.CommitID, nextEpochCommit.ID()) } + } else { + if nextEpochCommit != nil { + return nil, fmt.Errorf("next epoch not yet committed but got EpochCommit event") + } } result.CurrentEpochIdentityTable, err = BuildIdentityTable( @@@ -285,11 -252,11 +293,19 @@@ func (e *EpochProtocolStateEntry) Copy( if e == nil { return nil } ++<<<<<<< HEAD + return &ProtocolStateEntry{ + PreviousEpoch: e.PreviousEpoch.Copy(), + CurrentEpoch: *e.CurrentEpoch.Copy(), + NextEpoch: e.NextEpoch.Copy(), + EpochFallbackTriggered: e.EpochFallbackTriggered, ++======= + return &EpochProtocolStateEntry{ + PreviousEpoch: e.PreviousEpoch.Copy(), + CurrentEpoch: *e.CurrentEpoch.Copy(), + NextEpoch: e.NextEpoch.Copy(), + InvalidEpochTransitionAttempted: e.InvalidEpochTransitionAttempted, ++>>>>>>> master } } @@@ -313,20 -280,9 +329,20 @@@ func (e *RichEpochProtocolStateEntry) C } } +// CurrentEpochFinalView returns the final view of the current epoch, taking into account possible epoch extensions. +// If there are no epoch extensions, the final view is the final view of the current epoch setup, +// otherwise it is the final view of the last epoch extension. +func (e *RichProtocolStateEntry) CurrentEpochFinalView() uint64 { + l := len(e.CurrentEpoch.EpochExtensions) + if l > 0 { + return e.CurrentEpoch.EpochExtensions[l-1].FinalView + } + return e.CurrentEpochSetup.FinalView +} + // EpochPhase returns the current epoch phase. - // The receiver ProtocolStateEntry must be properly constructed. - func (e *ProtocolStateEntry) EpochPhase() EpochPhase { + // The receiver EpochProtocolStateEntry must be properly constructed. + func (e *EpochProtocolStateEntry) EpochPhase() EpochPhase { // The epoch phase is determined by how much information we have about the next epoch if e.NextEpoch == nil { return EpochPhaseStaking // if no information about the next epoch is known, we are in the Staking Phase diff --cc state/protocol/badger/mutator.go index 230bd73227,395aa197ce..0000000000 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@@ -662,49 -691,36 +691,77 @@@ func (m *FollowerState) Finalize(ctx co // We update metrics and emit protocol events for epoch state changes when // the block corresponding to the state change is finalized ++<<<<<<< HEAD + parentEpochStateSnapshot, err := m.protocolState.AtBlockID(header.ParentID) + if err != nil { + return fmt.Errorf("could not retrieve parent protocol state snapshot: %w", err) + } + epochStateSnapshot, err := m.protocolState.AtBlockID(blockID) + if err != nil { + return fmt.Errorf("could not retrieve protocol state snapshot: %w", err) + } + currentEpochSetup := epochStateSnapshot.EpochSetup() + epochFallbackTriggered := epochStateSnapshot.EpochFallbackTriggered() + + // if epoch fallback was not previously triggered, check whether this block triggers it + if epochFallbackTriggered && !parentEpochStateSnapshot.EpochFallbackTriggered() { ++======= + parentEpochState, err := m.protocolState.EpochStateAtBlockID(block.Header.ParentID) + if err != nil { + return fmt.Errorf("could not retrieve protocol state snapshot for parent: %w", err) + } + finalizingEpochState, err := m.protocolState.EpochStateAtBlockID(blockID) + if err != nil { + return fmt.Errorf("could not retrieve protocol state snapshot: %w", err) + } + currentEpochSetup := finalizingEpochState.EpochSetup() + epochFallbackTriggered, err := m.isEpochEmergencyFallbackTriggered() + if err != nil { + return fmt.Errorf("could not check persisted epoch emergency fallback flag: %w", err) + } + + // if epoch fallback was not previously triggered, check whether this block triggers it + // TODO(efm-recovery): remove separate global EFM flag + if !epochFallbackTriggered && finalizingEpochState.InvalidEpochTransitionAttempted() { + epochFallbackTriggered = true ++>>>>>>> master // emit the protocol event only the first time epoch fallback is triggered events = append(events, m.consumer.EpochEmergencyFallbackTriggered) metrics = append(metrics, m.metrics.EpochEmergencyFallbackTriggered) } - isFirstBlockOfEpoch, err := m.isFirstBlockOfEpoch(header, currentEpochSetup) + // Determine metric updates and protocol events related to epoch phase changes and epoch transitions. + epochPhaseMetrics, epochPhaseEvents, err := m.epochMetricsAndEventsOnBlockFinalized(parentEpochState, finalizingEpochState, header) if err != nil { ++<<<<<<< HEAD + return fmt.Errorf("could not check if block is first of epoch: %w", err) + } + if isFirstBlockOfEpoch { + epochTransitionMetrics, epochTransitionEvents := m.epochTransitionMetricsAndEventsOnBlockFinalized(header, currentEpochSetup) + if err != nil { + return fmt.Errorf("could not determine epoch transition metrics/events for finalized block: %w", err) + } + metrics = append(metrics, epochTransitionMetrics...) + events = append(events, epochTransitionEvents...) + } + + // Determine metric updates and protocol events related to epoch phase changes and epoch transitions. + // If epoch emergency fallback is triggered, the current epoch continues until + // the next spork - so skip these updates. + // TODO(EFM, #5732, #6013): needs update for EFM recovery + if !epochFallbackTriggered { + epochPhaseMetrics, epochPhaseEvents, err := m.epochPhaseMetricsAndEventsOnBlockFinalized(block) + if err != nil { + return fmt.Errorf("could not determine epoch phase metrics/events for finalized block: %w", err) + } + metrics = append(metrics, epochPhaseMetrics...) + events = append(events, epochPhaseEvents...) ++======= + return fmt.Errorf("could not determine epoch phase metrics/events for finalized block: %w", err) ++>>>>>>> master } + metrics = append(metrics, epochPhaseMetrics...) + events = append(events, epochPhaseEvents...) // Extract and validate version beacon events from the block seals. versionBeacons, err := m.versionBeaconOnBlockFinalized(block) @@@ -732,7 -748,14 +789,18 @@@ if err != nil { return fmt.Errorf("could not update sealed height: %w", err) } ++<<<<<<< HEAD + if isFirstBlockOfEpoch { ++======= + if epochFallbackTriggered { + err = operation.SetEpochEmergencyFallbackTriggered(blockID)(tx) + if err != nil { + return fmt.Errorf("could not set epoch fallback flag: %w", err) + } + } + // TODO(efm-recovery): we should be able to omit the `!epochFallbackTriggered` check here. + if isFirstBlockOfEpoch(parentEpochState, finalizingEpochState) && !epochFallbackTriggered { ++>>>>>>> master err = operation.InsertEpochFirstHeight(currentEpochSetup.Counter, header.Height)(tx) if err != nil { return fmt.Errorf("could not insert epoch first block height: %w", err) diff --cc state/protocol/badger/state.go index beef6343ec,298f0f5c03..0000000000 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@@ -968,3 -969,16 +968,19 @@@ func (state *State) populateCache() err return nil } ++<<<<<<< HEAD ++======= + + // isEpochEmergencyFallbackTriggered checks whether epoch fallback has been globally triggered. + // TODO(efm-recovery): Stop storing a global EFM flag, use parentState.EFMTriggered instead + // + // Returns: + // * (true, nil) if epoch fallback is triggered + // * (false, nil) if epoch fallback is not triggered (including if the flag is not set) + // * (false, err) if an unexpected error occurs + func (state *State) isEpochEmergencyFallbackTriggered() (bool, error) { + var triggered bool + err := state.db.View(operation.CheckEpochEmergencyFallbackTriggered(&triggered)) + return triggered, err + } ++>>>>>>> master diff --cc state/protocol/mock/epoch_protocol_state.go index a8685f222f,2ad2840d11..0000000000 --- a/state/protocol/mock/epoch_protocol_state.go +++ b/state/protocol/mock/epoch_protocol_state.go @@@ -112,24 -132,14 +132,28 @@@ func (_m *EpochProtocolState) EpochComm return r0 } +// EpochFallbackTriggered provides a mock function with given fields: +func (_m *DynamicProtocolState) EpochFallbackTriggered() bool { + ret := _m.Called() + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + // EpochPhase provides a mock function with given fields: - func (_m *DynamicProtocolState) EpochPhase() flow.EpochPhase { + func (_m *EpochProtocolState) EpochPhase() flow.EpochPhase { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for EpochPhase") + } + var r0 flow.EpochPhase if rf, ok := ret.Get(0).(func() flow.EpochPhase); ok { r0 = rf() @@@ -188,10 -210,32 +224,35 @@@ func (_m *EpochProtocolState) Identitie return r0 } ++<<<<<<< HEAD:state/protocol/mock/dynamic_protocol_state.go ++======= + // InvalidEpochTransitionAttempted provides a mock function with given fields: + func (_m *EpochProtocolState) InvalidEpochTransitionAttempted() bool { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for InvalidEpochTransitionAttempted") + } + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 + } + ++>>>>>>> master:state/protocol/mock/epoch_protocol_state.go // PreviousEpochExists provides a mock function with given fields: - func (_m *DynamicProtocolState) PreviousEpochExists() bool { + func (_m *EpochProtocolState) PreviousEpochExists() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for PreviousEpochExists") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() diff --cc state/protocol/mock/instance_params.go index 7b42147d2d,0ca3db5c8c..0000000000 --- a/state/protocol/mock/instance_params.go +++ b/state/protocol/mock/instance_params.go @@@ -12,6 -12,34 +12,37 @@@ type InstanceParams struct mock.Mock } ++<<<<<<< HEAD ++======= + // EpochFallbackTriggered provides a mock function with given fields: + func (_m *InstanceParams) EpochFallbackTriggered() (bool, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for EpochFallbackTriggered") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func() (bool, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 + } + ++>>>>>>> master // FinalizedRoot provides a mock function with given fields: func (_m *InstanceParams) FinalizedRoot() *flow.Header { ret := _m.Called() diff --cc state/protocol/mock/params.go index 677ba3d9ff,56cad5a925..0000000000 --- a/state/protocol/mock/params.go +++ b/state/protocol/mock/params.go @@@ -40,6 -48,34 +48,37 @@@ func (_m *Params) EpochCommitSafetyThre return r0 } ++<<<<<<< HEAD ++======= + // EpochFallbackTriggered provides a mock function with given fields: + func (_m *Params) EpochFallbackTriggered() (bool, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for EpochFallbackTriggered") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func() (bool, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 + } + ++>>>>>>> master // FinalizedRoot provides a mock function with given fields: func (_m *Params) FinalizedRoot() *flow.Header { ret := _m.Called() diff --cc state/protocol/protocol_state.go index 8dd9877b24,2dbbb457ab..0000000000 --- a/state/protocol/protocol_state.go +++ b/state/protocol/protocol_state.go @@@ -5,37 -5,46 +5,54 @@@ import "github.com/onflow/flow-go/storage/badger/transaction" ) - // InitialProtocolState returns constant data for given epoch. - // This interface can be only obtained for epochs that have progressed to epoch commit event. - type InitialProtocolState interface { - // Epoch returns counter of epoch. + // EpochProtocolState represents the subset of the Protocol State KVStore related to epochs: + // the Identity Table, DKG, cluster assignment, etc. + // EpochProtocolState is fork-aware and can change on a block-by-block basis. + // Each EpochProtocolState instance refers to the state with respect to some reference block. + type EpochProtocolState interface { + // Epoch returns the current epoch counter. Epoch() uint64 + // Clustering returns initial clustering from epoch setup. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! // No errors are expected during normal operations. Clustering() (flow.ClusterList, error) + // EpochSetup returns original epoch setup event that was used to initialize the protocol state. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! EpochSetup() *flow.EpochSetup + // EpochCommit returns original epoch commit event that was used to update the protocol state. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! EpochCommit() *flow.EpochCommit + // DKG returns information about DKG that was obtained from EpochCommit event. + // CAUTION: This describes the initial epoch configuration from the view point of the Epoch + // Smart Contract. It does _not_ account for subsequent node ejections. For Byzantine Fault + // Tolerance, the calling code must account for ejections! // No errors are expected during normal operations. DKG() (DKG, error) - // Entry Returns low-level protocol state entry that was used to initialize this object. - // It shouldn't be used by high-level logic, it is useful for some cases such as bootstrapping. - // Prefer using other methods to access protocol state. - Entry() *flow.RichProtocolStateEntry - } - - // DynamicProtocolState extends the InitialProtocolState with data that can change from block to block. - // It can be used to access the identity table at given block. - type DynamicProtocolState interface { - InitialProtocolState ++<<<<<<< HEAD + // EpochFallbackTriggered denotes whether an invalid epoch state transition was attempted + // on the fork ending in this block. Once the first block where this flag is true is finalized, epoch + // fallback mode is triggered. This flag is reset to false when finalizing a block that seals + // a valid EpochRecover service event. + EpochFallbackTriggered() bool ++======= + // InvalidEpochTransitionAttempted denotes whether an invalid epoch state transition was attempted + // on the fork ending this block. Once the first block where this flag is true is finalized, epoch + // fallback mode is triggered. + // TODO for 'leaving Epoch Fallback via special service event': at the moment, this is a one-way transition and requires a spork to recover - need to revisit for sporkless EFM recovery + InvalidEpochTransitionAttempted() bool + ++>>>>>>> master // PreviousEpochExists returns true if a previous epoch exists. This is true for all epoch // except those immediately following a spork. PreviousEpochExists() bool diff --cc state/protocol/protocol_state/epochs/factory.go index 6dd9884e85,4abce702ad..0000000000 --- a/state/protocol/protocol_state/epochs/factory.go +++ b/state/protocol/protocol_state/epochs/factory.go @@@ -44,11 -44,11 +44,16 @@@ func (f *EpochStateMachineFactory) Crea f.epochProtocolStateDB, parentState, mutator, - func(candidateView uint64, parentState *flow.RichProtocolStateEntry) (StateMachine, error) { + func(candidateView uint64, parentState *flow.RichEpochProtocolStateEntry) (StateMachine, error) { return NewHappyPathStateMachine(candidateView, parentState) }, ++<<<<<<< HEAD + func(candidateView uint64, parentState *flow.RichProtocolStateEntry) (StateMachine, error) { + return NewFallbackStateMachine(f.params, candidateView, parentState) ++======= + func(candidateView uint64, parentState *flow.RichEpochProtocolStateEntry) (StateMachine, error) { + return NewFallbackStateMachine(candidateView, parentState), nil ++>>>>>>> master }, ) } diff --cc state/protocol/protocol_state/epochs/fallback_statemachine.go index 8237488a90,e8719615c5..0000000000 --- a/state/protocol/protocol_state/epochs/fallback_statemachine.go +++ b/state/protocol/protocol_state/epochs/fallback_statemachine.go @@@ -24,22 -16,12 +24,31 @@@ type FallbackStateMachine struct var _ StateMachine = (*FallbackStateMachine)(nil) ++<<<<<<< HEAD +// NewFallbackStateMachine constructs a state machine for epoch fallback. It automatically sets +// EpochFallbackTriggered to true, thereby recording that we have entered epoch fallback mode. +// No errors are expected during normal operations. +func NewFallbackStateMachine(params protocol.GlobalParams, view uint64, parentState *flow.RichProtocolStateEntry) (*FallbackStateMachine, error) { + state := parentState.ProtocolStateEntry.Copy() + nextEpochCommitted := state.EpochPhase() == flow.EpochPhaseCommitted + // we are entering fallback mode, this logic needs to be executed only once + if !state.EpochFallbackTriggered { + // the next epoch has not been committed, but possibly setup, make sure it is cleared + if !nextEpochCommitted { + state.NextEpoch = nil + } + state.EpochFallbackTriggered = true + } + + sm := &FallbackStateMachine{ ++======= + // NewFallbackStateMachine constructs a state machine for epoch fallback, it automatically sets + // InvalidEpochTransitionAttempted to true, thereby recording that we have entered epoch fallback mode. + func NewFallbackStateMachine(view uint64, parentState *flow.RichEpochProtocolStateEntry) *FallbackStateMachine { + state := parentState.EpochProtocolStateEntry.Copy() + state.InvalidEpochTransitionAttempted = true + return &FallbackStateMachine{ ++>>>>>>> master baseStateMachine: baseStateMachine{ parentState: parentState, state: state, diff --cc state/protocol/protocol_state/epochs/happy_path_statemachine.go index 00467d518e,fdd2518338..0000000000 --- a/state/protocol/protocol_state/epochs/happy_path_statemachine.go +++ b/state/protocol/protocol_state/epochs/happy_path_statemachine.go @@@ -29,10 -28,10 +29,15 @@@ type HappyPathStateMachine struct var _ StateMachine = (*HappyPathStateMachine)(nil) // NewHappyPathStateMachine creates a new HappyPathStateMachine. -// An exception is returned in case the `InvalidEpochTransitionAttempted` flag is set in the `parentState`. This means that +// An exception is returned in case the `EpochFallbackTriggered` flag is set in the `parentState`. This means that // the protocol state evolution has reached an undefined state from the perspective of the happy path state machine. ++<<<<<<< HEAD +func NewHappyPathStateMachine(view uint64, parentState *flow.RichProtocolStateEntry) (*HappyPathStateMachine, error) { + if parentState.EpochFallbackTriggered { ++======= + func NewHappyPathStateMachine(view uint64, parentState *flow.RichEpochProtocolStateEntry) (*HappyPathStateMachine, error) { + if parentState.InvalidEpochTransitionAttempted { ++>>>>>>> master return nil, irrecoverable.NewExceptionf("cannot create happy path protocol state machine at view (%d) for a parent state"+ "which is in Epoch Fallback Mode", view) } @@@ -58,7 -57,7 +63,11 @@@ // CAUTION: the HappyPathStateMachine is left with a potentially dysfunctional state when this error occurs. Do NOT call the Build method // after such error and discard the HappyPathStateMachine! func (u *HappyPathStateMachine) ProcessEpochSetup(epochSetup *flow.EpochSetup) (bool, error) { ++<<<<<<< HEAD + err := protocol.IsValidExtendingEpochSetup(epochSetup, u.parentState) ++======= + err := protocol.IsValidExtendingEpochSetup(epochSetup, u.parentState.EpochProtocolStateEntry, u.parentState.CurrentEpochSetup) ++>>>>>>> master if err != nil { return false, fmt.Errorf("invalid epoch setup event: %w", err) } @@@ -139,60 -153,32 +148,91 @@@ func (u *HappyPathStateMachine) Process return true, nil } ++<<<<<<< HEAD +// ProcessEpochRecover returns the sentinel error `protocol.InvalidServiceEventError`, which +// indicates that `EpochRecover` are not expected on the happy path of epoch lifecycle. +func (u *HappyPathStateMachine) ProcessEpochRecover(*flow.EpochRecover) (bool, error) { + return false, protocol.NewInvalidServiceEventErrorf("epoch recover event received while on happy path") +} + +// When observing setup event for subsequent epoch, construct the EpochStateContainer for `ProtocolStateEntry.NextEpoch`. +// Context: +// Note that the `EpochStateContainer.ActiveIdentities` only contains the nodes that are *active* in the next epoch. Active means +// that these nodes are authorized to contribute to extending the chain. Nodes are listed in `ActiveIdentities` if and only if +// they are part of the EpochSetup event for the respective epoch. +// +// sanity checking SAFETY-CRITICAL INVARIANT (I): +// - Per convention, the `flow.EpochSetup` event should list the IdentitySkeletons in canonical order. This is useful +// for most efficient construction of the full active Identities for an epoch. We enforce this here at the gateway +// to the protocol state, when we incorporate new information from the EpochSetup event. +// - Note that the system smart contracts manage the identity table as an unordered set! For the protocol state, we desire a fixed +// ordering to simplify various implementation details, like the DKG. Therefore, we order identities in `flow.EpochSetup` during +// conversion from cadence to Go in the function `convert.ServiceEvent(flow.ChainID, flow.Event)` in package `model/convert` +// sanity checking SAFETY-CRITICAL INVARIANT (II): +// While ejection status and dynamic weight are not part of the EpochSetup event, we can supplement this information as follows: +// - Per convention, service events are delivered (asynchronously) in an *order-preserving* manner. Furthermore, weight changes or +// node ejection is entirely mediated by system smart contracts and delivered via service events. +// - Therefore, the EpochSetup event contains the up-to-date snapshot of the epoch participants. Any weight changes or node ejection +// that happened before should be reflected in the EpochSetup event. Specifically, the initial weight should be reduced and ejected +// nodes should be no longer listed in the EpochSetup event. +// - Hence, the following invariant must be satisfied by the system smart contracts for all active nodes in the upcoming epoch: +// (i) The Ejected flag is false. Node X being ejected in epoch N (necessarily via a service event emitted by the system +// smart contracts earlier) but also being listed in the setup event for the subsequent epoch (service event emitted by +// the system smart contracts later) is illegal. +// (ii) When the EpochSetup event is emitted / processed, the weight of all active nodes equals their InitialWeight and + +// For collector clusters, we rely on invariants (I) and (II) holding. See `committees.Cluster` for details, specifically function +// `constructInitialClusterIdentities(..)`. While the system smart contract must satisfy this invariant, we run a sanity check below. +func buildNextEpochActiveParticipants(activeIdentitiesLookup map[flow.Identifier]*flow.DynamicIdentityEntry, currentEpochSetup, nextEpochSetup *flow.EpochSetup) (flow.DynamicIdentityEntryList, error) { + nextEpochActiveIdentities := make(flow.DynamicIdentityEntryList, 0, len(nextEpochSetup.Participants)) + prevNodeID := nextEpochSetup.Participants[0].NodeID + for idx, nextEpochIdentitySkeleton := range nextEpochSetup.Participants { + // sanity checking invariant (I): + if idx > 0 && !flow.IsIdentifierCanonical(prevNodeID, nextEpochIdentitySkeleton.NodeID) { + return nil, protocol.NewInvalidServiceEventErrorf("epoch setup event lists active participants not in canonical ordering") + } + prevNodeID = nextEpochIdentitySkeleton.NodeID + + // sanity checking invariant (II.i): + currentEpochDynamicProperties, found := activeIdentitiesLookup[nextEpochIdentitySkeleton.NodeID] + if found && currentEpochDynamicProperties.Ejected { // invariant violated + return nil, protocol.NewInvalidServiceEventErrorf("node %v is ejected in current epoch %d but readmitted by EpochSetup event for epoch %d", nextEpochIdentitySkeleton.NodeID, currentEpochSetup.Counter, nextEpochSetup.Counter) + } + + nextEpochActiveIdentities = append(nextEpochActiveIdentities, &flow.DynamicIdentityEntry{ + NodeID: nextEpochIdentitySkeleton.NodeID, + Ejected: false, // according to invariant (II.i) + }) + } + return nextEpochActiveIdentities, nil ++======= + // TransitionToNextEpoch updates the notion of 'current epoch', 'previous' and 'next epoch' in the protocol + // state. An epoch transition is only allowed when: + // - next epoch has been set up, + // - next epoch has been committed, + // - invalid state transition has not been attempted (this is ensured by constructor), + // - candidate block is in the next epoch. + // No errors are expected during normal operations. + func (u *HappyPathStateMachine) TransitionToNextEpoch() error { + nextEpoch := u.state.NextEpoch + // Check i…
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Context
The Block Time Controller adjusts the block time to accommodate target epoch durations and end times. It uses The FinalView, TargetEndTime, and TargetDuration fields of the EpochSetup event to determine block time.
Definition of Done
Update the block time controller.
EpochExtended
event handler:epochInfo.curEpochFinalView
andepochInfo.curEpochTargetEndTime
fieldsepochInfo.nextEpochFinalView
tonil
(or set it in a different way)EpochRecovered
event handler:epochInfo.nextEpochFinalView
valueOpen Questions
TargetEndTime
be in theEpochRecover
event? (ie. from smart contract)TargetEndTime
be in theEpochExtension
? (or just in the block time controller)Further Reading
Design
Depends on
The text was updated successfully, but these errors were encountered: