diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 6f33c0fb6..0b3a9725d 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -998,6 +998,74 @@ func (c *chainWatcher) toSelfAmount(tx *wire.MsgTx) btcutil.Amount { return btcutil.Amount(fn.Sum(vals)) } +// finalizeCoopClose calls the aux closer to finalize a cooperative close +// transaction that has been confirmed on-chain. +func (c *chainWatcher) finalizeCoopClose(aux AuxChanCloser, + closeTx *wire.MsgTx) error { + + chanState := c.cfg.chanState + + // Get the shutdown info to extract the local delivery script. + shutdown, err := chanState.ShutdownInfo() + if err != nil { + return fmt.Errorf("get shutdown info: %w", err) + } + + // Build the AuxShutdownReq. + req := types.AuxShutdownReq{ + ChanPoint: chanState.FundingOutpoint, + ShortChanID: chanState.ShortChanID(), + Initiator: chanState.IsInitiator, + CommitBlob: chanState.LocalCommitment.CustomBlob, + FundingBlob: chanState.CustomBlob, + } + + // Shutdown info must be present in order to continue. + if shutdown.IsNone() { + return fmt.Errorf("failed to finalize coop close, shutdown " + + "info missing") + } + + // Extract close outputs from the transaction. We need to identify + // which outputs belong to local vs remote parties. + var localCloseOutput, remoteCloseOutput fn.Option[types.CloseOutput] + + // Get the delivery scripts for the local party. + var localDeliveryScript lnwire.DeliveryAddress + shutdown.WhenSome(func(s channeldb.ShutdownInfo) { + localDeliveryScript = s.DeliveryScript.Val + }) + + // Scan through the close transaction outputs to identify local and + // remote outputs. + for _, out := range closeTx.TxOut { + if len(localDeliveryScript) > 0 && + slices.Equal(out.PkScript, localDeliveryScript) { + + localCloseOutput = fn.Some(types.CloseOutput{ + Amt: btcutil.Amount(out.Value), + PkScript: out.PkScript, + DustLimit: chanState.LocalChanCfg.DustLimit, + }) + } else { + // This must be the remote output. + remoteCloseOutput = fn.Some(types.CloseOutput{ + Amt: btcutil.Amount(out.Value), + PkScript: out.PkScript, + DustLimit: chanState.RemoteChanCfg.DustLimit, + }) + } + } + + desc := types.AuxCloseDesc{ + AuxShutdownReq: req, + LocalCloseOutput: localCloseOutput, + RemoteCloseOutput: remoteCloseOutput, + } + + return aux.FinalizeClose(desc, closeTx) +} + // dispatchCooperativeClose processed a detect cooperative channel closure. // We'll use the spending transaction to locate our output within the // transaction, then clean up the database state. We'll also dispatch a @@ -1048,6 +1116,17 @@ func (c *chainWatcher) dispatchCooperativeClose(commitSpend *chainntnfs.SpendDet ChannelCloseSummary: closeSummary, } + // If we have an aux closer, finalize the cooperative close now that + // it's confirmed. + err = fn.MapOptionZ( + c.cfg.auxCloser, func(aux AuxChanCloser) error { + return c.finalizeCoopClose(aux, broadcastTx) + }, + ) + if err != nil { + return fmt.Errorf("finalize coop close: %w", err) + } + // With the event processed, we'll now notify all subscribers of the // event. c.Lock() diff --git a/lnwallet/chancloser/chancloser.go b/lnwallet/chancloser/chancloser.go index 90e91f615..6e5eaf393 100644 --- a/lnwallet/chancloser/chancloser.go +++ b/lnwallet/chancloser/chancloser.go @@ -974,33 +974,6 @@ func (c *ChanCloser) ReceiveClosingSigned( //nolint:funlen } c.closingTx = closeTx - // If there's an aux chan closer, then we'll finalize with it - // before we write to disk. - err = fn.MapOptionZ( - c.cfg.AuxCloser, func(aux AuxChanCloser) error { - channel := c.cfg.Channel - //nolint:ll - req := types.AuxShutdownReq{ - ChanPoint: c.chanPoint, - ShortChanID: c.cfg.Channel.ShortChanID(), - InternalKey: c.localInternalKey, - Initiator: channel.IsInitiator(), - CommitBlob: channel.LocalCommitmentBlob(), - FundingBlob: channel.FundingBlob(), - } - desc := types.AuxCloseDesc{ - AuxShutdownReq: req, - LocalCloseOutput: c.localCloseOutput, - RemoteCloseOutput: c.remoteCloseOutput, - } - - return aux.FinalizeClose(desc, closeTx) - }, - ) - if err != nil { - return noClosing, err - } - // Before publishing the closing tx, we persist it to the // database, such that it can be republished if something goes // wrong.