⚠️ Work in progress⚠️ - At the time of writing this document, the architecture is still being defined.This is pretty much a POC at the moment.
This document outlines the purpose of this module, its components and how they all interact with the other modules.
The StateMachine
module implements a FSM (Finite State Machine) that is responsible for managing the node lifecycle since its internal behaviour can be different depending on certain conditions that are used to determine the current state.
In a nutshell: The FSM guarantees that the node is always in one specific state and verifies state transition (i.e. edges) to/from valid states (i.e. vertices).
├── docs
│ ├── CHANGELOG.md # Changelog
│ ├── README.md # You are here
│ └── state-machine.diagram.md # State machine diagram (generated by visualizer/main.go)
├── fsm.go # Finite State Machine definition (events, states, transitions)
├── module.go # Implementation of the StateMachine module
└── visualizer
└── main.go # State machine diagram generator
High-level implementation details:
- The github.com/looplab/fsm library is used to implement the FSM
- Pocket builds a wrapper around
looplab/fsm
to integrate with the other modules - The
StateMachineModule
can be accessed via thebus
from any otherIntegratableModule
- State machine transitions emit
StateMachineTransitionEvent
events that subscribed pocket modules can listen to - The
node
has a central event handler for events that fan-out event handling to the relevant modules during state transitions
The FSM has a declarative definition of an initial state and a set of transitions that have an Event
, Source
states and a single Destination
state.
These are the main building blocks:
- Event: An event is a string that represents an action that can trigger a transition. For example, the event
start
can be used to trigger a transition from thestopped
state to thestarting
state. - State: A state is a string that represents a state that the FSM can be in. For example, the state
stopped
can be used to represent a state where the node is not running. - Callback: A callback is a function that is called when a transition occurs. For example, a callback can be used to log the transition or to perform some other action. Various types of callbacks essentially drive behaviour WHEN they are called and help build more complex behaviours like transition cancelling, etc. See the core FSM library documentation for more details.
- Initialization: A node always starts in the
Stopped
state. Once itStart
s, it transitions toP2P_Bootstrapping
. - P2P Bootstrapping: The P2P module handles
P2P_Bootstrapping
-> fills the peer address book -> triggers aP2P_IsBootstrapped
event -> transitions toP2P_Bootstrapped
.
- P2P_Bootstrapped: The Consensus module handles
P2P_Bootstrapped
-> triggers aConsensus_IsUnsynced
event -> transitions toConsensus_Unsynced
. - Consensus_Unsynced: Node is out of sync, the Consensus module sends
Consensus_IsSyncing
event -> transitions toConsensus_SyncMode
to start syncing with the rest of the network. - Consensus_SyncMode: The Consensus module runs
StartSyncing()
and requests blocks one by one from peers in its address book. - Node finishes syncing: When node completes syncing:
- if the node is a validator, the Consensus module sends
Consensus_IsSyncedValidator
event -> transitions toConsensus_Pacemaker
. - if the node is not a validator, the Consensus module sends
Consensus_IsSyncedNonValidator
event -> transitions toConsensus_Synced
.
- if the node is a validator, the Consensus module sends
- Consensus_Pacemaker: Node participates in the block generation process. If node receives a block proposal with height higher than its current height, the Consensus Module sends
Consensus_IsUnsynced
event -> transitions toConsensus_Unsynced
. - Consensus_Synced: Currently, the Consensus module never sends
Consensus_IsSyncedValidator
event, and non-validator node always stays inConsensus_SyncMode
.
A diagram of the current state machine definition can be found here.
NOTE: If you make changes to the state machine by changing states, events, or state transitions, you can re-generate the state machine diagram via:
make generate_node_state_machine_diagram