What changed, and why it matters
This commit adds a new read-only RPC command called listwatch to the bwatch plugin. It simply lets users and tests see what blockchain watches are currently active. There is no change to how watches are created, deleted, or processed, and no security-sensitive logic is modified.
No security action required; routine review/accept.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch registers a new plugin command listwatch and implements json_bwatch_list(), which iterates over four internal watch hash tables (scriptpubkey, outpoint, scid, blockdepth) and emits their keys plus common metadata (type, start_block, owners) as JSON. It is a pure introspection/diagnostic RPC with no side effects, no input parameters beyond the command itself, and no mutation of watch state.
Changed components
plugins/bwatch/bwatch.cplugins/bwatch/bwatch_interface.cplugins/bwatch/bwatch_interface.hInspect captured patch +91 / −0
diff --git a/plugins/bwatch/bwatch.c b/plugins/bwatch/bwatch.c
index 7f37212a..9d5571fe 100644
--- a/plugins/bwatch/bwatch.c
+++ b/plugins/bwatch/bwatch.c
@@ -296,6 +296,7 @@ static const struct plugin_command commands[] = {
{ "deloutpointwatch", json_bwatch_del_outpoint },
{ "delscidwatch", json_bwatch_del_scid },
{ "delblockdepthwatch", json_bwatch_del_blockdepth },
+ { "listwatch", json_bwatch_list },
};
int main(int argc, char *argv[])
diff --git a/plugins/bwatch/bwatch_interface.c b/plugins/bwatch/bwatch_interface.c
index 72b9823e..ea7c16aa 100644
--- a/plugins/bwatch/bwatch_interface.c
+++ b/plugins/bwatch/bwatch_interface.c
@@ -1,4 +1,5 @@
#include "config.h"
+#include <ccan/json_out/json_out.h>
#include <common/json_param.h>
#include <common/json_parse.h>
#include <common/json_stream.h>
@@ -507,3 +508,87 @@ struct command_result *json_bwatch_del_blockdepth(struct command *cmd,
NULL, NULL, NULL, start_block, owner);
return command_success(cmd, json_out_obj(cmd, "removed", "true"));
}
+
+/* Emit type / start_block / owners for one watch. */
+static void json_out_watch_common(struct json_out *jout,
+ enum watch_type type,
+ u32 start_block,
+ wirestring **owners)
+{
+ json_out_addstr(jout, "type", bwatch_get_watch_type_name(type));
+ json_out_add(jout, "start_block", false, "%u", start_block);
+ json_out_start(jout, "owners", '[');
+ for (size_t i = 0; i < tal_count(owners); i++)
+ json_out_addstr(jout, NULL, owners[i]);
+ json_out_end(jout, ']');
+}
+
+/* Dump every active watch as a flat array; per-type fields go first
+ * so the consumer can dispatch on shape. */
+struct command_result *json_bwatch_list(struct command *cmd,
+ const char *buffer,
+ const jsmntok_t *params)
+{
+ struct bwatch *bwatch = bwatch_of(cmd->plugin);
+ struct json_out *jout;
+ struct watch *w;
+ struct scriptpubkey_watches_iter sit;
+ struct outpoint_watches_iter oit;
+ struct scid_watches_iter scit;
+ struct blockdepth_watches_iter bdit;
+
+ if (!param(cmd, buffer, params, NULL))
+ return command_param_failed();
+
+ jout = json_out_new(cmd);
+ json_out_start(jout, NULL, '{');
+ json_out_start(jout, "watches", '[');
+
+ for (w = scriptpubkey_watches_first(bwatch->scriptpubkey_watches, &sit);
+ w;
+ w = scriptpubkey_watches_next(bwatch->scriptpubkey_watches, &sit)) {
+ json_out_start(jout, NULL, '{');
+ json_out_addstr(jout, "scriptpubkey",
+ tal_hexstr(tmpctx, w->key.scriptpubkey.script,
+ w->key.scriptpubkey.len));
+ json_out_watch_common(jout, w->type, w->start_block, w->owners);
+ json_out_end(jout, '}');
+ }
+
+ for (w = outpoint_watches_first(bwatch->outpoint_watches, &oit);
+ w;
+ w = outpoint_watches_next(bwatch->outpoint_watches, &oit)) {
+ json_out_start(jout, NULL, '{');
+ json_out_addstr(jout, "outpoint",
+ fmt_bitcoin_outpoint(tmpctx, &w->key.outpoint));
+ json_out_watch_common(jout, w->type, w->start_block, w->owners);
+ json_out_end(jout, '}');
+ }
+
+ for (w = scid_watches_first(bwatch->scid_watches, &scit);
+ w;
+ w = scid_watches_next(bwatch->scid_watches, &scit)) {
+ json_out_start(jout, NULL, '{');
+ json_out_add(jout, "blockheight", false, "%u",
+ short_channel_id_blocknum(w->key.scid));
+ json_out_add(jout, "txindex", false, "%u",
+ short_channel_id_txnum(w->key.scid));
+ json_out_add(jout, "outnum", false, "%u",
+ short_channel_id_outnum(w->key.scid));
+ json_out_watch_common(jout, w->type, w->start_block, w->owners);
+ json_out_end(jout, '}');
+ }
+
+ for (w = blockdepth_watches_first(bwatch->blockdepth_watches, &bdit);
+ w;
+ w = blockdepth_watches_next(bwatch->blockdepth_watches, &bdit)) {
+ json_out_start(jout, NULL, '{');
+ json_out_add(jout, "blockdepth", false, "%u", w->start_block);
+ json_out_watch_common(jout, w->type, w->start_block, w->owners);
+ json_out_end(jout, '}');
+ }
+
+ json_out_end(jout, ']');
+ json_out_end(jout, '}');
+ return command_success(cmd, jout);
+}
diff --git a/plugins/bwatch/bwatch_interface.h b/plugins/bwatch/bwatch_interface.h
index 5e5ec0c6..1e954358 100644
--- a/plugins/bwatch/bwatch_interface.h
+++ b/plugins/bwatch/bwatch_interface.h
@@ -61,6 +61,11 @@ struct command_result *json_bwatch_del_blockdepth(struct command *cmd,
const char *buffer,
const jsmntok_t *params);
+/* RPC handler: dump every active watch. */
+struct command_result *json_bwatch_list(struct command *cmd,
+ const char *buffer,
+ const jsmntok_t *params);
+
/* Send a block_processed RPC to watchman after a new block has been
* persisted. The next poll is started from the ack callback so we don't
* race ahead of watchman's view of the chain. Chains on the same poll
Why this scored 15/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.