bwatch: fire blockdepth notifications per block
What changed, and why it matters
This commit adds a new internal notification path in Core Lightning's block-watching plugin. It makes the plugin send a depth update to other parts of the node every time a new block arrives, so components waiting for confirmation milestones (like channel-close handling) wake up on time. The change itself is a feature/fix for reliability in hibernating environments such as Greenlight, not a patch for an externally reported security vulnerability. There is no evidence in the commit or supplied references of an exploitable weakness.
Review as normal code-quality/feature change. No urgent security action is indicated by the commit itself. If deploying, monitor for any regressions in subdaemon restart timing or duplicate watch_found notifications.
Security signals we found
New RPC-style notification path added (watch_found with depth + blockheight, no tx)
Explicit handling of stale watches after reorg (start_block > new_height skipped)
Ordering change: depth notifications fire before per-tx scan to avoid race with subdaemon startup
No input validation changes, no bounds checks beyond the existing depth arithmetic
No CVE, advisory, or security-relevant commit message language present
Evidence from the diff
The patch introduces bwatch_check_blockdepth_watches() and bwatch_send_blockdepth_found(). On each new block, bwatch now iterates active blockdepth watches and emits a watch_found RPC carrying only blockheight and depth (no transaction). Stale watches whose start_block is ahead of the current tip are skipped, with an explicit comment that they are awaiting deletion after a reorg/revert. The depth notification is ordered before per-transaction scanning so restart-marker watches can spin up subdaemons before outpoint notifications for the same block. This is architectural/behavioral code, not a memory-safety or cryptographic fix.
Changed components
plugins/bwatch/bwatch.cplugins/bwatch/bwatch_interface.cplugins/bwatch/bwatch_interface.hplugins/bwatch/bwatch_scanner.cplugins/bwatch/bwatch_scanner.hInspect captured patch +62 / −0
diff --git a/plugins/bwatch/bwatch.c b/plugins/bwatch/bwatch.c
index 3a6b05c0..1b31f98b 100644
--- a/plugins/bwatch/bwatch.c
+++ b/plugins/bwatch/bwatch.c
@@ -163,6 +163,10 @@ static struct command_result *handle_block(struct command *cmd,
return fetch_block_handle(cmd, bwatch->current_height + 1);
}
+ /* Depth first: restart-marker watches (e.g. onchaind/
+ * channel_close) start subdaemons before outpoint watches
+ * fire for the same block. */
+ bwatch_check_blockdepth_watches(cmd, bwatch, block_height);
bwatch_process_block_txs(cmd, bwatch, block, block_height,
&blockhash);
}
diff --git a/plugins/bwatch/bwatch_interface.c b/plugins/bwatch/bwatch_interface.c
index 7248fe0f..767fc999 100644
--- a/plugins/bwatch/bwatch_interface.c
+++ b/plugins/bwatch/bwatch_interface.c
@@ -58,6 +58,29 @@ void bwatch_send_watch_found(struct command *cmd,
send_outreq(req);
}
+/* Send a blockdepth depth notification to lightningd: same watch_found
+ * RPC shape but with depth + blockheight only (no tx). */
+void bwatch_send_blockdepth_found(struct command *cmd,
+ const struct watch *w,
+ u32 depth,
+ u32 blockheight)
+{
+ struct command *aux = aux_command(cmd);
+ struct out_req *req;
+
+ req = jsonrpc_request_start(aux, "watch_found",
+ notify_ack, notify_ack, NULL);
+ json_add_u32(req->js, "blockheight", blockheight);
+ json_add_u32(req->js, "depth", depth);
+
+ json_array_start(req->js, "owners");
+ for (size_t i = 0; i < tal_count(w->owners); i++)
+ json_add_string(req->js, NULL, w->owners[i]);
+ json_array_end(req->js);
+
+ send_outreq(req);
+}
+
/* Tell one owner that a previously-reported watch_found was rolled back. */
void bwatch_send_watch_revert(struct command *cmd,
const char *owner,
diff --git a/plugins/bwatch/bwatch_interface.h b/plugins/bwatch/bwatch_interface.h
index 092d3f21..7cea7c7b 100644
--- a/plugins/bwatch/bwatch_interface.h
+++ b/plugins/bwatch/bwatch_interface.h
@@ -14,6 +14,12 @@ void bwatch_send_watch_found(struct command *cmd,
u32 txindex,
u32 index);
+/* Send blockdepth depth notification to lightningd (no tx, just depth + height) */
+void bwatch_send_blockdepth_found(struct command *cmd,
+ const struct watch *w,
+ u32 depth,
+ u32 blockheight);
+
void bwatch_send_watch_revert(struct command *cmd,
const char *owner,
u32 blockheight);
diff --git a/plugins/bwatch/bwatch_scanner.c b/plugins/bwatch/bwatch_scanner.c
index 3dc13d96..6f268c96 100644
--- a/plugins/bwatch/bwatch_scanner.c
+++ b/plugins/bwatch/bwatch_scanner.c
@@ -161,3 +161,26 @@ void bwatch_process_block_txs(struct command *cmd,
check_scid_watches(cmd, bwatch, block, blockheight);
}
+
+/* Fire depth notifications for every active blockdepth watch.
+ * A watch with start_block > new_height is stale: its confirming block
+ * was reorged away, watch_revert has been sent, but the del hasn't
+ * arrived yet — skip it until deletion clears it from the table. */
+void bwatch_check_blockdepth_watches(struct command *cmd,
+ struct bwatch *bwatch,
+ u32 new_height)
+{
+ struct blockdepth_watches_iter it;
+ struct watch *w;
+
+ /* We only have one per channel or so in practice, so don't optimize */
+ for (w = blockdepth_watches_first(bwatch->blockdepth_watches, &it);
+ w;
+ w = blockdepth_watches_next(bwatch->blockdepth_watches, &it)) {
+ if (w->start_block > new_height)
+ continue; /* stale — awaiting deletion */
+
+ u32 depth = new_height - w->start_block + 1;
+ bwatch_send_blockdepth_found(cmd, w, depth, new_height);
+ }
+}
diff --git a/plugins/bwatch/bwatch_scanner.h b/plugins/bwatch/bwatch_scanner.h
index 3d52c063..4769d26c 100644
--- a/plugins/bwatch/bwatch_scanner.h
+++ b/plugins/bwatch/bwatch_scanner.h
@@ -12,4 +12,10 @@ void bwatch_process_block_txs(struct command *cmd,
u32 blockheight,
const struct bitcoin_blkid *blockhash);
+/* Fire depth notifications for every active blockdepth watch at
+ * new_height. Called once per new block on the happy path. */
+void bwatch_check_blockdepth_watches(struct command *cmd,
+ struct bwatch *bwatch,
+ u32 new_height);
+
#endif /* LIGHTNING_PLUGINS_BWATCH_BWATCH_SCANNER_H */
Why this scored 23/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.