Skip to content
Draft

Bwatch #9069

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
60b0b6b
common: add helpers for bitcoin blockids.
rustyrussell Apr 23, 2026
64344d7
common/json_stream: use json_out_addstrn for better efficiency.
rustyrussell Apr 23, 2026
c92e3f7
libplugin: expose json_add_keypath.
rustyrussell Apr 23, 2026
f6bc95f
common: expose json_hex_to_be32/be64
sangbida Apr 23, 2026
d2f261f
common: extract param_string_array from xpay into common.
rustyrussell Apr 23, 2026
d6cb52e
bwatch: add skeleton and makefile
sangbida Apr 23, 2026
10d8ee7
bwatch: add wire format
sangbida Apr 23, 2026
0a021ba
bwatch: add typed hash tables for watches
sangbida Apr 23, 2026
773af77
bwatch: persist block history
sangbida Apr 23, 2026
1426629
bwatch: persist watches
sangbida Apr 23, 2026
30350a0
bwatch: add addwatch/delwatch helpers
sangbida Apr 23, 2026
86bf85d
bwatch: poll chain and append blocks
sangbida Apr 23, 2026
cf1999b
bwatch: notify watchman on block_processed
sangbida Apr 23, 2026
fd6371b
bwatch: detect reorgs and roll back tip
sangbida Apr 23, 2026
d20de11
bwatch: add watch_found and watch_revert notifications
sangbida Apr 23, 2026
437dccd
bwatch: scan blocks for scriptpubkey and outpoint matches
sangbida Apr 23, 2026
c7b5571
bwatch: scan blocks for scid matches
sangbida Apr 23, 2026
8693634
bwatch: fire blockdepth notifications per block
sangbida Apr 23, 2026
e60281c
bwatch: send chaininfo to watchman on startup
sangbida Apr 23, 2026
382b412
bwatch: add scriptpubkey watch RPCs
sangbida Apr 23, 2026
9a53b7c
bwatch: add outpoint watch RPCs
sangbida Apr 23, 2026
890e07d
bwatch: add scid watch RPCs
sangbida Apr 23, 2026
9e09a66
bwatch: add blockdepth watch RPCs
sangbida Apr 23, 2026
ff0a7c1
bwatch: add listwatch RPC
sangbida Apr 23, 2026
164c00a
bwatch: thread per-watch parameter through block scanning
sangbida Apr 23, 2026
d385f04
bwatch: add rescan engine for historical blocks
sangbida Apr 23, 2026
4ba6cfc
bwatch: trigger rescan when a watch is added behind tip
sangbida Apr 23, 2026
10aff1b
bwatch: notify watch owners on reorg
sangbida Apr 21, 2026
871813b
pyln-testing: shorten bwatch poll interval
sangbida Apr 23, 2026
ff1098a
lightningd: add watchman.h declarations
sangbida Apr 23, 2026
e24bd56
lightningd: add watchman storage and persistence skeleton
sangbida Apr 23, 2026
1cabb92
lightningd/plugin: add on_plugin_ready callback hook
sangbida Apr 23, 2026
743949f
lightningd: add send_to_bwatch and watchman_ack
sangbida Apr 23, 2026
a95caa1
lightningd: replay pending ops on plugin ready
sangbida Apr 23, 2026
15e27f8
lightningd: register getwatchmanheight + chaininfo bwatch RPCs
sangbida Apr 23, 2026
46a7c81
lightningd: register block_processed + revert_block_processed RPCs
sangbida Apr 23, 2026
a7f30f8
lightningd: register watch_found / watch_revert dispatch
sangbida Apr 23, 2026
d147b21
lightningd: add typed watchman_watch_scriptpubkey + unwatch
sangbida Apr 23, 2026
530632f
lightningd: add typed watchman_watch_outpoint + unwatch
sangbida Apr 23, 2026
041073a
lightningd: add typed watchman_watch_scid + unwatch
sangbida Apr 23, 2026
aeb8864
lightningd: add typed watchman_watch_blockdepth + unwatch
sangbida Apr 23, 2026
b1ed8de
wallet: add our_outputs + our_txs schema migrations
sangbida Apr 20, 2026
8147256
wallet: add bwatch wallet UTXO infrastructure
sangbida Apr 20, 2026
b2eb2ea
wallet: add bwatch p2wpkh watch_found handler
sangbida Apr 20, 2026
876e12b
wallet: add bwatch p2tr watch_found handler
sangbida Apr 20, 2026
97050d1
wallet: add bwatch p2sh_p2wpkh watch_found handler
sangbida Apr 20, 2026
c1e4885
wallet: handle bwatch wallet/utxo spend notifications
sangbida Apr 20, 2026
29470fa
wallet: watch change scriptpubkey on unconfirmed UTXOs
sangbida Apr 21, 2026
c07ebdb
wallet: add wallet_add_bwatch_derkey
sangbida Apr 21, 2026
c6c2ad9
wallet: add wallet_scriptpubkey_to_keyidx
sangbida Apr 21, 2026
fe60fe0
wallet: add migrate_backfill_bwatch_tables
sangbida Apr 21, 2026
6c81b1a
lightningd: instantiate watchman and register wallet watches at startup
sangbida Apr 21, 2026
c44388c
tests: skip all integration tests during bwatch migration
sangbida Apr 21, 2026
54ed3ab
tests: refresh autogenerated mocks for bwatch + watchman migration
sangbida Apr 21, 2026
44216ca
lightningd: migrate gossip get_txout to bwatch SCID watches
sangbida Apr 21, 2026
d6abc11
lightningd: migrate channel funding scriptpubkey watch to bwatch
sangbida Apr 21, 2026
1d28024
lightningd: add bwatch funding-depth path
sangbida Apr 21, 2026
4ff2009
lightningd: replace funding_depth_cb with bwatch path
sangbida Apr 21, 2026
2edcd10
lightningd: add bwatch funding-spent + splice scaffolding
sangbida Apr 21, 2026
910ac7b
lightningd: switch funding watches to bwatch
sangbida Apr 21, 2026
dbd924f
lightningd: drop chaintopology funding callbacks
sangbida Apr 21, 2026
0a0aa69
lightningd: scaffold onchaind bwatch tracking
sangbida Apr 21, 2026
f8b6f56
lightningd: roll back onchaind on funding-spend reorg
sangbida Apr 21, 2026
f92febf
lightningd: add inert onchaind bwatch handlers
sangbida Apr 22, 2026
b9d60c4
lightningd: drive onchaind from bwatch
sangbida Apr 22, 2026
b293f39
lightningd: drop onchaind chaintopology + replay machinery
sangbida Apr 22, 2026
ba0b8e8
lightningd: let onchaind register HTLC second-stage watches via bwatch
sangbida Apr 22, 2026
4b92b38
lightningd: tear down onchaind bwatch state on irrevocable resolution
sangbida Apr 22, 2026
8257420
lightningd: extract broadcast.{c,h} from chaintopology
sangbida Apr 22, 2026
ab4ee1f
lightningd: move outgoing_txs and rebroadcast_timer onto struct light…
sangbida Apr 22, 2026
dfa08df
lightningd: move bitcoind onto struct lightningd
sangbida Apr 22, 2026
049d08e
lightningd: extract feerate logic from chaintopology
sangbida Apr 22, 2026
fa8790c
lightningd: move feerate state to watchman
sangbida Apr 22, 2026
b81bc47
lightningd: drop poll_seconds and request_ctx from chain_topology
sangbida Apr 22, 2026
0ef47fb
lightningd: drop legacy unconfirmed-tx + splice-after-coopclose watches
sangbida Apr 22, 2026
64cd813
lightningd: delete legacy watch.{c,h}
sangbida Apr 23, 2026
11a4d26
lightningd: drop chaintopology block tracking, route block height thr…
sangbida Apr 23, 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
5 changes: 3 additions & 2 deletions lightningd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
LIGHTNINGD_SRC := \
lightningd/anchorspend.c \
lightningd/bitcoind.c \
lightningd/broadcast.c \
lightningd/chaintopology.c \
lightningd/watchman.c \
lightningd/channel.c \
lightningd/channel_control.c \
lightningd/channel_gossip.c \
Expand Down Expand Up @@ -43,8 +45,7 @@ LIGHTNINGD_SRC := \
lightningd/routehint.c \
lightningd/runes.c \
lightningd/subd.c \
lightningd/wait.c \
lightningd/watch.c
lightningd/wait.c

