Skip to content
Draft

Bwatch #9069

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
56820cc
common: add helpers for bitcoin blockids.
rustyrussell Nov 11, 2025
9575d5d
common/json_stream: use json_out_addstrn for better efficiency.
rustyrussell Nov 11, 2025
23265e8
libplugin: expose json_add_keypath.
rustyrussell Feb 6, 2026
2557b90
common: expose json_hex_to_be32/be64
sangbida Feb 6, 2026
afc21ee
common: extract param_string_array from xpay into common.
rustyrussell Feb 6, 2026
acd820d
bwatch: add skeleton and makefile
sangbida Apr 19, 2026
1fc76d1
bwatch: add wire format
sangbida Apr 19, 2026
2748fd2
bwatch: add typed hash tables for watches
sangbida Apr 19, 2026
ce1b02e
bwatch: persist block history
sangbida Apr 19, 2026
03e2926
bwatch: persist watches
sangbida Apr 19, 2026
45672ff
bwatch: add addwatch/delwatch helpers
sangbida Apr 19, 2026
beb3470
bwatch: poll chain and append blocks
sangbida Apr 20, 2026
ad3fec3
bwatch: notify watchman on block_processed
sangbida Apr 20, 2026
f572eeb
bwatch: detect reorgs and roll back tip
sangbida Apr 20, 2026
69a2cd5
bwatch: add watch_found and watch_revert notifications
sangbida Apr 20, 2026
5b112f5
bwatch: scan blocks for scriptpubkey and outpoint matches
sangbida Apr 20, 2026
4e69251
bwatch: scan blocks for scid matches
sangbida Apr 20, 2026
4a33263
bwatch: fire blockdepth notifications per block
sangbida Apr 20, 2026
8c5c2f5
bwatch: send chaininfo to watchman on startup
sangbida Apr 20, 2026
072afdd
bwatch: add scriptpubkey watch RPCs
sangbida Apr 20, 2026
bc9d37d
bwatch: add outpoint watch RPCs
sangbida Apr 20, 2026
5db1509
bwatch: add scid watch RPCs
sangbida Apr 20, 2026
3ee3d63
bwatch: add blockdepth watch RPCs
sangbida Apr 20, 2026
83a2392
bwatch: add listwatch RPC
sangbida Apr 20, 2026
2140374
bwatch: thread per-watch parameter through block scanning
sangbida Apr 20, 2026
c0bb857
bwatch: add rescan engine for historical blocks
sangbida Apr 20, 2026
793e1ee
bwatch: trigger rescan when a watch is added behind tip
sangbida Apr 20, 2026
4ed6eb8
bwatch: notify watch owners on reorg
sangbida Apr 21, 2026
6f8bf7f
pyln-testing: shorten bwatch poll interval
sangbida Apr 20, 2026
32623a5
lightningd: add watchman.h declarations
sangbida Apr 20, 2026
7ce2866
lightningd: add watchman storage and persistence skeleton
sangbida Apr 20, 2026
4eea1fe
lightningd/plugin: add on_plugin_ready callback hook
sangbida Apr 20, 2026
8c66c74
lightningd: add send_to_bwatch and watchman_ack
sangbida Apr 20, 2026
b96ad19
lightningd: replay pending ops on plugin ready
sangbida Apr 20, 2026
1fba39f
lightningd: register getwatchmanheight + chaininfo bwatch RPCs
sangbida Apr 20, 2026
56279ff
lightningd: register block_processed + revert_block_processed RPCs
sangbida Apr 20, 2026
8ac4114
lightningd: register watch_found / watch_revert dispatch
sangbida Apr 20, 2026
d2b0647
lightningd: add typed watchman_watch_scriptpubkey + unwatch
sangbida Apr 20, 2026
a372ef1
lightningd: add typed watchman_watch_outpoint + unwatch
sangbida Apr 20, 2026
b10ee28
lightningd: add typed watchman_watch_scid + unwatch
sangbida Apr 20, 2026
c93d36c
lightningd: add typed watchman_watch_blockdepth + unwatch
sangbida Apr 20, 2026
838a7d0
wallet: add our_outputs + our_txs schema migrations
sangbida Apr 20, 2026
2f2fc37
wallet: add bwatch wallet UTXO infrastructure
sangbida Apr 20, 2026
c6cf21c
wallet: add bwatch p2wpkh watch_found handler
sangbida Apr 20, 2026
c55f29f
wallet: add bwatch p2tr watch_found handler
sangbida Apr 20, 2026
cc22d0e
wallet: add bwatch p2sh_p2wpkh watch_found handler
sangbida Apr 20, 2026
7f6e5d4
wallet: handle bwatch wallet/utxo spend notifications
sangbida Apr 20, 2026
c0de19b
wallet: watch change scriptpubkey on unconfirmed UTXOs
sangbida Apr 21, 2026
53884fd
wallet: add wallet_add_bwatch_derkey
sangbida Apr 21, 2026
6415c0a
wallet: add wallet_scriptpubkey_to_keyidx
sangbida Apr 21, 2026
e27302c
wallet: add migrate_backfill_bwatch_tables
sangbida Apr 21, 2026
9a90e8b
lightningd: instantiate watchman and register wallet watches at startup
sangbida Apr 21, 2026
9bd8473
tests: skip all integration tests during bwatch migration
sangbida Apr 21, 2026
885405c
tests: refresh autogenerated mocks for bwatch + watchman migration
sangbida Apr 21, 2026
c82a9a9
lightningd: migrate gossip get_txout to bwatch SCID watches
sangbida Apr 21, 2026
90f5d41
lightningd: migrate channel funding scriptpubkey watch to bwatch
sangbida Apr 21, 2026
e919915
lightningd: add bwatch funding-depth path
sangbida Apr 21, 2026
1958906
lightningd: replace funding_depth_cb with bwatch path
sangbida Apr 21, 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
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
1 change: 1 addition & 0 deletions contrib/pyln-testing/pyln/testing/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,7 @@ def __init__(

self.opts['dev-fast-gossip'] = None
self.opts['dev-bitcoind-poll'] = 1
self.opts['bwatch-poll-interval'] = 500 # 0.5s for fast test feedback
self.prefix = 'lightningd-%d' % (node_id)
# Log to stdout so we see it in failure cases, and log file for TailableProc.
self.opts['log-file'] = ['-', os.path.join(lightning_dir, "log")]
Expand Down
33 changes: 33 additions & 0 deletions db/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,39 @@ s64 db_get_intvar(struct db *db, const char *varname, s64 defval)
return res;
}

