Skip to content

Commit ae080ed

Browse files
committed
protofsm: convert state machine args into config
1 parent 14820fa commit ae080ed

File tree

2 files changed

+69
-41
lines changed

2 files changed

+69
-41
lines changed

protofsm/state_machine.go

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -122,46 +122,60 @@ type stateQuery[Event any, Env Environment] struct {
122122
//
123123
// TODO(roasbeef): terminal check, daemon event execution, init?
124124
type StateMachine[Event any, Env Environment] struct {
125-
currentState State[Event, Env]
126-
env Env
127-
128-
daemon DaemonAdapters
125+
cfg StateMachineCfg[Event, Env]
129126

127+
// events is the channel that will be used to send new events to the
128+
// FSM.
130129
events chan Event
131130

132-
quit chan struct{}
133-
wg sync.WaitGroup
134-
135131
// newStateEvents is an EventDistributor that will be used to notify
136132
// any relevant callers of new state transitions that occur.
137133
newStateEvents *fn.EventDistributor[State[Event, Env]]
138134

135+
// stateQuery is a channel that will be used by outside callers to
136+
// query the internal state machine state.
139137
stateQuery chan stateQuery[Event, Env]
140138

141-
initEvent fn.Option[DaemonEvent]
142-
143139
startOnce sync.Once
144140
stopOnce sync.Once
145141

146142
// TODO(roasbeef): also use that context guard here?
143+
quit chan struct{}
144+
wg sync.WaitGroup
145+
}
146+
147+
// StateMachineCfg is a configuration struct that's used to create a new state
148+
// machine.
149+
type StateMachineCfg[Event any, Env Environment] struct {
150+
// Daemon is a set of adapters that will be used to bridge the FSM to
151+
// the daemon.
152+
Daemon DaemonAdapters
153+
154+
// InitialState is the initial state of the state machine.
155+
InitialState State[Event, Env]
156+
157+
// Env is the environment that the state machine will use to execute.
158+
Env Env
159+
160+
// InitEvent is an optional event that will be sent to the state
161+
// machine as if it was emitted at the onset of the state machine. This
162+
// can be used to set up tracking state such as a txid confirmation
163+
// event.
164+
InitEvent fn.Option[DaemonEvent]
147165
}
148166

149167
// NewStateMachine creates a new state machine given a set of daemon adapters,
150168
// an initial state, an environment, and an event to process as if emitted at
151169
// the onset of the state machine. Such an event can be used to set up tracking
152170
// state such as a txid confirmation event.
153-
func NewStateMachine[Event any, Env Environment](adapters DaemonAdapters,
154-
initialState State[Event, Env], env Env,
155-
initEvent fn.Option[DaemonEvent]) StateMachine[Event, Env] {
171+
func NewStateMachine[Event any, Env Environment](cfg StateMachineCfg[Event, Env],
172+
) StateMachine[Event, Env] {
156173

157174
return StateMachine[Event, Env]{
158-
daemon: adapters,
175+
cfg: cfg,
159176
events: make(chan Event, 1),
160-
currentState: initialState,
161177
stateQuery: make(chan stateQuery[Event, Env]),
162178
quit: make(chan struct{}),
163-
env: env,
164-
initEvent: initEvent,
165179
newStateEvents: fn.NewEventDistributor[State[Event, Env]](),
166180
}
167181
}
@@ -240,7 +254,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(event DaemonEvent) error {
240254
// any preconditions as well as post-send events.
241255
case *SendMsgEvent[Event]:
242256
sendAndCleanUp := func() error {
243-
err := s.daemon.SendMessages(
257+
err := s.cfg.Daemon.SendMessages(
244258
daemonEvent.TargetPeer, daemonEvent.Msgs,
245259
)
246260
if err != nil {
@@ -304,7 +318,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(event DaemonEvent) error {
304318
// If this is a broadcast transaction event, then we'll broadcast with
305319
// the label attached.
306320
case *BroadcastTxn:
307-
err := s.daemon.BroadcastTransaction(
321+
err := s.cfg.Daemon.BroadcastTransaction(
308322
daemonEvent.Tx, daemonEvent.Label,
309323
)
310324
if err != nil {
@@ -318,7 +332,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(event DaemonEvent) error {
318332
// The state machine has requested a new event to be sent once a
319333
// transaction spending a specified outpoint has confirmed.
320334
case *RegisterSpend[Event]:
321-
spendEvent, err := s.daemon.RegisterSpendNtfn(
335+
spendEvent, err := s.cfg.Daemon.RegisterSpendNtfn(
322336
&daemonEvent.OutPoint, daemonEvent.PkScript,
323337
daemonEvent.HeightHint,
324338
)
@@ -354,7 +368,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(event DaemonEvent) error {
354368
// specified txid+pkScript pair has confirmed.
355369
case *RegisterConf[Event]:
356370
numConfs := daemonEvent.NumConfs.UnwrapOr(1)
357-
confEvent, err := s.daemon.RegisterConfirmationsNtfn(
371+
confEvent, err := s.cfg.Daemon.RegisterConfirmationsNtfn(
358372
&daemonEvent.Txid, daemonEvent.PkScript,
359373
numConfs, daemonEvent.HeightHint,
360374
)
@@ -394,9 +408,8 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(event DaemonEvent) error {
394408
// applyEvents applies a new event to the state machine. This will continue
395409
// until no further events are emitted by the state machine. Along the way,
396410
// we'll also ensure to execute any daemon events that are emitted.
397-
func (s *StateMachine[Event, Env]) applyEvents(newEvent Event) (State[Event, Env], error) {
398-
// TODO(roasbeef): make starting state as part of env?
399-
currentState := s.currentState
411+
func (s *StateMachine[Event, Env]) applyEvents(currentState State[Event, Env],
412+
newEvent Event) (State[Event, Env], error) {
400413

401414
eventQueue := fn.NewQueue(newEvent)
402415

@@ -409,7 +422,7 @@ func (s *StateMachine[Event, Env]) applyEvents(newEvent Event) (State[Event, Env
409422
// Apply the state transition function of the current
410423
// state given this new event and our existing env.
411424
transition, err := currentState.ProcessEvent(
412-
event, s.env,
425+
event, s.cfg.Env,
413426
)
414427
if err != nil {
415428
return err
@@ -472,12 +485,11 @@ func (s *StateMachine[Event, Env]) applyEvents(newEvent Event) (State[Event, Env
472485
func (s *StateMachine[Event, Env]) driveMachine() {
473486
defer s.wg.Done()
474487

475-
// TODO(roasbeef): move into env? read only to start with
476-
currentState := s.currentState
488+
currentState := s.cfg.InitialState
477489

478490
// Before we start, if we have an init daemon event specified, then
479491
// we'll handle that now.
480-
err := fn.MapOptionZ(s.initEvent, func(event DaemonEvent) error {
492+
err := fn.MapOptionZ(s.cfg.InitEvent, func(event DaemonEvent) error {
481493
return s.executeDaemonEvent(event)
482494
})
483495
if err != nil {
@@ -495,7 +507,7 @@ func (s *StateMachine[Event, Env]) driveMachine() {
495507
// machine forward until we either run out of internal events,
496508
// or we reach a terminal state.
497509
case newEvent := <-s.events:
498-
newState, err := s.applyEvents(newEvent)
510+
newState, err := s.applyEvents(currentState, newEvent)
499511
if err != nil {
500512
// TODO(roasbeef): hard error?
501513
log.Errorf("unable to apply event: %v", err)
@@ -508,7 +520,7 @@ func (s *StateMachine[Event, Env]) driveMachine() {
508520
// state machine and call any relevant clean up call
509521
// backs that might have been registered.
510522
if currentState.IsTerminal() {
511-
err := s.env.CleanUp()
523+
err := s.cfg.Env.CleanUp()
512524
if err != nil {
513525
log.Errorf("unable to clean up "+
514526
"env: %v", err)

protofsm/state_machine_test.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,13 @@ func TestStateMachineTerminateCleanup(t *testing.T) {
223223

224224
adapters := newDaemonAdapters()
225225

226-
stateMachine := NewStateMachine[dummyEvents, *dummyEnv](
227-
adapters, startingState, env, fn.None[DaemonEvent](),
228-
)
226+
cfg := StateMachineCfg[dummyEvents, *dummyEnv]{
227+
Daemon: adapters,
228+
InitialState: startingState,
229+
Env: env,
230+
InitEvent: fn.None[DaemonEvent](),
231+
}
232+
stateMachine := NewStateMachine(cfg)
229233
stateMachine.Start()
230234
defer stateMachine.Stop()
231235

@@ -261,9 +265,13 @@ func TestStateMachineOnInitDaemonEvent(t *testing.T) {
261265
PostSendEvent: fn.Some(dummyEvents(&goToFin{})),
262266
}
263267

264-
stateMachine := NewStateMachine[dummyEvents, *dummyEnv](
265-
adapters, startingState, env, fn.Some[DaemonEvent](initEvent),
266-
)
268+
cfg := StateMachineCfg[dummyEvents, *dummyEnv]{
269+
Daemon: adapters,
270+
InitialState: startingState,
271+
Env: env,
272+
InitEvent: fn.Some[DaemonEvent](initEvent),
273+
}
274+
stateMachine := NewStateMachine(cfg)
267275

268276
// Before we start up the state machine, we'll assert that the send
269277
// message adapter is called on start up.
@@ -304,9 +312,13 @@ func TestStateMachineInternalEvents(t *testing.T) {
304312

305313
adapters := newDaemonAdapters()
306314

307-
stateMachine := NewStateMachine[dummyEvents, *dummyEnv](
308-
adapters, startingState, env, fn.None[DaemonEvent](),
309-
)
315+
cfg := StateMachineCfg[dummyEvents, *dummyEnv]{
316+
Daemon: adapters,
317+
InitialState: startingState,
318+
Env: env,
319+
InitEvent: fn.None[DaemonEvent](),
320+
}
321+
stateMachine := NewStateMachine(cfg)
310322
stateMachine.Start()
311323
defer stateMachine.Stop()
312324

@@ -355,9 +367,13 @@ func TestStateMachineDaemonEvents(t *testing.T) {
355367

356368
adapters := newDaemonAdapters()
357369

358-
stateMachine := NewStateMachine[dummyEvents, *dummyEnv](
359-
adapters, startingState, env, fn.None[DaemonEvent](),
360-
)
370+
cfg := StateMachineCfg[dummyEvents, *dummyEnv]{
371+
Daemon: adapters,
372+
InitialState: startingState,
373+
Env: env,
374+
InitEvent: fn.None[DaemonEvent](),
375+
}
376+
stateMachine := NewStateMachine(cfg)
361377
stateMachine.Start()
362378
defer stateMachine.Stop()
363379

0 commit comments

Comments
 (0)