LIGHTNINGD_SRC_NOHDR := \
lightningd/configs.c \
Expand Down
8 changes: 4 additions & 4 deletions lightningd/anchorspend.c
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ static struct wally_psbt *anchor_psbt(const tal_t *ctx,

/* PSBT knows how to spend utxos. */
psbt = psbt_using_utxos(ctx, ld->wallet, utxos,
default_locktime(ld->topology),
default_locktime(ld),
BITCOIN_TX_RBF_SEQUENCE, NULL);

/* BOLT #3:
Expand Down Expand Up @@ -379,7 +379,7 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
if (!amount_msat_accumulate(&total_value, val->msat))
abort();

feerate_target = feerate_for_target(ld->topology, val->block);
feerate_target = feerate_for_target(ld, val->block);

/* If the feerate for the commitment tx is already
* sufficient, don't try for anchor. */
Expand Down Expand Up @@ -451,7 +451,7 @@ static struct bitcoin_tx *spend_anchor(const tal_t *ctx,
block_target = unimportant_deadline->block;
if (block_target < get_block_height(ld->topology) + 12)
block_target = get_block_height(ld->topology) + 12;
feerate_target = feerate_for_target(ld->topology, block_target);
feerate_target = feerate_for_target(ld, block_target);

/* If the feerate for the commitment tx is already
* sufficient, don't try for anchor. */
Expand Down Expand Up @@ -602,7 +602,7 @@ static void create_and_broadcast_anchor(struct channel *channel,
fmt_amount_sat(tmpctx, anch->anchor_spend_fee));

/* Send it! */
broadcast_tx(anch->adet, ld->topology, channel, take(newtx), NULL, true, 0, NULL,
broadcast_tx(anch->adet, ld, channel, take(newtx), NULL, true, 0, NULL,
refresh_anchor_spend, anch);
}

Expand Down
1 change: 0 additions & 1 deletion lightningd/bitcoind.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

struct bitcoin_blkid;
struct bitcoin_tx_output;
struct block;
struct feerate_est;
struct lightningd;
struct ripemd160;
Expand Down
Loading
Loading