diff --git a/tests/test_pay.py b/tests/test_pay.py index dc9748e37e28..150c0e6c7c0e 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -7048,3 +7048,27 @@ def test_htlc_tlv_crash(node_factory): l1.rpc.waitsendpay(inv1['payment_hash'], TIMEOUT) l1.rpc.waitsendpay(inv2['payment_hash'], TIMEOUT) + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +def test_bolt11_annotation_after_restart(node_factory): + """Test bolt11 field persists in listpays after restart. Fixes #6978""" + l1, l2 = node_factory.line_graph(2) + + inv = l2.rpc.invoice(100000, 'test_bolt11_persist', 'Test BOLT11 persistence')['bolt11'] + l1.rpc.pay(inv) + + pays_before = l1.rpc.listpays()['pays'] + payment_before = [p for p in pays_before if 'bolt11' in p and p['bolt11'] == inv][0] + assert payment_before['status'] == 'complete' + assert payment_before['bolt11'] == inv + payment_hash = payment_before['payment_hash'] + + l1.restart() + + pays_after = l1.rpc.listpays()['pays'] + payment_after = [p for p in pays_after if 'bolt11' in p and p['bolt11'] == inv][0] + assert payment_after['payment_hash'] == payment_hash + assert payment_after['status'] == 'complete' + assert payment_after['bolt11'] == inv diff --git a/wallet/db.c b/wallet/db.c index 6057952dd9c1..e3b0c0a5efbd 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -35,6 +35,8 @@ static void migrate_our_funding(struct lightningd *ld, struct db *db); static void migrate_last_tx_to_psbt(struct lightningd *ld, struct db *db); +static void migrate_add_payment_id_to_htlcs(struct lightningd *ld, struct db *db); + static void migrate_inflight_last_tx_to_psbt(struct lightningd *ld, struct db *db); @@ -1092,6 +1094,8 @@ static struct migration dbmigrations[] = { ")"), NULL}, /* We do a lookup before each append, to avoid duplicates */ {SQL("CREATE INDEX chain_moves_utxo_idx ON chain_moves (utxo)"), NULL}, + /* Add payment_id column to channel_htlcs */ + {NULL, migrate_add_payment_id_to_htlcs}, {NULL, migrate_from_account_db}, }; @@ -2080,6 +2084,31 @@ static void migrate_initialize_alias_local(struct lightningd *ld, } } +/* Add payment_id column to channel_htlcs */ +static void migrate_add_payment_id_to_htlcs(struct lightningd *ld, struct db *db) +{ + struct db_stmt *stmt; + + stmt = db_prepare_v2(db, SQL("ALTER TABLE channel_htlcs ADD COLUMN payment_id BIGINT")); + db_exec_prepared_v2(stmt); + tal_free(stmt); + + stmt = db_prepare_v2(db, SQL("UPDATE channel_htlcs " + "SET payment_id = (" + " SELECT p.id FROM payments p " + " WHERE p.payment_hash = channel_htlcs.payment_hash " + " AND p.partid = channel_htlcs.partid " + " AND p.groupid = channel_htlcs.groupid " + " LIMIT 1" + ")")); + db_exec_prepared_v2(stmt); + tal_free(stmt); + + stmt = db_prepare_v2(db, SQL("CREATE INDEX channel_htlcs_payment_id_idx ON channel_htlcs (payment_id)")); + db_exec_prepared_v2(stmt); + tal_free(stmt); +} + /* Insert address type as `ADDR_ALL` for issued addresses */ static void insert_addrtype_to_addresses(struct lightningd *ld, struct db *db) diff --git a/wallet/wallet.c b/wallet/wallet.c index ddc933d70ced..39da571b0f7a 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3316,11 +3316,29 @@ void wallet_htlc_save_out(struct wallet *wallet, struct htlc_out *out) { struct db_stmt *stmt; + u64 payment_id = 0; /* We absolutely need the incoming HTLC to be persisted before * we can persist it's dependent */ assert(out->in == NULL || out->in->dbid != 0); + /* Get payment_id if this is an outgoing payment */ + if (out->am_origin) { + struct db_stmt *payment_stmt = db_prepare_v2(wallet->db, + SQL("SELECT id FROM payments " + "WHERE payment_hash = ? AND partid = ? AND groupid = ? " + "LIMIT 1")); + db_bind_sha256(payment_stmt, &out->payment_hash); + db_bind_u64(payment_stmt, out->partid); + db_bind_u64(payment_stmt, out->groupid); + db_query_prepared(payment_stmt); + + if (db_step(payment_stmt)) { + payment_id = db_col_u64(payment_stmt, "id"); + } + tal_free(payment_stmt); + } + stmt = db_prepare_v2( wallet->db, SQL("INSERT INTO channel_htlcs (" @@ -3339,8 +3357,9 @@ void wallet_htlc_save_out(struct wallet *wallet, " partid," " groupid," " fees_msat," - " min_commit_num" - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?);")); + " min_commit_num," + " payment_id" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?, ?);")); out->dbid = htlcs_index_created(wallet->ld, out->key.id, @@ -3384,6 +3403,11 @@ void wallet_htlc_save_out(struct wallet *wallet, db_bind_u64(stmt, min_u64(chan->next_index[LOCAL]-1, chan->next_index[REMOTE]-1)); + if (payment_id > 0) + db_bind_u64(stmt, payment_id); + else + db_bind_null(stmt); + db_exec_prepared_v2(take(stmt)); }