Documentation ¶
Index ¶
- type EpochStateMachine
- type EpochStateMachineFactory
- type FallbackStateMachine
- func (u *FallbackStateMachine) Build() (updatedState *flow.EpochProtocolStateEntry, stateID flow.Identifier, ...)
- func (u *FallbackStateMachine) EjectIdentity(nodeID flow.Identifier) error
- func (u *FallbackStateMachine) ParentState() *flow.RichEpochProtocolStateEntry
- func (m *FallbackStateMachine) ProcessEpochCommit(_ *flow.EpochCommit) (bool, error)
- func (m *FallbackStateMachine) ProcessEpochSetup(_ *flow.EpochSetup) (bool, error)
- func (m *FallbackStateMachine) TransitionToNextEpoch() error
- func (u *FallbackStateMachine) View() uint64
- type HappyPathStateMachine
- func (u *HappyPathStateMachine) Build() (updatedState *flow.EpochProtocolStateEntry, stateID flow.Identifier, ...)
- func (u *HappyPathStateMachine) EjectIdentity(nodeID flow.Identifier) error
- func (u *HappyPathStateMachine) ParentState() *flow.RichEpochProtocolStateEntry
- func (u *HappyPathStateMachine) ProcessEpochCommit(epochCommit *flow.EpochCommit) (bool, error)
- func (u *HappyPathStateMachine) ProcessEpochSetup(epochSetup *flow.EpochSetup) (bool, error)
- func (u *HappyPathStateMachine) TransitionToNextEpoch() error
- func (u *HappyPathStateMachine) View() uint64
- type StateMachine
- type StateMachineFactoryMethod
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type EpochStateMachine ¶
type EpochStateMachine struct {
// contains filtered or unexported fields
}
EpochStateMachine is a hierarchical state machine that encapsulates the logic for protocol-compliant evolution of Epoch-related sub-state. EpochStateMachine processes a subset of service events that are relevant for the Epoch state, and ignores all other events. EpochStateMachine delegates the processing of service events to an embedded StateMachine, which is either a HappyPathStateMachine or a FallbackStateMachine depending on the operation mode of the protocol. It relies on Key-Value Store to read the parent state and to persist the snapshot of the updated Epoch state.
func NewEpochStateMachine ¶
func NewEpochStateMachine( candidateView uint64, parentBlockID flow.Identifier, params protocol.GlobalParams, setups storage.EpochSetups, commits storage.EpochCommits, epochProtocolStateDB storage.EpochProtocolStateEntries, parentState protocol.KVStoreReader, mutator protocol_state.KVStoreMutator, happyPathStateMachineFactory StateMachineFactoryMethod, epochFallbackStateMachineFactory StateMachineFactoryMethod, ) (*EpochStateMachine, error)
NewEpochStateMachine creates a new higher-level hierarchical state machine for protocol-compliant evolution of Epoch-related sub-state. NewEpochStateMachine performs initialization of state machine depending on the operation mode of the protocol. - for the happy path, it initializes a HappyPathStateMachine, - for the epoch fallback mode it initializes a FallbackStateMachine. No errors are expected during normal operations.
func (*EpochStateMachine) Build ¶
func (e *EpochStateMachine) Build() (*transaction.DeferredBlockPersist, error)
Build schedules updates to the protocol state by obtaining the updated state from the active state machine, preparing deferred DB updates and committing updated sub-state ID to the KV store. ATTENTION: In mature implementation all parts of the Dynamic Protocol State will rely on the Key-Value Store as storage but to avoid a large refactoring we are using a hybrid approach where only the epoch state ID is stored in the KV Store but the actual epoch state is stored separately, nevertheless, the epoch state ID is used to sanity check if the epoch state is consistent with the KV Store. Using this approach, we commit the epoch sub-state to the KV Store which in affects the Dynamic Protocol State ID which is essentially hash of the KV Store.
func (*EpochStateMachine) EvolveState ¶
func (e *EpochStateMachine) EvolveState(sealedServiceEvents []flow.ServiceEvent) error
EvolveState applies the state change(s) on the Epoch sub-state based on information from the candidate block (under construction). Information that potentially changes the state (compared to the parent block's state):
- Service Events sealed in the candidate block
- the candidate block's view (already provided at construction time)
CAUTION: EvolveState MUST be called for all candidate blocks, even if `sealedServiceEvents` is empty! This is because also the absence of expected service events by a certain view can also result in the Epoch state changing. (For example, not having received the EpochCommit event for the next epoch, but approaching the end of the current epoch.)
The block's payload might contain epoch preparation service events for the next epoch. In this case, we need to update the tentative protocol state. We need to validate whether all information is available in the protocol state to go to the next epoch when needed. In cases where there is a bug in the smart contract, it could be that this happens too late, and we should trigger epoch fallback mode. No errors are expected during normal operations.
func (*EpochStateMachine) ParentState ¶
func (e *EpochStateMachine) ParentState() protocol.KVStoreReader
ParentState returns parent state associated with this state machine.
func (*EpochStateMachine) View ¶
func (e *EpochStateMachine) View() uint64
View returns the view associated with this state machine. The view of the state machine equals the view of the block carrying the respective updates.
type EpochStateMachineFactory ¶
type EpochStateMachineFactory struct {
// contains filtered or unexported fields
}
EpochStateMachineFactory is a factory for creating EpochStateMachine instances. It holds all the necessary data to create a new instance of EpochStateMachine.
func NewEpochStateMachineFactory ¶
func NewEpochStateMachineFactory( params protocol.GlobalParams, setups storage.EpochSetups, commits storage.EpochCommits, epochProtocolStateDB storage.EpochProtocolStateEntries, ) *EpochStateMachineFactory
func (*EpochStateMachineFactory) Create ¶
func (f *EpochStateMachineFactory) Create(candidateView uint64, parentBlockID flow.Identifier, parentState protocol.KVStoreReader, mutator protocol_state.KVStoreMutator) (protocol_state.KeyValueStoreStateMachine, error)
Create creates a new instance of an underlying type that operates on KV Store and is created for a specific candidate block. No errors are expected during normal operations.
type FallbackStateMachine ¶
type FallbackStateMachine struct {
// contains filtered or unexported fields
}
FallbackStateMachine is a special structure that encapsulates logic for processing service events when protocol is in epoch fallback mode. The FallbackStateMachine ignores EpochSetup and EpochCommit events but still processes ejection events.
Whenever invalid epoch state transition has been observed only epochFallbackStateMachines must be created for subsequent views. TODO for 'leaving Epoch Fallback via special service event': this might need to change.
func NewFallbackStateMachine ¶
func NewFallbackStateMachine(view uint64, parentState *flow.RichEpochProtocolStateEntry) *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 (*FallbackStateMachine) Build ¶
func (u *FallbackStateMachine) Build() (updatedState *flow.EpochProtocolStateEntry, stateID flow.Identifier, hasChanges bool)
Build returns updated protocol state entry, state ID and a flag indicating if there were any changes. CAUTION: Do NOT call Build, if the baseStateMachine instance has returned a `protocol.InvalidServiceEventError` at any time during its lifetime. After this error, the baseStateMachine is left with a potentially dysfunctional state and should be discarded.
func (*FallbackStateMachine) EjectIdentity ¶
func (u *FallbackStateMachine) EjectIdentity(nodeID flow.Identifier) error
EjectIdentity updates identity table by changing the node's participation status to 'ejected'. Should pass identity which is already present in the table, otherwise an exception will be raised. Expected errors during normal operations: - `protocol.InvalidServiceEventError` if the updated identity is not found in current and adjacent epochs.
func (*FallbackStateMachine) ParentState ¶
func (u *FallbackStateMachine) ParentState() *flow.RichEpochProtocolStateEntry
ParentState returns parent protocol state associated with this state machine.
func (*FallbackStateMachine) ProcessEpochCommit ¶
func (m *FallbackStateMachine) ProcessEpochCommit(_ *flow.EpochCommit) (bool, error)
ProcessEpochCommit processes epoch commit service events, for epoch fallback we are ignoring this event.
func (*FallbackStateMachine) ProcessEpochSetup ¶
func (m *FallbackStateMachine) ProcessEpochSetup(_ *flow.EpochSetup) (bool, error)
ProcessEpochSetup processes epoch setup service events, for epoch fallback we are ignoring this event.
func (*FallbackStateMachine) TransitionToNextEpoch ¶
func (m *FallbackStateMachine) TransitionToNextEpoch() error
TransitionToNextEpoch performs transition to next epoch, in epoch fallback no transitions are possible. TODO for 'leaving Epoch Fallback via special service event' this might need to change.
type HappyPathStateMachine ¶
type HappyPathStateMachine struct {
// contains filtered or unexported fields
}
HappyPathStateMachine is a dedicated structure for evolving the Epoch-related portion of the overall Protocol State. Based on the content of a new block, it updates epoch data, including the identity table, on the happy path. The HappyPathStateMachine guarantees protocol-compliant evolution of Epoch-related sub-state via the following state transitions:
- epoch setup: transitions current epoch from staking to setup phase, creates next epoch protocol state when processed.
- epoch commit: transitions current epoch from setup to commit phase, commits next epoch protocol state when processed.
- epoch transition: on the first block of the new epoch (Formally, the block's parent is still in the last epoch, while the new block has a view in the next epoch. Caution: the block's view is not necessarily the first view in the epoch, as there might be leader failures)
- identity changes: updates identity table for previous (if available), current, and next epoch (if available).
All updates are applied to a copy of parent protocol state, so parent protocol state is not modified. The stateMachine internally tracks the current protocol state. A separate instance should be created for each block to process the updates therein.
func NewHappyPathStateMachine ¶
func NewHappyPathStateMachine(view uint64, parentState *flow.RichEpochProtocolStateEntry) (*HappyPathStateMachine, error)
NewHappyPathStateMachine creates a new HappyPathStateMachine. An exception is returned in case the `InvalidEpochTransitionAttempted` 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.
func (*HappyPathStateMachine) Build ¶
func (u *HappyPathStateMachine) Build() (updatedState *flow.EpochProtocolStateEntry, stateID flow.Identifier, hasChanges bool)
Build returns updated protocol state entry, state ID and a flag indicating if there were any changes. CAUTION: Do NOT call Build, if the baseStateMachine instance has returned a `protocol.InvalidServiceEventError` at any time during its lifetime. After this error, the baseStateMachine is left with a potentially dysfunctional state and should be discarded.
func (*HappyPathStateMachine) EjectIdentity ¶
func (u *HappyPathStateMachine) EjectIdentity(nodeID flow.Identifier) error
EjectIdentity updates identity table by changing the node's participation status to 'ejected'. Should pass identity which is already present in the table, otherwise an exception will be raised. Expected errors during normal operations: - `protocol.InvalidServiceEventError` if the updated identity is not found in current and adjacent epochs.
func (*HappyPathStateMachine) ParentState ¶
func (u *HappyPathStateMachine) ParentState() *flow.RichEpochProtocolStateEntry
ParentState returns parent protocol state associated with this state machine.
func (*HappyPathStateMachine) ProcessEpochCommit ¶
func (u *HappyPathStateMachine) ProcessEpochCommit(epochCommit *flow.EpochCommit) (bool, error)
ProcessEpochCommit updates current protocol state with data from epoch commit event. Observing an epoch setup commit, transitions protocol state from setup to commit phase. At this point, we have finished construction of the next epoch. As a result of this operation protocol state for next epoch will be committed. Returned boolean indicates if event triggered a transition in the state machine or not. Implementors must never return (true, error). Expected errors indicating that we are leaving the happy-path of the epoch transitions
- `protocol.InvalidServiceEventError` - if the service event is invalid or is not a valid state transition for the current protocol state. 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 (*HappyPathStateMachine) ProcessEpochSetup ¶
func (u *HappyPathStateMachine) ProcessEpochSetup(epochSetup *flow.EpochSetup) (bool, error)
ProcessEpochSetup updates the protocol state with data from the epoch setup event. Observing an epoch setup event also affects the identity table for current epoch:
- it transitions the protocol state from Staking to Epoch Setup phase
- we stop returning identities from previous+current epochs and instead returning identities from current+next epochs.
As a result of this operation protocol state for the next epoch will be created. Returned boolean indicates if event triggered a transition in the state machine or not. Implementors must never return (true, error). Expected errors indicating that we are leaving the happy-path of the epoch transitions
- `protocol.InvalidServiceEventError` - if the service event is invalid or is not a valid state transition for the current protocol state. 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 (*HappyPathStateMachine) TransitionToNextEpoch ¶
func (u *HappyPathStateMachine) TransitionToNextEpoch() error
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.
type StateMachine ¶
type StateMachine interface { // Build returns updated protocol state entry, state ID and a flag indicating if there were any changes. // CAUTION: // Do NOT call Build, if the StateMachine instance has returned a `protocol.InvalidServiceEventError` // at any time during its lifetime. After this error, the StateMachine is left with a potentially // dysfunctional state and should be discarded. Build() (updatedState *flow.EpochProtocolStateEntry, stateID flow.Identifier, hasChanges bool) // ProcessEpochSetup updates the internally-maintained interim Epoch state with data from epoch setup event. // Processing an epoch setup event also affects the identity table for the current epoch. // Specifically, we transition the Epoch state from staking to setup phase, we stop returning // identities from previous+current epochs and start returning identities from current+next epochs. // As a result of this operation protocol state for the next epoch will be created. // Returned boolean indicates if event triggered a transition in the state machine or not. // Implementors must never return (true, error). // Expected errors indicating that we are leaving the happy-path of the epoch transitions // - `protocol.InvalidServiceEventError` - if the service event is invalid or is not a valid state transition for the current protocol state. // CAUTION: the StateMachine is left with a potentially dysfunctional state when this error occurs. Do NOT call the Build method // after such error and discard the StateMachine! ProcessEpochSetup(epochSetup *flow.EpochSetup) (bool, error) // ProcessEpochCommit updates the internally-maintained interim Epoch state with data from epoch commit event. // Observing an epoch setup commit, transitions protocol state from setup to commit phase. // At this point, we have finished construction of the next epoch. // As a result of this operation protocol state for next epoch will be committed. // Returned boolean indicates if event triggered a transition in the state machine or not. // Implementors must never return (true, error). // Expected errors indicating that we are leaving the happy-path of the epoch transitions // - `protocol.InvalidServiceEventError` - if the service event is invalid or is not a valid state transition for the current protocol state. // CAUTION: the StateMachine is left with a potentially dysfunctional state when this error occurs. Do NOT call the Build method // after such error and discard the StateMachine! ProcessEpochCommit(epochCommit *flow.EpochCommit) (bool, error) // EjectIdentity updates identity table by changing the node's participation status to 'ejected'. // Should pass identity which is already present in the table, otherwise an exception will be raised. // Expected errors during normal operations: // - `protocol.InvalidServiceEventError` if the updated identity is not found in current and adjacent epochs. EjectIdentity(nodeID flow.Identifier) error // TransitionToNextEpoch transitions our reference frame of 'current epoch' to the pending but committed epoch. // Epoch transition is only allowed when: // - next epoch has been committed, // - candidate block is in the next epoch. // No errors are expected during normal operations. TransitionToNextEpoch() error // View returns the view associated with this state machine. // The view of the state machine equals the view of the block carrying the respective updates. View() uint64 // ParentState returns parent protocol state associated with this state machine. ParentState() *flow.RichEpochProtocolStateEntry }
StateMachine implements a low-level interface for state-changing operations on the Epoch state. It is used by higher level logic to coordinate the Epoch handover, evolving its internal state when Epoch-related Service Events are sealed or specific view-thresholds are reached.
The StateMachine is fork-aware, in that it starts with the Epoch state of the parent block and evolves the state based on the relevant information in the child block (specifically Service Events sealed in the child block and the child block's view). A separate instance must be created for each block that is being processed. Calling `Build()` constructs a snapshot of the resulting Epoch state.
type StateMachineFactoryMethod ¶
type StateMachineFactoryMethod func(candidateView uint64, parentState *flow.RichEpochProtocolStateEntry) (StateMachine, error)
StateMachineFactoryMethod is a factory method to create state machines for evolving the protocol's epoch state. Currently, we have `HappyPathStateMachine` and `FallbackStateMachine` as StateMachine implementations, whose constructors both have the same signature as StateMachineFactoryMethod.