github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/sync/initial-sync/fsm.go (about)

     1  package initialsync
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  	"time"
     8  
     9  	"github.com/libp2p/go-libp2p-core/peer"
    10  	types "github.com/prysmaticlabs/eth2-types"
    11  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    12  	"github.com/prysmaticlabs/prysm/proto/interfaces"
    13  	"github.com/prysmaticlabs/prysm/shared/timeutils"
    14  )
    15  
    16  const (
    17  	stateNew stateID = iota
    18  	stateScheduled
    19  	stateDataParsed
    20  	stateSkipped
    21  	stateSent
    22  )
    23  
    24  const (
    25  	eventTick eventID = iota
    26  	eventDataReceived
    27  )
    28  
    29  // stateID is unique handle for a state.
    30  type stateID uint8
    31  
    32  // eventID is unique handle for an event.
    33  type eventID uint8
    34  
    35  // stateMachineManager is a collection of managed FSMs.
    36  type stateMachineManager struct {
    37  	keys     []types.Slot
    38  	machines map[types.Slot]*stateMachine
    39  	handlers map[stateID]map[eventID]eventHandlerFn
    40  }
    41  
    42  // stateMachine holds a state of a single block processing FSM.
    43  // Each FSM allows deterministic state transitions: State(S) x Event(E) -> Actions (A), State(S').
    44  type stateMachine struct {
    45  	smm     *stateMachineManager
    46  	start   types.Slot
    47  	state   stateID
    48  	pid     peer.ID
    49  	blocks  []interfaces.SignedBeaconBlock
    50  	updated time.Time
    51  }
    52  
    53  // eventHandlerFn is an event handler function's signature.
    54  type eventHandlerFn func(m *stateMachine, data interface{}) (newState stateID, err error)
    55  
    56  // newStateMachineManager returns fully initialized state machine manager.
    57  func newStateMachineManager() *stateMachineManager {
    58  	return &stateMachineManager{
    59  		keys:     make([]types.Slot, 0, lookaheadSteps),
    60  		machines: make(map[types.Slot]*stateMachine, lookaheadSteps),
    61  		handlers: make(map[stateID]map[eventID]eventHandlerFn),
    62  	}
    63  }
    64  
    65  // addHandler attaches an event handler to a state event.
    66  func (smm *stateMachineManager) addEventHandler(event eventID, state stateID, fn eventHandlerFn) {
    67  	if _, ok := smm.handlers[state]; !ok {
    68  		smm.handlers[state] = make(map[eventID]eventHandlerFn)
    69  	}
    70  	if _, ok := smm.handlers[state][event]; !ok {
    71  		smm.handlers[state][event] = fn
    72  	}
    73  }
    74  
    75  // addStateMachine allocates memory for new FSM.
    76  func (smm *stateMachineManager) addStateMachine(startSlot types.Slot) *stateMachine {
    77  	smm.machines[startSlot] = &stateMachine{
    78  		smm:     smm,
    79  		start:   startSlot,
    80  		state:   stateNew,
    81  		blocks:  []interfaces.SignedBeaconBlock{},
    82  		updated: timeutils.Now(),
    83  	}
    84  	smm.recalculateMachineAttribs()
    85  	return smm.machines[startSlot]
    86  }
    87  
    88  // removeStateMachine frees memory of a processed/finished FSM.
    89  func (smm *stateMachineManager) removeStateMachine(startSlot types.Slot) error {
    90  	if _, ok := smm.machines[startSlot]; !ok {
    91  		return fmt.Errorf("state for machine %v is not found", startSlot)
    92  	}
    93  	smm.machines[startSlot].blocks = nil
    94  	delete(smm.machines, startSlot)
    95  	smm.recalculateMachineAttribs()
    96  	return nil
    97  }
    98  
    99  // removeAllStateMachines removes all managed machines.
   100  func (smm *stateMachineManager) removeAllStateMachines() error {
   101  	for _, key := range smm.keys {
   102  		if err := smm.removeStateMachine(key); err != nil {
   103  			return err
   104  		}
   105  	}
   106  	smm.recalculateMachineAttribs()
   107  	return nil
   108  }
   109  
   110  // recalculateMachineAttribs updates cached attributes, which are used for efficiency.
   111  func (smm *stateMachineManager) recalculateMachineAttribs() {
   112  	keys := make([]types.Slot, 0, lookaheadSteps)
   113  	for key := range smm.machines {
   114  		keys = append(keys, key)
   115  	}
   116  	sort.Slice(keys, func(i, j int) bool {
   117  		return keys[i] < keys[j]
   118  	})
   119  	smm.keys = keys
   120  }
   121  
   122  // findStateMachine returns a state machine for a given start slot (if exists).
   123  func (smm *stateMachineManager) findStateMachine(startSlot types.Slot) (*stateMachine, bool) {
   124  	fsm, ok := smm.machines[startSlot]
   125  	return fsm, ok
   126  }
   127  
   128  // highestStartSlot returns the start slot for the latest known state machine.
   129  func (smm *stateMachineManager) highestStartSlot() (types.Slot, error) {
   130  	if len(smm.keys) == 0 {
   131  		return 0, errors.New("no state machine exist")
   132  	}
   133  	key := smm.keys[len(smm.keys)-1]
   134  	return smm.machines[key].start, nil
   135  }
   136  
   137  // allMachinesInState checks whether all registered state machines are in the same state.
   138  func (smm *stateMachineManager) allMachinesInState(state stateID) bool {
   139  	if len(smm.machines) == 0 {
   140  		return false
   141  	}
   142  	for _, fsm := range smm.machines {
   143  		if fsm.state != state {
   144  			return false
   145  		}
   146  	}
   147  	return true
   148  }
   149  
   150  // String returns human readable representation of a FSM collection.
   151  func (smm *stateMachineManager) String() string {
   152  	return fmt.Sprintf("%v", smm.machines)
   153  }
   154  
   155  // setState updates the current state of a given state machine.
   156  func (m *stateMachine) setState(name stateID) {
   157  	if m.state == name {
   158  		return
   159  	}
   160  	m.state = name
   161  	m.updated = timeutils.Now()
   162  }
   163  
   164  // trigger invokes the event handler on a given state machine.
   165  func (m *stateMachine) trigger(event eventID, data interface{}) error {
   166  	handlers, ok := m.smm.handlers[m.state]
   167  	if !ok {
   168  		return fmt.Errorf("no event handlers registered for event: %v, state: %v", event, m.state)
   169  	}
   170  	if handlerFn, ok := handlers[event]; ok {
   171  		state, err := handlerFn(m, data)
   172  		if err != nil {
   173  			return err
   174  		}
   175  		m.setState(state)
   176  	}
   177  	return nil
   178  }
   179  
   180  // isFirst checks whether a given machine has the lowest start slot.
   181  func (m *stateMachine) isFirst() bool {
   182  	return m.start == m.smm.keys[0]
   183  }
   184  
   185  // isLast checks whether a given machine has the highest start slot.
   186  func (m *stateMachine) isLast() bool {
   187  	return m.start == m.smm.keys[len(m.smm.keys)-1]
   188  }
   189  
   190  // String returns human-readable representation of a FSM state.
   191  func (m *stateMachine) String() string {
   192  	return fmt.Sprintf("{%d:%s}", helpers.SlotToEpoch(m.start), m.state)
   193  }
   194  
   195  // String returns human-readable representation of a state.
   196  func (s stateID) String() string {
   197  	states := map[stateID]string{
   198  		stateNew:        "new",
   199  		stateScheduled:  "scheduled",
   200  		stateDataParsed: "dataParsed",
   201  		stateSkipped:    "skipped",
   202  		stateSent:       "sent",
   203  	}
   204  	if _, ok := states[s]; !ok {
   205  		return "stateUnknown"
   206  	}
   207  	return states[s]
   208  }
   209  
   210  // String returns human-readable representation of an event.
   211  func (e eventID) String() string {
   212  	events := map[eventID]string{
   213  		eventTick:         "tick",
   214  		eventDataReceived: "dataReceived",
   215  	}
   216  	if _, ok := events[e]; !ok {
   217  		return "eventUnknown"
   218  	}
   219  	return events[e]
   220  }