From 54ef84e8915f2618cc98fe003ef09825606d64e3 Mon Sep 17 00:00:00 2001 From: Chris Guida Date: Thu, 26 Oct 2023 14:02:13 +1030 Subject: [PATCH] display paid_outpoint on invoices when present Changelog-Added: JSON-RPC: `listinvoices` new field `paid_outpoint` if an invoice is paid onchain. --- lightningd/invoice.c | 8 +++++- lightningd/invoice.h | 2 ++ lightningd/pay.c | 2 +- lightningd/test/run-invoice-select-inchan.c | 3 ++- wallet/invoices.c | 28 ++++++++++++++++++++- wallet/invoices.h | 4 ++- 6 files changed, 42 insertions(+), 5 deletions(-) diff --git a/lightningd/invoice.c b/lightningd/invoice.c index b86d243df..aaa3cb25a 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -60,6 +60,12 @@ static void json_add_invoice_fields(struct json_stream *response, json_add_amount_msat(response, "amount_received_msat", inv->received); json_add_u64(response, "paid_at", inv->paid_timestamp); + if (inv->paid_outpoint) { + json_object_start(response, "paid_outpoint"); + json_add_txid(response, "txid", &inv->paid_outpoint->txid); + json_add_num(response, "outnum", inv->paid_outpoint->n); + json_object_end(response); + } json_add_preimage(response, "payment_preimage", &inv->r); } if (inv->description) @@ -322,7 +328,7 @@ invoice_payment_hooks_done(struct invoice_payment_hook_payload *payload STEALS) /* Paid or expired in the meantime. */ if (!invoices_resolve(ld->wallet->invoices, inv_dbid, payload->msat, - payload->label)) { + payload->label, payload->outpoint)) { if (payload->set) htlc_set_fail(payload->set, take(failmsg_incorrect_or_unknown( NULL, ld, payload->set->htlcs[0]))); diff --git a/lightningd/invoice.h b/lightningd/invoice.h index 024538f62..c231f0e31 100644 --- a/lightningd/invoice.h +++ b/lightningd/invoice.h @@ -30,6 +30,8 @@ struct invoice_details { struct amount_msat received; /* Set if state == PAID; time paid */ u64 paid_timestamp; + /* Set if state == PAID and invoice paid on chain; outpoint containing the payment */ + const struct bitcoin_outpoint *paid_outpoint; /* BOLT11 or BOLT12 encoding for this invoice */ const char *invstring; diff --git a/lightningd/pay.c b/lightningd/pay.c index c29ba26f7..3bba874d4 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1518,7 +1518,7 @@ static struct command_result *self_payment(struct lightningd *ld, /* These should not fail, given the above succeded! */ if (!invoices_find_by_rhash(ld->wallet->invoices, &inv_dbid, rhash) - || !invoices_resolve(ld->wallet->invoices, inv_dbid, msat, NULL)) { + || !invoices_resolve(ld->wallet->invoices, inv_dbid, msat, inv->label, NULL)) { log_broken(ld->log, "Could not resolve invoice %"PRIu64"!?!", inv_dbid); return sendpay_fail(cmd, payment, PAY_DESTINATION_PERM_FAIL, NULL, NULL, "broken"); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 781ad7f91..5137beb54 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -449,7 +449,8 @@ struct db_stmt *invoices_next(struct invoices *invoices UNNEEDED, bool invoices_resolve(struct invoices *invoices UNNEEDED, u64 inv_dbid UNNEEDED, struct amount_msat received UNNEEDED, - const struct json_escape *label UNNEEDED) + const struct json_escape *label UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED) { fprintf(stderr, "invoices_resolve called!\n"); abort(); } /* Generated stub for invoices_waitany */ void invoices_waitany(const tal_t *ctx UNNEEDED, diff --git a/wallet/invoices.c b/wallet/invoices.c index fc5553147..892be3f37 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -81,6 +81,7 @@ static struct invoice_details *wallet_stmt2invoice_details(const tal_t *ctx, struct db_stmt *stmt) { struct invoice_details *dtl = tal(ctx, struct invoice_details); + struct bitcoin_outpoint *paid_outpoint; dtl->state = db_col_int(stmt, "state"); db_col_preimage(stmt, "payment_key", &dtl->r); @@ -101,10 +102,23 @@ static struct invoice_details *wallet_stmt2invoice_details(const tal_t *ctx, dtl->pay_index = db_col_u64(stmt, "pay_index"); dtl->received = db_col_amount_msat(stmt, "msatoshi_received"); dtl->paid_timestamp = db_col_u64(stmt, "paid_timestamp"); + if (!db_col_is_null(stmt, "paid_txid")) { + paid_outpoint = tal(ctx, struct bitcoin_outpoint); + db_col_txid(stmt, "paid_txid", + &paid_outpoint->txid); + paid_outpoint->n + = db_col_int(stmt, "paid_outnum"); + dtl->paid_outpoint = paid_outpoint; + } else { + db_col_ignore(stmt, "paid_outnum"); + dtl->paid_outpoint = NULL; + } } else { db_col_ignore(stmt, "pay_index"); db_col_ignore(stmt, "msatoshi_received"); db_col_ignore(stmt, "paid_timestamp"); + db_col_ignore(stmt, "paid_txid"); + db_col_ignore(stmt, "paid_outnum"); } dtl->invstring = db_col_strdup(dtl, stmt, "bolt11"); @@ -606,7 +620,8 @@ static void maybe_mark_offer_used(struct db *db, u64 inv_dbid) bool invoices_resolve(struct invoices *invoices, u64 inv_dbid, struct amount_msat received, - const struct json_escape *label) + const struct json_escape *label, + const struct bitcoin_outpoint *outpoint) { struct db_stmt *stmt; s64 pay_index; @@ -626,12 +641,21 @@ bool invoices_resolve(struct invoices *invoices, " , pay_index=?" " , msatoshi_received=?" " , paid_timestamp=?" + " , paid_txid=?" + " , paid_outnum=?" " , updated_index=?" " WHERE id=?;")); db_bind_int(stmt, PAID); db_bind_u64(stmt, pay_index); db_bind_amount_msat(stmt, &received); db_bind_u64(stmt, paid_timestamp); + if (outpoint) { + db_bind_txid(stmt, &outpoint->txid); + db_bind_int(stmt, outpoint->n); + } else { + db_bind_null(stmt); + db_bind_null(stmt); + } db_bind_u64(stmt, invoice_index_update_status(invoices->wallet->ld, label, PAID)); @@ -742,6 +766,8 @@ struct invoice_details *invoices_get_details(const tal_t *ctx, ", pay_index" ", msatoshi_received" ", paid_timestamp" + ", paid_txid" + ", paid_outnum" ", bolt11" ", description" ", features" diff --git a/wallet/invoices.h b/wallet/invoices.h index a60af45d1..59f882ceb 100644 --- a/wallet/invoices.h +++ b/wallet/invoices.h @@ -204,13 +204,15 @@ struct db_stmt *invoices_next(struct invoices *invoices, * @inv_dbid - the invoice to mark as paid. * @received - the actual amount received. * @label - the label of the invoice. + * @outpoint - the outpoint (if onchain). * * If the invoice is not UNPAID, returns false. */ bool invoices_resolve(struct invoices *invoices, u64 inv_dbid, struct amount_msat received, - const struct json_escape *label); + const struct json_escape *label, + const struct bitcoin_outpoint *outpoint); /** * invoices_waitany - Wait for any invoice to be paid.