From aadf6e2daae0d3b09b4305a44aec05d0bf1d3523 Mon Sep 17 00:00:00 2001 From: Slyghtning Date: Sat, 28 Feb 2026 10:26:28 +0100 Subject: [PATCH] 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. --- staticaddr/loopin/actions.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/staticaddr/loopin/actions.go b/staticaddr/loopin/actions.go index 837a6eb8..70a27811 100644 --- a/staticaddr/loopin/actions.go +++ b/staticaddr/loopin/actions.go @@ -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