void db_set_blobvar(struct db *db, const char *varname, const u8 *val, size_t len)
{
size_t changes;
struct db_stmt *stmt = db_prepare_v2(db, SQL("UPDATE vars SET blobval=? WHERE name=?;"));
db_bind_blob(stmt, val, len);
db_bind_text(stmt, varname);
db_exec_prepared_v2(stmt);
changes = db_count_changes(stmt);
tal_free(stmt);

if (changes == 0) {
stmt = db_prepare_v2(db, SQL("INSERT INTO vars (name, blobval) VALUES (?, ?);"));
db_bind_text(stmt, varname);
db_bind_blob(stmt, val, len);
db_exec_prepared_v2(stmt);
tal_free(stmt);
}
}

const u8 *db_get_blobvar(const tal_t *ctx, struct db *db, const char *varname)
{
struct db_stmt *stmt = db_prepare_v2(
db, SQL("SELECT blobval FROM vars WHERE name=? LIMIT 1"));
db_bind_text(stmt, varname);

const u8 *res = NULL;
if (db_query_prepared_canfail(stmt) && db_step(stmt))
res = db_col_arr(ctx, stmt, "blobval", u8);

tal_free(stmt);
return res;
}

/* Leak tracking. */

/* By making the update conditional on the current value we expect we
Expand Down
5 changes: 5 additions & 0 deletions db/exec.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <ccan/short_types/short_types.h>
#include <ccan/take/take.h>
#include <ccan/tal/tal.h>

struct db;

Expand All @@ -23,6 +24,10 @@ void db_set_intvar(struct db *db, const char *varname, s64 val);
*/
s64 db_get_intvar(struct db *db, const char *varname, s64 defval);

void db_set_blobvar(struct db *db, const char *varname, const u8 *val, size_t len);
/* Returns a tal-allocated blob, or NULL if not found. */
const u8 *db_get_blobvar(const tal_t *ctx, struct db *db, const char *varname);

/* Get the current data version (entries). */
u32 db_data_version_get(struct db *db);

Expand Down
1 change: 1 addition & 0 deletions lightningd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ LIGHTNINGD_SRC := \
lightningd/anchorspend.c \
lightningd/bitcoind.c \
lightningd/chaintopology.c \
lightningd/watchman.c \
lightningd/channel.c \
lightningd/channel_control.c \
lightningd/channel_gossip.c \
Expand Down
Loading
Loading