From 84ddced2b5c1bfbbd0ad4731f636c2c02327a300 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 4 Aug 2026 15:49:26 -0700 Subject: [PATCH] lnwallet/chancloser: record the remote close output only when accepted In this commit, we hold off on recording the remote party's close output until we've decided we can act on their Shutdown. ReceiveShutdown wrote the field before it looked at the state, so a Shutdown that arrives at a point where we have nothing to do with it, say once we've already finished the negotiation, would still overwrite the output we settled on before being turned away with ErrInvalidState. The output we report for the close then describes a message we rejected. Nothing acts on this today, as we hand the outputs to the caller only after ClosingTx tells it the negotiation finished, but the field is what we report to the party that asked for the close, so we may as well only fill it in from a message we accepted. --- lnwallet/chancloser/chancloser.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lnwallet/chancloser/chancloser.go b/lnwallet/chancloser/chancloser.go index 60106af0d..a95dbac0f 100644 --- a/lnwallet/chancloser/chancloser.go +++ b/lnwallet/chancloser/chancloser.go @@ -593,10 +593,13 @@ func (c *ChanCloser) ReceiveShutdown(msg lnwire.Shutdown) ( noShutdown := fn.None[lnwire.Shutdown]() // We'll track their remote close output, even if it's dust in BTC - // terms, it might still carry value in custom channel terms. + // terms, it might still carry value in custom channel terms. We only + // commit it to our state in the branches below that go on to accept the + // message: a Shutdown that shows up at a point where we can't act on it + // has no business overwriting an output we already settled on. _, dustAmt := c.cfg.Channel.RemoteBalanceDust() _, remoteBalance := c.cfg.Channel.CommitBalances() - c.remoteCloseOutput = fn.Some(CloseOutput{ + remoteCloseOutput := fn.Some(CloseOutput{ Amt: remoteBalance, DustLimit: dustAmt, PkScript: msg.Address, @@ -643,6 +646,7 @@ func (c *ChanCloser) ReceiveShutdown(msg lnwire.Shutdown) ( // address. We'll use this when we craft the closure // transaction. c.remoteDeliveryScript = msg.Address + c.remoteCloseOutput = remoteCloseOutput // We'll generate a shutdown message of our own to send across // the wire. @@ -692,6 +696,7 @@ func (c *ChanCloser) ReceiveShutdown(msg lnwire.Shutdown) ( // address, we'll record their preferred delivery closing // script. c.remoteDeliveryScript = msg.Address + c.remoteCloseOutput = remoteCloseOutput // At this point, we can now start the fee negotiation state, by // constructing and sending our initial signature for what we