github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/state/protocol/protocol_state/epochs/base_statemachine.go (about) 1 package epochs 2 3 import ( 4 "github.com/onflow/flow-go/model/flow" 5 "github.com/onflow/flow-go/state/protocol" 6 ) 7 8 // baseStateMachine implements common logic for evolving protocol state both in happy path and epoch fallback 9 // operation modes. It partially implements `StateMachine` and is used as building block for more complex implementations. 10 type baseStateMachine struct { 11 parentState *flow.RichProtocolStateEntry 12 state *flow.ProtocolStateEntry 13 view uint64 14 15 // The following fields are maps from NodeID → DynamicIdentityEntry for the nodes that are *active* in the respective epoch. 16 // Active means that these nodes are authorized to contribute to extending the chain. Formally, a node is active if and only 17 // if it is listed in the EpochSetup event for the respective epoch. Note that map values are pointers, so writes to map values 18 // will modify the respective DynamicIdentityEntry in `state`. 19 20 prevEpochIdentitiesLookup map[flow.Identifier]*flow.DynamicIdentityEntry // lookup for nodes active in the previous epoch, may be nil or empty 21 currentEpochIdentitiesLookup map[flow.Identifier]*flow.DynamicIdentityEntry // lookup for nodes active in the current epoch, never nil or empty 22 nextEpochIdentitiesLookup map[flow.Identifier]*flow.DynamicIdentityEntry // lookup for nodes active in the next epoch, may be nil or empty 23 } 24 25 // Build returns updated protocol state entry, state ID and a flag indicating if there were any changes. 26 // CAUTION: 27 // Do NOT call Build, if the baseStateMachine instance has returned a `protocol.InvalidServiceEventError` 28 // at any time during its lifetime. After this error, the baseStateMachine is left with a potentially 29 // dysfunctional state and should be discarded. 30 func (u *baseStateMachine) Build() (updatedState *flow.ProtocolStateEntry, stateID flow.Identifier, hasChanges bool) { 31 updatedState = u.state.Copy() 32 stateID = updatedState.ID() 33 hasChanges = stateID != u.parentState.ID() 34 return 35 } 36 37 // View returns the view associated with this state machine. 38 // The view of the state machine equals the view of the block carrying the respective updates. 39 func (u *baseStateMachine) View() uint64 { 40 return u.view 41 } 42 43 // ParentState returns parent protocol state associated with this state machine. 44 func (u *baseStateMachine) ParentState() *flow.RichProtocolStateEntry { 45 return u.parentState 46 } 47 48 // ensureLookupPopulated ensures that current and next epoch identities lookups are populated. 49 // We use this to avoid populating lookups on every UpdateIdentity call. 50 func (u *baseStateMachine) ensureLookupPopulated() { 51 if len(u.currentEpochIdentitiesLookup) > 0 { 52 return 53 } 54 u.rebuildIdentityLookup() 55 } 56 57 // rebuildIdentityLookup re-generates lookups of *active* participants for 58 // previous (optional, if u.state.PreviousEpoch ≠ nil), current (required) and 59 // next epoch (optional, if u.state.NextEpoch ≠ nil). 60 func (u *baseStateMachine) rebuildIdentityLookup() { 61 if u.state.PreviousEpoch != nil { 62 u.prevEpochIdentitiesLookup = u.state.PreviousEpoch.ActiveIdentities.Lookup() 63 } else { 64 u.prevEpochIdentitiesLookup = nil 65 } 66 u.currentEpochIdentitiesLookup = u.state.CurrentEpoch.ActiveIdentities.Lookup() 67 if u.state.NextEpoch != nil { 68 u.nextEpochIdentitiesLookup = u.state.NextEpoch.ActiveIdentities.Lookup() 69 } else { 70 u.nextEpochIdentitiesLookup = nil 71 } 72 } 73 74 // EjectIdentity updates identity table by changing the node's participation status to 'ejected'. 75 // Should pass identity which is already present in the table, otherwise an exception will be raised. 76 // Expected errors during normal operations: 77 // - `protocol.InvalidServiceEventError` if the updated identity is not found in current and adjacent epochs. 78 func (u *baseStateMachine) EjectIdentity(nodeID flow.Identifier) error { 79 u.ensureLookupPopulated() 80 prevEpochIdentity, foundInPrev := u.prevEpochIdentitiesLookup[nodeID] 81 if foundInPrev { 82 prevEpochIdentity.Ejected = true 83 } 84 currentEpochIdentity, foundInCurrent := u.currentEpochIdentitiesLookup[nodeID] 85 if foundInCurrent { 86 currentEpochIdentity.Ejected = true 87 } 88 nextEpochIdentity, foundInNext := u.nextEpochIdentitiesLookup[nodeID] 89 if foundInNext { 90 nextEpochIdentity.Ejected = true 91 } 92 if !foundInPrev && !foundInCurrent && !foundInNext { 93 return protocol.NewInvalidServiceEventErrorf("expected to find identity for "+ 94 "prev, current or next epoch, but (%v) was not found", nodeID) 95 } 96 return nil 97 }