mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
staticaddr/loopin: keep htlc timeout sweep resumable
A shutdown while publishing or monitoring the HTLC timeout sweep should not transition the loop-in to Failed. Return NoOp on context cancellation in those actions so the persisted state remains a recovery point. Add focused tests for shutdown during publication retry and confirmation monitoring.
This commit is contained in:
parent
58fbe2230e
commit
0c0cee377b
2 changed files with 81 additions and 4 deletions
|
|
@ -810,10 +810,10 @@ func (f *FSM) SweepHtlcTimeoutAction(ctx context.Context,
|
|||
|
||||
select {
|
||||
// The context is cancelled when the server is shutting
|
||||
// down. In that case we give up broadcasting attempts
|
||||
// and return an error.
|
||||
// down. Keep the current state so recovery resumes
|
||||
// broadcasting attempts after restart.
|
||||
case <-ctx.Done():
|
||||
return f.HandleError(ctx.Err())
|
||||
return fsm.NoOp
|
||||
|
||||
case <-time.After(htlcTimeoutSweepRetryDelay):
|
||||
}
|
||||
|
|
@ -847,6 +847,10 @@ func (f *FSM) MonitorHtlcTimeoutSweepAction(ctx context.Context,
|
|||
)
|
||||
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return fsm.NoOp
|
||||
}
|
||||
|
||||
err = fmt.Errorf("unable to register to the htlc timeout "+
|
||||
"sweep tx: %w", err)
|
||||
|
||||
|
|
@ -856,6 +860,10 @@ func (f *FSM) MonitorHtlcTimeoutSweepAction(ctx context.Context,
|
|||
for {
|
||||
select {
|
||||
case err := <-errChan:
|
||||
if ctx.Err() != nil {
|
||||
return fsm.NoOp
|
||||
}
|
||||
|
||||
return f.HandleError(err)
|
||||
|
||||
case conf := <-htlcTimeoutTxidChan:
|
||||
|
|
@ -879,7 +887,7 @@ func (f *FSM) MonitorHtlcTimeoutSweepAction(ctx context.Context,
|
|||
return OnHtlcTimeoutSwept
|
||||
|
||||
case <-ctx.Done():
|
||||
return f.HandleError(ctx.Err())
|
||||
return fsm.NoOp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,6 +207,75 @@ func TestMonitorInvoiceAndHtlcTxReRegistersOnConfErr(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestSweepHtlcTimeoutActionNoOpOnShutdown ensures that a shutdown during
|
||||
// timeout sweep publication keeps the FSM in the same state so it can resume
|
||||
// after restart.
|
||||
func TestSweepHtlcTimeoutActionNoOpOnShutdown(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
cancel()
|
||||
|
||||
mockLnd := test.NewMockLnd()
|
||||
f := &FSM{
|
||||
StateMachine: &fsm.StateMachine{},
|
||||
cfg: &Config{
|
||||
LndClient: mockLnd.Client,
|
||||
WalletKit: mockLnd.WalletKit,
|
||||
},
|
||||
loopIn: &StaticAddressLoopIn{},
|
||||
}
|
||||
|
||||
event := f.SweepHtlcTimeoutAction(ctx, nil)
|
||||
require.Equal(t, fsm.NoOp, event)
|
||||
require.Nil(t, f.LastActionError)
|
||||
}
|
||||
|
||||
// TestMonitorHtlcTimeoutSweepActionNoOpOnShutdown ensures that a shutdown
|
||||
// while waiting for the timeout sweep confirmation keeps the FSM resumable.
|
||||
func TestMonitorHtlcTimeoutSweepActionNoOpOnShutdown(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mockLnd := test.NewMockLnd()
|
||||
sweepAddr, err := mockLnd.WalletKit.NextAddr(ctx, "", 0, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
f := &FSM{
|
||||
StateMachine: &fsm.StateMachine{},
|
||||
cfg: &Config{
|
||||
ChainNotifier: mockLnd.ChainNotifier,
|
||||
},
|
||||
loopIn: &StaticAddressLoopIn{
|
||||
HtlcTimeoutSweepAddress: sweepAddr,
|
||||
InitiationHeight: uint32(mockLnd.Height),
|
||||
},
|
||||
}
|
||||
|
||||
resultChan := make(chan fsm.EventType, 1)
|
||||
go func() {
|
||||
resultChan <- f.MonitorHtlcTimeoutSweepAction(ctx, nil)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-mockLnd.RegisterConfChannel:
|
||||
case <-ctx.Done():
|
||||
t.Fatalf("timeout sweep conf registration not received: %v",
|
||||
ctx.Err())
|
||||
}
|
||||
|
||||
cancel()
|
||||
|
||||
select {
|
||||
case event := <-resultChan:
|
||||
require.Equal(t, fsm.NoOp, event)
|
||||
require.Nil(t, f.LastActionError)
|
||||
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("timeout sweep monitor did not return")
|
||||
}
|
||||
}
|
||||
|
||||
// TestInitHtlcActionPreservesRouteHints asserts that static-address loop-in
|
||||
// propagates explicit route hints into the encoded swap invoice sent to the
|
||||
// server.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue