From dc6a5f81de3e017bd45bc00fd72349c6ad5f8f07 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 16 Dec 2024 11:09:52 +1030 Subject: [PATCH] lightningd: populate listsendpays destination from injectpaymentonion if we can. If they give us the invstring, we can at least set who signed the invoice. Of course, it might not be a real node_id (with blinded paths). Signed-off-by: Rusty Russell --- lightningd/pay.c | 34 ++++++++++++++++++++++++++++++---- tests/test_pay.py | 20 ++++++++++++++++---- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/lightningd/pay.c b/lightningd/pay.c index 45c3a6bf4847..b633803a2c46 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1788,7 +1788,8 @@ static void register_payment_and_waiter(struct command *cmd, const char *label, const char *invstring, struct sha256 *local_invreq_id, - const struct secret *shared_secret) + const struct secret *shared_secret, + const struct node_id *destination TAKES) { wallet_add_payment(cmd, cmd->ld->wallet, @@ -1798,7 +1799,7 @@ static void register_payment_and_waiter(struct command *cmd, partid, groupid, PAYMENT_PENDING, - NULL, + destination, destination_msat, msat_sent, total_msat, @@ -1845,6 +1846,7 @@ static struct command_result *json_injectpaymentonion(struct command *cmd, struct htlc_out *hout; const struct wallet_payment *prev_payment; const char *explanation; + struct node_id *destination; if (!param_check(cmd, buffer, params, p_req("onion", param_bin_from_hex, &onion), @@ -1926,6 +1928,28 @@ static struct command_result *json_injectpaymentonion(struct command *cmd, failtlvtype, failtlvpos, explanation); } + /* If we have and can decode invstring, we extract destination for listsendpays */ + if (invstring) { + struct bolt11 *b11; + char *fail; + + b11 = bolt11_decode(cmd, invstring, NULL, NULL, NULL, &fail); + if (b11) { + destination = &b11->receiver_id; + } else { + struct tlv_invoice *b12; + + b12 = invoice_decode(cmd, invstring, strlen(invstring), + NULL, NULL, &fail); + if (b12 && b12->invoice_node_id) { + destination = tal(cmd, struct node_id); + node_id_from_pubkey(destination, b12->invoice_node_id); + } else + destination = NULL; + } + } else + destination = NULL; + if (payload->final) { struct selfpay *selfpay; @@ -1944,7 +1968,8 @@ static struct command_result *json_injectpaymentonion(struct command *cmd, *partid, *groupid, *destination_msat, *msat, AMOUNT_MSAT(0), label, invstring, local_invreq_id, - &shared_secret); + &shared_secret, + destination); /* Mark it pending now, though htlc_set_add might * not resolve immediately */ @@ -2058,7 +2083,8 @@ static struct command_result *json_injectpaymentonion(struct command *cmd, *partid, *groupid, *destination_msat, *msat, AMOUNT_MSAT(0), label, invstring, local_invreq_id, - &shared_secret); + &shared_secret, + destination); /* If unknown, we set this equal (so accounting logs 0 fees) */ if (amount_msat_eq(*destination_msat, AMOUNT_MSAT(0))) diff --git a/tests/test_pay.py b/tests/test_pay.py index 3a771ac2d72e..aa0274652680 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -6210,6 +6210,8 @@ def test_injectpaymentonion_3hop(node_factory, executor): assert lsp['payment_hash'] == inv3['payment_hash'] assert lsp['status'] == 'complete' assert lsp['amount_msat'] == 1000 + # We didn't give it an invstring, so it doesn't know destination + assert 'destination' not in lsp def test_injectpaymentonion_selfpay(node_factory, executor): @@ -6227,6 +6229,7 @@ def test_injectpaymentonion_selfpay(node_factory, executor): ret = l1.rpc.injectpaymentonion(onion=onion['onion'], payment_hash=inv4['payment_hash'], + invstring=inv4['bolt11'], amount_msat=1000, cltv_expiry=blockheight + 18, partid=1, @@ -6238,6 +6241,7 @@ def test_injectpaymentonion_selfpay(node_factory, executor): assert lsp['partid'] == 1 assert lsp['payment_hash'] == inv4['payment_hash'] assert lsp['status'] == 'complete' + assert lsp['destination'] == l1.info['id'] # Test self-pay with MPP. inv5 = l1.rpc.invoice(1000, "test_injectpaymentonion5", "test_injectpaymentonion5") @@ -6278,6 +6282,7 @@ def test_injectpaymentonion_selfpay(node_factory, executor): assert lsp['partid'] == 1 or lsp['partid'] == 2 assert lsp['payment_hash'] == inv5['payment_hash'] assert lsp['status'] == 'complete' + assert 'destination' not in lsp assert len(lsps) == 2 # Check listpays gives a reasonable result! @@ -6362,7 +6367,8 @@ def test_injectpaymentonion_selfpay(node_factory, executor): amount_msat=1000, cltv_expiry=blockheight + 18, partid=1, - groupid=0) + groupid=0, + invstring=inv10['invoice']) assert sha256(bytes.fromhex(ret['payment_preimage'])).hexdigest() == decoded['invoice_payment_hash'] # The label for the invoice is deterministic. label = f"{decoded['offer_id']}-{decoded['invreq_payer_id']}-0" @@ -6372,6 +6378,7 @@ def test_injectpaymentonion_selfpay(node_factory, executor): assert lsp['partid'] == 1 assert lsp['payment_hash'] == inv4['payment_hash'] assert lsp['status'] == 'complete' + assert lsp['destination'] == l1.info['id'] def test_injectpaymentonion_blindedpath(node_factory, executor): @@ -6440,7 +6447,8 @@ def test_injectpaymentonion_blindedpath(node_factory, executor): amount_msat=1000, cltv_expiry=blockheight + 18 + 6, partid=1, - groupid=0) + groupid=0, + invstring=inv7['invoice']) assert sha256(bytes.fromhex(ret['payment_preimage'])).hexdigest() == decoded['invoice_payment_hash'] # The label for l2's invoice is deterministic. label = f"{decoded['offer_id']}-{decoded['invreq_payer_id']}-0" @@ -6492,7 +6500,8 @@ def test_injectpaymentonion_blindedpath(node_factory, executor): amount_msat=1001, cltv_expiry=blockheight + 18 + 6, partid=1, - groupid=0) + groupid=0, + invstring=inv8['invoice']) assert sha256(bytes.fromhex(ret['payment_preimage'])).hexdigest() == decoded['invoice_payment_hash'] # The label for l4's invoice is deterministic. label = f"{decoded['offer_id']}-{decoded['invreq_payer_id']}-0" @@ -6502,6 +6511,7 @@ def test_injectpaymentonion_blindedpath(node_factory, executor): assert lsp['partid'] == 1 assert lsp['payment_hash'] == decoded['invoice_payment_hash'] assert lsp['status'] == 'complete' + assert lsp['destination'] == decoded['invoice_node_id'] # Finally, with blinded path which starts with us. offer = l4.rpc.offer('any') @@ -6535,7 +6545,8 @@ def test_injectpaymentonion_blindedpath(node_factory, executor): amount_msat=1001, cltv_expiry=blockheight + 18 + 6, partid=1, - groupid=0) + groupid=0, + invstring=inv9['invoice']) assert sha256(bytes.fromhex(ret['payment_preimage'])).hexdigest() == decoded['invoice_payment_hash'] # The label for the invoice is deterministic. label = f"{decoded['offer_id']}-{decoded['invreq_payer_id']}-0" @@ -6545,6 +6556,7 @@ def test_injectpaymentonion_blindedpath(node_factory, executor): assert lsp['partid'] == 1 assert lsp['payment_hash'] == decoded['invoice_payment_hash'] assert lsp['status'] == 'complete' + assert lsp['destination'] == decoded['invoice_node_id'] def test_injectpaymentonion_failures(node_factory, executor):