Skip to content

Commit bf10e31

Browse files
committed
protofsm: convert state machine args into config
1 parent d17e737 commit bf10e31

File tree

2 files changed

+61
-37
lines changed

2 files changed

+61
-37
lines changed

protofsm/state_machine.go

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -119,46 +119,60 @@ type stateQuery[Event any, Env Environment] struct {
119119
//
120120
// TODO(roasbeef): terminal check, daemon event execution, init?
121121
type StateMachine[Event any, Env Environment] struct {
122-
currentState State[Event, Env]
123-
env Env
124-
125-
daemon DaemonAdapters
122+
cfg StateMachineCfg[Event, Env]
126123

124+
// events is the channel that will be used to send new events to the
125+
// FSM.
127126
events chan Event
128127

129-
quit chan struct{}
130-
wg sync.WaitGroup
131-
132128
// newStateEvents is an EventDistributor that will be used to notify
133129
// any relevant callers of new state transitions that occur.
134130
newStateEvents *fn.EventDistributor[State[Event, Env]]
135131

132+
// stateQuery is a channel that will be used by outside callers to
133+
// query the internal state machine state.
136134
stateQuery chan stateQuery[Event, Env]
137135

138-
initEvent fn.Option[DaemonEvent]
139-
140136
startOnce sync.Once
141137
stopOnce sync.Once
142138

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

146164
// NewStateMachine creates a new state machine given a set of daemon adapters,
147165
// an initial state, an environment, and an event to process as if emitted at
148166
// the onset of the state machine. Such an event can be used to set up tracking
149167
// state such as a txid confirmation event.
150-
func NewStateMachine[Event any, Env Environment](adapters DaemonAdapters,
151-
initialState State[Event, Env], env Env,
152-
initEvent fn.Option[DaemonEvent]) StateMachine[Event, Env] {
168+
func NewStateMachine[Event any, Env Environment](cfg StateMachineCfg[Event, Env],
169+
) StateMachine[Event, Env] {
153170

154171
return StateMachine[Event, Env]{
155-
daemon: adapters,
172+
cfg: cfg,
156173
events: make(chan Event, 1),
157-
currentState: initialState,
158174
stateQuery: make(chan stateQuery[Event, Env]),
159175
quit: make(chan struct{}),
160-
env: env,
161-
initEvent: initEvent,
162176
newStateEvents: fn.NewEventDistributor[State[Event, Env]](),
163177
}
164178
}
@@ -237,7 +251,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(event DaemonEvent) error {
237251
// any preconditions as well as post-send events.
238252
case *SendMsgEvent[Event]:
239253
sendAndCleanUp := func() error {
240-
err := s.daemon.SendMessages(
254+
err := s.cfg.Daemon.SendMessages(
241255
daemonEvent.TargetPeer, daemonEvent.Msgs,
242256
)
243257
if err != nil {
@@ -301,7 +315,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(event DaemonEvent) error {
301315
// If this is a broadcast transaction event, then we'll broadcast with
302316
// the label attached.
303317
case *BroadcastTxn:
304-
err := s.daemon.BroadcastTransaction(
318+
err := s.cfg.Daemon.BroadcastTransaction(
305319
daemonEvent.Tx, daemonEvent.Label,
306320
)
307321
if err != nil {
@@ -315,7 +329,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(event DaemonEvent) error {
315329
// The state machine has requested a new event to be sent once a
316330
// transaction spending a specified outpoint has confirmed.
317331
case *RegisterSpend[Event]:
318-
spendEvent, err := s.daemon.RegisterSpendNtfn(
332+
spendEvent, err := s.cfg.Daemon.RegisterSpendNtfn(
319333
&daemonEvent.OutPoint, daemonEvent.PkScript,
320334
daemonEvent.HeightHint,
321335
)
@@ -351,7 +365,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(event DaemonEvent) error {
351365
// specified txid+pkScript pair has confirmed.
352366
case *RegisterConf[Event]:
353367
numConfs := daemonEvent.NumConfs.UnwrapOr(1)
354-
confEvent, err := s.daemon.RegisterConfirmationsNtfn(
368+
confEvent, err := s.cfg.Daemon.RegisterConfirmationsNtfn(
355369
&daemonEvent.Txid, daemonEvent.PkScript,
356370
numConfs, daemonEvent.HeightHint,
357371
)
@@ -391,9 +405,8 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(event DaemonEvent) error {
391405
// applyEvents applies a new event to the state machine. This will continue
392406
// until no further events are emitted by the state machine. Along the way,
393407
// we'll also ensure to execute any daemon events that are emitted.
394-
func (s *StateMachine[Event, Env]) applyEvents(newEvent Event) (State[Event, Env], error) {
395-
// TODO(roasbeef): make starting state as part of env?
396-
currentState := s.currentState
408+
func (s *StateMachine[Event, Env]) applyEvents(currentState State[Event, Env],
409+
newEvent Event) (State[Event, Env], error) {
397410

398411
eventQueue := fn.NewQueue(newEvent)
399412

@@ -406,7 +419,7 @@ func (s *StateMachine[Event, Env]) applyEvents(newEvent Event) (State[Event, Env
406419
// Apply the state transition function of the current
407420
// state given this new event and our existing env.
408421
transition, err := currentState.ProcessEvent(
409-
event, s.env,
422+
event, s.cfg.Env,
410423
)
411424
if err != nil {
412425
return err
@@ -469,12 +482,11 @@ func (s *StateMachine[Event, Env]) applyEvents(newEvent Event) (State[Event, Env
469482
func (s *StateMachine[Event, Env]) driveMachine() {
470483
defer s.wg.Done()
471484

472-
// TODO(roasbeef): move into env? read only to start with
473-
currentState := s.currentState
485+
currentState := s.cfg.InitialState
474486

475487
// Before we start, if we have an init daemon event specified, then
476488
// we'll handle that now.
477-
err := fn.MapOptionZ(s.initEvent, func(event DaemonEvent) error {
489+
err := fn.MapOptionZ(s.cfg.InitEvent, func(event DaemonEvent) error {
478490
return s.executeDaemonEvent(event)
479491
})
480492
if err != nil {
@@ -492,7 +504,7 @@ func (s *StateMachine[Event, Env]) driveMachine() {
492504
// machine forward until we either run out of internal events,
493505
// or we reach a terminal state.
494506
case newEvent := <-s.events:
495-
newState, err := s.applyEvents(newEvent)
507+
newState, err := s.applyEvents(currentState, newEvent)
496508
if err != nil {
497509
// TODO(roasbeef): hard error?
498510
log.Errorf("unable to apply event: %v", err)

protofsm/state_machine_test.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,13 @@ func TestStateMachineOnInitDaemonEvent(t *testing.T) {
228228
PostSendEvent: fn.Some(dummyEvents(&goToFin{})),
229229
}
230230

231-
stateMachine := NewStateMachine[dummyEvents, *dummyEnv](
232-
adapters, startingState, env, fn.Some[DaemonEvent](initEvent),
233-
)
231+
cfg := StateMachineCfg[dummyEvents, *dummyEnv]{
232+
Daemon: adapters,
233+
InitialState: startingState,
234+
Env: env,
235+
InitEvent: fn.Some[DaemonEvent](initEvent),
236+
}
237+
stateMachine := NewStateMachine(cfg)
234238

235239
// Before we start up the state machine, we'll assert that the send
236240
// message adapter is called on start up.
@@ -270,9 +274,13 @@ func TestStateMachineInternalEvents(t *testing.T) {
270274

271275
adapters := newDaemonAdapters()
272276

273-
stateMachine := NewStateMachine[dummyEvents, *dummyEnv](
274-
adapters, startingState, env, fn.None[DaemonEvent](),
275-
)
277+
cfg := StateMachineCfg[dummyEvents, *dummyEnv]{
278+
Daemon: adapters,
279+
InitialState: startingState,
280+
Env: env,
281+
InitEvent: fn.None[DaemonEvent](),
282+
}
283+
stateMachine := NewStateMachine(cfg)
276284
stateMachine.Start()
277285
defer stateMachine.Stop()
278286

@@ -317,9 +325,13 @@ func TestStateMachineDaemonEvents(t *testing.T) {
317325

318326
adapters := newDaemonAdapters()
319327

320-
stateMachine := NewStateMachine[dummyEvents, *dummyEnv](
321-
adapters, startingState, env, fn.None[DaemonEvent](),
322-
)
328+
cfg := StateMachineCfg[dummyEvents, *dummyEnv]{
329+
Daemon: adapters,
330+
InitialState: startingState,
331+
Env: env,
332+
InitEvent: fn.None[DaemonEvent](),
333+
}
334+
stateMachine := NewStateMachine(cfg)
323335
stateMachine.Start()
324336
defer stateMachine.Stop()
325337

0 commit comments

Comments
 (0)