Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4e60819
common: add helpers for bitcoin blockids.
rustyrussell Apr 23, 2026
400f97d
common/json_stream: use json_out_addstrn for better efficiency.
rustyrussell Apr 23, 2026
6664d34
ccan: add json_escape_len in json_out_addstrn
sangbida May 1, 2026
0217078
libplugin: expose json_add_keypath.
rustyrussell Apr 23, 2026
7574f21
common: expose json_hex_to_be32/be64
sangbida Apr 23, 2026
8979279
common: extract param_string_array from xpay into common.
rustyrussell Apr 23, 2026
0b75131
bwatch: add skeleton and makefile
sangbida Apr 23, 2026
083a87a
bwatch: add wire format
sangbida Apr 23, 2026
cb9ab59
bwatch: add typed hash tables for watches
sangbida Apr 23, 2026
6a9714a
bwatch: persist block history
sangbida Apr 23, 2026
8fb0bad
bwatch: persist watches
sangbida Apr 23, 2026
4ab1bb7
bwatch: add addwatch/delwatch helpers
sangbida Apr 23, 2026
61380a4
bwatch: poll chain and append blocks
sangbida Apr 23, 2026
0a519bc
bwatch: notify watchman on block_processed
sangbida Apr 23, 2026
108781b
bwatch: detect reorgs and roll back tip
sangbida Apr 23, 2026
e90a273
bwatch: add watch_found and watch_revert notifications
sangbida Apr 23, 2026
7bc78e0
bwatch: scan blocks for scriptpubkey and outpoint matches
sangbida Apr 23, 2026
8f05d8d
bwatch: scan blocks for scid matches
sangbida Apr 23, 2026
f4bcc99
bwatch: fire blockdepth notifications per block
sangbida Apr 23, 2026
519fcf2
bwatch: send chaininfo to watchman on startup
sangbida Apr 23, 2026
d932703
bwatch: add scriptpubkey watch RPCs
sangbida Apr 23, 2026
5f2f2ee
bwatch: add outpoint watch RPCs
sangbida Apr 23, 2026
0585cce
bwatch: add scid watch RPCs
sangbida Apr 23, 2026
ea58738
bwatch: add blockdepth watch RPCs
sangbida Apr 23, 2026
0726367
bwatch: add listwatch RPC
sangbida Apr 23, 2026
8a374f6
bwatch: thread per-watch parameter through block scanning
sangbida Apr 23, 2026
e2089ea
bwatch: add rescan engine for historical blocks
sangbida Apr 23, 2026
c8b2ffe
bwatch: trigger rescan when a watch is added behind tip
sangbida Apr 23, 2026
7dd6672
bwatch: notify watch owners on reorg
sangbida Apr 21, 2026
b0b4db4
pytest: add tests for bwatch.
sangbida Apr 29, 2026
f5ba9f1
doc: document bwatch-poll-interval in lightningd-config.5
sangbida Apr 29, 2026
9f75c34
tests: update cli tests to reflect bwatch rpcs
sangbida Apr 29, 2026
e2b1c44
tests: wait for bwatch block write before stopping in test_db_hook
sangbida May 6, 2026
f9dc155
bwatch: gate chain polling behind --experimental-bwatch
sangbida May 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions bitcoin/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,16 +228,24 @@ void bitcoin_block_blkid(const struct bitcoin_block *b,
*out = b->hdr.hash;
}

static bool bitcoin_blkid_to_hex(const struct bitcoin_blkid *blockid,
char *hexstr, size_t hexstr_len)
bool bitcoin_blkid_from_hex(const char *hexstr, size_t hexstr_len,
struct bitcoin_blkid *blkid)
{
struct bitcoin_txid fake_txid;
fake_txid.shad = blockid->shad;
return bitcoin_txid_to_hex(&fake_txid, hexstr, hexstr_len);
if (!hex_decode(hexstr, hexstr_len, blkid, sizeof(*blkid)))
return false;
reverse_bytes(blkid->shad.sha.u.u8, sizeof(blkid->shad.sha.u.u8));
return true;
}

