@@ -27,30 +27,95 @@ var (
2727
2828// States.
2929var (
30+ // Deposited signals that funds at a static address have reached the
31+ // confirmation height.
3032 Deposited = fsm .StateType ("Deposited" )
3133
34+ // Withdrawing signals that the withdrawal transaction has been
35+ // broadcast, awaiting sufficient confirmations.
3236 Withdrawing = fsm .StateType ("Withdrawing" )
3337
38+ // Withdrawn signals that the withdrawal transaction has been confirmed.
3439 Withdrawn = fsm .StateType ("Withdrawn" )
3540
41+ // LoopingIn signals that the deposit is locked for a loop in swap.
42+ LoopingIn = fsm .StateType ("LoopingIn" )
43+
44+ // LoopedIn signals that the loop in swap has been successfully
45+ // completed. It implies that we signed the sweepless sweep tx for the
46+ // server.
47+ LoopedIn = fsm .StateType ("LoopedIn" )
48+
49+ // SweepHtlcTimeout signals that the htlc timeout path is in the
50+ // process of being swept.
51+ SweepHtlcTimeout = fsm .StateType ("SweepHtlcTimeout" )
52+
53+ // HtlcTimeoutSwept signals that the htlc timeout path has been swept.
54+ HtlcTimeoutSwept = fsm .StateType ("HtlcTimeoutSwept" )
55+
56+ // PublishExpiredDeposit signals that the deposit has expired and we are
57+ // in the process of publishing the expiry sweep transaction.
3658 PublishExpiredDeposit = fsm .StateType ("PublishExpiredDeposit" )
3759
60+ // WaitForExpirySweep signals that the expiry sweep transaction has been
61+ // published and we are waiting for it to be confirmed.
3862 WaitForExpirySweep = fsm .StateType ("WaitForExpirySweep" )
3963
64+ // Expired signals that the deposit has expired and the expiry sweep
65+ // transaction has been confirmed sufficiently.
4066 Expired = fsm .StateType ("Expired" )
4167
68+ // Failed signals that the deposit has neither been withdrawn nor looped
69+ // in, but expired.
4270 Failed = fsm .StateType ("Failed" )
4371)
4472
4573// Events.
4674var (
47- OnStart = fsm .EventType ("OnStart" )
75+ // OnStart is sent to the fsm once the deposit outpoint has been
76+ // sufficiently confirmed. It transitions the fsm into the Deposited
77+ // state from where we can trigger a withdrawal, a loopin or an expiry.
78+ OnStart = fsm .EventType ("OnStart" )
79+
80+ // OnWithdrawInitiated is sent to the fsm when a withdrawal has been
81+ // initiated.
4882 OnWithdrawInitiated = fsm .EventType ("OnWithdrawInitiated" )
49- OnWithdrawn = fsm .EventType ("OnWithdrawn" )
50- OnExpiry = fsm .EventType ("OnExpiry" )
51- OnExpiryPublished = fsm .EventType ("OnExpiryPublished" )
52- OnExpirySwept = fsm .EventType ("OnExpirySwept" )
53- OnRecover = fsm .EventType ("OnRecover" )
83+
84+ // OnWithdrawn is sent to the fsm when a withdrawal has been confirmed.
85+ OnWithdrawn = fsm .EventType ("OnWithdrawn" )
86+
87+ // OnLoopinInitiated is sent to the fsm when a loop in has been
88+ // initiated.
89+ OnLoopinInitiated = fsm .EventType ("OnLoopinInitiated" )
90+
91+ // OnSweepingHtlcTimeout is sent to the fsm when the htlc timeout path
92+ // is being swept. This indicates that the server didn't pay the swap
93+ // invoice, but the htlc tx was published, from we which we need to
94+ // sweep the htlc timeout path.
95+ OnSweepingHtlcTimeout = fsm .EventType ("OnSweepingHtlcTimeout" )
96+
97+ // OnHtlcTimeoutSwept is sent to the fsm when the htlc timeout path has
98+ // been swept.
99+ OnHtlcTimeoutSwept = fsm .EventType ("OnHtlcTimeoutSwept" )
100+
101+ // OnLoopedIn is sent to the fsm when the user intents to use the
102+ // deposit for a loop in swap.
103+ OnLoopedIn = fsm .EventType ("OnLoopedIn" )
104+
105+ // OnExpiry is sent to the fsm when the deposit has expired.
106+ OnExpiry = fsm .EventType ("OnExpiry" )
107+
108+ // OnExpiryPublished is sent to the fsm when the expiry sweep tx has
109+ // been published.
110+ OnExpiryPublished = fsm .EventType ("OnExpiryPublished" )
111+
112+ // OnExpirySwept is sent to the fsm when the expiry sweep tx has been
113+ // confirmed.
114+ OnExpirySwept = fsm .EventType ("OnExpirySwept" )
115+
116+ // OnRecover is sent to the fsm when it should recover from client
117+ // restart.
118+ OnRecover = fsm .EventType ("OnRecover" )
54119)
55120
56121// FSM is the state machine that handles the instant out.
@@ -81,12 +146,12 @@ func NewFSM(ctx context.Context, deposit *Deposit, cfg *ManagerConfig,
81146 params , err := cfg .AddressManager .GetStaticAddressParameters (ctx )
82147 if err != nil {
83148 return nil , fmt .Errorf ("unable to get static address " +
84- "parameters: %v " , err )
149+ "parameters: %w " , err )
85150 }
86151
87152 address , err := cfg .AddressManager .GetStaticAddress (ctx )
88153 if err != nil {
89- return nil , fmt .Errorf ("unable to get static address: %v " , err )
154+ return nil , fmt .Errorf ("unable to get static address: %w " , err )
90155 }
91156
92157 depoFsm := & FSM {
@@ -149,7 +214,7 @@ func (f *FSM) handleBlockNotification(currentHeight uint32) {
149214 err := f .SendEvent (OnExpiry , nil )
150215 if err != nil {
151216 log .Debugf ("error sending OnExpiry " +
152- "event: %v " , err )
217+ "event: %w " , err )
153218 }
154219 }()
155220 }
@@ -169,6 +234,7 @@ func (f *FSM) DepositStatesV0() fsm.States {
169234 Transitions : fsm.Transitions {
170235 OnExpiry : PublishExpiredDeposit ,
171236 OnWithdrawInitiated : Withdrawing ,
237+ OnLoopinInitiated : LoopingIn ,
172238 OnRecover : Deposited ,
173239 },
174240 Action : fsm .NoOpAction ,
@@ -228,11 +294,55 @@ func (f *FSM) DepositStatesV0() fsm.States {
228294 },
229295 Action : fsm .NoOpAction ,
230296 },
297+ LoopingIn : fsm.State {
298+ Transitions : fsm.Transitions {
299+ // This event is triggered when the loop in
300+ // payment has been received. We consider the
301+ // swap to be completed and transition to a
302+ // final state.
303+ OnLoopedIn : LoopedIn ,
304+
305+ // If the deposit expires while the loop in is
306+ // still pending, we publish the expiry sweep.
307+ OnExpiry : PublishExpiredDeposit ,
308+
309+ // We encounter this signal if the server
310+ // published the htlc tx without paying us. We
311+ // then need to monitor for the timeout path to
312+ // open up to sweep it.
313+ OnSweepingHtlcTimeout : SweepHtlcTimeout ,
314+
315+ OnLoopinInitiated : LoopingIn ,
316+
317+ OnRecover : LoopingIn ,
318+ fsm .OnError : Deposited ,
319+ },
320+ Action : fsm .NoOpAction ,
321+ },
322+ LoopedIn : fsm.State {
323+ Transitions : fsm.Transitions {
324+ OnExpiry : Expired ,
325+ },
326+ Action : f .FinalizeDepositAction ,
327+ },
328+ SweepHtlcTimeout : fsm.State {
329+ Transitions : fsm.Transitions {
330+ OnHtlcTimeoutSwept : HtlcTimeoutSwept ,
331+ OnRecover : SweepHtlcTimeout ,
332+ },
333+ Action : fsm .NoOpAction ,
334+ },
335+ HtlcTimeoutSwept : fsm.State {
336+ Transitions : fsm.Transitions {
337+ OnExpiry : HtlcTimeoutSwept ,
338+ },
339+ Action : f .FinalizeDepositAction ,
340+ },
231341 Withdrawn : fsm.State {
232342 Transitions : fsm.Transitions {
233343 OnExpiry : Expired ,
234344 },
235- Action : f .WithdrawnDepositAction ,
345+ Action : f .FinalizeDepositAction ,
236346 },
237347 Failed : fsm.State {
238348 Transitions : fsm.Transitions {
@@ -256,24 +366,36 @@ func (f *FSM) updateDeposit(notification fsm.Notification) {
256366 )
257367
258368 f .deposit .SetState (notification .NextState )
259-
260- // Don't update the deposit if we are in an initial state or if we
261- // are transitioning from an initial state to a failed state.
262- d := f .deposit
263- if d .IsInState (fsm .EmptyState ) || d .IsInState (Deposited ) ||
264- (notification .PreviousState == Deposited && d .IsInState (
265- Failed ,
266- )) {
267-
369+ if skipUpdate (notification , f .deposit ) {
268370 return
269371 }
270372
271373 err := f .cfg .Store .UpdateDeposit (f .ctx , f .deposit )
272374 if err != nil {
273- f .Errorf ("unable to update deposit: %v " , err )
375+ f .Errorf ("unable to update deposit: %w " , err )
274376 }
275377}
276378
379+ // skipUpdate returns true if the deposit should not be updated for the given
380+ // notification.
381+ func skipUpdate (notification fsm.Notification , d * Deposit ) bool {
382+ prevState := notification .PreviousState
383+
384+ // Don't update the deposit if we are in an initial state or if we are
385+ // transitioning from an initial state to a failed state.
386+ if d .IsInState (fsm .EmptyState ) || d .IsInState (Deposited ) &&
387+ prevState == fsm .EmptyState ||
388+ d .IsInState (prevState ) ||
389+ prevState == Deposited && d .IsInState (Deposited ) ||
390+ prevState == Deposited && d .IsInState (Failed ) ||
391+ prevState == LoopingIn && d .IsInState (LoopingIn ) {
392+
393+ return true
394+ }
395+
396+ return false
397+ }
398+
277399// Infof logs an info message with the deposit outpoint.
278400func (f * FSM ) Infof (format string , args ... interface {}) {
279401 log .Infof (
0 commit comments