bwatch: scan blocks for scid matches
What changed, and why it matters
This commit adds a new block-scanning step in Core Lightning's 'bwatch' plugin. After the existing checks for transaction scripts and outpoints, it now also checks 'short channel ID' (scid) watches. If a watched scid's encoded block height matches the block just processed, it looks up the exact transaction and output number encoded in the scid and reports the result to lightningd. If the encoded position doesn't exist, it reports 'not found' so lightningd can clean up the watch. There is no indication in the commit that this fixes a security vulnerability; it appears to be a missing functional feature.
No security action required. Review as normal feature code; ensure the out-of-range cleanup behavior matches lightningd's expectations for WATCH_SCID lifecycle.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces maybe_fire_scid_watch() and check_scid_watches() in plugins/bwatch/bwatch_scanner.c. After bwatch_process_block_txs() iterates transactions for scriptpubkey/outpoint matching, it now calls check_scid_watches() to iterate the scid_watches map. For each WATCH_SCID whose short_channel_id_blocknum() equals the current blockheight, it extracts txindex and outnum from the scid. If txindex is beyond the block’s transaction count or outnum is beyond the transaction’s outputs, it logs at LOG_BROKEN and sends bwatch_send_watch_found() with tx=NULL so lightningd treats it as not-found. Otherwise it sends watch_found with the confirmed transaction. This is a straightforward completion of the scanner’s watch types and includes defensive range checks and cleanup signaling.
Changed components
plugins/bwatch/bwatch_scanner.cInspect captured patch +61 / −0
diff --git a/plugins/bwatch/bwatch_scanner.c b/plugins/bwatch/bwatch_scanner.c
index d64c7138..3dc13d96 100644
--- a/plugins/bwatch/bwatch_scanner.c
+++ b/plugins/bwatch/bwatch_scanner.c
@@ -90,6 +90,65 @@ static void check_tx_against_all_watches(struct command *cmd,
check_outpoint_watches(cmd, bwatch, tx, blockheight, blockhash, txindex);
}
+/* Fire watch_found for a scid watch anchored to this block. */
+static void maybe_fire_scid_watch(struct command *cmd,
+ const struct bitcoin_block *block,
+ u32 blockheight,
+ const struct watch *w)
+{
+ struct bitcoin_tx *tx;
+ u32 scid_blockheight, txindex, outnum;
+
+ assert(w->type == WATCH_SCID);
+
+ /* The scid pins the watch to one specific block. */
+ scid_blockheight = short_channel_id_blocknum(w->key.scid);
+ if (scid_blockheight != blockheight)
+ return;
+
+ txindex = short_channel_id_txnum(w->key.scid);
+ outnum = short_channel_id_outnum(w->key.scid);
+
+ /* Out-of-range (txindex or outnum) means the scid doesn't match
+ * anything on this chain; fire watch_found with tx=NULL so
+ * lightningd cleans the watch up. */
+ if (txindex >= tal_count(block->tx)) {
+ plugin_log(cmd->plugin, LOG_BROKEN,
+ "scid watch blockheight=%u txindex=%u outnum=%u: txindex out of range (block has %zu txs)",
+ blockheight, txindex, outnum, tal_count(block->tx));
+ bwatch_send_watch_found(cmd, NULL, blockheight, w, txindex, outnum);
+ return;
+ }
+ tx = block->tx[txindex];
+ if (outnum >= tx->wtx->num_outputs) {
+ plugin_log(cmd->plugin, LOG_BROKEN,
+ "scid watch blockheight=%u txindex=%u outnum=%u: outnum out of range (tx has %zu outputs)",
+ blockheight, txindex, outnum, tx->wtx->num_outputs);
+ bwatch_send_watch_found(cmd, NULL, blockheight, w, txindex, outnum);
+ return;
+ }
+
+ /* Found it: tell lightningd the scid output is confirmed. */
+ bwatch_send_watch_found(cmd, tx, blockheight, w, txindex, outnum);
+}
+
+/* Walk every scid watch and fire watch_found for any whose encoded
+ * blockheight matches this block. */
+static void check_scid_watches(struct command *cmd,
+ struct bwatch *bwatch,
+ const struct bitcoin_block *block,
+ u32 blockheight)
+{
+ struct scid_watches_iter it;
+ struct watch *scid_w;
+
+ for (scid_w = scid_watches_first(bwatch->scid_watches, &it);
+ scid_w;
+ scid_w = scid_watches_next(bwatch->scid_watches, &it)) {
+ maybe_fire_scid_watch(cmd, block, blockheight, scid_w);
+ }
+}
+
void bwatch_process_block_txs(struct command *cmd,
struct bwatch *bwatch,
const struct bitcoin_block *block,
@@ -99,4 +158,6 @@ void bwatch_process_block_txs(struct command *cmd,
for (size_t i = 0; i < tal_count(block->tx); i++)
check_tx_against_all_watches(cmd, bwatch, block->tx[i],
blockheight, blockhash, i);
+
+ check_scid_watches(cmd, bwatch, block, blockheight);
}
Why this scored 12/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.