char *fmt_bitcoin_blkid(const tal_t *ctx,
const struct bitcoin_blkid *blkid)
bool bitcoin_blkid_to_hex(const struct bitcoin_blkid *blkid,
char *hexstr, size_t hexstr_len)
{
struct sha256_double rev = blkid->shad;
reverse_bytes(rev.sha.u.u8, sizeof(rev.sha.u.u8));
return hex_encode(&rev, sizeof(rev), hexstr, hexstr_len);
}

char *fmt_bitcoin_blkid(const tal_t *ctx, const struct bitcoin_blkid *blkid)
{
char *hexstr = tal_arr(ctx, char, hex_str_size(sizeof(*blkid)));

Expand Down
5 changes: 5 additions & 0 deletions bitcoin/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ void fromwire_chainparams(const u8 **cursor, size_t *max,
const struct chainparams **chainparams);
void towire_chainparams(u8 **cursor, const struct chainparams *chainparams);

bool bitcoin_blkid_from_hex(const char *hexstr, size_t hexstr_len,
struct bitcoin_blkid *blkid);
bool bitcoin_blkid_to_hex(const struct bitcoin_blkid *blkid,
char *hexstr, size_t hexstr_len);

char *fmt_bitcoin_blkid(const tal_t *ctx,
const struct bitcoin_blkid *blkid);

Expand Down
9 changes: 0 additions & 9 deletions bitcoin/test/run-bitcoin_block_from_hex.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,6 @@ static const char block[] =

STRUCTEQ_DEF(sha256_double, 0, sha);

static bool bitcoin_blkid_from_hex(const char *hexstr, size_t hexstr_len,
struct bitcoin_blkid *blockid)
{
struct bitcoin_txid fake_txid;
if (!bitcoin_txid_from_hex(hexstr, hexstr_len, &fake_txid))
return false;
blockid->shad = fake_txid.shad;
return true;
}
int main(int argc, const char *argv[])
{
struct bitcoin_blkid prev;
Expand Down
2 changes: 1 addition & 1 deletion ccan/ccan/json_out/json_out.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ bool json_out_addstrn(struct json_out *jout,
struct json_escape *e;

if (json_escape_needed(str, len)) {
e = json_escape(NULL, str);
e = json_escape_len(NULL, str, len);
str = e->s;
len = strlen(str);
} else
Expand Down
16 changes: 16 additions & 0 deletions common/json_param.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,22 @@ struct command_result *param_string_or_array(struct command *cmd, const char *na
return param_string(cmd, name, buffer, tok, &(*result)->str);
}

struct command_result *param_string_array(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
const char ***arr)
{
size_t i;
const jsmntok_t *s;

if (tok->type != JSMN_ARRAY)
return command_fail_badparam(cmd, name, buffer, tok,
"should be an array");
*arr = tal_arr(cmd, const char *, tok->size);
json_for_each_arr(i, s, tok)
(*arr)[i] = json_strdup(*arr, buffer, s);
return NULL;
}

struct command_result *param_invstring(struct command *cmd, const char *name,
const char * buffer, const jsmntok_t *tok,
const char **str)
Expand Down
5 changes: 5 additions & 0 deletions common/json_param.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ struct command_result *param_string_or_array(struct command *cmd, const char *na
const char * buffer, const jsmntok_t *tok,
struct str_or_arr **result);

/* Array of strings */
struct command_result *param_string_array(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
const char ***arr);

/* Extract an invoice string from a generic string, strip the `lightning:`
* prefix from it if needed. */
struct command_result *param_invstring(struct command *cmd, const char *name,
Expand Down
7 changes: 7 additions & 0 deletions common/json_parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,13 @@ bool json_to_txid(const char *buffer, const jsmntok_t *tok,
tok->end - tok->start, txid);
}

bool json_to_bitcoin_blkid(const char *buffer, const jsmntok_t *tok,
struct bitcoin_blkid *blkid)
{
return bitcoin_blkid_from_hex(buffer + tok->start,
tok->end - tok->start, blkid);
}

bool json_to_outpoint(const char *buffer, const jsmntok_t *tok,
struct bitcoin_outpoint *op)
{
Expand Down
4 changes: 4 additions & 0 deletions common/json_parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ bool json_to_msat(const char *buffer, const jsmntok_t *tok,
bool json_to_txid(const char *buffer, const jsmntok_t *tok,
struct bitcoin_txid *txid);

/* Extract a bitcoin blkid from this */
bool json_to_bitcoin_blkid(const char *buffer, const jsmntok_t *tok,
struct bitcoin_blkid *blkid);

/* Extract a bitcoin outpoint from this */
bool json_to_outpoint(const char *buffer, const jsmntok_t *tok,
struct bitcoin_outpoint *op);
Expand Down
13 changes: 13 additions & 0 deletions common/json_parse_simple.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "config.h"
#include <assert.h>
#include <ccan/mem/mem.h>
#include <ccan/str/hex/hex.h>
#include <ccan/tal/str/str.h>
#include <common/json_parse_simple.h>
#include <common/utils.h>
Expand Down Expand Up @@ -151,6 +152,18 @@ bool json_to_bool(const char *buffer, const jsmntok_t *tok, bool *b)
return false;
}

bool json_hex_to_be32(const char *buffer, const jsmntok_t *tok, be32 *val)
{
return hex_decode(buffer + tok->start, tok->end - tok->start,
val, sizeof(*val));
}

bool json_hex_to_be64(const char *buffer, const jsmntok_t *tok, be64 *val)
{
return hex_decode(buffer + tok->start, tok->end - tok->start,
val, sizeof(*val));
}


bool json_tok_is_num(const char *buffer, const jsmntok_t *tok)
{
Expand Down
7 changes: 7 additions & 0 deletions common/json_parse_simple.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#ifndef LIGHTNING_COMMON_JSON_PARSE_SIMPLE_H
#define LIGHTNING_COMMON_JSON_PARSE_SIMPLE_H
#include "config.h"
#include <ccan/endian/endian.h>
#include <ccan/short_types/short_types.h>
#include <ccan/tal/tal.h>

Expand Down Expand Up @@ -51,6 +52,12 @@ bool json_to_double(const char *buffer, const jsmntok_t *tok, double *num);
/* Extract boolean from this */
bool json_to_bool(const char *buffer, const jsmntok_t *tok, bool *b);

/* Extract big-endian 32-bit from hex string (for datastore) */
bool json_hex_to_be32(const char *buffer, const jsmntok_t *tok, be32 *val);

/* Extract big-endian 64-bit from hex string (for datastore) */
bool json_hex_to_be64(const char *buffer, const jsmntok_t *tok, be64 *val);

/* Is this a number? [0..9]+ */
bool json_tok_is_num(const char *buffer, const jsmntok_t *tok);

Expand Down
31 changes: 21 additions & 10 deletions common/json_stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,22 @@ void json_add_primitive(struct json_stream *js,
tal_free_if_taken(val);
}

void json_add_stringn(struct json_stream *js,
const char *fieldname,
const char *str TAKES,
size_t len)
{
if (json_filter_ok(js->filter, fieldname))
json_out_addstrn(js->jout, fieldname, str, len);
if (taken(str))
tal_free(str);
}

void json_add_string(struct json_stream *js,
const char *fieldname,
const char *str TAKES)
{
if (json_filter_ok(js->filter, fieldname))
json_out_addstr(js->jout, fieldname, str);
tal_free_if_taken(str);
json_add_stringn(js, fieldname, str, strlen(str));
}

static char *json_member_direct(struct json_stream *js,
Expand Down Expand Up @@ -298,13 +307,6 @@ void json_add_s32(struct json_stream *result, const char *fieldname,
json_add_primitive_fmt(result, fieldname, "%d", value);
}

void json_add_stringn(struct json_stream *result, const char *fieldname,
const char *value TAKES, size_t value_len)
{
json_add_str_fmt(result, fieldname, "%.*s", (int)value_len, value);
tal_free_if_taken(value);
}

void json_add_bool(struct json_stream *result, const char *fieldname, bool value)
{
json_add_primitive(result, fieldname, value ? "true" : "false");
Expand Down Expand Up @@ -455,6 +457,15 @@ void json_add_txid(struct json_stream *result, const char *fieldname,
json_add_string(result, fieldname, hex);
}

void json_add_bitcoin_blkid(struct json_stream *result, const char *fieldname,
const struct bitcoin_blkid *blkid)
{
char hex[hex_str_size(sizeof(*blkid))];

bitcoin_blkid_to_hex(blkid, hex, sizeof(hex));
json_add_string(result, fieldname, hex);
}

void json_add_outpoint(struct json_stream *result, const char *fieldname,
const struct bitcoin_outpoint *out)
{
Expand Down
5 changes: 5 additions & 0 deletions common/json_stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct short_channel_id;
struct sha256;
struct preimage;
struct bitcoin_tx;
struct bitcoin_blkid;
struct wally_psbt;
struct lease_rates;
struct wireaddr;
Expand Down Expand Up @@ -310,6 +311,10 @@ void json_add_channel_id(struct json_stream *response,
void json_add_txid(struct json_stream *result, const char *fieldname,
const struct bitcoin_txid *txid);

/* '"fieldname" : <hexrev>' or "<hexrev>" if fieldname is NULL */
void json_add_bitcoin_blkid(struct json_stream *result, const char *fieldname,
const struct bitcoin_blkid *blkid);

/* '"fieldname" : "txid:n" */
void json_add_outpoint(struct json_stream *result, const char *fieldname,
const struct bitcoin_outpoint *out);
Expand Down
9 changes: 9 additions & 0 deletions doc/lightningd-config.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,15 @@ command, so they invoices can also be paid onchain.

The *name* is an ISO-4217 name (e.g. USD), which will be passed to *currencyrate* to fetch the exchange rate for that currency on each bookkeeper event. Setting *name* to the empty string is equivalent not setting it.

* **bwatch-poll-interval**=*MILLISECONDS* [plugin `bwatch`]

Delay between polls for new blocks from `bitcoind` (default: 30000).

* **experimental-bwatch** [plugin `bwatch`]

Enable the experimental *bwatch* chain watcher. Without this, the
plugin stays loaded but does not poll `bitcoind` or process watches.

### Networking options

Note that for simple setups, the implicit *autolisten* option does the
Expand Down
24 changes: 23 additions & 1 deletion plugins/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ PLUGIN_TXPREPARE_OBJS := $(PLUGIN_TXPREPARE_SRC:.c=.o)
PLUGIN_BCLI_SRC := plugins/bcli.c
PLUGIN_BCLI_OBJS := $(PLUGIN_BCLI_SRC:.c=.o)

PLUGIN_BWATCH_SRC := plugins/bwatch/bwatch.c \
plugins/bwatch/bwatch_store.c \
plugins/bwatch/bwatch_scanner.c \
plugins/bwatch/bwatch_interface.c \
plugins/bwatch/bwatch_wiregen.c
PLUGIN_BWATCH_HEADER := plugins/bwatch/bwatch.h \
plugins/bwatch/bwatch_store.h \
plugins/bwatch/bwatch_scanner.h \
plugins/bwatch/bwatch_interface.h \
plugins/bwatch/bwatch_wiregen.h
PLUGIN_BWATCH_OBJS := $(PLUGIN_BWATCH_SRC:.c=.o)

PLUGIN_COMMANDO_SRC := plugins/commando.c
PLUGIN_COMMANDO_OBJS := $(PLUGIN_COMMANDO_SRC:.c=.o)

Expand Down Expand Up @@ -82,6 +94,7 @@ PLUGIN_ALL_SRC := \
$(PLUGIN_AUTOCLEAN_SRC) \
$(PLUGIN_chanbackup_SRC) \
$(PLUGIN_BCLI_SRC) \
$(PLUGIN_BWATCH_SRC) \
$(PLUGIN_COMMANDO_SRC) \
$(PLUGIN_FUNDER_SRC) \
$(PLUGIN_TOPOLOGY_SRC) \
Expand All @@ -102,12 +115,14 @@ PLUGIN_ALL_HEADER := \
$(PLUGIN_FUNDER_HEADER) \
$(PLUGIN_PAY_LIB_HEADER) \
$(PLUGIN_OFFERS_HEADER) \
$(PLUGIN_SPENDER_HEADER)
$(PLUGIN_SPENDER_HEADER) \
$(PLUGIN_BWATCH_HEADER)

C_PLUGINS := \
plugins/autoclean \
plugins/chanbackup \
plugins/bcli \
plugins/bwatch/bwatch \
plugins/commando \
plugins/funder \
plugins/topology \
Expand Down Expand Up @@ -185,6 +200,13 @@ plugins/exposesecret: $(PLUGIN_EXPOSESECRET_OBJS) $(PLUGIN_LIB_OBJS) libcommon.a

plugins/bcli: $(PLUGIN_BCLI_OBJS) $(PLUGIN_LIB_OBJS) libcommon.a

plugins/bwatch/bwatch.o: $(PLUGIN_BWATCH_HEADER)
plugins/bwatch/bwatch_store.o: $(PLUGIN_BWATCH_HEADER)
plugins/bwatch/bwatch_scanner.o: $(PLUGIN_BWATCH_HEADER)
plugins/bwatch/bwatch_interface.o: $(PLUGIN_BWATCH_HEADER)
plugins/bwatch/bwatch_wiregen.o: $(PLUGIN_BWATCH_HEADER)
plugins/bwatch/bwatch: $(PLUGIN_BWATCH_OBJS) $(PLUGIN_LIB_OBJS) libcommon.a

plugins/keysend: $(PLUGIN_KEYSEND_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) libcommon.a
$(PLUGIN_KEYSEND_OBJS): $(PLUGIN_PAY_LIB_HEADER) libcommon.a

Expand Down
7 changes: 0 additions & 7 deletions plugins/bkpr/blockheights.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,6 @@ u32 find_blockheight(const struct bkpr *bkpr,
return e ? e->height : 0;
}

static bool json_hex_to_be32(const char *buffer, const jsmntok_t *tok,
be32 *val)
{
return hex_decode(buffer + tok->start, tok->end - tok->start,
val, sizeof(*val));
}

struct blockheights *init_blockheights(const tal_t *ctx,
struct command *init_cmd)
{
Expand Down
8 changes: 1 addition & 7 deletions plugins/bkpr/bookkeeper.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <common/coin_mvt.h>
#include <common/iso4217.h>
#include <common/json_param.h>
#include <common/json_parse_simple.h>
#include <common/json_stream.h>
#include <common/memleak.h>
#include <common/mkdatastorekey.h>
Expand Down Expand Up @@ -1836,13 +1837,6 @@ static const struct plugin_command commands[] = {
},
};

static bool json_hex_to_be64(const char *buffer, const jsmntok_t *tok,
be64 *val)
{
return hex_decode(buffer + tok->start, tok->end - tok->start,
val, sizeof(*val));
}

static void memleak_scan_currencyrates(struct htable *memtable,
currencymap_t *currency_rates)
{
Expand Down
Loading
Loading