From 081bfeb9803aa83acab2a510df957a7fec000e07 Mon Sep 17 00:00:00 2001 From: Lagrang3 Date: Fri, 24 Jan 2025 13:53:45 +0100 Subject: [PATCH] renepay: resolve self payments with fake node Always use a fake destination node, the self-payments are no longer a corner case for the routing problem in this way. Also it is ok for get_routes to return routes with zero length. Changelog-None. Signed-off-by: Lagrang3 --- plugins/renepay/main.c | 24 ++++---- plugins/renepay/mods.c | 105 ++++++++------------------------- plugins/renepay/route.h | 3 +- plugins/renepay/routebuilder.c | 2 + plugins/renepay/sendpay.c | 2 - tests/test_renepay.py | 8 +++ 6 files changed, 46 insertions(+), 98 deletions(-) diff --git a/plugins/renepay/main.c b/plugins/renepay/main.c index 6649bfdaebb8..90e60a02423d 100644 --- a/plugins/renepay/main.c +++ b/plugins/renepay/main.c @@ -344,7 +344,6 @@ static struct command_result *json_pay(struct command *cmd, const char *buf, pinfo->blinded_paths = NULL; pinfo->blinded_payinfos = NULL; - payment->routing_destination = &pinfo->destination; } else { pinfo->payment_secret = NULL; pinfo->routehints = NULL; @@ -370,19 +369,18 @@ static struct command_result *json_pay(struct command *cmd, const char *buf, max_final_cltv = final_cltv; } pinfo->final_cltv = max_final_cltv; - - /* When dealing with BOLT12 blinded paths we compute the - * routing targeting a fake node to enable - * multi-destination minimum-cost-flow. Every blinded - * path entry node will be linked to this fake node - * using fake channels as well. */ - payment->routing_destination = - tal(payment, struct node_id); - if (!node_id_from_hexstr( - "02""0000000000000000000000000000000000000000000000000000000000000001", - 66, payment->routing_destination)) - abort(); } + /* When dealing with BOLT12 blinded paths we compute the + * routing targeting a fake node to enable + * multi-destination minimum-cost-flow. Every blinded + * path entry node will be linked to this fake node + * using fake channels as well. This is also useful for + * solving self-payments. */ + payment->routing_destination = tal(payment, struct node_id); + if (!node_id_from_hexstr("0200000000000000000000000000000000000" + "00000000000000000000000000001", + 66, payment->routing_destination)) + abort(); if (!payment_set_constraints( payment, *msat, *maxfee, *maxdelay, *retryfor, diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index 62a203f3ae57..f2622c24a498 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -92,6 +92,13 @@ static struct command_result *payment_rpc_failure(struct command *cmd, json_tok_full_len(toks), json_tok_full(buffer, toks)); } +static void add_hintchan(struct payment *payment, const struct node_id *src, + const struct node_id *dst, u16 cltv_expiry_delta, + const struct short_channel_id scid, u32 fee_base_msat, + u32 fee_proportional_millionths, + const struct amount_msat *chan_htlc_min, + const struct amount_msat *chan_htlc_max); + /***************************************************************************** * previoussuccess * @@ -249,88 +256,23 @@ REGISTER_PAYMENT_MODIFIER(initial_sanity_checks, initial_sanity_checks_cb); /***************************************************************************** * selfpay - * - * Checks if the payment destination is the sender's node and perform a self - * payment. */ -static struct command_result *selfpay_success(struct command *cmd, - const char *method UNUSED, - const char *buf, - const jsmntok_t *tok, - struct payment *payment) -{ - struct preimage preimage; - const char *err; - err = json_scan(tmpctx, buf, tok, "{payment_preimage:%}", - JSON_SCAN(json_to_preimage, &preimage)); - if (err) - plugin_err( - cmd->plugin, "selfpay didn't have payment_preimage: %.*s", - json_tok_full_len(tok), json_tok_full(buf, tok)); - - - payment_note(payment, LOG_DBG, "Paid with self-pay."); - return payment_success(payment, &preimage); -} -static struct command_result *selfpay_failure(struct command *cmd, - const char *method UNUSED, - const char *buf, - const jsmntok_t *tok, - struct payment *payment) -{ - struct payment_result *result = - tal_sendpay_result_from_json(tmpctx, buf, tok); - if (result == NULL) { - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, - "Unable to parse sendpay failure: %.*s", - json_tok_full_len(tok), json_tok_full(buf, tok)); - return payment_fail(payment, LIGHTNINGD, - "Self pay failed for unknown reason"); - } - return payment_fail(payment, result->code, "%s", result->message); -} - static struct command_result *selfpay_cb(struct payment *payment) { - if (!node_id_eq(&pay_plugin->my_id, - &payment->payment_info.destination)) { - return payment_continue(payment); - } - - struct command *cmd = payment_command(payment); - if (!cmd) - plugin_err(pay_plugin->plugin, - "Selfpay: cannot get a valid cmd."); - - struct payment_info *pinfo = &payment->payment_info; - struct out_req *req; - req = jsonrpc_request_start(cmd, "renesendpay", selfpay_success, - selfpay_failure, payment); - json_add_sha256(req->js, "payment_hash", &pinfo->payment_hash); - json_add_u64(req->js, "partid", 0); - json_add_u64(req->js, "groupid", payment->groupid); - json_add_string(req->js, "invoice", pinfo->invstr); - json_add_node_id(req->js, "destination", &pinfo->destination); - json_add_amount_msat(req->js, "amount_msat", pinfo->amount); - json_add_amount_msat(req->js, "total_amount_msat", pinfo->amount); - json_add_u32(req->js, "final_cltv", pinfo->final_cltv); - if (pinfo->label) - json_add_string(req->js, "label", pinfo->label); - if (pinfo->description) - json_add_string(req->js, "description", pinfo->description); - /* An empty route means a payment to oneself, pathlen=0 */ - json_array_start(req->js, "route"); - json_array_end(req->js); - if (pinfo->payment_secret) - json_add_secret(req->js, "payment_secret", - pinfo->payment_secret); - else { - assert(pinfo->blinded_paths); - const struct blinded_path *bpath = pinfo->blinded_paths[0]; - json_myadd_blinded_path(req->js, "blinded_path", bpath); + /* A different approach to self-pay: create a fake channel from the + * bolt11 destination to the routing_destination (a fake node_id). */ + if (!payment->payment_info.blinded_paths) { + struct amount_msat htlc_min = AMOUNT_MSAT(0); + struct amount_msat htlc_max = AMOUNT_MSAT((u64)1000*100000000); + struct short_channel_id scid = {.u64 = 0}; + add_hintchan(payment, &payment->payment_info.destination, + payment->routing_destination, + /* cltv delta = */ 0, scid, + /* base fee = */ 0, + /* ppm = */ 0, &htlc_min, &htlc_max); } - return send_outreq(req); + return payment_continue(payment); } REGISTER_PAYMENT_MODIFIER(selfpay, selfpay_cb); @@ -755,8 +697,7 @@ static struct command_result *compute_routes_cb(struct payment *payment) /* Send get_routes a note that it should discard the last hop because we * are actually solving a multiple destinations problem. */ - bool blinded_destination = - payment->payment_info.blinded_paths != NULL; + bool blinded_destination = true; // TODO: add an algorithm selector here /* We let this return an unlikely path, as it's better to try once than @@ -1291,9 +1232,9 @@ REGISTER_PAYMENT_CONDITION(retry, retry_cb); // add check pre-approved invoice void *payment_virtual_program[] = { /*0*/ OP_CALL, &previoussuccess_pay_mod, - /*2*/ OP_CALL, &selfpay_pay_mod, - /*4*/ OP_CALL, &knowledgerelax_pay_mod, - /*6*/ OP_CALL, &getmychannels_pay_mod, + /*2*/ OP_CALL, &knowledgerelax_pay_mod, + /*4*/ OP_CALL, &getmychannels_pay_mod, + /*6*/ OP_CALL, &selfpay_pay_mod, /*8*/ OP_CALL, &refreshgossmap_pay_mod, /*10*/ OP_CALL, &routehints_pay_mod, /*12*/ OP_CALL, &blindedhints_pay_mod, diff --git a/plugins/renepay/route.h b/plugins/renepay/route.h index 8b3ecd7b7728..1528385a3650 100644 --- a/plugins/renepay/route.h +++ b/plugins/renepay/route.h @@ -170,7 +170,8 @@ static inline u32 route_delay(const struct route *route) { assert(route); assert(route->hops); - assert(tal_count(route->hops) > 0); + if (tal_count(route->hops) == 0) + return 0; const size_t pathlen = tal_count(route->hops); assert(route->hops[0].delay >= route->hops[pathlen - 1].delay); return route->hops[0].delay - route->hops[pathlen - 1].delay; diff --git a/plugins/renepay/routebuilder.c b/plugins/renepay/routebuilder.c index eeb35e500d59..4e9d15142326 100644 --- a/plugins/renepay/routebuilder.c +++ b/plugins/renepay/routebuilder.c @@ -65,6 +65,8 @@ route_check_constraints(struct route *route, struct gossmap *gossmap, assert(route); assert(route->hops); const size_t pathlen = tal_count(route->hops); + if (pathlen == 0) + return RENEPAY_NOERROR; if (!amount_msat_eq(route->amount_deliver, route->hops[pathlen - 1].amount)) return RENEPAY_PRECONDITION_ERROR; diff --git a/plugins/renepay/sendpay.c b/plugins/renepay/sendpay.c index 1903fb3bccdb..60a6c0cb6cec 100644 --- a/plugins/renepay/sendpay.c +++ b/plugins/renepay/sendpay.c @@ -435,8 +435,6 @@ static struct command_result *waitblockheight_done(struct command *cmd, renesendpay->sent_amount); json_add_amount_msat(req->js, "destination_msat", renesendpay->deliver_amount); - json_add_amount_msat(req->js, "destination_msat", - renesendpay->deliver_amount); json_add_u32(req->js, "cltv_expiry", initial_cltv_delta(renesendpay) + renesendpay->blockheight); diff --git a/tests/test_renepay.py b/tests/test_renepay.py index 4b30e7e17bdc..4463f8f4866a 100644 --- a/tests/test_renepay.py +++ b/tests/test_renepay.py @@ -848,3 +848,11 @@ def test_offers(node_factory): invoice = l1.rpc.fetchinvoice(offer)['invoice'] response = l1.rpc.call("renepay", {"invstring": invoice}) assert response["status"] == "complete" + + +def test_offer_selfpay(node_factory): + """We can fetch an pay our own offer""" + l1 = node_factory.get_node() + offer = l1.rpc.offer(amount="2msat", description="test_offer_path_self")["bolt12"] + inv = l1.rpc.fetchinvoice(offer)["invoice"] + l1.rpc.call("renepay", {"invstring": inv})