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  }