Skip to content

Commit bb8783b

Browse files
committed
lightningd: fix db constraint error when fixing up old blocks.
It's very hard to reproduce, since we only consider UTXOs we are watching, but scanning from the first block we know about is wrong, because we don't care about blocks which we only put in the db in response to old gossip queries. I'm not sure how Sjors got into the state where they see their own UTXO spend in a block they don't have in the database, but we shouldn't crash: ``` 2025-12-07T11:25:18.163Z **BROKEN** lightningd: Error executing statement: wallet/wallet.c:4913: UPDATE outputs SET spend_height = ?, status = ? WHERE prev_out_tx = ? AND prev_out_index = ?: FOREIGN KEY constraint failed 2025-12-07T11:25:18.163Z **BROKEN** lightningd: Error executing statement: wallet/wallet.c:4913: UPDATE outputs SET spend_height = ?, status = ? WHERE prev_out_tx = ? AND prev_out_index = ?: FOREIGN KEY constraint failed 2025-12-07T11:25:18.179Z **BROKEN** lightningd: FATAL SIGNAL 6 (version v25.12) 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: common/daemon.c:46 (send_backtrace) 0x5e263feb9292 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: common/daemon.c:83 (crashdump) 0x5e263feb92e1 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0x70ee6f64532f 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0x70ee6f69eb2c 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0x70ee6f64527d 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0x70ee6f6288fe 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: lightningd/log.c:1054 (fatal_vfmt) 0x5e263fe38e10 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: wallet/db.c:95 (db_error) 0x5e263fe6f7ef 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: db/utils.c:326 (db_fatal) 0x5e263feaa797 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: db/utils.c:200 (db_exec_prepared_v2) 0x5e263feaab85 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: wallet/wallet.c:4924 (wallet_outpoint_spend) 0x5e263fe7de2f 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: lightningd/chaintopology.c:929 (topo_update_spends) 0x5e263fe0af81 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: lightningd/chaintopology.c:1532 (fixup_scan_block) 0x5e263fe0c268 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: lightningd/bitcoind.c:503 (getrawblockbyheight_callback) 0x5e263fe09280 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: lightningd/plugin.c:701 (plugin_response_handle) 0x5e263fe5bf2b 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: lightningd/plugin.c:790 (plugin_read_json) 0x5e263fe610e7 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:60 (next_plan) 0x5e263fef3629 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:422 (do_plan) 0x5e263fef3afa 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:439 (io_ready) 0x5e263fef3bb7 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: ccan/ccan/io/poll.c:470 (io_loop) 0x5e263fef55c5 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: lightningd/io_loop_with_timers.c:22 (io_loop_with_timers) 0x5e263fe308e9 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: lightningd/lightningd.c:1492 (main) 0x5e263fe363ce 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0x70ee6f62a1c9 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0x70ee6f62a28a 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0x5e263fe06bf4 2025-12-07T11:25:18.180Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0xffffffffffffffff ``` Changelog-Fixes: lightningd: crash on fixup scan with old blocks. Signed-off-by: Rusty Russell <[email protected]>
1 parent ed43977 commit bb8783b

File tree

3 files changed

+15
-8
lines changed

3 files changed

+15
-8
lines changed

lightningd/chaintopology.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1420,7 +1420,7 @@ void setup_topology(struct chain_topology *topo)
14201420
fixup = db_get_intvar(topo->ld->wallet->db, "fixup_block_scan", -1);
14211421
if (fixup == -1) {
14221422
/* Never done fixup: this is set to non-zero if we have blocks. */
1423-
topo->old_block_scan = wallet_blocks_minheight(topo->ld->wallet);
1423+
topo->old_block_scan = wallet_blocks_contig_minheight(topo->ld->wallet);
14241424
db_set_intvar(topo->ld->wallet->db, "fixup_block_scan",
14251425
topo->old_block_scan);
14261426
} else {

wallet/wallet.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2554,16 +2554,22 @@ u32 wallet_blocks_maxheight(struct wallet *w)
25542554
return max;
25552555
}
25562556

2557-
u32 wallet_blocks_minheight(struct wallet *w)
2557+
u32 wallet_blocks_contig_minheight(struct wallet *w)
25582558
{
25592559
u32 min = 0;
2560-
struct db_stmt *stmt = db_prepare_v2(w->db, SQL("SELECT MIN(height) FROM blocks;"));
2560+
struct db_stmt *stmt = db_prepare_v2(w->db, SQL("SELECT MAX(b.height)"
2561+
" FROM blocks b"
2562+
" WHERE NOT EXISTS ("
2563+
" SELECT 1"
2564+
" FROM blocks b2"
2565+
" WHERE b2.height = b.height - 1"));
25612566
db_query_prepared(stmt);
25622567

2563-
/* If we ever processed a block we'll get the latest block in the chain */
2568+
/* If we ever processed a block we'll get the first block in
2569+
* the last run of blocks */
25642570
if (db_step(stmt)) {
2565-
if (!db_col_is_null(stmt, "MIN(height)")) {
2566-
min = db_col_int(stmt, "MIN(height)");
2571+
if (!db_col_is_null(stmt, "MAX(b.height)")) {
2572+
min = db_col_int(stmt, "MAX(b.height)");
25672573
}
25682574
}
25692575
tal_free(stmt);

wallet/wallet.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -790,13 +790,14 @@ void wallet_channel_stats_incr_out_fulfilled(struct wallet *w, u64 cdbid, struct
790790
u32 wallet_blocks_maxheight(struct wallet *w);
791791

792792
/**
793-
* Retrieve the blockheight of the first block processed by lightningd.
793+
* Retrieve the blockheight of the first block processed by lightningd (ignoring
794+
* backfilled blocks for gossip).
794795
*
795796
* Will return the 0 if the wallet was never used before.
796797
*
797798
* @w: wallet to load from.
798799
*/
799-
u32 wallet_blocks_minheight(struct wallet *w);
800+
u32 wallet_blocks_contig_minheight(struct wallet *w);
800801

801802
/**
802803
* wallet_extract_owned_outputs - given a tx, extract all of our outputs

0 commit comments

Comments
 (0)