lightningd: explicitly unwatch funding scriptpubkey when channel finally closed.
What changed, and why it matters
This change adds an explicit cleanup step that tells the node to stop monitoring the funding transaction once a channel is finally closed. The commit message says this is not strictly needed today because an existing destructor already handles it, but it will matter in a future refactor. There is no direct evidence this fixes an exploitable security bug.
No immediate security action required. Treat as normal maintenance/refactoring. If a security advisory later claims this commit fixes a vulnerability, verify against vendor-authored references.
Security signals we found
Adds explicit unwatch of funding scriptPubKey on channel deletion
Commit message states current destructor already makes this unnecessary
Future refactor motivation ('move watching out to bwatch')
No mention of vulnerability, CVE, bug, or security issue in commit or supplied references
Evidence from the diff
The patch introduces channel_unwatch_funding() in lightningd/peer_control.c and calls it from delete_channel() in lightningd/channel.c before wallet_channel_close(). It computes the 2-of-2 P2WSH funding scriptPubKey and calls unwatch_scriptpubkey() to remove the on-chain watch, skipping stub channels. Test stubs are updated. The commit message frames this as future-proofing for moving watch logic to a separate bwatch process, not as a current vulnerability fix.
Changed components
lightningd/channel.clightningd/peer_control.clightningd/peer_control.hlightningd/test/run-invoice-select-inchan.cwallet/test/run-wallet.cInspect captured patch +51 / −1
diff --git a/lightningd/channel.c b/lightningd/channel.c
index fa22c40a..db05cf57 100644
--- a/lightningd/channel.c
+++ b/lightningd/channel.c
@@ -92,8 +92,10 @@ void delete_channel(struct channel *channel STEALS,
struct peer *peer = channel->peer;
struct lightningd *ld = peer->ld;
-
if (channel->dbid != 0) {
+ /* We no longer care about the funding transaction */
+ channel_unwatch_funding(ld, channel);
+
wallet_channel_close(ld->wallet, channel);
/* Never open at all, not ours. */
if (completely_eliminate)
diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c
index 8ae452fd..fdabbc70 100644
--- a/lightningd/peer_control.c
+++ b/lightningd/peer_control.c
@@ -2523,6 +2523,25 @@ void channel_watch_funding(struct lightningd *ld, struct channel *channel)
channel_watch_wrong_funding(ld, channel);
}
+void channel_unwatch_funding(struct lightningd *ld, struct channel *channel)
+{
+ const u8 *funding_wscript = bitcoin_redeem_2of2(tmpctx,
+ &channel->local_funding_pubkey,
+ &channel->channel_info.remote_fundingkey);
+
+ /* This is stub channel, we don't watch anything! */
+ if (channel->scid && is_stub_scid(*channel->scid))
+ return;
+
+ unwatch_scriptpubkey(channel, ld->topology,
+ scriptpubkey_p2wsh(tmpctx, funding_wscript),
+ &channel->funding,
+ channel->funding_sats,
+ channel_funding_found,
+ channel);
+ /* FIXME: unwatch txo and depth too? */
+}
+
static void json_add_peer(struct lightningd *ld,
struct json_stream *response,
struct peer *p,
diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h
index 91160fc1..279a2f91 100644
--- a/lightningd/peer_control.h
+++ b/lightningd/peer_control.h
@@ -136,6 +136,7 @@ void update_channel_from_inflight(struct lightningd *ld,
/* Watch for funding tx. */
void channel_watch_funding(struct lightningd *ld, struct channel *channel);
+void channel_unwatch_funding(struct lightningd *ld, struct channel *channel);
/* Watch for spend of funding tx. */
void channel_watch_funding_out(struct lightningd *ld, struct channel *channel);
diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c
index a03e4f6c..d82a9c09 100644
--- a/lightningd/test/run-invoice-select-inchan.c
+++ b/lightningd/test/run-invoice-select-inchan.c
@@ -658,6 +658,19 @@ u8 *towire_onchaind_dev_memleak(const tal_t *ctx UNNEEDED)
/* Generated stub for towire_openingd_dev_memleak */
u8 *towire_openingd_dev_memleak(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "towire_openingd_dev_memleak called!\n"); abort(); }
+/* Generated stub for unwatch_scriptpubkey_ */
+bool unwatch_scriptpubkey_(const tal_t *ctx UNNEEDED,
+ struct chain_topology *topo UNNEEDED,
+ const u8 *scriptpubkey TAKES UNNEEDED,
+ const struct bitcoin_outpoint *expected_outpoint UNNEEDED,
+ struct amount_sat expected_amount UNNEEDED,
+ void (*cb)(struct lightningd *ld UNNEEDED,
+ const struct bitcoin_tx *tx UNNEEDED,
+ u32 outnum UNNEEDED,
+ const struct txlocator *loc UNNEEDED,
+ void *) UNNEEDED,
+ void *arg UNNEEDED)
+{ fprintf(stderr, "unwatch_scriptpubkey_ called!\n"); abort(); }
/* Generated stub for wallet_channel_save */
void wallet_channel_save(struct wallet *w UNNEEDED, struct channel *chan UNNEEDED)
{ fprintf(stderr, "wallet_channel_save called!\n"); abort(); }
diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c
index 542cffce..1b2e8ac2 100644
--- a/wallet/test/run-wallet.c
+++ b/wallet/test/run-wallet.c
@@ -879,6 +879,21 @@ void migrate_from_account_db(struct lightningd *ld UNNEEDED, struct db *db UNNEE
{
}
+bool unwatch_scriptpubkey_(const tal_t *ctx UNNEEDED,
+ struct chain_topology *topo UNNEEDED,
+ const u8 *scriptpubkey TAKES UNNEEDED,
+ const struct bitcoin_outpoint *expected_outpoint UNNEEDED,
+ struct amount_sat expected_amount UNNEEDED,
+ void (*cb)(struct lightningd *ld UNNEEDED,
+ const struct bitcoin_tx *tx UNNEEDED,
+ u32 outnum UNNEEDED,
+ const struct txlocator *loc UNNEEDED,
+ void *) UNNEEDED,
+ void *arg UNNEEDED)
+{
+ return true;
+}
+
/**
* mempat -- Set the memory to a pattern
*
Why this scored 17/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.