contractcourt+lnwallet: move aux close finalization to chain watcher

We now execute the aux chan closer finalization within the chain
watcher. This is better as we don't need to rely on the remote party
being online and sending us a message. Instead we do the finalization
once the on-chain transaction has been confirmed.
This commit is contained in:
George Tsagkarelis committed 2025-12-04 12:18:14 +01:00
1 parent fd772d45b8
commit 9a88999c41
2 files changed
+79 -27

No files matched your search

+79
View File
@@ -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()
-27
View File
@@ -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.