diff --git a/lightningd/invoice.c b/lightningd/invoice.c index e73f40b78..b3ab3913b 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1380,7 +1380,10 @@ static struct command_result *json_delinvoice(struct command *cmd, } details->description = tal_free(details->description); } else { - if (!invoices_delete(wallet->invoices, inv_dbid)) { + if (!invoices_delete(wallet->invoices, inv_dbid, + details->state, + details->label, + details->invstring)) { log_broken(cmd->ld->log, "Error attempting to remove invoice %"PRIu64, inv_dbid); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index b14654d2e..1fe147b4e 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -366,7 +366,11 @@ bool invoices_create(struct invoices *invoices UNNEEDED, const struct sha256 *local_offer_id UNNEEDED) { fprintf(stderr, "invoices_create called!\n"); abort(); } /* Generated stub for invoices_delete */ -bool invoices_delete(struct invoices *invoices UNNEEDED, u64 inv_dbid UNNEEDED) +bool invoices_delete(struct invoices *invoices UNNEEDED, + u64 inv_dbid UNNEEDED, + enum invoice_status status UNNEEDED, + const struct json_escape *label UNNEEDED, + const char *invstring UNNEEDED) { fprintf(stderr, "invoices_delete called!\n"); abort(); } /* Generated stub for invoices_delete_description */ bool invoices_delete_description(struct invoices *invoices UNNEEDED, diff --git a/tests/test_invoices.py b/tests/test_invoices.py index f8f2425ad..16c724d3f 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -708,7 +708,8 @@ def test_listinvoices_filter(node_factory): def test_wait_invoices(node_factory, executor): - l1, l2 = node_factory.line_graph(2) + # We use delexpiredinvoice + l1, l2 = node_factory.line_graph(2, opts={'allow-deprecated-apis': True}) # Asking for 0 gives us current index. waitres = l2.rpc.call('wait', {'subsystem': 'invoices', 'indexname': 'created', 'nextvalue': 0}) @@ -753,7 +754,7 @@ def test_wait_invoices(node_factory, executor): 'updated': 1} # Now check expiry works. - l2.rpc.invoice(42, 'invlabel2', 'invdesc2', expiry=2) + inv2 = l2.rpc.invoice(42, 'invlabel2', 'invdesc2', expiry=2) waitres = l2.rpc.call('wait', {'subsystem': 'invoices', 'indexname': 'updated', 'nextvalue': 2}) assert waitres == {'subsystem': 'invoices', @@ -762,9 +763,40 @@ def test_wait_invoices(node_factory, executor): # {'label': 'invlabel2', 'bolt11': inv2['bolt11'], 'status': 'expired'} 'details': {'status': 'expired'}} - # Deleting correctly produces 3, not another 2! - l2.rpc.delinvoice('invlabel2', 'expired') + # Now for deletions + waitres = l2.rpc.call('wait', {'subsystem': 'invoices', 'indexname': 'deleted', 'nextvalue': 0}) + assert waitres == {'subsystem': 'invoices', + 'deleted': 0} + waitfut = executor.submit(l2.rpc.call, 'wait', {'subsystem': 'invoices', 'indexname': 'deleted', 'nextvalue': 1}) + time.sleep(1) + l2.rpc.delinvoice('invlabel', 'paid') + waitres = waitfut.result(TIMEOUT) + + assert waitres == {'subsystem': 'invoices', + 'deleted': 1, + 'details': {'label': 'invlabel', + 'bolt11': inv['bolt11'], + 'status': 'paid'}} + + # Second returns instantly, without any details. + waitres = l2.rpc.call('wait', {'subsystem': 'invoices', 'indexname': 'deleted', 'nextvalue': 1}) + assert waitres == {'subsystem': 'invoices', + 'deleted': 1} + + # Now check delexpiredinvoice works. + waitfut = executor.submit(l2.rpc.call, 'wait', {'subsystem': 'invoices', 'indexname': 'deleted', 'nextvalue': 2}) + time.sleep(1) + l2.rpc.delexpiredinvoice() + waitres = waitfut.result(TIMEOUT) + + assert waitres == {'subsystem': 'invoices', + 'deleted': 2, + 'details': {'label': 'invlabel2', + 'bolt11': inv2['bolt11'], + 'status': 'expired'}} + + # Creating a new on gives us 3, not another 2! waitfut = executor.submit(l2.rpc.call, 'wait', {'subsystem': 'invoices', 'indexname': 'created', 'nextvalue': 3}) time.sleep(1) inv = l2.rpc.invoice(42, 'invlabel2', 'invdesc2') diff --git a/wallet/invoices.c b/wallet/invoices.c index e0b455915..3dca73897 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -396,7 +396,10 @@ bool invoices_find_unpaid(struct invoices *invoices, } } -bool invoices_delete(struct invoices *invoices, u64 inv_dbid) +bool invoices_delete(struct invoices *invoices, u64 inv_dbid, + enum invoice_status status, + const struct json_escape *label, + const char *invstring) { struct db_stmt *stmt; int changes; @@ -413,6 +416,7 @@ bool invoices_delete(struct invoices *invoices, u64 inv_dbid) return false; } /* Tell all the waiters about the fact that it was deleted. */ + invoice_index_deleted(invoices->wallet->ld, status, label, invstring); trigger_invoice_waiter_expire_or_delete(invoices, inv_dbid, true); return true; } @@ -437,14 +441,25 @@ bool invoices_delete_description(struct invoices *invoices, u64 inv_dbid) void invoices_delete_expired(struct invoices *invoices, u64 max_expiry_time) { - struct db_stmt *stmt; - stmt = db_prepare_v2(invoices->wallet->db, SQL( - "DELETE FROM invoices" - " WHERE state = ?" - " AND expiry_time <= ?;")); - db_bind_int(stmt, EXPIRED); - db_bind_u64(stmt, max_expiry_time); - db_exec_prepared_v2(take(stmt)); + u64 *ids = expired_ids(tmpctx, invoices->wallet->db, max_expiry_time, + EXPIRED); + + for (size_t i = 0; i < tal_count(ids); i++) { + struct db_stmt *stmt; + const struct invoice_details *details; + + details = invoices_get_details(tmpctx, invoices, ids[i]); + stmt = db_prepare_v2(invoices->wallet->db, SQL( + "DELETE FROM invoices" + " WHERE id = ?;")); + db_bind_u64(stmt, ids[i]); + db_exec_prepared_v2(take(stmt)); + + invoice_index_deleted(invoices->wallet->ld, + details->state, + details->label, + details->invstring); + } } struct db_stmt *invoices_first(struct invoices *invoices, diff --git a/wallet/invoices.h b/wallet/invoices.h index 1425c8e1c..e0cbccd28 100644 --- a/wallet/invoices.h +++ b/wallet/invoices.h @@ -106,7 +106,11 @@ bool invoices_find_unpaid(struct invoices *invoices, * * Return false on failure. */ -bool invoices_delete(struct invoices *invoices, u64 inv_dbid); +bool invoices_delete(struct invoices *invoices, + u64 inv_dbid, + enum invoice_status status, + const struct json_escape *label, + const char *invstring); /** * invoices_delete_description - Remove description from an invoice