From 8c445475fca9e8da10f7feb1ae87dc8194ff12b8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 9 Feb 2026 11:40:35 +1030 Subject: [PATCH 1/2] pytest: test for bkpr_listbalances after emergencyrecover. Signed-off-by: Rusty Russell --- tests/test_misc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_misc.py b/tests/test_misc.py index 5baebbcdce42..c2e4f0ec72ce 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -3136,6 +3136,7 @@ def test_emergencyrecoverpenaltytxn(node_factory, bitcoind): @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "deletes database, which is assumed sqlite3") +@pytest.mark.xfail(strict=True) def test_emergencyrecover(node_factory, bitcoind): """ Test emergencyrecover @@ -3177,6 +3178,9 @@ def test_emergencyrecover(node_factory, bitcoind): wait_for(lambda: l1.rpc.listfunds()["channels"][0]["state"] == "ONCHAIN") wait_for(lambda: l2.rpc.listfunds()["channels"][0]["state"] == "ONCHAIN") + # Does bookkeeper get upset? + l1.rpc.bkpr_listbalances() + withdraw = l1.rpc.withdraw(l2.rpc.newaddr('bech32')['bech32'], 'all') # Should have two inputs assert len(bitcoind.rpc.decoderawtransaction(withdraw['tx'])['vin']) == 2 From fe8311c837f7c9df200de365a5a3c15e7940867e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 9 Feb 2026 11:41:25 +1030 Subject: [PATCH 2/2] bkpr: limp along if we lost our db. We can't really do decent bookkeeping any more, but don't crash! ``` bookkeeper: plugins/bkpr/recorder.c:178: find_txo_chain: Assertion `acct->open_event_db_id' failed. bookkeeper: FATAL SIGNAL 6 (version v25.12) 0xaaaab7d51a7f send_backtrace common/daemon.c:38 0xaaaab7d51b2b crashdump common/daemon.c:83 0xffff8c0b07cf ??? ???:0 0xffff8bdf7608 __pthread_kill_implementation ./nptl/pthread_kill.c:44 0xffff8bdacb3b __GI_raise ../sysdeps/posix/raise.c:26 0xffff8bd97dff __GI_abort ./stdlib/abort.c:79 0xffff8bda5cbf __assert_fail_base ./assert/assert.c:96 0xffff8bda5d2f __assert_fail ./assert/assert.c:105 0xaaaab7d41fd7 find_txo_chain plugins/bkpr/recorder.c:178 0xaaaab7d421fb account_onchain_closeheight plugins/bkpr/recorder.c:291 0xaaaab7d37687 do_account_close_checks plugins/bkpr/bookkeeper.c:884 0xaaaab7d38203 parse_and_log_chain_move plugins/bkpr/bookkeeper.c:1261 0xaaaab7d3871f listchainmoves_done plugins/bkpr/bookkeeper.c:171 0xaaaab7d4811f handle_rpc_reply plugins/libplugin.c:1073 0xaaaab7d4827b rpc_conn_read_response plugins/libplugin.c:1377 0xaaaab7d889a7 next_plan ccan/ccan/io/io.c:60 0xaaaab7d88f7b do_plan ccan/ccan/io/io.c:422 0xaaaab7d89053 io_ready ccan/ccan/io/io.c:439 ``` Fixes: https://github.com/ElementsProject/lightning/issues/8854 Changelog-Fixed: Plugins: `bkpr_listbalances` no longer crashes if we lost our db, then do emergencyrecover and close a channel. Reported-by: https://github.com/enaples --- plugins/bkpr/recorder.c | 9 ++++++++- tests/test_misc.py | 3 +-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 82c3498c4a70..4b5170f61d99 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -175,7 +175,14 @@ bool find_txo_chain(const tal_t *ctx, bool is_complete = true; const char *start_acct_name; - assert(acct->open_event_db_id); + /* If we have lost our database and used recovery, this can be + * NULL. That's the least of our problems though! */ + if (!acct->open_event_db_id) { + plugin_log(cmd->plugin, LOG_BROKEN, + "Cannot find the open_event for %s: did we lose our db?", + acct->name); + return false; + } open_ev = find_chain_event_by_id(ctx, bkpr, cmd, *acct->open_event_db_id); diff --git a/tests/test_misc.py b/tests/test_misc.py index c2e4f0ec72ce..9e5416fd78fa 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -3136,13 +3136,12 @@ def test_emergencyrecoverpenaltytxn(node_factory, bitcoind): @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "deletes database, which is assumed sqlite3") -@pytest.mark.xfail(strict=True) def test_emergencyrecover(node_factory, bitcoind): """ Test emergencyrecover """ l1, l2 = node_factory.get_nodes(2, opts=[{'may_reconnect': True, - 'broken_log': 'ERROR: Unknown commitment #.*, recovering our funds'}, + 'broken_log': 'ERROR: Unknown commitment #.*, recovering our funds|plugin-bookkeeper: Cannot find the open_event for '}, {'may_reconnect': True}]) l1.rpc.connect(l2.info['id'], 'localhost', l2.port)