loopin: exit htlc timeout sweep retry loop on context cancellation

SweepHtlcTimeoutAction used a select with a default case containing a
blocking time.After. When the context was canceled, it logged the error
but continued the retry loop instead of returning. The default case
also meant that ctx.Done was only checked when it was already signaled,
while an hour-long sleep blocked without listening for cancellation.

Replace the default+time.After pattern with a proper select on both
ctx.Done and time.After so the function exits promptly on shutdown.
This commit is contained in:
Slyghtning 2026-02-28 10:26:28 +01:00
parent 63d8f5560e
commit aadf6e2daa
No known key found for this signature in database
GPG key ID: F82D456EA023C9BF

View file

@ -705,6 +705,10 @@ func (f *FSM) MonitorInvoiceAndHtlcTxAction(ctx context.Context,
}
}
// htlcTimeoutSweepRetryDelay is the delay between retries when publishing the
// htlc timeout sweep transaction fails.
const htlcTimeoutSweepRetryDelay = time.Hour
// SweepHtlcTimeoutAction is called if the server published the htlc tx without
// paying the invoice. We wait for the timeout path to open up and sweep the
// funds back to us.
@ -714,22 +718,22 @@ func (f *FSM) SweepHtlcTimeoutAction(ctx context.Context,
for {
err := f.createAndPublishHtlcTimeoutSweepTx(ctx)
if err == nil {
break
return OnHtlcTimeoutSweepPublished
}
f.Errorf("unable to create and publish htlc timeout sweep "+
"tx: %v, retrying in %v", err, time.Hour.String())
"tx: %v, retrying in %v", err, htlcTimeoutSweepRetryDelay)
select {
// The context is cancelled when the server is shutting
// down. In that case we give up broadcasting attempts
// and return an error.
case <-ctx.Done():
f.Errorf("%v", ctx.Err())
return f.HandleError(ctx.Err())
default:
<-time.After(1 * time.Hour)
case <-time.After(htlcTimeoutSweepRetryDelay):
}
}
return OnHtlcTimeoutSweepPublished
}
// MonitorHtlcTimeoutSweepAction is called after the htlc timeout sweep tx has