From 6c75e0b478b6e6cce9fa74912650489e4450a1e4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:09:35 +0930 Subject: [PATCH 01/13] pytest: test for splicing while channel is not announced yet. ``` DEBUG lightningd: Got depth change 2->3 for e9e31956f77c3844ee2e6e4607dbfebdee95a9aa549668a7a429b8246a6a29de **BROKEN** lightningd: FATAL SIGNAL 6 (version v25.09-20-g003ba4a) **BROKEN** lightningd: backtrace: common/daemon.c:41 (send_backtrace) 0x619bef20e274 **BROKEN** lightningd: backtrace: common/daemon.c:78 (crashdump) 0x619bef20e408 **BROKEN** lightningd: backtrace: ./signal/../sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0 ((null)) 0x7a1ccf24532f **BROKEN** lightningd: backtrace: ./nptl/pthread_kill.c:44 (__pthread_kill_implementation) 0x7a1ccf29eb2c **BROKEN** lightningd: backtrace: ./nptl/pthread_kill.c:78 (__pthread_kill_internal) 0x7a1ccf29eb2c **BROKEN** lightningd: backtrace: ./nptl/pthread_kill.c:89 (__GI___pthread_kill) 0x7a1ccf29eb2c **BROKEN** lightningd: backtrace: ../sysdeps/posix/raise.c:26 (__GI_raise) 0x7a1ccf24527d **BROKEN** lightningd: backtrace: ./stdlib/abort.c:79 (__GI_abort) 0x7a1ccf2288fe **BROKEN** lightningd: backtrace: ./assert/assert.c:96 (__assert_fail_base) 0x7a1ccf22881a **BROKEN** lightningd: backtrace: ./assert/assert.c:105 (__assert_fail) 0x7a1ccf23b516 **BROKEN** lightningd: backtrace: lightningd/peer_control.c:2202 (funding_depth_cb) 0x619bef1ac497 **BROKEN** lightningd: backtrace: lightningd/watch.c:223 (txw_fire) 0x619bef1cfcbf **BROKEN** lightningd: backtrace: lightningd/watch.c:292 (watch_topology_changed) 0x619bef1cffa4 **BROKEN** lightningd: backtrace: lightningd/chaintopology.c:829 (updates_complete) 0x619bef144a8c **BROKEN** lightningd: backtrace: lightningd/chaintopology.c:1047 (get_new_block) 0x619bef14561e ``` Signed-off-by: Rusty Russell --- tests/test_splicing.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/test_splicing.py b/tests/test_splicing.py index 996afcec3897..784cee866d8c 100644 --- a/tests/test_splicing.py +++ b/tests/test_splicing.py @@ -556,3 +556,30 @@ def test_route_by_old_scid(node_factory, bitcoind): wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['connected'] is True) l1.rpc.sendpay(route, inv2['payment_hash'], payment_secret=inv2['payment_secret']) l1.rpc.waitsendpay(inv2['payment_hash']) + + +@pytest.mark.xfail(strict=True) +def test_splice_unannounced(node_factory, bitcoind): + l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=False, opts={'experimental-splicing': None}) + + chan_id = l1.get_channel_id(l2) + + # add extra sats to pay fee + funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True) + result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt']) + result = l1.rpc.splice_update(chan_id, result['psbt']) + assert(result['commitments_secured'] is False) + result = l1.rpc.splice_update(chan_id, result['psbt']) + assert(result['commitments_secured'] is True) + result = l1.rpc.signpsbt(result['psbt']) + result = l1.rpc.splice_signed(chan_id, result['signed_psbt']) + + l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE') + l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE') + + bitcoind.generate_block(1, wait_for_mempool=1) + + l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL') + bitcoind.generate_block(1) + sync_blockheight(bitcoind, [l1, l2]) From 23dbd46e7b7e31beec0eb0ac2f93fa7ac0c878a4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:10:35 +0930 Subject: [PATCH 02/13] lightningd: cancel watching original funding when we switch to the new one via splice. This happens if the channel is *not* announcable yet. Then we hit the assertion in funding_depth_cb that the txid is the same as the current funding.txid. Signed-off-by: Rusty Russell Changelog-EXPERIMENTAL: fixed crash when we splice a channel which hasn't been announced yet. --- lightningd/channel_control.c | 5 ++++- lightningd/peer_control.c | 8 ++++++++ lightningd/peer_control.h | 1 + lightningd/test/run-invoice-select-inchan.c | 10 ++++++++++ tests/test_splicing.py | 3 ++- wallet/test/run-wallet.c | 10 ++++++++++ 6 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index a72f959837ae..b291816f2cff 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -1134,7 +1134,8 @@ static void handle_peer_splice_locked(struct channel *channel, const u8 *msg) wallet_htlcsigs_confirm_inflight(channel->peer->ld->wallet, channel, &inflight->funding->outpoint); - update_channel_from_inflight(channel->peer->ld, channel, inflight, true); + /* Stop watching previous funding tx (could be, for announcement) */ + channel_unwatch_funding(channel->peer->ld, channel); /* Stash prev funding data so we can log it after scid is updated * (to get the blockheight) */ @@ -1142,6 +1143,8 @@ static void handle_peer_splice_locked(struct channel *channel, const u8 *msg) prev_funding_sats = channel->funding_sats; prev_funding_out = channel->funding; + update_channel_from_inflight(channel->peer->ld, channel, inflight, true); + channel->our_msat.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ channel->msat_to_us_min.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ channel->msat_to_us_max.millisatoshis += splice_amnt * 1000; /* Raw: splicing */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 19780fae7e7a..2f11dcb80794 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2382,6 +2382,14 @@ void channel_watch_wrong_funding(struct lightningd *ld, struct channel *channel) } } +/* We need to do this before we change channel funding (for splice), otherwise + * funding_depth_cb will fail the assertion that it's the current funding tx */ +void channel_unwatch_funding(struct lightningd *ld, struct channel *channel) +{ + tal_free(find_txwatch(ld->topology, + &channel->funding.txid, funding_depth_cb, channel)); +} + void channel_watch_funding(struct lightningd *ld, struct channel *channel) { log_debug(channel->log, "Watching for funding txid: %s", diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 382d0826baf1..63826f5b628d 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -131,6 +131,7 @@ void update_channel_from_inflight(struct lightningd *ld, const struct channel_inflight *inflight, bool is_splice); +void channel_unwatch_funding(struct lightningd *ld, struct channel *channel); void channel_watch_funding(struct lightningd *ld, struct channel *channel); /* If this channel has a "wrong funding" shutdown, watch that too. */ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 00eef5e44b70..1c234c5dae90 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -291,6 +291,16 @@ struct channel *find_channel_by_id(const struct peer *peer UNNEEDED, struct plugin *find_plugin_for_command(struct lightningd *ld UNNEEDED, const char *cmd_name UNNEEDED) { fprintf(stderr, "find_plugin_for_command called!\n"); abort(); } +/* Generated stub for find_txwatch_ */ +struct txwatch *find_txwatch_(struct chain_topology *topo UNNEEDED, + const struct bitcoin_txid *txid UNNEEDED, + enum watch_result (*cb)(struct lightningd *ld UNNEEDED, + const struct bitcoin_txid * UNNEEDED, + const struct bitcoin_tx * UNNEEDED, + unsigned int depth UNNEEDED, + void *arg) UNNEEDED, + void *arg UNNEEDED) +{ fprintf(stderr, "find_txwatch_ called!\n"); abort(); } /* Generated stub for fixup_htlcs_out */ void fixup_htlcs_out(struct lightningd *ld UNNEEDED) { fprintf(stderr, "fixup_htlcs_out called!\n"); abort(); } diff --git a/tests/test_splicing.py b/tests/test_splicing.py index 784cee866d8c..50d91b2f9303 100644 --- a/tests/test_splicing.py +++ b/tests/test_splicing.py @@ -558,7 +558,8 @@ def test_route_by_old_scid(node_factory, bitcoind): l1.rpc.waitsendpay(inv2['payment_hash']) -@pytest.mark.xfail(strict=True) +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') def test_splice_unannounced(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2, fundamount=1000000, wait_for_announce=False, opts={'experimental-splicing': None}) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 5a6ad596c3b7..4d96e83bb1bf 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -264,6 +264,16 @@ void fatal(const char *fmt UNNEEDED, ...) /* Generated stub for fatal_vfmt */ void fatal_vfmt(const char *fmt UNNEEDED, va_list ap UNNEEDED) { fprintf(stderr, "fatal_vfmt called!\n"); abort(); } +/* Generated stub for find_txwatch_ */ +struct txwatch *find_txwatch_(struct chain_topology *topo UNNEEDED, + const struct bitcoin_txid *txid UNNEEDED, + enum watch_result (*cb)(struct lightningd *ld UNNEEDED, + const struct bitcoin_txid * UNNEEDED, + const struct bitcoin_tx * UNNEEDED, + unsigned int depth UNNEEDED, + void *arg) UNNEEDED, + void *arg UNNEEDED) +{ fprintf(stderr, "find_txwatch_ called!\n"); abort(); } /* Generated stub for force_peer_disconnect */ void force_peer_disconnect(struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED, From a281444d5e83daf0be178b1da89a6fb426fd5d7f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:16:58 +0930 Subject: [PATCH 03/13] lightningd: fix crash in channel_control. I got a NULL deref on `infcopy->remote_funding = *inflight->funding->splice_remote_funding` at once point in testing, so this should prevent that from happening, yet still allow us to catch it in CI if it happens again. Signed-off-by: Rusty Russell --- lightningd/channel_control.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index b291816f2cff..78102f2579f0 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -1827,6 +1828,11 @@ bool peer_start_channeld(struct channel *channel, if (inflight->splice_locked_memonly) continue; + if (!inflight->funding->splice_remote_funding) { + send_backtrace("Inflight has no splice_remote_funding?!"); + continue; + } + infcopy = tal(inflights, struct inflight); infcopy->remote_funding = *inflight->funding->splice_remote_funding; From 65a9736299540f139d5dee5a8a85e03253f07c47 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:17:40 +0930 Subject: [PATCH 04/13] pytest: add test that we notice height change of sendpsbt with no change. Signed-off-by: Rusty Russell --- tests/test_wallet.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index b26679b1856c..436f14e66d4b 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -1887,6 +1887,35 @@ def test_onchain_missing_no_p2tr_migrate(node_factory, bitcoind): l2.daemon.wait_for_log('Rescan finished! 1 outputs recovered') +@pytest.mark.xfail(strict=True) +@pytest.mark.parametrize("restart", [False, True]) +def test_sendpsbt_confirm(node_factory, bitcoind, restart): + """We should see our sendpsbt in wallet, and that it gets confirmed""" + l1, l2 = node_factory.get_nodes(2) + l1.fundwallet(100000) + + psbt = l1.rpc.fundpsbt(satoshi=10000, + feerate=7500, + startweight=42)['psbt'] + psbt = l2.rpc.addpsbtoutput(10000, psbt)['psbt'] + psbt = l1.rpc.signpsbt(psbt)['signed_psbt'] + sent = l1.rpc.sendpsbt(psbt) + + # Unconfirmed + lt = only_one([t for t in l1.rpc.listtransactions()['transactions'] if t['rawtx'] == sent['tx']]) + assert lt['blockheight'] == 0 + + if restart: + l1.restart() + + bitcoind.generate_block(1, wait_for_mempool=sent['txid']) + sync_blockheight(bitcoind, [l1]) + + # Should be confirmed now! + lt = only_one([t for t in l1.rpc.listtransactions()['transactions'] if t['rawtx'] == sent['tx']]) + assert lt['blockheight'] == bitcoind.rpc.getblockcount() + + def test_old_htlcs_cleanup(node_factory, bitcoind): """We lazily delete htlcs from channel_htlcs table""" l1, l2 = node_factory.line_graph(2) From 1c6204804a3547d8807947f6a19192a8cf5327c4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:17:40 +0930 Subject: [PATCH 05/13] wallet: make sure to watch all txids in transactions table. We watch if they are to do with a channel, or have outputs going to us, but otherwise we didn't, so we never updated the blockheight in the db. Signed-off-by: Rusty Russell Changelog-Fixed: JSON-RPC: `listtransactions` now correctly updates `blockheight` for txs created by `sendpsbt` which have no change outputs. --- lightningd/chaintopology.c | 37 +++++++++++++++++++++++++++++++++++++ lightningd/chaintopology.h | 8 ++++++++ tests/test_wallet.py | 1 - wallet/wallet.c | 13 ++++++++++--- wallet/walletrpc.c | 9 ++++++--- 5 files changed, 61 insertions(+), 7 deletions(-) diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 59f004f732a7..7d02e9245395 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -356,6 +356,37 @@ static void watch_for_utxo_reconfirmation(struct chain_topology *topo, } } +static enum watch_result tx_confirmed(struct lightningd *ld, + const struct bitcoin_txid *txid, + const struct bitcoin_tx *tx, + unsigned int depth, + void *unused) +{ + /* We don't actually need to do anything here: the fact that we were + * watching the tx made chaintopology.c update the transaction depth */ + if (depth != 0) + return DELETE_WATCH; + return KEEP_WATCHING; +} + +void watch_unconfirmed_txid(struct lightningd *ld, + struct chain_topology *topo, + const struct bitcoin_txid *txid) +{ + watch_txid(ld->wallet, topo, txid, tx_confirmed, NULL); +} + +static void watch_for_unconfirmed_txs(struct lightningd *ld, + struct chain_topology *topo) +{ + struct bitcoin_txid *txids; + + txids = wallet_transactions_by_height(tmpctx, ld->wallet, 0); + log_debug(ld->log, "Got %zu unconfirmed transactions", tal_count(txids)); + for (size_t i = 0; i < tal_count(txids); i++) + watch_unconfirmed_txid(ld, topo, &txids[i]); +} + /* Mutual recursion via timer. */ static void next_updatefee_timer(struct chain_topology *topo); @@ -1028,6 +1059,7 @@ static void remove_tip(struct chain_topology *topo) /* This may have unconfirmed txs: reconfirm as we add blocks. */ watch_for_utxo_reconfirmation(topo, topo->ld->wallet); + block_map_del(topo->block_map, b); /* These no longer exist, so gossipd drops any reference to them just @@ -1478,6 +1510,11 @@ void setup_topology(struct chain_topology *topo) /* May have unconfirmed txs: reconfirm as we add blocks. */ watch_for_utxo_reconfirmation(topo, topo->ld->wallet); + + /* We usually watch txs because we have outputs coming to us, or they're + * related to a channel. But not if they're created by sendpsbt without any + * outputs to us. */ + watch_for_unconfirmed_txs(topo->ld, topo); db_commit_transaction(topo->ld->wallet->db); tal_free(local_ctx); diff --git a/lightningd/chaintopology.h b/lightningd/chaintopology.h index 4f80d21622a8..8a867b0e21f1 100644 --- a/lightningd/chaintopology.h +++ b/lightningd/chaintopology.h @@ -12,6 +12,7 @@ struct command; struct lightningd; struct peer; struct txwatch; +struct wallet; /* We keep the last three in case there are outliers (for min/max) */ #define FEE_HISTORY_NUM 3 @@ -280,4 +281,11 @@ void topology_add_sync_waiter_(const tal_t *ctx, /* In channel_control.c */ void notify_feerate_change(struct lightningd *ld); + +/* We want to update db when this txid is confirmed. We always do this + * if it's related to a channel or incoming funds, but sendpsbt without + * change would be otherwise untracked. */ +void watch_unconfirmed_txid(struct lightningd *ld, + struct chain_topology *topo, + const struct bitcoin_txid *txid); #endif /* LIGHTNING_LIGHTNINGD_CHAINTOPOLOGY_H */ diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 436f14e66d4b..c2cd96aafafc 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -1887,7 +1887,6 @@ def test_onchain_missing_no_p2tr_migrate(node_factory, bitcoind): l2.daemon.wait_for_log('Rescan finished! 1 outputs recovered') -@pytest.mark.xfail(strict=True) @pytest.mark.parametrize("restart", [False, True]) def test_sendpsbt_confirm(node_factory, bitcoind, restart): """We should see our sendpsbt in wallet, and that it gets confirmed""" diff --git a/wallet/wallet.c b/wallet/wallet.c index 599e2f920ade..91962ba6bb1b 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -5189,9 +5189,16 @@ struct bitcoin_txid *wallet_transactions_by_height(const tal_t *ctx, struct db_stmt *stmt; struct bitcoin_txid *txids = tal_arr(ctx, struct bitcoin_txid, 0); int count = 0; - stmt = db_prepare_v2( - w->db, SQL("SELECT id FROM transactions WHERE blockheight=?")); - db_bind_int(stmt, blockheight); + + /* Note: blockheight=NULL is not the same as is NULL! */ + if (blockheight == 0) { + stmt = db_prepare_v2( + w->db, SQL("SELECT id FROM transactions WHERE blockheight IS NULL")); + } else { + stmt = db_prepare_v2( + w->db, SQL("SELECT id FROM transactions WHERE blockheight=?")); + db_bind_int(stmt, blockheight); + } db_query_prepared(stmt); while (db_step(stmt)) { diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 677db8bab66f..ab011b553b4c 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -953,7 +953,6 @@ static void maybe_notify_new_external_send(struct lightningd *ld, wallet_save_chain_mvt(ld, take(mvt)); } - static void sendpsbt_done(struct bitcoind *bitcoind UNUSED, bool success, const char *msg, struct sending_psbt *sending) @@ -985,10 +984,14 @@ static void sendpsbt_done(struct bitcoind *bitcoind UNUSED, } wallet_transaction_add(ld->wallet, sending->wtx, 0, 0); + wally_txid(sending->wtx, &txid); /* Extract the change output and add it to the DB */ - wallet_extract_owned_outputs(ld->wallet, sending->wtx, false, NULL); - wally_txid(sending->wtx, &txid); + if (wallet_extract_owned_outputs(ld->wallet, sending->wtx, false, NULL) == 0) { + /* If we're not watching it for selfish reasons (i.e. pure send to + * others), make sure we're watching it so we can update depth in db */ + watch_unconfirmed_txid(ld, ld->topology, &txid); + } for (size_t i = 0; i < sending->psbt->num_outputs; i++) maybe_notify_new_external_send(ld, &txid, i, sending->psbt); From 39cc1955dca0d908550e61dc53c3d8e2da5d8e27 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:17:40 +0930 Subject: [PATCH 06/13] memleak: make notleak() work even before memleak is initalized. It now simply renames tal names, so it's harmless to do even if we're not going to do memleak detection. Signed-off-by: Rusty Russell --- cli/Makefile | 1 + common/configdir.c | 4 ++-- common/memleak.c | 12 +++++------- common/trace.c | 6 +----- tools/Makefile | 2 +- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/cli/Makefile b/cli/Makefile index 82dea907ea61..05c07100d9d1 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -10,6 +10,7 @@ LIGHTNING_CLI_COMMON_OBJS := \ common/configdir.o \ common/configvar.o \ common/json_parse_simple.o \ + common/memleak.o \ common/status_levels.o \ common/utils.o \ common/version.o diff --git a/common/configdir.c b/common/configdir.c index 248c8e73e010..0926c44fd569 100644 --- a/common/configdir.c +++ b/common/configdir.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -36,8 +37,7 @@ static char *opt_set_abspath(const char *arg, char **p) /* Tal wrappers for opt. */ static void *opt_allocfn(size_t size) { - return tal_arr_label(NULL, char, size, - TAL_LABEL(opt_allocfn_notleak, "")); + return notleak(tal_arr(NULL, char, size)); } static void *tal_reallocfn(void *ptr, size_t size) diff --git a/common/memleak.c b/common/memleak.c index 7ac0da7c7bf3..de321a9a7c66 100644 --- a/common/memleak.c +++ b/common/memleak.c @@ -58,9 +58,6 @@ struct tal_backtrace { void *notleak_(void *ptr, bool plus_children) { const char *name; - /* If we're not tracking, don't do anything. */ - if (!memleak_track) - return cast_const(void *, ptr); /* We use special tal names to mark notleak */ name = tal_name(ptr); @@ -69,12 +66,14 @@ void *notleak_(void *ptr, bool plus_children) /* Don't mark more than once! */ if (!strstr(name, "**NOTLEAK")) { + /* Don't use tmpctx: it might not be set up yet! */ if (plus_children) - name = tal_fmt(tmpctx, "%s **NOTLEAK_IGNORE_CHILDREN**", + name = tal_fmt(NULL, "%s **NOTLEAK_IGNORE_CHILDREN**", name); else - name = tal_fmt(tmpctx, "%s **NOTLEAK**", name); + name = tal_fmt(NULL, "%s **NOTLEAK**", name); tal_set_name(ptr, name); + tal_free(name); } return cast_const(void *, ptr); @@ -331,8 +330,7 @@ static void call_memleak_helpers(struct htable *memtable, const tal_t *p) if (strends(name, "struct memleak_helper")) { const struct memleak_helper *mh = i; mh->cb(memtable, p); - } else if (strends(name, " **NOTLEAK**") - || strends(name, "_notleak")) { + } else if (strends(name, " **NOTLEAK**")) { memleak_ptr(memtable, i); memleak_scan_obj(memtable, i); } else if (strends(name, diff --git a/common/trace.c b/common/trace.c index eade3171f790..f64faa0ef440 100644 --- a/common/trace.c +++ b/common/trace.c @@ -184,15 +184,11 @@ static inline void trace_check_tree(void) {} static void trace_init(void) { const char *dev_trace_file; - const char notleak_name[] = "struct span **NOTLEAK**"; if (active_spans) return; - active_spans = tal_arrz(NULL, struct span, 1); - /* We're usually too early for memleak to be initialized, so mark - * this notleak manually! */ - tal_set_name(active_spans, notleak_name); + active_spans = notleak(tal_arrz(NULL, struct span, 1)); current = NULL; dev_trace_file = getenv("CLN_DEV_TRACE_FILE"); diff --git a/tools/Makefile b/tools/Makefile index 6b92ebdd5555..3922ce0c8566 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -18,7 +18,7 @@ tools/headerversions: $(FORCE) tools/headerversions.o libccan.a tools/headerversions.o: ccan/config.h tools/check-bolt: tools/check-bolt.o $(TOOLS_COMMON_OBJS) -tools/hsmtool: tools/hsmtool.o $(TOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/amount.o common/autodata.o common/bech32.o common/bech32_util.o common/bigsize.o common/codex32.o common/configdir.o common/configvar.o common/derive_basepoints.o common/descriptor_checksum.o common/hsm_encryption.o common/key_derive.o common/node_id.o common/version.o wire/fromwire.o wire/towire.o +tools/hsmtool: tools/hsmtool.o $(TOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/amount.o common/autodata.o common/bech32.o common/bech32_util.o common/bigsize.o common/codex32.o common/configdir.o common/configvar.o common/derive_basepoints.o common/descriptor_checksum.o common/hsm_encryption.o common/key_derive.o common/node_id.o common/version.o common/memleak.o wire/fromwire.o wire/towire.o tools/lightning-hsmtool: tools/hsmtool cp $< $@ From 0562ebf1fa928c1be3379bef77a21ecdcfcbe6be Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:40:07 +0930 Subject: [PATCH 07/13] common: add new_htable() macro to allocate, initialize and setup memleak coverage for any typed hash table. You can now simply add per-tal-object helpers for memleak, but our older pattern required calling memleak functions explicitly during memleak handling. Hash tables in particular need to be dynamically allocated (we override the allocators using htable_set_allocator and assume this), so it makes sense to have a helper macro that does all three. This eliminates a huge amount of code. Signed-off-by: Rusty Russell --- channeld/full_channel.c | 14 +++------- common/memleak.h | 12 +++++++++ connectd/connectd.c | 27 +++++-------------- gossipd/gossipd.c | 4 +-- lightningd/chaintopology.c | 12 +++------ lightningd/lightningd.c | 24 ++++++----------- lightningd/memdump.c | 13 --------- lightningd/onchain_control.c | 12 +-------- plugins/askrene/askrene.c | 8 ------ plugins/askrene/layer.c | 23 +++------------- plugins/askrene/layer.h | 2 -- plugins/askrene/reserve.c | 9 +------ plugins/askrene/reserve.h | 2 -- plugins/chanbackup.c | 17 ++---------- plugins/channel_hint.c | 10 +------ .../test/run-chain_moves_duplicate-detect.c | 4 --- wallet/test/run-db.c | 4 --- ...un-migrate_remove_chain_moves_duplicates.c | 4 --- wallet/test/run-wallet.c | 4 --- wallet/txfilter.c | 8 +----- wallet/txfilter.h | 3 --- wallet/wallet.c | 10 +------ wallet/wallet.h | 4 --- 23 files changed, 46 insertions(+), 184 deletions(-) diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 699ccdf40ca4..cf27c69c11ad 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -17,12 +17,6 @@ /* Needs to be at end, since it doesn't include its own hdrs */ #include "full_channel_error_names_gen.h" -static void memleak_help_htlcmap(struct htable *memtable, - struct htlc_map *htlcs) -{ - memleak_scan_htable(memtable, &htlcs->raw); -} - /* This is a dangerous thing! Because we apply HTLCs in many places * in bulk, we can temporarily go negative. You must check balance_ok() * at the end! */ @@ -113,11 +107,9 @@ struct channel *new_full_channel(const tal_t *ctx, option_wumbo, opener); - if (channel) { - channel->htlcs = tal(channel, struct htlc_map); - htlc_map_init(channel->htlcs); - memleak_add_helper(channel->htlcs, memleak_help_htlcmap); - } + if (channel) + channel->htlcs = new_htable(channel, htlc_map); + return channel; } diff --git a/common/memleak.h b/common/memleak.h index a15413b23f34..b9782e651ceb 100644 --- a/common/memleak.h +++ b/common/memleak.h @@ -109,6 +109,18 @@ void memleak_scan_region(struct htable *memtable, const void *p, size_t len); /* Objects inside this htable (which is opaque to memleak) are not leaks. */ void memleak_scan_htable(struct htable *memtable, const struct htable *ht); +/* Allocate a htable, set up memleak scan automatically (assumes &p->raw == p) */ +#define new_htable(ctx, type) \ + ({ \ + const struct htable *raw; \ + struct type *p = tal(ctx, struct type); \ + type##_init(p); \ + raw = &p->raw; \ + assert((void *)raw == (void *)p); \ + memleak_add_helper(raw, memleak_scan_htable); \ + p; \ + }) + /* Objects inside this uintmap (which is opaque to memleak) are not leaks. */ #define memleak_scan_uintmap(memtable, umap) \ memleak_scan_intmap_(memtable, uintmap_unwrap_(umap)) diff --git a/connectd/connectd.c b/connectd/connectd.c index f04f2caa798d..69014fdbb8bb 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -2021,9 +2021,6 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg) /* Now delete daemon and those which it has pointers to. */ memleak_scan_obj(memtable, daemon); - memleak_scan_htable(memtable, &daemon->peers->raw); - memleak_scan_htable(memtable, &daemon->scid_htable->raw); - memleak_scan_htable(memtable, &daemon->important_ids->raw); found_leak = dump_memleak(memtable, memleak_status_broken, NULL); daemon_conn_send(daemon->master, @@ -2437,14 +2434,6 @@ static struct io_plan *recv_gossip(struct io_conn *conn, return daemon_conn_read_next(conn, daemon->gossipd); } -/*~ This is a hook used by the memleak code: it can't see pointers - * inside hash tables, so we give it a hint here. */ -static void memleak_daemon_cb(struct htable *memtable, struct daemon *daemon) -{ - memleak_scan_htable(memtable, &daemon->peers->raw); - memleak_scan_htable(memtable, &daemon->connecting->raw); -} - static void gossipd_failed(struct daemon_conn *gossipd) { status_failed(STATUS_FAIL_GOSSIP_IO, "gossipd exited?"); @@ -2464,14 +2453,13 @@ int main(int argc, char *argv[]) daemon = tal(NULL, struct daemon); daemon->developer = developer; daemon->connection_counter = 1; - daemon->peers = tal(daemon, struct peer_htable); + /* htable_new is our helper which allocates a htable, initializes it + * and set up the memleak callback so our memleak code can see objects + * inside it */ + daemon->peers = new_htable(daemon, peer_htable); daemon->listeners = tal_arr(daemon, struct io_listener *, 0); - peer_htable_init(daemon->peers); - memleak_add_helper(daemon, memleak_daemon_cb); - daemon->connecting = tal(daemon, struct connecting_htable); - connecting_htable_init(daemon->connecting); - daemon->important_ids = tal(daemon, struct important_id_htable); - important_id_htable_init(daemon->important_ids); + daemon->connecting = new_htable(daemon, connecting_htable); + daemon->important_ids = new_htable(daemon, important_id_htable); timers_init(&daemon->timers, time_mono()); daemon->gossmap_raw = NULL; daemon->shutting_down = false; @@ -2481,8 +2469,7 @@ int main(int argc, char *argv[]) daemon->dev_exhausted_fds = false; /* We generally allow 1MB per second per peer, except for dev testing */ daemon->gossip_stream_limit = 1000000; - daemon->scid_htable = tal(daemon, struct scid_htable); - scid_htable_init(daemon->scid_htable); + daemon->scid_htable = new_htable(daemon, scid_htable); /* stdin == control */ daemon->master = daemon_conn_new(daemon, STDIN_FILENO, recv_req, NULL, diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 41807050806f..40bfe551fa9e 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -479,7 +479,6 @@ static void dev_gossip_memleak(struct daemon *daemon, const u8 *msg) memleak_ptr(memtable, msg); /* Now delete daemon and those which it has pointers to. */ memleak_scan_obj(memtable, daemon); - memleak_scan_htable(memtable, &daemon->peers->raw); dev_seeker_memleak(memtable, daemon->seeker); gossmap_manage_memleak(memtable, daemon->gm); @@ -632,8 +631,7 @@ int main(int argc, char *argv[]) daemon = tal(NULL, struct daemon); daemon->developer = developer; daemon->dev_gossip_time = NULL; - daemon->peers = tal(daemon, struct peer_node_id_map); - peer_node_id_map_init(daemon->peers); + daemon->peers = new_htable(daemon, peer_node_id_map); daemon->deferred_txouts = tal_arr(daemon, struct short_channel_id, 0); daemon->current_blockheight = 0; /* i.e. unknown */ diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 7d02e9245395..5214b490ed0e 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -1229,14 +1229,10 @@ struct chain_topology *new_topology(struct lightningd *ld, struct logger *log) struct chain_topology *topo = tal(ld, struct chain_topology); topo->ld = ld; - topo->block_map = tal(topo, struct block_map); - block_map_init(topo->block_map); - topo->outgoing_txs = tal(topo, struct outgoing_tx_map); - outgoing_tx_map_init(topo->outgoing_txs); - topo->txwatches = tal(topo, struct txwatch_hash); - txwatch_hash_init(topo->txwatches); - topo->txowatches = tal(topo, struct txowatch_hash); - txowatch_hash_init(topo->txowatches); + topo->block_map = new_htable(topo, block_map); + topo->outgoing_txs = new_htable(topo, outgoing_tx_map); + topo->txwatches = new_htable(topo, txwatch_hash); + topo->txowatches = new_htable(topo, txowatch_hash); topo->log = log; topo->bitcoind = new_bitcoind(topo, ld, log); topo->poll_seconds = 30; diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 5a350daeb43e..7a529b489e13 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -190,42 +190,34 @@ static struct lightningd *new_lightningd(const tal_t *ctx) * list attached to the channel structure itself, or even left them in * the database rather than making an in-memory version. Obviously * I was in a premature optimization mood when I wrote this: */ - ld->htlcs_in = tal(ld, struct htlc_in_map); - htlc_in_map_init(ld->htlcs_in); + ld->htlcs_in = new_htable(ld, htlc_in_map); /*~ Note also: we didn't need to use an allocation here! We could * have simply made the `struct htlc_out_map` a member. But we * override the htable allocation routines to use tal(), and they * want a tal parent, so we always make our hash table a tallocated * object. */ - ld->htlcs_out = tal(ld, struct htlc_out_map); - htlc_out_map_init(ld->htlcs_out); + ld->htlcs_out = new_htable(ld, htlc_out_map); /*~ This is the hash table of peers: converted from a * linked-list as part of the 100k-peers project! */ - ld->peers = tal(ld, struct peer_node_id_map); - peer_node_id_map_init(ld->peers); + ld->peers = new_htable(ld, peer_node_id_map); /*~ And this was done at the same time, for db lookups at startup */ - ld->peers_by_dbid = tal(ld, struct peer_dbid_map); - peer_dbid_map_init(ld->peers_by_dbid); + ld->peers_by_dbid = new_htable(ld, peer_dbid_map); /*~ This speeds lookups for short_channel_ids to their channels. */ - ld->channels_by_scid = tal(ld, struct channel_scid_map); - channel_scid_map_init(ld->channels_by_scid); + ld->channels_by_scid = new_htable(ld, channel_scid_map); /*~ Coin movements in db are indexed by the channel dbid. */ - ld->channels_by_dbid = tal(ld, struct channel_dbid_map); - channel_dbid_map_init(ld->channels_by_dbid); + ld->channels_by_dbid = new_htable(ld, channel_dbid_map); /*~ For multi-part payments, we need to keep some incoming payments * in limbo until we get all the parts, or we time them out. */ - ld->htlc_sets = tal(ld, struct htlc_set_map); - htlc_set_map_init(ld->htlc_sets); + ld->htlc_sets = new_htable(ld, htlc_set_map); /*~ We keep a map of closed channels. Mainly so we can respond to peers * who talk to us about long-closed channels. */ - ld->closed_channels = tal(ld, struct closed_channel_map); - closed_channel_map_init(ld->closed_channels); + ld->closed_channels = new_htable(ld, closed_channel_map); /*~ We have a multi-entry log-book infrastructure: we define a 10MB log * book to hold all the entries (and trims as necessary), and multiple diff --git a/lightningd/memdump.c b/lightningd/memdump.c index b2f32a10636f..31d84b0e057f 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -194,19 +194,6 @@ static bool lightningd_check_leaks(struct command *cmd) memleak_ptr(memtable, cmd); memleak_ignore_children(memtable, cmd); - /* First delete known false positives. */ - memleak_scan_htable(memtable, &ld->topology->txwatches->raw); - memleak_scan_htable(memtable, &ld->topology->txowatches->raw); - memleak_scan_htable(memtable, &ld->topology->outgoing_txs->raw); - memleak_scan_htable(memtable, &ld->htlcs_in->raw); - memleak_scan_htable(memtable, &ld->htlcs_out->raw); - memleak_scan_htable(memtable, &ld->htlc_sets->raw); - memleak_scan_htable(memtable, &ld->peers->raw); - memleak_scan_htable(memtable, &ld->peers_by_dbid->raw); - memleak_scan_htable(memtable, &ld->channels_by_scid->raw); - memleak_scan_htable(memtable, &ld->closed_channels->raw); - wallet_memleak_scan(memtable, ld->wallet); - /* Now delete ld and those which it has pointers to. */ memleak_scan_obj(memtable, ld); diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index b82523ab1b38..4d2723a5d1db 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -49,13 +49,6 @@ static bool replay_tx_eq_txid(const struct replay_tx *rtx, HTABLE_DEFINE_NODUPS_TYPE(struct replay_tx, replay_tx_keyof, txid_hash, replay_tx_eq_txid, replay_tx_hash); -/* Helper for memleak detection */ -static void memleak_replay_tx_hash(struct htable *memtable, - struct replay_tx_hash *replay_tx_hash) -{ - memleak_scan_htable(memtable, &replay_tx_hash->raw); -} - /* We dump all the known preimages when onchaind starts up. */ static void onchaind_tell_fulfill(struct channel *channel) { @@ -1904,11 +1897,8 @@ void onchaind_replay_channels(struct lightningd *ld) channel_state_name(channel), blockheight); /* We're in replay mode */ - channel->onchaind_replay_watches = tal(channel, struct replay_tx_hash); + channel->onchaind_replay_watches = new_htable(channel, replay_tx_hash); channel->onchaind_replay_height = blockheight; - replay_tx_hash_init(channel->onchaind_replay_watches); - memleak_add_helper(channel->onchaind_replay_watches, - memleak_replay_tx_hash); onchaind_funding_spent(channel, tx, blockheight); onchaind_replay(channel); diff --git a/plugins/askrene/askrene.c b/plugins/askrene/askrene.c index 9664d2487d7f..7f7f2d0d50c3 100644 --- a/plugins/askrene/askrene.c +++ b/plugins/askrene/askrene.c @@ -1290,13 +1290,6 @@ static const struct plugin_command commands[] = { }, }; -static void askrene_markmem(struct plugin *plugin, struct htable *memtable) -{ - struct askrene *askrene = get_askrene(plugin); - layer_memleak_mark(askrene, memtable); - reserve_memleak_mark(askrene, memtable); -} - static const char *init(struct command *init_cmd, const char *buf UNUSED, const jsmntok_t *config UNUSED) { @@ -1316,7 +1309,6 @@ static const char *init(struct command *init_cmd, "{id:%}", JSON_SCAN(json_to_node_id, &askrene->my_id)); plugin_set_data(plugin, askrene); - plugin_set_memleak_handler(plugin, askrene_markmem); load_layers(askrene, init_cmd); diff --git a/plugins/askrene/layer.c b/plugins/askrene/layer.c index 281e32a03c7b..4cbe657ac637 100644 --- a/plugins/askrene/layer.c +++ b/plugins/askrene/layer.c @@ -162,14 +162,10 @@ struct layer *new_temp_layer(const tal_t *ctx, struct askrene *askrene, const ch l->askrene = askrene; l->name = tal_strdup(l, name); l->persistent = false; - l->local_channels = tal(l, struct local_channel_hash); - local_channel_hash_init(l->local_channels); - l->local_updates = tal(l, struct local_update_hash); - local_update_hash_init(l->local_updates); - l->constraints = tal(l, struct constraint_hash); - constraint_hash_init(l->constraints); - l->biases = tal(l, struct bias_hash); - bias_hash_init(l->biases); + l->local_channels = new_htable(l, local_channel_hash); + l->local_updates = new_htable(l, local_update_hash); + l->constraints = new_htable(l, constraint_hash); + l->biases = new_htable(l, bias_hash); l->disabled_nodes = tal_arr(l, struct node_id, 0); return l; @@ -1162,14 +1158,3 @@ bool layer_disables_node(const struct layer *layer, } return false; } - -void layer_memleak_mark(struct askrene *askrene, struct htable *memtable) -{ - struct layer *l; - list_for_each(&askrene->layers, l, list) { - memleak_scan_htable(memtable, &l->constraints->raw); - memleak_scan_htable(memtable, &l->local_channels->raw); - memleak_scan_htable(memtable, &l->local_updates->raw); - memleak_scan_htable(memtable, &l->biases->raw); - } -} diff --git a/plugins/askrene/layer.h b/plugins/askrene/layer.h index e9b06fd5a4e6..b93f228ff05d 100644 --- a/plugins/askrene/layer.h +++ b/plugins/askrene/layer.h @@ -135,6 +135,4 @@ bool layer_disables_chan(const struct layer *layer, const struct short_channel_i /* For explain_failure: did this layer disable this node? */ bool layer_disables_node(const struct layer *layer, const struct node_id *node); -/* Scan for memleaks */ -void layer_memleak_mark(struct askrene *askrene, struct htable *memtable); #endif /* LIGHTNING_PLUGINS_ASKRENE_LAYER_H */ diff --git a/plugins/askrene/reserve.c b/plugins/askrene/reserve.c index abec9372dfc7..678ca248d667 100644 --- a/plugins/askrene/reserve.c +++ b/plugins/askrene/reserve.c @@ -36,9 +36,7 @@ HTABLE_DEFINE_DUPS_TYPE(struct reserve, reserve_scidd, hash_scidd, struct reserve_htable *new_reserve_htable(const tal_t *ctx) { - struct reserve_htable *reserved = tal(ctx, struct reserve_htable); - reserve_htable_init(reserved); - return reserved; + return new_htable(ctx, reserve_htable); } void reserve_add(struct reserve_htable *reserved, @@ -176,8 +174,3 @@ const char *fmt_reservations(const tal_t *ctx, } return ret; } - -void reserve_memleak_mark(struct askrene *askrene, struct htable *memtable) -{ - memleak_scan_htable(memtable, &askrene->reserved->raw); -} diff --git a/plugins/askrene/reserve.h b/plugins/askrene/reserve.h index 1c9e71eaaf60..868756ca0d9a 100644 --- a/plugins/askrene/reserve.h +++ b/plugins/askrene/reserve.h @@ -50,6 +50,4 @@ void json_add_reservations(struct json_stream *js, const struct reserve_htable *reserved, const char *fieldname); -/* Scan for memleaks */ -void reserve_memleak_mark(struct askrene *askrene, struct htable *memtable); #endif /* LIGHTNING_PLUGINS_ASKRENE_RESERVE_H */ diff --git a/plugins/chanbackup.c b/plugins/chanbackup.c index 11091edd92c4..16d6cb6189df 100644 --- a/plugins/chanbackup.c +++ b/plugins/chanbackup.c @@ -1040,10 +1040,8 @@ static void setup_backup_map(struct command *init_cmd, const jsmntok_t *datastore, *t; size_t i, total = 0; - cb->backups = tal(cb, struct backup_map); - backup_map_init(cb->backups); - cb->peers = tal(cb, struct peer_map); - peer_map_init(cb->peers); + cb->backups = new_htable(cb, backup_map); + cb->peers = new_htable(cb, peer_map); json_out_start(params, NULL, '{'); json_out_start(params, "key", '['); @@ -1084,14 +1082,6 @@ static void setup_backup_map(struct command *init_cmd, "Loaded %zu stored backups for peers", total); } -static void chanbackup_mark_mem(struct plugin *plugin, - struct htable *memtable) -{ - const struct chanbackup *cb = chanbackup(plugin); - memleak_scan_htable(memtable, &cb->backups->raw); - memleak_scan_htable(memtable, &cb->peers->raw); -} - static const char *init(struct command *init_cmd, const char *buf UNUSED, const jsmntok_t *config UNUSED) @@ -1132,9 +1122,6 @@ static const char *init(struct command *init_cmd, unlink_noerr("scb.tmp"); maybe_create_new_scb(init_cmd->plugin, scb_chan); - - plugin_set_memleak_handler(init_cmd->plugin, - chanbackup_mark_mem); return NULL; } diff --git a/plugins/channel_hint.c b/plugins/channel_hint.c index 1db37791ea0a..7a14f3992a44 100644 --- a/plugins/channel_hint.c +++ b/plugins/channel_hint.c @@ -23,12 +23,6 @@ bool channel_hint_eq(const struct channel_hint *a, a->scid.dir == b->dir; } -static void memleak_help_channel_hint_map(struct htable *memtable, - struct channel_hint_map *channel_hints) -{ - memleak_scan_htable(memtable, &channel_hints->raw); -} - void channel_hint_to_json(const char *name, const struct channel_hint *hint, struct json_stream *dest) { @@ -211,9 +205,7 @@ struct channel_hint *channel_hint_from_json(const tal_t *ctx, struct channel_hint_set *channel_hint_set_new(const tal_t *ctx) { struct channel_hint_set *set = tal(ctx, struct channel_hint_set); - set->hints = tal(set, struct channel_hint_map); - channel_hint_map_init(set->hints); - memleak_add_helper(set->hints, memleak_help_channel_hint_map); + set->hints = new_htable(set, channel_hint_map); return set; } diff --git a/wallet/test/run-chain_moves_duplicate-detect.c b/wallet/test/run-chain_moves_duplicate-detect.c index fdf62145e64a..d8e236b9dfdd 100644 --- a/wallet/test/run-chain_moves_duplicate-detect.c +++ b/wallet/test/run-chain_moves_duplicate-detect.c @@ -162,10 +162,6 @@ struct invoices *invoices_new(const tal_t *ctx UNNEEDED, struct wallet *wallet UNNEEDED, struct timers *timers UNNEEDED) { fprintf(stderr, "invoices_new called!\n"); abort(); } -/* Generated stub for memleak_scan_outpointfilter */ -void memleak_scan_outpointfilter(struct htable *memtable UNNEEDED, - const struct outpointfilter *opf UNNEEDED) -{ fprintf(stderr, "memleak_scan_outpointfilter called!\n"); abort(); } /* Generated stub for new_channel */ struct channel *new_channel(struct peer *peer UNNEEDED, u64 dbid UNNEEDED, /* NULL or stolen */ diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 106cd38026ae..86d6501463a3 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -170,10 +170,6 @@ struct invoices *invoices_new(const tal_t *ctx UNNEEDED, void logv(struct logger *logger UNNEEDED, enum log_level level UNNEEDED, const struct node_id *node_id UNNEEDED, bool call_notifier UNNEEDED, const char *fmt UNNEEDED, va_list ap UNNEEDED) { fprintf(stderr, "logv called!\n"); abort(); } -/* Generated stub for memleak_scan_outpointfilter */ -void memleak_scan_outpointfilter(struct htable *memtable UNNEEDED, - const struct outpointfilter *opf UNNEEDED) -{ fprintf(stderr, "memleak_scan_outpointfilter called!\n"); abort(); } /* Generated stub for mk_mvt_tags_ */ struct mvt_tags mk_mvt_tags_(enum mvt_tag tag UNNEEDED, ...) { fprintf(stderr, "mk_mvt_tags_ called!\n"); abort(); } diff --git a/wallet/test/run-migrate_remove_chain_moves_duplicates.c b/wallet/test/run-migrate_remove_chain_moves_duplicates.c index f9ed9699df27..67b7a20f7731 100644 --- a/wallet/test/run-migrate_remove_chain_moves_duplicates.c +++ b/wallet/test/run-migrate_remove_chain_moves_duplicates.c @@ -174,10 +174,6 @@ struct invoices *invoices_new(const tal_t *ctx UNNEEDED, void logv(struct logger *logger UNNEEDED, enum log_level level UNNEEDED, const struct node_id *node_id UNNEEDED, bool call_notifier UNNEEDED, const char *fmt UNNEEDED, va_list ap UNNEEDED) { fprintf(stderr, "logv called!\n"); abort(); } -/* Generated stub for memleak_scan_outpointfilter */ -void memleak_scan_outpointfilter(struct htable *memtable UNNEEDED, - const struct outpointfilter *opf UNNEEDED) -{ fprintf(stderr, "memleak_scan_outpointfilter called!\n"); abort(); } /* Generated stub for migrate_from_account_db */ void migrate_from_account_db(struct lightningd *ld UNNEEDED, struct db *db UNNEEDED) { fprintf(stderr, "migrate_from_account_db called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 4d96e83bb1bf..ea0eb1d8f968 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -664,10 +664,6 @@ void lockin_complete(struct channel *channel UNNEEDED, void logv(struct logger *logger UNNEEDED, enum log_level level UNNEEDED, const struct node_id *node_id UNNEEDED, bool call_notifier UNNEEDED, const char *fmt UNNEEDED, va_list ap UNNEEDED) { fprintf(stderr, "logv called!\n"); abort(); } -/* Generated stub for memleak_scan_outpointfilter */ -void memleak_scan_outpointfilter(struct htable *memtable UNNEEDED, - const struct outpointfilter *opf UNNEEDED) -{ fprintf(stderr, "memleak_scan_outpointfilter called!\n"); abort(); } /* Generated stub for mk_mvt_tags_ */ struct mvt_tags mk_mvt_tags_(enum mvt_tag tag UNNEEDED, ...) { fprintf(stderr, "mk_mvt_tags_ called!\n"); abort(); } diff --git a/wallet/txfilter.c b/wallet/txfilter.c index 10ef774e41b0..0ce6f46a7a2c 100644 --- a/wallet/txfilter.c +++ b/wallet/txfilter.c @@ -128,12 +128,6 @@ void outpointfilter_remove(struct outpointfilter *of, struct outpointfilter *outpointfilter_new(tal_t *ctx) { struct outpointfilter *opf = tal(ctx, struct outpointfilter); - opf->set = tal(opf, struct outpointset); - outpointset_init(opf->set); + opf->set = new_htable(opf, outpointset); return opf; } - -void memleak_scan_outpointfilter(struct htable *memtable, const struct outpointfilter *opf) -{ - memleak_scan_htable(memtable, &opf->set->raw); -} diff --git a/wallet/txfilter.h b/wallet/txfilter.h index c9152bffd390..2e72b68e1b93 100644 --- a/wallet/txfilter.h +++ b/wallet/txfilter.h @@ -64,9 +64,6 @@ bool outpointfilter_matches(struct outpointfilter *of, void outpointfilter_remove(struct outpointfilter *of, const struct bitcoin_outpoint *outpoint); -void memleak_scan_outpointfilter(struct htable *memtable, - const struct outpointfilter *opf); - /* Useful for other callers */ size_t scriptpubkey_hash(const u8 *out); diff --git a/wallet/wallet.c b/wallet/wallet.c index 91962ba6bb1b..1f7d980ac895 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -181,8 +181,7 @@ static void our_addresses_add_for_index(struct wallet *w, u32 i) static void our_addresses_init(struct wallet *w) { w->our_addresses_maxindex = 0; - w->our_addresses = tal(w, struct wallet_address_htable); - wallet_address_htable_init(w->our_addresses); + w->our_addresses = new_htable(w, wallet_address_htable); our_addresses_add_for_index(w, w->our_addresses_maxindex); } @@ -6918,13 +6917,6 @@ struct local_anchor_info *wallet_get_local_anchors(const tal_t *ctx, return anchors; } -void wallet_memleak_scan(struct htable *memtable, const struct wallet *w) -{ - memleak_scan_outpointfilter(memtable, w->utxoset_outpoints); - memleak_scan_outpointfilter(memtable, w->owned_outpoints); - memleak_scan_htable(memtable, &w->our_addresses->raw); -} - struct issued_address_type *wallet_list_addresses(const tal_t *ctx, struct wallet *wallet, u64 liststart, const u32 *listlimit) { diff --git a/wallet/wallet.h b/wallet/wallet.h index f956196b37cd..ccadb121e180 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1928,8 +1928,4 @@ void wallet_datastore_save_payment_description(struct db *db, void migrate_setup_coinmoves(struct lightningd *ld, struct db *db); void migrate_remove_chain_moves_duplicates(struct lightningd *ld, struct db *db); -/** - * wallet_memleak_scan - Check for memleaks in wallet. - */ -void wallet_memleak_scan(struct htable *memtable, const struct wallet *w); #endif /* LIGHTNING_WALLET_WALLET_H */ From 5a57ae873b08f1ea960f56642334a056d10f8270 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:40:18 +0930 Subject: [PATCH 08/13] lightningd: try harder to ensure uniqueness in --dev-save-plugin-io names. Incorporate a time: this covers the restart case as well. And make it time_mono(), which doesn't get overridden when we override normal wall time. Signed-off-by: Rusty Russell --- lightningd/plugin.c | 10 +++++++++- tests/test_misc.py | 8 +------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 1dba9a94dd87..e1c8130d3233 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -2688,16 +2688,24 @@ static void dev_save_plugin_io(struct plugins *plugins, const char *buf, size_t len) { static size_t counter; + static u64 starttime; const char *file; int fd; if (!plugins->dev_save_io) return; + /* If we reexec, we still want unique names */ + if (!starttime) { + struct timemono start = time_mono(); + starttime = start.ts.tv_sec * 1000000 + start.ts.tv_nsec / 1000; + } + file = path_join(tmpctx, plugins->dev_save_io, - take(tal_fmt(NULL, "%s-%s-%u-%zu", + take(tal_fmt(NULL, "%s-%s-%u-%"PRIu64"-%zu", type, name, (unsigned int)getpid(), + starttime, counter++))); fd = open(file, O_CREAT|O_EXCL|O_WRONLY, 0600); if (fd < 0 || !write_all(fd, buf, len)) diff --git a/tests/test_misc.py b/tests/test_misc.py index 0a6aba28dabe..112c07065b7c 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -3668,8 +3668,6 @@ def test_version_reexec(node_factory, bitcoind): # We use a file to tell our openingd wrapper where the real one is with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "openingd-real"), 'w') as f: f.write(os.path.abspath('lightningd/lightning_openingd')) - # Internal restart doesn't work well with --dev-save-plugin-io - del l1.daemon.opts['dev-save-plugin-io'] l1.start() # This is a "version" message verfile = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "openingd-version") @@ -4545,11 +4543,7 @@ def test_setconfig_changed(node_factory, bitcoind): @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "deletes database, which is assumed sqlite3") def test_recover_command(node_factory, bitcoind): - l1 = node_factory.get_node(start=False) - # Internal restart doesn't work well with --dev-save-plugin-io - del l1.daemon.opts['dev-save-plugin-io'] - l1.start() - l2 = node_factory.get_node() + l1, l2 = node_factory.get_nodes(2) l1oldid = l1.info['id'] From b7f4b97e3d03c0c5cba5a8e83d463c172068b9c2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:40:18 +0930 Subject: [PATCH 09/13] bitcoin: remove unused scriptpubkey_opreturn_padded. I noticed, because it pulled in randomness routines. Signed-off-by: Rusty Russell --- bitcoin/script.c | 11 ----------- bitcoin/script.h | 6 ------ 2 files changed, 17 deletions(-) diff --git a/bitcoin/script.c b/bitcoin/script.c index 63974644a34c..18461a8396b4 100644 --- a/bitcoin/script.c +++ b/bitcoin/script.c @@ -187,17 +187,6 @@ u8 *scriptpubkey_p2pkh(const tal_t *ctx, const struct bitcoin_address *addr) return script; } -u8 *scriptpubkey_opreturn_padded(const tal_t *ctx) -{ - u8 *script = tal_arr(ctx, u8, 0); - u8 random[20]; - randombytes_buf(random, sizeof(random)); - - add_op(&script, OP_RETURN); - script_push_bytes(&script, random, sizeof(random)); - return script; -} - /* Create an input script which spends p2pkh */ u8 *bitcoin_redeem_p2pkh(const tal_t *ctx, const struct pubkey *pubkey, const struct bitcoin_signature *sig) diff --git a/bitcoin/script.h b/bitcoin/script.h index 5b96d696ff15..c0a005c17d57 100644 --- a/bitcoin/script.h +++ b/bitcoin/script.h @@ -27,12 +27,6 @@ u8 *scriptpubkey_p2sh_hash(const tal_t *ctx, const struct ripemd160 *redeemhash) /* Create an output script using p2pkh */ u8 *scriptpubkey_p2pkh(const tal_t *ctx, const struct bitcoin_address *addr); -/* Create a prunable output script with 20 random bytes. - * This is needed since a spend from a p2wpkh to an `OP_RETURN` without - * any other outputs would result in a transaction smaller than the - * minimum size. */ -u8 *scriptpubkey_opreturn_padded(const tal_t *ctx); - /* Create an input script which spends p2pkh */ u8 *bitcoin_redeem_p2pkh(const tal_t *ctx, const struct pubkey *pubkey, const struct bitcoin_signature *sig); From ba34d99aef77fb98347746b4e5ef7009009d4f13 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:40:18 +0930 Subject: [PATCH 10/13] autogenerate-rpc-examples.py: add examples for listchainmoves and listchannelmoves. Signed-off-by: Rusty Russell --- contrib/msggen/msggen/schema.json | 589 +++++++++++++++++++++++++++++ doc/schemas/listchainmoves.json | 353 +++++++++++++++++ doc/schemas/listchannelmoves.json | 236 ++++++++++++ tests/autogenerate-rpc-examples.py | 14 + 4 files changed, 1192 insertions(+) diff --git a/contrib/msggen/msggen/schema.json b/contrib/msggen/msggen/schema.json index a0f42b9c8d4f..dadbb6ff0fd9 100644 --- a/contrib/msggen/msggen/schema.json +++ b/contrib/msggen/msggen/schema.json @@ -16812,6 +16812,359 @@ ], "resources": [ "Main web site: " + ], + "examples": [ + { + "request": { + "id": "example:listchainmoves#1", + "method": "listchainmoves", + "params": {} + }, + "response": { + "chainmoves": [ + { + "created_index": 1, + "account_id": "wallet", + "credit_msat": 200000000000, + "debit_msat": 0, + "timestamp": 1758192762, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "994185cba7723715c0aa1d1859ce2781116776cea917035c90f8f04c9f4e095e:1", + "output_msat": 200000000000, + "blockheight": 104 + }, + { + "created_index": 2, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 0, + "debit_msat": 0, + "timestamp": 1758192777, + "primary_tag": "channel_open", + "extra_tags": [], + "utxo": "542906c8a9d90596592459a9484f4286a3200f6540599c83b43af2ac4166c6ca:1", + "peer_id": "nodeid010101010101010101010101010101010101010101010101010101010101", + "output_msat": 1000000000, + "blockheight": 109 + }, + { + "created_index": 3, + "account_id": "wallet", + "credit_msat": 2000000000, + "debit_msat": 0, + "timestamp": 1758192780, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "19e9e42f2f2097ea1dc18d7eb670bc53c90cbe31bb1daba8e94abf3c6b60d2dc:1", + "output_msat": 2000000000, + "blockheight": 110 + }, + { + "created_index": 4, + "account_id": "wallet", + "credit_msat": 0, + "debit_msat": 200000000000, + "timestamp": 1738530000, + "primary_tag": "withdrawal", + "extra_tags": [], + "utxo": "994185cba7723715c0aa1d1859ce2781116776cea917035c90f8f04c9f4e095e:1", + "spending_txid": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc", + "output_msat": 200000000000, + "blockheight": 111 + }, + { + "created_index": 5, + "account_id": "wallet", + "credit_msat": 198995073000, + "debit_msat": 0, + "timestamp": 1738530000, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc:1", + "output_msat": 198995073000, + "blockheight": 111 + }, + { + "created_index": 6, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 1000000000, + "debit_msat": 0, + "timestamp": 1738530000, + "primary_tag": "channel_open", + "extra_tags": [ + "opener" + ], + "utxo": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc:0", + "peer_id": "nodeid030303030303030303030303030303030303030303030303030303030303", + "output_msat": 1000000000, + "blockheight": 111 + }, + { + "created_index": 7, + "account_id": "wallet", + "credit_msat": 2000000000, + "debit_msat": 0, + "timestamp": 1758192792, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "b6d0090efbeb347fa59f90b321d6906cdf86779c15477582979fa427249f71f5:1", + "output_msat": 2000000000, + "blockheight": 114 + }, + { + "created_index": 8, + "account_id": "wallet", + "credit_msat": 0, + "debit_msat": 198995073000, + "timestamp": 1758192795, + "primary_tag": "withdrawal", + "extra_tags": [], + "utxo": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc:1", + "spending_txid": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec", + "output_msat": 198995073000, + "blockheight": 115 + }, + { + "created_index": 9, + "account_id": "wallet", + "credit_msat": 197990453000, + "debit_msat": 0, + "timestamp": 1758192795, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec:0", + "output_msat": 197990453000, + "blockheight": 115 + }, + { + "created_index": 10, + "account_id": "f8fc83a432cbfb2fffe222cc06727fdd977b5dd10ebd6707158e799e6f522d9f", + "credit_msat": 1000000000, + "debit_msat": 0, + "timestamp": 1758192795, + "primary_tag": "channel_open", + "extra_tags": [ + "opener" + ], + "utxo": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec:1", + "peer_id": "nodeid050505050505050505050505050505050505050505050505050505050505", + "output_msat": 1000000000, + "blockheight": 115 + }, + { + "created_index": 11, + "account_id": "wallet", + "credit_msat": 486914000, + "debit_msat": 0, + "timestamp": 1738520000, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "874b26d4c523a902fdc44b88ec000eb5c3fe8754c9d44190a140561e24e77781:0", + "output_msat": 486914000, + "blockheight": 121 + }, + { + "created_index": 12, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 489809898, + "timestamp": 1738520000, + "primary_tag": "channel_close", + "extra_tags": [], + "utxo": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc:0", + "spending_txid": "txid010101010101010101010101010101010101010101010101010101010101", + "output_msat": 1000000000, + "output_count": 2, + "blockheight": 121 + }, + { + "created_index": 13, + "account_id": "external", + "credit_msat": 510190000, + "debit_msat": 0, + "timestamp": 1738520000, + "primary_tag": "to_them", + "extra_tags": [], + "utxo": "874b26d4c523a902fdc44b88ec000eb5c3fe8754c9d44190a140561e24e77781:1", + "originating_account": "channelid0230000230000230000230000230000230000230000230000230000", + "output_msat": 510190000, + "blockheight": 121 + }, + { + "created_index": 14, + "account_id": "wallet", + "credit_msat": 2000000000, + "debit_msat": 0, + "timestamp": 1758192808, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "c9c9bec064382b6a6fb2a30d8923949b3c9f732465542b96e9ad1b5eebda4c7d:0", + "output_msat": 2000000000, + "blockheight": 122 + }, + { + "created_index": 15, + "account_id": "wallet", + "credit_msat": 0, + "debit_msat": 197990453000, + "timestamp": 1738500000, + "primary_tag": "withdrawal", + "extra_tags": [], + "utxo": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec:0", + "spending_txid": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682", + "output_msat": 197990453000, + "blockheight": 123 + }, + { + "created_index": 16, + "account_id": "wallet", + "credit_msat": 196985833000, + "debit_msat": 0, + "timestamp": 1738500000, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682:1", + "output_msat": 196985833000, + "blockheight": 123 + }, + { + "created_index": 17, + "account_id": "channelid0230200230200230200230200230200230200230200230200230200", + "credit_msat": 1000000000, + "debit_msat": 0, + "timestamp": 1738500000, + "primary_tag": "channel_open", + "extra_tags": [ + "opener" + ], + "utxo": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682:0", + "peer_id": "nodeid030303030303030303030303030303030303030303030303030303030303", + "output_msat": 1000000000, + "blockheight": 123 + } + ] + } + }, + { + "request": { + "id": "example:listchainmoves#2", + "method": "listchainmoves", + "params": { + "index": "created", + "start": 10 + } + }, + "response": { + "chainmoves": [ + { + "created_index": 10, + "account_id": "f8fc83a432cbfb2fffe222cc06727fdd977b5dd10ebd6707158e799e6f522d9f", + "credit_msat": 1000000000, + "debit_msat": 0, + "timestamp": 1758192795, + "primary_tag": "channel_open", + "extra_tags": [ + "opener" + ], + "utxo": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec:1", + "peer_id": "nodeid050505050505050505050505050505050505050505050505050505050505", + "output_msat": 1000000000, + "blockheight": 115 + }, + { + "created_index": 11, + "account_id": "wallet", + "credit_msat": 486914000, + "debit_msat": 0, + "timestamp": 1738520000, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "874b26d4c523a902fdc44b88ec000eb5c3fe8754c9d44190a140561e24e77781:0", + "output_msat": 486914000, + "blockheight": 121 + }, + { + "created_index": 12, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 489809898, + "timestamp": 1738520000, + "primary_tag": "channel_close", + "extra_tags": [], + "utxo": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc:0", + "spending_txid": "txid010101010101010101010101010101010101010101010101010101010101", + "output_msat": 1000000000, + "output_count": 2, + "blockheight": 121 + }, + { + "created_index": 13, + "account_id": "external", + "credit_msat": 510190000, + "debit_msat": 0, + "timestamp": 1738520000, + "primary_tag": "to_them", + "extra_tags": [], + "utxo": "874b26d4c523a902fdc44b88ec000eb5c3fe8754c9d44190a140561e24e77781:1", + "originating_account": "channelid0230000230000230000230000230000230000230000230000230000", + "output_msat": 510190000, + "blockheight": 121 + }, + { + "created_index": 14, + "account_id": "wallet", + "credit_msat": 2000000000, + "debit_msat": 0, + "timestamp": 1758192808, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "c9c9bec064382b6a6fb2a30d8923949b3c9f732465542b96e9ad1b5eebda4c7d:0", + "output_msat": 2000000000, + "blockheight": 122 + }, + { + "created_index": 15, + "account_id": "wallet", + "credit_msat": 0, + "debit_msat": 197990453000, + "timestamp": 1738500000, + "primary_tag": "withdrawal", + "extra_tags": [], + "utxo": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec:0", + "spending_txid": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682", + "output_msat": 197990453000, + "blockheight": 123 + }, + { + "created_index": 16, + "account_id": "wallet", + "credit_msat": 196985833000, + "debit_msat": 0, + "timestamp": 1738500000, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682:1", + "output_msat": 196985833000, + "blockheight": 123 + }, + { + "created_index": 17, + "account_id": "channelid0230200230200230200230200230200230200230200230200230200", + "credit_msat": 1000000000, + "debit_msat": 0, + "timestamp": 1738500000, + "primary_tag": "channel_open", + "extra_tags": [ + "opener" + ], + "utxo": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682:0", + "peer_id": "nodeid030303030303030303030303030303030303030303030303030303030303", + "output_msat": 1000000000, + "blockheight": 123 + } + ] + } + } ] }, "listchannelmoves.json": { @@ -16961,6 +17314,242 @@ ], "resources": [ "Main web site: " + ], + "examples": [ + { + "request": { + "id": "example:listchannelmoves#1", + "method": "listchannelmoves", + "params": {} + }, + "response": { + "channelmoves": [ + { + "created_index": 1, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 500000000, + "debit_msat": 0, + "timestamp": 1738520000, + "primary_tag": "invoice", + "payment_hash": "paymenthashdelpay10101010101010101010101010101010101010101010101", + "fees_msat": 0 + }, + { + "created_index": 2, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 500000000, + "timestamp": 1738520000, + "primary_tag": "invoice", + "payment_hash": "8a46ab91013146df39e98ad7c89505fbb5419f110e928f7a393e8304f3057df7", + "part_id": 0, + "group_id": 1, + "fees_msat": 0 + }, + { + "created_index": 3, + "account_id": "f8fc83a432cbfb2fffe222cc06727fdd977b5dd10ebd6707158e799e6f522d9f", + "credit_msat": 0, + "debit_msat": 500000000, + "timestamp": 1758192801, + "primary_tag": "invoice", + "payment_hash": "88969daaf02214a1928e6eb62a237a8ee557f3da93b2c44f3901432c88f4334b", + "part_id": 0, + "group_id": 1, + "fees_msat": 0 + }, + { + "created_index": 4, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10000, + "timestamp": 1758192801, + "primary_tag": "routed", + "payment_hash": "paymenthashinvl0310031003100310031003100310031003100310031003100", + "fees_msat": 1 + }, + { + "created_index": 5, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10001, + "debit_msat": 0, + "timestamp": 1758192801, + "primary_tag": "routed", + "payment_hash": "paymenthashinvl0310031003100310031003100310031003100310031003100", + "fees_msat": 1 + }, + { + "created_index": 6, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10000, + "timestamp": 1758192801, + "primary_tag": "routed", + "payment_hash": "paymenthashkey01k101k101k101k101k101k101k101k101k101k101k101k101", + "fees_msat": 1 + }, + { + "created_index": 7, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10001, + "debit_msat": 0, + "timestamp": 1758192801, + "primary_tag": "routed", + "payment_hash": "paymenthashkey01k101k101k101k101k101k101k101k101k101k101k101k101", + "fees_msat": 1 + }, + { + "created_index": 8, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10000101, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey02k201k201k201k201k201k201k201k201k201k201k201k201", + "fees_msat": 101 + }, + { + "created_index": 9, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10000202, + "debit_msat": 0, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey02k201k201k201k201k201k201k201k201k201k201k201k201", + "fees_msat": 101 + }, + { + "created_index": 10, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10000, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey03k301k301k301k301k301k301k301k301k301k301k301k301", + "fees_msat": 1 + }, + { + "created_index": 11, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10001, + "debit_msat": 0, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey03k301k301k301k301k301k301k301k301k301k301k301k301", + "fees_msat": 1 + }, + { + "created_index": 12, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 50000, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashinvl0320032003200320032003200320032003200320032003200", + "fees_msat": 1 + }, + { + "created_index": 13, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 50001, + "debit_msat": 0, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashinvl0320032003200320032003200320032003200320032003200", + "fees_msat": 1 + }, + { + "created_index": 14, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 100000, + "timestamp": 1758192803, + "primary_tag": "invoice", + "payment_hash": "paymenthashinvl0330033003300330033003300330033003300330033003300", + "part_id": 0, + "group_id": 1, + "fees_msat": 0 + }, + { + "created_index": 15, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10001, + "timestamp": 1758192803, + "primary_tag": "routed", + "payment_hash": "61b929204f4db4f38e0412b2bd4c3c03668dad3575fb05f4e15a2214046c2bff", + "fees_msat": 1 + }, + { + "created_index": 16, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10002, + "debit_msat": 0, + "timestamp": 1758192803, + "primary_tag": "routed", + "payment_hash": "61b929204f4db4f38e0412b2bd4c3c03668dad3575fb05f4e15a2214046c2bff", + "fees_msat": 1 + }, + { + "created_index": 17, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 0, + "debit_msat": 1000000, + "timestamp": 1758192821, + "primary_tag": "invoice", + "payment_hash": "paymenthashsdinvsi10si10si10si10si10si10si10si10si10si10si10si10", + "part_id": 0, + "group_id": 1, + "fees_msat": 0 + }, + { + "created_index": 18, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 1000, + "debit_msat": 0, + "timestamp": 1758192821, + "primary_tag": "invoice", + "payment_hash": "paymenthashinvl0270027002700270027002700270027002700270027002700", + "fees_msat": 0 + } + ] + } + }, + { + "request": { + "id": "example:listchannelmoves#2", + "method": "listchannelmoves", + "params": { + "index": "created", + "start": 10, + "limit": 2 + } + }, + "response": { + "channelmoves": [ + { + "created_index": 10, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10000, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey03k301k301k301k301k301k301k301k301k301k301k301k301", + "fees_msat": 1 + }, + { + "created_index": 11, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10001, + "debit_msat": 0, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey03k301k301k301k301k301k301k301k301k301k301k301k301", + "fees_msat": 1 + } + ] + } + } ] }, "listchannels.json": { diff --git a/doc/schemas/listchainmoves.json b/doc/schemas/listchainmoves.json index 985105e93a55..eeec3e6b902e 100644 --- a/doc/schemas/listchainmoves.json +++ b/doc/schemas/listchainmoves.json @@ -197,5 +197,358 @@ ], "resources": [ "Main web site: " + ], + "examples": [ + { + "request": { + "id": "example:listchainmoves#1", + "method": "listchainmoves", + "params": {} + }, + "response": { + "chainmoves": [ + { + "created_index": 1, + "account_id": "wallet", + "credit_msat": 200000000000, + "debit_msat": 0, + "timestamp": 1758192762, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "994185cba7723715c0aa1d1859ce2781116776cea917035c90f8f04c9f4e095e:1", + "output_msat": 200000000000, + "blockheight": 104 + }, + { + "created_index": 2, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 0, + "debit_msat": 0, + "timestamp": 1758192777, + "primary_tag": "channel_open", + "extra_tags": [], + "utxo": "542906c8a9d90596592459a9484f4286a3200f6540599c83b43af2ac4166c6ca:1", + "peer_id": "nodeid010101010101010101010101010101010101010101010101010101010101", + "output_msat": 1000000000, + "blockheight": 109 + }, + { + "created_index": 3, + "account_id": "wallet", + "credit_msat": 2000000000, + "debit_msat": 0, + "timestamp": 1758192780, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "19e9e42f2f2097ea1dc18d7eb670bc53c90cbe31bb1daba8e94abf3c6b60d2dc:1", + "output_msat": 2000000000, + "blockheight": 110 + }, + { + "created_index": 4, + "account_id": "wallet", + "credit_msat": 0, + "debit_msat": 200000000000, + "timestamp": 1738530000, + "primary_tag": "withdrawal", + "extra_tags": [], + "utxo": "994185cba7723715c0aa1d1859ce2781116776cea917035c90f8f04c9f4e095e:1", + "spending_txid": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc", + "output_msat": 200000000000, + "blockheight": 111 + }, + { + "created_index": 5, + "account_id": "wallet", + "credit_msat": 198995073000, + "debit_msat": 0, + "timestamp": 1738530000, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc:1", + "output_msat": 198995073000, + "blockheight": 111 + }, + { + "created_index": 6, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 1000000000, + "debit_msat": 0, + "timestamp": 1738530000, + "primary_tag": "channel_open", + "extra_tags": [ + "opener" + ], + "utxo": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc:0", + "peer_id": "nodeid030303030303030303030303030303030303030303030303030303030303", + "output_msat": 1000000000, + "blockheight": 111 + }, + { + "created_index": 7, + "account_id": "wallet", + "credit_msat": 2000000000, + "debit_msat": 0, + "timestamp": 1758192792, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "b6d0090efbeb347fa59f90b321d6906cdf86779c15477582979fa427249f71f5:1", + "output_msat": 2000000000, + "blockheight": 114 + }, + { + "created_index": 8, + "account_id": "wallet", + "credit_msat": 0, + "debit_msat": 198995073000, + "timestamp": 1758192795, + "primary_tag": "withdrawal", + "extra_tags": [], + "utxo": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc:1", + "spending_txid": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec", + "output_msat": 198995073000, + "blockheight": 115 + }, + { + "created_index": 9, + "account_id": "wallet", + "credit_msat": 197990453000, + "debit_msat": 0, + "timestamp": 1758192795, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec:0", + "output_msat": 197990453000, + "blockheight": 115 + }, + { + "created_index": 10, + "account_id": "f8fc83a432cbfb2fffe222cc06727fdd977b5dd10ebd6707158e799e6f522d9f", + "credit_msat": 1000000000, + "debit_msat": 0, + "timestamp": 1758192795, + "primary_tag": "channel_open", + "extra_tags": [ + "opener" + ], + "utxo": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec:1", + "peer_id": "nodeid050505050505050505050505050505050505050505050505050505050505", + "output_msat": 1000000000, + "blockheight": 115 + }, + { + "created_index": 11, + "account_id": "wallet", + "credit_msat": 486914000, + "debit_msat": 0, + "timestamp": 1738520000, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "874b26d4c523a902fdc44b88ec000eb5c3fe8754c9d44190a140561e24e77781:0", + "output_msat": 486914000, + "blockheight": 121 + }, + { + "created_index": 12, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 489809898, + "timestamp": 1738520000, + "primary_tag": "channel_close", + "extra_tags": [], + "utxo": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc:0", + "spending_txid": "txid010101010101010101010101010101010101010101010101010101010101", + "output_msat": 1000000000, + "output_count": 2, + "blockheight": 121 + }, + { + "created_index": 13, + "account_id": "external", + "credit_msat": 510190000, + "debit_msat": 0, + "timestamp": 1738520000, + "primary_tag": "to_them", + "extra_tags": [], + "utxo": "874b26d4c523a902fdc44b88ec000eb5c3fe8754c9d44190a140561e24e77781:1", + "originating_account": "channelid0230000230000230000230000230000230000230000230000230000", + "output_msat": 510190000, + "blockheight": 121 + }, + { + "created_index": 14, + "account_id": "wallet", + "credit_msat": 2000000000, + "debit_msat": 0, + "timestamp": 1758192808, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "c9c9bec064382b6a6fb2a30d8923949b3c9f732465542b96e9ad1b5eebda4c7d:0", + "output_msat": 2000000000, + "blockheight": 122 + }, + { + "created_index": 15, + "account_id": "wallet", + "credit_msat": 0, + "debit_msat": 197990453000, + "timestamp": 1738500000, + "primary_tag": "withdrawal", + "extra_tags": [], + "utxo": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec:0", + "spending_txid": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682", + "output_msat": 197990453000, + "blockheight": 123 + }, + { + "created_index": 16, + "account_id": "wallet", + "credit_msat": 196985833000, + "debit_msat": 0, + "timestamp": 1738500000, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682:1", + "output_msat": 196985833000, + "blockheight": 123 + }, + { + "created_index": 17, + "account_id": "channelid0230200230200230200230200230200230200230200230200230200", + "credit_msat": 1000000000, + "debit_msat": 0, + "timestamp": 1738500000, + "primary_tag": "channel_open", + "extra_tags": [ + "opener" + ], + "utxo": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682:0", + "peer_id": "nodeid030303030303030303030303030303030303030303030303030303030303", + "output_msat": 1000000000, + "blockheight": 123 + } + ] + } + }, + { + "request": { + "id": "example:listchainmoves#2", + "method": "listchainmoves", + "params": { + "index": "created", + "start": 10 + } + }, + "response": { + "chainmoves": [ + { + "created_index": 10, + "account_id": "f8fc83a432cbfb2fffe222cc06727fdd977b5dd10ebd6707158e799e6f522d9f", + "credit_msat": 1000000000, + "debit_msat": 0, + "timestamp": 1758192795, + "primary_tag": "channel_open", + "extra_tags": [ + "opener" + ], + "utxo": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec:1", + "peer_id": "nodeid050505050505050505050505050505050505050505050505050505050505", + "output_msat": 1000000000, + "blockheight": 115 + }, + { + "created_index": 11, + "account_id": "wallet", + "credit_msat": 486914000, + "debit_msat": 0, + "timestamp": 1738520000, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "874b26d4c523a902fdc44b88ec000eb5c3fe8754c9d44190a140561e24e77781:0", + "output_msat": 486914000, + "blockheight": 121 + }, + { + "created_index": 12, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 489809898, + "timestamp": 1738520000, + "primary_tag": "channel_close", + "extra_tags": [], + "utxo": "94418b652c9a0d79129552d317dcc37cb55afda1387257a22c7f16aa3981b7bc:0", + "spending_txid": "txid010101010101010101010101010101010101010101010101010101010101", + "output_msat": 1000000000, + "output_count": 2, + "blockheight": 121 + }, + { + "created_index": 13, + "account_id": "external", + "credit_msat": 510190000, + "debit_msat": 0, + "timestamp": 1738520000, + "primary_tag": "to_them", + "extra_tags": [], + "utxo": "874b26d4c523a902fdc44b88ec000eb5c3fe8754c9d44190a140561e24e77781:1", + "originating_account": "channelid0230000230000230000230000230000230000230000230000230000", + "output_msat": 510190000, + "blockheight": 121 + }, + { + "created_index": 14, + "account_id": "wallet", + "credit_msat": 2000000000, + "debit_msat": 0, + "timestamp": 1758192808, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "c9c9bec064382b6a6fb2a30d8923949b3c9f732465542b96e9ad1b5eebda4c7d:0", + "output_msat": 2000000000, + "blockheight": 122 + }, + { + "created_index": 15, + "account_id": "wallet", + "credit_msat": 0, + "debit_msat": 197990453000, + "timestamp": 1738500000, + "primary_tag": "withdrawal", + "extra_tags": [], + "utxo": "32de3d1592062670eb8630875a28705cc1988b7f83f8c712bf9d39be2152efec:0", + "spending_txid": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682", + "output_msat": 197990453000, + "blockheight": 123 + }, + { + "created_index": 16, + "account_id": "wallet", + "credit_msat": 196985833000, + "debit_msat": 0, + "timestamp": 1738500000, + "primary_tag": "deposit", + "extra_tags": [], + "utxo": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682:1", + "output_msat": 196985833000, + "blockheight": 123 + }, + { + "created_index": 17, + "account_id": "channelid0230200230200230200230200230200230200230200230200230200", + "credit_msat": 1000000000, + "debit_msat": 0, + "timestamp": 1738500000, + "primary_tag": "channel_open", + "extra_tags": [ + "opener" + ], + "utxo": "0137213d852e76d48f0270a78218d2f562ac0a8974f814cab66376537cfd1682:0", + "peer_id": "nodeid030303030303030303030303030303030303030303030303030303030303", + "output_msat": 1000000000, + "blockheight": 123 + } + ] + } + } ] } diff --git a/doc/schemas/listchannelmoves.json b/doc/schemas/listchannelmoves.json index d98a72b777a8..eba0f45a35db 100644 --- a/doc/schemas/listchannelmoves.json +++ b/doc/schemas/listchannelmoves.json @@ -145,5 +145,241 @@ ], "resources": [ "Main web site: " + ], + "examples": [ + { + "request": { + "id": "example:listchannelmoves#1", + "method": "listchannelmoves", + "params": {} + }, + "response": { + "channelmoves": [ + { + "created_index": 1, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 500000000, + "debit_msat": 0, + "timestamp": 1738520000, + "primary_tag": "invoice", + "payment_hash": "paymenthashdelpay10101010101010101010101010101010101010101010101", + "fees_msat": 0 + }, + { + "created_index": 2, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 500000000, + "timestamp": 1738520000, + "primary_tag": "invoice", + "payment_hash": "8a46ab91013146df39e98ad7c89505fbb5419f110e928f7a393e8304f3057df7", + "part_id": 0, + "group_id": 1, + "fees_msat": 0 + }, + { + "created_index": 3, + "account_id": "f8fc83a432cbfb2fffe222cc06727fdd977b5dd10ebd6707158e799e6f522d9f", + "credit_msat": 0, + "debit_msat": 500000000, + "timestamp": 1758192801, + "primary_tag": "invoice", + "payment_hash": "88969daaf02214a1928e6eb62a237a8ee557f3da93b2c44f3901432c88f4334b", + "part_id": 0, + "group_id": 1, + "fees_msat": 0 + }, + { + "created_index": 4, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10000, + "timestamp": 1758192801, + "primary_tag": "routed", + "payment_hash": "paymenthashinvl0310031003100310031003100310031003100310031003100", + "fees_msat": 1 + }, + { + "created_index": 5, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10001, + "debit_msat": 0, + "timestamp": 1758192801, + "primary_tag": "routed", + "payment_hash": "paymenthashinvl0310031003100310031003100310031003100310031003100", + "fees_msat": 1 + }, + { + "created_index": 6, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10000, + "timestamp": 1758192801, + "primary_tag": "routed", + "payment_hash": "paymenthashkey01k101k101k101k101k101k101k101k101k101k101k101k101", + "fees_msat": 1 + }, + { + "created_index": 7, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10001, + "debit_msat": 0, + "timestamp": 1758192801, + "primary_tag": "routed", + "payment_hash": "paymenthashkey01k101k101k101k101k101k101k101k101k101k101k101k101", + "fees_msat": 1 + }, + { + "created_index": 8, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10000101, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey02k201k201k201k201k201k201k201k201k201k201k201k201", + "fees_msat": 101 + }, + { + "created_index": 9, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10000202, + "debit_msat": 0, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey02k201k201k201k201k201k201k201k201k201k201k201k201", + "fees_msat": 101 + }, + { + "created_index": 10, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10000, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey03k301k301k301k301k301k301k301k301k301k301k301k301", + "fees_msat": 1 + }, + { + "created_index": 11, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10001, + "debit_msat": 0, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey03k301k301k301k301k301k301k301k301k301k301k301k301", + "fees_msat": 1 + }, + { + "created_index": 12, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 50000, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashinvl0320032003200320032003200320032003200320032003200", + "fees_msat": 1 + }, + { + "created_index": 13, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 50001, + "debit_msat": 0, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashinvl0320032003200320032003200320032003200320032003200", + "fees_msat": 1 + }, + { + "created_index": 14, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 100000, + "timestamp": 1758192803, + "primary_tag": "invoice", + "payment_hash": "paymenthashinvl0330033003300330033003300330033003300330033003300", + "part_id": 0, + "group_id": 1, + "fees_msat": 0 + }, + { + "created_index": 15, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10001, + "timestamp": 1758192803, + "primary_tag": "routed", + "payment_hash": "61b929204f4db4f38e0412b2bd4c3c03668dad3575fb05f4e15a2214046c2bff", + "fees_msat": 1 + }, + { + "created_index": 16, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10002, + "debit_msat": 0, + "timestamp": 1758192803, + "primary_tag": "routed", + "payment_hash": "61b929204f4db4f38e0412b2bd4c3c03668dad3575fb05f4e15a2214046c2bff", + "fees_msat": 1 + }, + { + "created_index": 17, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 0, + "debit_msat": 1000000, + "timestamp": 1758192821, + "primary_tag": "invoice", + "payment_hash": "paymenthashsdinvsi10si10si10si10si10si10si10si10si10si10si10si10", + "part_id": 0, + "group_id": 1, + "fees_msat": 0 + }, + { + "created_index": 18, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 1000, + "debit_msat": 0, + "timestamp": 1758192821, + "primary_tag": "invoice", + "payment_hash": "paymenthashinvl0270027002700270027002700270027002700270027002700", + "fees_msat": 0 + } + ] + } + }, + { + "request": { + "id": "example:listchannelmoves#2", + "method": "listchannelmoves", + "params": { + "index": "created", + "start": 10, + "limit": 2 + } + }, + "response": { + "channelmoves": [ + { + "created_index": 10, + "account_id": "a397dd9b3e44afcb67f3f3ce1d649b74a8ade63e35505985e4cc1828634f69a2", + "credit_msat": 0, + "debit_msat": 10000, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey03k301k301k301k301k301k301k301k301k301k301k301k301", + "fees_msat": 1 + }, + { + "created_index": 11, + "account_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", + "credit_msat": 10001, + "debit_msat": 0, + "timestamp": 1758192802, + "primary_tag": "routed", + "payment_hash": "paymenthashkey03k301k301k301k301k301k301k301k301k301k301k301k301", + "fees_msat": 1 + } + ] + } + } ] } diff --git a/tests/autogenerate-rpc-examples.py b/tests/autogenerate-rpc-examples.py index bfd564288c4f..1036adbd7a55 100644 --- a/tests/autogenerate-rpc-examples.py +++ b/tests/autogenerate-rpc-examples.py @@ -972,6 +972,19 @@ def generate_bookkeeper_examples(l2, l3, c23_2_chan_id): raise +def generate_coinmvt_examples(l2): + """Generates listchannelmoves and listchainmoves rpc examples""" + try: + logger.info('listcoinmoves Start...') + update_example(node=l2, method='listchainmoves', params={}) + update_example(node=l2, method='listchainmoves', params={'index': 'created', 'start': 10}) + update_example(node=l2, method='listchannelmoves', params={}) + update_example(node=l2, method='listchannelmoves', params={'index': 'created', 'start': 10, 'limit': 2}) + except Exception as e: + logger.error(f'Error in generating coinmoves examples: {e}') + raise + + def generate_offers_renepay_examples(l1, l2, inv_l21, inv_l34): """Covers all offers and renepay related examples""" try: @@ -2096,6 +2109,7 @@ def list_missing_examples(): c23_2, c23res2, c34_2, inv_l11, inv_l21, inv_l22, inv_l31, inv_l32, inv_l34 = generate_transactions_examples(l1, l2, l3, l4, l5, c25, bitcoind) rune_l21 = generate_runes_examples(l1, l2, l3) generate_datastore_examples(l2) + generate_coinmvt_examples(l2) generate_bookkeeper_examples(l2, l3, c23res2['channel_id']) offer_l23, inv_req_l1_l22 = generate_offers_renepay_examples(l1, l2, inv_l21, inv_l34) generate_askrene_examples(l1, l2, l3, c12, c23_2) From 7b03963e4ac3a34b0a8d8cf73e74a9f864d51565 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:40:18 +0930 Subject: [PATCH 11/13] lightningd: remove unused `start_time` field in bitcoind_getfilteredblock. Signed-off-by: Rusty Russell --- lightningd/bitcoind.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index 771ccf2f9f0a..879e2d87fe7c 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -703,7 +703,6 @@ struct filteredblock_call { struct filteredblock *result; struct filteredblock_outpoint **outpoints; size_t current_outpoint; - struct timeabs start_time; u32 height; }; @@ -858,7 +857,6 @@ void bitcoind_getfilteredblock_(const tal_t *ctx, call->arg = arg; call->height = height; assert(call->cb != NULL); - call->start_time = time_now(); call->result = NULL; call->current_outpoint = 0; From 9fe47b1933463cc9e8cff9499aec2385f1bb6e6f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:40:18 +0930 Subject: [PATCH 12/13] tests: use timemono not time_now() for duration measurement. Signed-off-by: Rusty Russell --- bitcoin/test/run-secret_eq_consttime.c | 16 ++++++++-------- onchaind/test/run-grind_feerate.c | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bitcoin/test/run-secret_eq_consttime.c b/bitcoin/test/run-secret_eq_consttime.c index 4f21ee191f11..bfba7ceb5029 100644 --- a/bitcoin/test/run-secret_eq_consttime.c +++ b/bitcoin/test/run-secret_eq_consttime.c @@ -22,7 +22,7 @@ static struct timerel const_time_test(struct secret *s1, struct secret *s2, size_t off) { - struct timeabs start, end; + struct timemono start, end; int result = 0; memset(s1, 0, RUNS * sizeof(*s1)); @@ -31,16 +31,16 @@ static struct timerel const_time_test(struct secret *s1, for (size_t i = 0; i < RUNS; i++) s2[i].data[off] = i; - start = time_now(); + start = time_mono(); for (size_t i = 0; i < RUNS; i++) result += secret_eq_consttime(&s1[i], &s2[i]); - end = time_now(); + end = time_mono(); if (result != RUNS / 256) errx(1, "Expected %u successes at offset %zu, not %u!", RUNS / 256, off, result); - return time_between(end, start); + return timemono_between(end, start); } static inline bool secret_eq_nonconst(const struct secret *a, @@ -53,7 +53,7 @@ static struct timerel nonconst_time_test(struct secret *s1, struct secret *s2, size_t off) { - struct timeabs start, end; + struct timemono start, end; int result = 0; memset(s1, 0, RUNS * sizeof(*s1)); @@ -62,16 +62,16 @@ static struct timerel nonconst_time_test(struct secret *s1, for (size_t i = 0; i < RUNS; i++) s2[i].data[off] = i; - start = time_now(); + start = time_mono(); for (size_t i = 0; i < RUNS; i++) result += secret_eq_nonconst(&s1[i], &s2[i]); - end = time_now(); + end = time_mono(); if (result != RUNS / 256) errx(1, "Expected %u successes at offset %zu, not %u!", RUNS / 256, off, result); - return time_between(end, start); + return timemono_between(end, start); } static struct secret *s1, *s2; diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index e413f100d043..c9b1d4dd5368 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -356,7 +356,7 @@ int main(int argc, char *argv[]) struct amount_sat fee; struct pubkey htlc_key; struct keyset *keys; - struct timeabs start, end; + struct timemono start, end; int iterations = 1000; u8 *spk = tal_arr(tmpctx, u8, 1); spk[0] = 0x00; @@ -388,15 +388,15 @@ int main(int argc, char *argv[]) max_possible_feerate = 250000; min_possible_feerate = max_possible_feerate + 1 - iterations; - start = time_now(); + start = time_mono(); if (!grind_htlc_tx_fee(&fee, tx, &sig, wscript, 663)) abort(); - end = time_now(); + end = time_mono(); assert(amount_sat_eq(fee, AMOUNT_SAT(165750))); printf("%u iterations in %"PRIu64" msec = %"PRIu64" nsec each\n", iterations, - time_to_msec(time_between(end, start)), - time_to_nsec(time_divide(time_between(end, start), iterations))); + time_to_msec(timemono_between(end, start)), + time_to_nsec(time_divide(timemono_between(end, start), iterations))); common_shutdown(); return 0; From c217e3a7e10b8d16ac7e15148070c0e85597a9a3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Oct 2025 11:40:18 +0930 Subject: [PATCH 13/13] lightningd: fix scb remote_to_self_delay information. This was changing all the time when I tried to make autogenerate-rpc-examples.py reproducible. Turns out it was being corrupted (it does suspicious things with pointers); rather than try to diagnose it, I simply rewrote the code to create it only when we need it. Signed-off-by: Rusty Russell --- lightningd/channel.c | 25 -------------- lightningd/channel.h | 4 --- lightningd/dual_open_control.c | 19 ----------- lightningd/peer_control.c | 37 +++++++++++++++------ lightningd/test/run-invoice-select-inchan.c | 7 ++++ 5 files changed, 34 insertions(+), 58 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index 354249a4c24c..1390a4868713 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -343,7 +343,6 @@ struct channel *new_unsaved_channel(struct peer *peer, channel->openchannel_signed_cmd = NULL; channel->state = DUALOPEND_OPEN_INIT; channel->owner = NULL; - channel->scb = NULL; channel->reestablished = false; memset(&channel->billboard, 0, sizeof(channel->billboard)); channel->billboard.transient = tal_fmt(channel, "%s", @@ -578,30 +577,6 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->billboard.transient = tal_strdup(channel, transient_billboard); channel->channel_info = *channel_info; - /* If it's a unix domain socket connection, we don't save it */ - if (peer->addr.itype == ADDR_INTERNAL_WIREADDR) { - channel->scb = tal(channel, struct modern_scb_chan); - channel->scb->id = dbid; - /* More useful to have last_known_addr, if avail */ - if (peer->last_known_addr) - channel->scb->addr = *peer->last_known_addr; - channel->scb->addr = peer->addr.u.wireaddr.wireaddr; - channel->scb->node_id = peer->id; - channel->scb->funding = *funding; - channel->scb->cid = *cid; - channel->scb->funding_sats = funding_sats; - channel->scb->type = channel_type_dup(channel->scb, type); - - struct tlv_scb_tlvs *scb_tlvs = tlv_scb_tlvs_new(channel); - scb_tlvs->shachain = &channel->their_shachain.chain; - scb_tlvs->basepoints = &channel->channel_info.theirbase; - scb_tlvs->opener = &channel->opener; - scb_tlvs->remote_to_self_delay = &channel->channel_info.their_config.to_self_delay; - - channel->scb->tlvs = scb_tlvs; - } else - channel->scb = NULL; - if (!log) { channel->log = new_logger(channel, peer->ld->log_book, diff --git a/lightningd/channel.h b/lightningd/channel.h index af2f18a54b8a..fd8e031b0c39 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -343,10 +343,6 @@ struct channel { /* Lease commited max part per thousandth channel fee (ppm * 1000) */ u16 lease_chan_max_ppt; - /* `Channel-shell` of this channel - * (Minimum information required to backup this channel). */ - struct modern_scb_chan *scb; - /* Do we allow the peer to set any fee it wants? */ bool ignore_fee_limits; diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 1e46c06a991c..ab84e3f498bd 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1467,28 +1467,9 @@ wallet_commit_channel(struct lightningd *ld, &commitment_feerate); channel->min_possible_feerate = commitment_feerate; channel->max_possible_feerate = commitment_feerate; - if (channel->peer->addr.itype == ADDR_INTERNAL_WIREADDR) { - channel->scb = tal(channel, struct modern_scb_chan); - channel->scb->id = channel->dbid; - channel->scb->addr = channel->peer->addr.u.wireaddr.wireaddr; - channel->scb->node_id = channel->peer->id; - channel->scb->funding = *funding; - channel->scb->cid = channel->cid; - channel->scb->funding_sats = total_funding; - - struct tlv_scb_tlvs *scb_tlvs = tlv_scb_tlvs_new(channel); - scb_tlvs->shachain = &channel->their_shachain.chain; - scb_tlvs->basepoints = &channel_info->theirbase; - scb_tlvs->opener = &channel->opener; - scb_tlvs->remote_to_self_delay = &channel_info->their_config.to_self_delay; - - channel->scb->tlvs = scb_tlvs; - } else - channel->scb = NULL; tal_free(channel->type); channel->type = channel_type_dup(channel, type); - channel->scb->type = channel_type_dup(channel->scb, type); if (our_upfront_shutdown_script) channel->shutdown_scriptpubkey[LOCAL] diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 2f11dcb80794..4cc212eff2d7 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2488,15 +2488,35 @@ static void json_add_scb(struct command *cmd, struct json_stream *response, struct channel *c) { - u8 *scb = tal_arr(cmd, u8, 0); + u8 *scb_wire = tal_arr(cmd, u8, 0); + struct modern_scb_chan *scb; - /* Update shachain & basepoints in SCB. */ - c->scb->tlvs->shachain = &c->their_shachain.chain; - c->scb->tlvs->basepoints = &c->channel_info.theirbase; - towire_modern_scb_chan(&scb, c->scb); + /* Don't do scb for unix domain sockets. */ + if (c->peer->addr.itype != ADDR_INTERNAL_WIREADDR) + return; + + scb = tal(tmpctx, struct modern_scb_chan); + scb->id = c->dbid; + /* More useful to have last_known_addr, if avail */ + if (c->peer->last_known_addr) + scb->addr = *c->peer->last_known_addr; + else + scb->addr = c->peer->addr.u.wireaddr.wireaddr; + scb->node_id = c->peer->id; + scb->funding = c->funding; + scb->cid = c->cid; + scb->funding_sats = c->funding_sats; + scb->type = channel_type_dup(scb, c->type); - json_add_hex_talarr(response, fieldname, - scb); + scb->tlvs = tlv_scb_tlvs_new(scb); + scb->tlvs->shachain = &c->their_shachain.chain; + scb->tlvs->basepoints = &c->channel_info.theirbase; + scb->tlvs->opener = &c->opener; + scb->tlvs->remote_to_self_delay = &c->channel_info.their_config.to_self_delay; + + towire_modern_scb_chan(&scb_wire, scb); + + json_add_hex_talarr(response, fieldname, scb_wire); } /* This will return a SCB for all the channels currently loaded @@ -2521,9 +2541,6 @@ static struct command_result *json_staticbackup(struct command *cmd, peer = peer_node_id_map_next(cmd->ld->peers, &it)) { struct channel *channel; list_for_each(&peer->channels, channel, list){ - /* cppcheck-suppress uninitvar - false positive on channel */ - if (!channel->scb) - continue; json_add_scb(cmd, NULL, response, channel); } } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 1c234c5dae90..1c885a1b2c84 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -144,6 +144,10 @@ const char *channel_state_name(const struct channel *channel UNNEEDED) /* Generated stub for channel_state_str */ const char *channel_state_str(enum channel_state state UNNEEDED) { fprintf(stderr, "channel_state_str called!\n"); abort(); } +/* Generated stub for channel_type_dup */ +struct channel_type *channel_type_dup(const tal_t *ctx UNNEEDED, + const struct channel_type *t UNNEEDED) +{ fprintf(stderr, "channel_type_dup called!\n"); abort(); } /* Generated stub for channel_type_has */ bool channel_type_has(const struct channel_type *type UNNEEDED, int feature UNNEEDED) { fprintf(stderr, "channel_type_has called!\n"); abort(); } @@ -979,6 +983,9 @@ void subd_send_fd(struct subd *sd UNNEEDED, int fd UNNEEDED) /* Generated stub for subd_send_msg */ void subd_send_msg(struct subd *sd UNNEEDED, const u8 *msg_out UNNEEDED) { fprintf(stderr, "subd_send_msg called!\n"); abort(); } +/* Generated stub for tlv_scb_tlvs_new */ +struct tlv_scb_tlvs *tlv_scb_tlvs_new(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "tlv_scb_tlvs_new called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); }