github.com/amazechain/amc@v0.1.3/internal/sync/initial-sync/fsm.go (about)

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