diff --git a/plugins/renepay/Makefile b/plugins/renepay/Makefile index d28ca33018af..46d19e959251 100644 --- a/plugins/renepay/Makefile +++ b/plugins/renepay/Makefile @@ -10,7 +10,6 @@ PLUGIN_RENEPAY_SRC := \ plugins/renepay/routebuilder.c \ plugins/renepay/routetracker.c \ plugins/renepay/routefail.c \ - plugins/renepay/sendpay.c \ plugins/renepay/uncertainty.c \ plugins/renepay/mods.c \ plugins/renepay/errorcodes.c \ @@ -30,7 +29,6 @@ PLUGIN_RENEPAY_HDRS := \ plugins/renepay/routebuilder.h \ plugins/renepay/routetracker.h \ plugins/renepay/routefail.h \ - plugins/renepay/sendpay.h \ plugins/renepay/uncertainty.h \ plugins/renepay/mods.h \ plugins/renepay/errorcodes.h \ diff --git a/plugins/renepay/main.c b/plugins/renepay/main.c index 21790f26f6fd..0ced76c01afe 100644 --- a/plugins/renepay/main.c +++ b/plugins/renepay/main.c @@ -19,7 +19,6 @@ #include #include #include -#include #include // TODO(eduardo): notice that pending attempts performed with another @@ -460,10 +459,6 @@ static const struct plugin_command commands[] = { "renepay", json_renepay }, - { - "renesendpay", - json_renesendpay - }, }; static const struct plugin_notification notifications[] = { diff --git a/plugins/renepay/mods.c b/plugins/renepay/mods.c index e6da2d108fcb..23e2a22c36d4 100644 --- a/plugins/renepay/mods.c +++ b/plugins/renepay/mods.c @@ -753,10 +753,30 @@ REGISTER_PAYMENT_MODIFIER(compute_routes, compute_routes_cb); * request calling sendpay. */ -static struct command_result *send_routes_cb(struct payment *payment) +static struct command_result *waitblockheight_done(struct command *cmd, + const char *method UNUSED, + const char *buf, + const jsmntok_t *result, + struct payment *payment) { - assert(payment); - struct routetracker *routetracker = payment->routetracker; + const char *err; + struct command *aux_cmd; + struct route *route; + struct routetracker *routetracker; + + err = json_scan(tmpctx, buf, result, "{blockheight:%}", + JSON_SCAN(json_to_u32, &payment->blockheight)); + payment->blockheight += 1; + + if (err) { + plugin_err(pay_plugin->plugin, + "Failed to read blockheight from waitblockheight " + "response: %s", + err); + return payment_continue(payment); + } + + routetracker = payment->routetracker; assert(routetracker); if (!routetracker->computed_routes || tal_count(routetracker->computed_routes) == 0) { @@ -765,12 +785,11 @@ static struct command_result *send_routes_cb(struct payment *payment) __func__); return payment_continue(payment); } - struct command *cmd = payment_command(payment); - assert(cmd); for (size_t i = 0; i < tal_count(routetracker->computed_routes); i++) { - struct route *route = routetracker->computed_routes[i]; + aux_cmd = aux_command(cmd); + route = routetracker->computed_routes[i]; - route_sendpay_request(cmd, take(route), payment); + route_sendpay_request(aux_cmd, take(route), payment); payment_note(payment, LOG_INFORM, "Sent route request: partid=%" PRIu64 @@ -785,6 +804,22 @@ static struct command_result *send_routes_cb(struct payment *payment) return payment_continue(payment); } +static struct command_result *send_routes_cb(struct payment *payment) +{ + struct command *cmd; + struct out_req *req; + assert(payment); + cmd = payment_command(payment); + if (!cmd) + plugin_err(pay_plugin->plugin, + "send_routes_pay_mod: cannot get a valid cmd."); + req = + jsonrpc_request_start(cmd, "waitblockheight", waitblockheight_done, + payment_rpc_failure, payment); + json_add_num(req->js, "blockheight", 0); + return send_outreq(req); +} + REGISTER_PAYMENT_MODIFIER(send_routes, send_routes_cb); /***************************************************************************** diff --git a/plugins/renepay/payment.h b/plugins/renepay/payment.h index e3d30e301c10..8a27922a11e6 100644 --- a/plugins/renepay/payment.h +++ b/plugins/renepay/payment.h @@ -73,6 +73,9 @@ struct payment { struct plugin_timer *waitresult_timer; struct routetracker *routetracker; + + /* use this to build the onions */ + u32 blockheight; }; static inline const struct sha256 payment_hash(const struct payment *p) diff --git a/plugins/renepay/routetracker.c b/plugins/renepay/routetracker.c index b30a4a54423f..6291a773f5d3 100644 --- a/plugins/renepay/routetracker.c +++ b/plugins/renepay/routetracker.c @@ -1,5 +1,7 @@ #include "config.h" #include +#include +#include #include #include #include @@ -23,21 +25,18 @@ struct routetracker *new_routetracker(const tal_t *ctx, struct payment *payment) struct routetracker *rt = tal(ctx, struct routetracker); rt->computed_routes = tal_arr(rt, struct route *, 0); - rt->sent_routes = tal(rt, struct route_map); rt->finalized_routes = tal_arr(rt, struct route *, 0); - if (!rt->computed_routes || !rt->sent_routes || !rt->finalized_routes) + if (!rt->computed_routes || !rt->finalized_routes) /* bad allocation */ return tal_free(rt); - route_map_init(rt->sent_routes); return rt; } bool routetracker_have_results(struct routetracker *routetracker) { - return route_map_count(routetracker->sent_routes) == 0 && - tal_count(routetracker->finalized_routes) > 0; + return tal_count(routetracker->finalized_routes) > 0; } void routetracker_cleanup(struct routetracker *routetracker) @@ -150,7 +149,7 @@ static void remove_route(struct route *route, struct route_map *map) * - or after listsendpays reveals some pending route that we didn't * previously know about. */ static void route_pending_register(struct routetracker *routetracker, - struct route *route) + struct route *route TAKES) { assert(route); assert(routetracker); @@ -165,17 +164,12 @@ static void route_pending_register(struct routetracker *routetracker, __func__, fmt_routekey(tmpctx, &route->key)); - if (!route_map_del(routetracker->sent_routes, route)) - plugin_err(pay_plugin->plugin, - "%s: tracking a route (%s) not computed by this " - "payment call", - __func__, - fmt_routekey(tmpctx, &route->key)); - uncertainty_commit_htlcs(pay_plugin->uncertainty, route); - if (!tal_steal(pay_plugin->pending_routes, route) || - !route_map_add(pay_plugin->pending_routes, route) || + if (taken(route)) + tal_steal(pay_plugin->pending_routes, route); + + if (!route_map_add(pay_plugin->pending_routes, route) || !tal_add_destructor2(route, remove_route, pay_plugin->pending_routes)) plugin_err(pay_plugin->plugin, "%s: failed to register route.", @@ -192,93 +186,6 @@ static void route_pending_register(struct routetracker *routetracker, } } -/* Callback function for sendpay request success. */ -static struct command_result *sendpay_done(struct command *cmd, - const char *method UNUSED, - const char *buf, - const jsmntok_t *result, - struct route *route) -{ - assert(route); - struct payment *payment = route_get_payment_verify(route); - route_pending_register(payment->routetracker, route); - - const jsmntok_t *t; - size_t i; - bool ret; - - const jsmntok_t *secretstok = - json_get_member(buf, result, "shared_secrets"); - - if (secretstok) { - assert(secretstok->type == JSMN_ARRAY); - - route->shared_secrets = - tal_arr(route, struct secret, secretstok->size); - json_for_each_arr(i, t, secretstok) - { - ret = json_to_secret(buf, t, &route->shared_secrets[i]); - assert(ret); - } - } else - route->shared_secrets = NULL; - return command_still_pending(cmd); -} - -/* sendpay really only fails immediately in two ways: - * 1. We screwed up and misused the API. - * 2. The first peer is disconnected. - */ -static struct command_result *sendpay_failed(struct command *cmd, - const char *method UNUSED, - const char *buf, - const jsmntok_t *tok, - struct route *route) -{ - assert(route); - struct payment *payment = route_get_payment_verify(route); - struct routetracker *routetracker = payment->routetracker; - assert(routetracker); - - enum jsonrpc_errcode errcode; - const char *msg; - const char *err; - - err = json_scan(tmpctx, buf, tok, "{code:%,message:%}", - JSON_SCAN(json_to_jsonrpc_errcode, &errcode), - JSON_SCAN_TAL(tmpctx, json_strdup, &msg)); - if (err) - plugin_err(pay_plugin->plugin, - "Unable to parse sendpay error: %s, json: %.*s", err, - json_tok_full_len(tok), json_tok_full(buf, tok)); - - payment_note(payment, LOG_INFORM, - "Sendpay failed: partid=%" PRIu64 - " errorcode:%d message=%s", - route->key.partid, errcode, msg); - - if (errcode != PAY_TRY_OTHER_ROUTE) { - plugin_log(pay_plugin->plugin, LOG_UNUSUAL, - "Strange error from sendpay: %.*s", - json_tok_full_len(tok), json_tok_full(buf, tok)); - } - - /* There is no new knowledge from this kind of failure. - * We just disable this scid. */ - struct short_channel_id_dir scidd_disable = { - .scid = route->hops[0].scid, .dir = route->hops[0].direction}; - payment_disable_chan(payment, scidd_disable, LOG_INFORM, - "sendpay didn't like first hop: %s", msg); - - if (!route_map_del(routetracker->sent_routes, route)) - plugin_err(pay_plugin->plugin, - "%s: route (%s) is not marked as sent", - __func__, - fmt_routekey(tmpctx, &route->key)); - tal_free(route); - return command_still_pending(cmd); -} - void payment_collect_results(struct payment *payment, struct preimage **payment_preimage, enum jsonrpc_errcode *final_error, @@ -345,59 +252,230 @@ void payment_collect_results(struct payment *payment, tal_resize(&routetracker->finalized_routes, 0); } +static void sphinx_append_blinded_path(const tal_t *ctx, + struct sphinx_path *sp, + const struct blinded_path *blinded_path, + const struct amount_msat deliver, + const struct amount_msat total, + const u32 final_cltv) +{ + const size_t pathlen = tal_count(blinded_path->path); + bool ret; + + for (size_t i = 0; i < pathlen; i++) { + bool first = (i == 0); + bool final = (i == pathlen - 1); + + const struct blinded_path_hop *bhop = blinded_path->path[i]; + const u8 *payload = onion_blinded_hop( + ctx, final ? &deliver : NULL, final ? &total : NULL, + final ? &final_cltv : NULL, bhop->encrypted_recipient_data, + first ? &blinded_path->first_path_key : NULL); + // FIXME: better handle error here + ret = sphinx_add_hop_has_length( + sp, + first ? &blinded_path->first_node_id.pubkey + : &bhop->blinded_node_id, + take(payload)); + assert(ret); + } +} + +static void sphinx_append_final_hop(const tal_t *ctx, + struct sphinx_path *sp, + const struct secret *payment_secret, + const struct node_id *node, + const struct amount_msat deliver, + const struct amount_msat total, + const u32 final_cltv, + const u8 *payment_metadata) +{ + struct pubkey destination; + bool ret = pubkey_from_node_id(&destination, node); + assert(ret); + + const u8 *payload = onion_final_hop(ctx, deliver, final_cltv, total, + payment_secret, payment_metadata); + // FIXME: better handle error here + ret = sphinx_add_hop_has_length(sp, &destination, take(payload)); + assert(ret); +} + +static const u8 *create_onion( + const tal_t *ctx, const unsigned int blockheight, + const struct route_hop *route, const struct sha256 *payment_hash, + const u32 final_cltv_delta, const struct blinded_path *blinded_path, + const struct secret *payment_secret, const struct amount_msat total_amount, + const struct amount_msat deliver_amount, const u8 *metadata, + const struct node_id first_node, const size_t first_index, + struct secret **shared_secrets) +{ + bool ret; + const tal_t *this_ctx = tal(ctx, tal_t); + struct node_id current_node = first_node; + struct pubkey node; + const u8 *payload; + const size_t pathlen = tal_count(route); + + struct sphinx_path *sp = sphinx_path_new(this_ctx, payment_hash->u.u8, + sizeof(payment_hash->u.u8)); + + for (size_t i = first_index; i < pathlen; i++) { + /* Encrypted message is for node[i] but the data is hop[i+1], + * therein lays the problem with sendpay's API. */ + ret = pubkey_from_node_id(&node, ¤t_node); + assert(ret); + + const struct route_hop *hop = &route[i]; + payload = onion_nonfinal_hop(this_ctx, &hop->scid, hop->amount, + hop->delay + blockheight); + // FIXME: better handle error here + ret = sphinx_add_hop_has_length(sp, &node, take(payload)); + assert(ret); + current_node = route[i].node_id; + } + + const u32 final_cltv = final_cltv_delta + blockheight; + if (blinded_path) { + sphinx_append_blinded_path(this_ctx, sp, blinded_path, + deliver_amount, total_amount, + final_cltv); + } else { + sphinx_append_final_hop(this_ctx, sp, payment_secret, + ¤t_node, deliver_amount, + total_amount, final_cltv, metadata); + } + + struct onionpacket *packet = + create_onionpacket(this_ctx, sp, ROUTING_INFO_SIZE, shared_secrets); + *shared_secrets = tal_steal(ctx, *shared_secrets); + + const u8 *onion = serialize_onionpacket(ctx, packet); + tal_free(this_ctx); + return onion; +} + +static u32 initial_cltv_delta(const struct route *route, + const struct payment_info *pinfo) +{ + if (tal_count(route->hops) == 0) + return pinfo->final_cltv; + return route->hops[0].delay; +} + +static struct command_result *sendonion_done(struct command *aux_cmd, + const char *method UNUSED, + const char *buffer UNUSED, + const jsmntok_t *toks UNUSED, + struct payment *payment UNUSED) +{ + return aux_command_done(aux_cmd); +} + +static struct command_result * +sendonion_fail(struct command *aux_cmd, const char *method, const char *buffer, + const jsmntok_t *toks, struct payment *payment UNUSED) +{ + plugin_log(aux_cmd->plugin, LOG_DBG, "%s failed with: %.*s", method, + json_tok_full_len(toks), json_tok_full(buffer, toks)); + return aux_command_done(aux_cmd); +} + struct command_result *route_sendpay_request(struct command *cmd, struct route *route TAKES, struct payment *payment) { - const struct payment_info *pinfo = &payment->payment_info; - struct out_req *req = jsonrpc_request_start( - cmd, "renesendpay", sendpay_done, sendpay_failed, route); - - const size_t pathlen = tal_count(route->hops); - json_add_sha256(req->js, "payment_hash", &pinfo->payment_hash); - json_add_u64(req->js, "partid", route->key.partid); - json_add_u64(req->js, "groupid", route->key.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", route->amount_deliver); - 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); - - json_array_start(req->js, "route"); - /* An empty route means a payment to oneself, pathlen=0 */ - for (size_t j = 0; j < pathlen; j++) { - const struct route_hop *hop = &route->hops[j]; - json_object_start(req->js, NULL); + // build onion + const u8 *onion; + const struct payment_info *pinfo; + const struct blinded_path *blinded_path = NULL; + struct out_req *req; + + pinfo = &payment->payment_info; + if (tal_count(pinfo->blinded_paths) > 0) { + assert(tal_count(pinfo->blinded_paths) < route->path_num); + blinded_path = pinfo->blinded_paths[route->path_num]; + } + + if (tal_count(route->hops) > 0) { + onion = create_onion( + route, payment->blockheight, route->hops, + &pinfo->payment_hash, pinfo->final_cltv, blinded_path, + pinfo->payment_secret, pinfo->amount, route->amount_deliver, + /* metadata = */ NULL, route->hops[0].node_id, 1, + &route->shared_secrets); + } else { + /* This is either a self-payment or a payment through a blinded + * path that starts at our node. */ + onion = create_onion(route, payment->blockheight, route->hops, + &pinfo->payment_hash, pinfo->final_cltv, + blinded_path, pinfo->payment_secret, + pinfo->amount, route->amount_deliver, + /* metadata = */ NULL, pay_plugin->my_id, + 0, &route->shared_secrets); + } + + // send onion + // FIXME: use injectpaymentonion in both cases + if (tal_count(route->hops) > 0) { + req = jsonrpc_request_start(cmd, "sendonion", sendonion_done, + sendonion_fail, payment); + json_add_hex_talarr(req->js, "onion", onion); + json_add_sha256(req->js, "payment_hash", &pinfo->payment_hash); + json_add_u64(req->js, "partid", route->key.partid); + json_add_u64(req->js, "groupid", route->key.groupid); + json_add_amount_msat(req->js, "amount_msat", + route->amount_deliver); + if (pinfo->label) + json_add_string(req->js, "label", pinfo->label); + if (pinfo->invstr) + json_add_string(req->js, "bolt11", pinfo->invstr); + if (pinfo->description) + json_add_string(req->js, "description", + pinfo->description); + json_add_node_id(req->js, "destination", &pinfo->destination); + json_add_amount_msat(req->js, "total_amount_msat", + pinfo->amount); + + json_array_start(req->js, "shared_secrets"); + for (size_t i = 0; i < tal_count(route->shared_secrets); i++) { + json_add_secret(req->js, NULL, + &route->shared_secrets[i]); + } + json_array_end(req->js); + + const struct route_hop *hop = &route->hops[0]; + json_object_start(req->js, "first_hop"); + json_add_amount_msat(req->js, "amount_msat", hop->amount); json_add_node_id(req->js, "id", &hop->node_id); json_add_short_channel_id(req->js, "channel", hop->scid); - json_add_amount_msat(req->js, "amount_msat", hop->amount); - json_add_num(req->js, "direction", hop->direction); - json_add_u32(req->js, "delay", hop->delay); - json_add_string(req->js, "style", "tlv"); + json_add_num(req->js, "delay", + hop->delay + payment->blockheight); json_object_end(req->js); - } - json_array_end(req->js); - - /* Either we have a payment_secret for BOLT11 or blinded_paths for - * BOLT12 */ - 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[route->path_num]; - json_myadd_blinded_path(req->js, "blinded_path", bpath); + // FIXME: No localinvreqid is provided + } else { + req = jsonrpc_request_start(cmd, "injectpaymentonion", + sendonion_done, sendonion_fail, + payment); + json_add_hex_talarr(req->js, "onion", onion); + json_add_sha256(req->js, "payment_hash", &pinfo->payment_hash); + json_add_u64(req->js, "partid", route->key.partid); + json_add_u64(req->js, "groupid", route->key.groupid); + json_add_amount_msat(req->js, "amount_msat", + route->amount_sent); + if (pinfo->label) + json_add_string(req->js, "label", pinfo->label); + if (pinfo->invstr) + json_add_string(req->js, "invstring", pinfo->invstr); + json_add_amount_msat(req->js, "destination_msat", + route->amount_deliver); + json_add_u32(req->js, "cltv_expiry", + initial_cltv_delta(route, pinfo) + + payment->blockheight); + // FIXME: No localinvreqid is provided } - - route_map_add(payment->routetracker->sent_routes, route); - if (taken(route)) - tal_steal(payment->routetracker->sent_routes, route); + route_pending_register(payment->routetracker, route); return send_outreq(req); } diff --git a/plugins/renepay/routetracker.h b/plugins/renepay/routetracker.h index 2e4c440a2339..32c18c998fd7 100644 --- a/plugins/renepay/routetracker.h +++ b/plugins/renepay/routetracker.h @@ -10,10 +10,6 @@ struct routetracker{ /* Routes that we compute and are kept here before sending them. */ struct route **computed_routes; - /* Routes that we sendpay and are still waiting for rpc returning - * success. */ - struct route_map *sent_routes; - /* Routes that have concluded (either SENDPAY_FAILED or * SENDPAY_COMPLETE). */ struct route **finalized_routes; diff --git a/plugins/renepay/sendpay.c b/plugins/renepay/sendpay.c deleted file mode 100644 index 3c3049902147..000000000000 --- a/plugins/renepay/sendpay.c +++ /dev/null @@ -1,568 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include - -static struct command_result *param_route_hops(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - struct route_hop **hops) -{ - size_t i; - const jsmntok_t *t; - const char *err; - - if (tok->type != JSMN_ARRAY) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s must be an array", name); - - *hops = tal_arr(cmd, struct route_hop, tok->size); - json_for_each_arr(i, t, tok) - { - struct amount_msat amount_msat; - struct node_id id; - struct short_channel_id channel; - unsigned delay, direction; - - err = json_scan(tmpctx, buffer, t, - "{amount_msat:%,id:%,channel:%,direction:%,delay:%}", - JSON_SCAN(json_to_msat, &amount_msat), - JSON_SCAN(json_to_node_id, &id), - JSON_SCAN(json_to_short_channel_id, &channel), - JSON_SCAN(json_to_number, &direction), - JSON_SCAN(json_to_number, &delay) - ); - if (err != NULL) { - return command_fail( - cmd, JSONRPC2_INVALID_PARAMS, - "Error parsing route_hop %s[%zu]: %s", name, i, - err); - } - - if (direction != 0 && direction != 1) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "direction must be either 0 or 1"); - - (*hops)[i].amount = amount_msat; - (*hops)[i].node_id = id; - (*hops)[i].delay = delay; - (*hops)[i].scid = channel; - (*hops)[i].direction = direction; - } - return NULL; -} - -static struct command_result *param_blinded_path(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - struct blinded_path **blinded_path) -{ - size_t i; - const jsmntok_t *t, *pathtok, *datatok; - const char *err; - - *blinded_path = tal(cmd, struct blinded_path); - err = json_scan( - tmpctx, buffer, tok, "{first_node_id:%,first_path_key:%}", - JSON_SCAN(json_to_pubkey, &(*blinded_path)->first_node_id.pubkey), - JSON_SCAN(json_to_pubkey, &(*blinded_path)->first_path_key)); - if (err != NULL) { - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Error parsing blinded_path %s: %s", name, - err); - } - pathtok = json_get_member(buffer, tok, "path"); - if (!pathtok) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s does not have a path", name); - if (pathtok->type != JSMN_ARRAY) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "path in %s must be an array", name); - - (*blinded_path)->path = - tal_arr(*blinded_path, struct blinded_path_hop *, pathtok->size); - json_for_each_arr(i, t, pathtok) - { - (*blinded_path)->path[i] = - tal((*blinded_path)->path, struct blinded_path_hop); - struct blinded_path_hop *hop = (*blinded_path)->path[i]; - - err = - json_scan(tmpctx, buffer, t, "{blinded_node_id:%}", - JSON_SCAN(json_to_pubkey, &hop->blinded_node_id)); - if (err != NULL) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Error parsing path[%zu]: %s", i, - err); - - datatok = - json_get_member(buffer, t, "encrypted_recipient_data"); - if (!datatok) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Error parsing path[%zu]: unable " - "to get encrypted_recipient_data", - i); - hop->encrypted_recipient_data = - json_tok_bin_from_hex(hop, buffer, datatok); - } - return NULL; -} - -struct renesendpay { - struct route_hop *route; - struct sha256 payment_hash; - u64 groupid, partid; - - u32 final_cltv; - struct amount_msat total_amount; - struct amount_msat deliver_amount; - struct amount_msat sent_amount; - struct node_id destination; - - struct secret *payment_secret; - struct blinded_path *blinded_path; - - const char *invoice, *label, *description; - const u8 *metadata; - - struct secret *shared_secrets; - unsigned int blockheight; -}; - -static struct command_result *rpc_fail(struct command *cmd, - const char *method, - const char *buffer, - const jsmntok_t *toks, - struct renesendpay *renesendpay) -{ - plugin_log(cmd->plugin, LOG_UNUSUAL, - "renesendpay failed calling %s: %.*s", method, - json_tok_full_len(toks), json_tok_full(buffer, toks)); - const jsmntok_t *codetok = json_get_member(buffer, toks, "code"); - const jsmntok_t *msgtok = json_get_member(buffer, toks, "message"); - const char *msg; - if (msgtok) - msg = json_strdup(tmpctx, buffer, msgtok); - else - msg = ""; - u32 errcode; - if (codetok != NULL) - json_to_u32(buffer, codetok, &errcode); - else - errcode = PLUGIN_ERROR; - struct json_stream *response = jsonrpc_stream_fail( - cmd, errcode, - tal_fmt(tmpctx, "%s failed: %s", method, msg ? msg : "\"\"")); - json_object_start(response, "data"); - json_add_sha256(response, "payment_hash", &renesendpay->payment_hash); - json_add_string(response, "status", "failed"); - json_add_amount_msat(response, "amount_sent_msat", - renesendpay->sent_amount); - json_object_end(response); - return command_finished(cmd, response); -} - -static void sphinx_append_blinded_path(const tal_t *ctx, - struct sphinx_path *sp, - const struct blinded_path *blinded_path, - const struct amount_msat deliver, - const struct amount_msat total, - const u32 final_cltv) -{ - const size_t pathlen = tal_count(blinded_path->path); - bool ret; - - for (size_t i = 0; i < pathlen; i++) { - bool first = (i == 0); - bool final = (i == pathlen - 1); - - const struct blinded_path_hop *bhop = blinded_path->path[i]; - const u8 *payload = onion_blinded_hop( - ctx, final ? &deliver : NULL, final ? &total : NULL, - final ? &final_cltv : NULL, bhop->encrypted_recipient_data, - first ? &blinded_path->first_path_key : NULL); - // FIXME: better handle error here - ret = sphinx_add_hop_has_length( - sp, - first ? &blinded_path->first_node_id.pubkey - : &bhop->blinded_node_id, - take(payload)); - assert(ret); - } -} - -static void sphinx_append_final_hop(const tal_t *ctx, - struct sphinx_path *sp, - const struct secret *payment_secret, - const struct node_id *node, - const struct amount_msat deliver, - const struct amount_msat total, - const u32 final_cltv, - const u8 *payment_metadata) -{ - struct pubkey destination; - bool ret = pubkey_from_node_id(&destination, node); - assert(ret); - - const u8 *payload = onion_final_hop(ctx, deliver, final_cltv, total, - payment_secret, payment_metadata); - // FIXME: better handle error here - ret = sphinx_add_hop_has_length(sp, &destination, take(payload)); - assert(ret); -} - -static const u8 *create_onion(const tal_t *ctx, - struct renesendpay *renesendpay, - const struct node_id first_node, - const size_t first_index) -{ - bool ret; - const tal_t *this_ctx = tal(ctx, tal_t); - struct node_id current_node = first_node; - struct pubkey node; - const u8 *payload; - const size_t pathlen = tal_count(renesendpay->route); - - struct sphinx_path *sp = - sphinx_path_new(this_ctx, renesendpay->payment_hash.u.u8, - sizeof(renesendpay->payment_hash.u.u8)); - - for (size_t i = first_index; i < pathlen; i++) { - /* Encrypted message is for node[i] but the data is hop[i+1], - * therein lays the problem with sendpay's API. */ - ret = pubkey_from_node_id(&node, ¤t_node); - assert(ret); - - struct route_hop *hop = &renesendpay->route[i]; - payload = - onion_nonfinal_hop(this_ctx, &hop->scid, hop->amount, - hop->delay + renesendpay->blockheight); - // FIXME: better handle error here - ret = sphinx_add_hop_has_length(sp, &node, take(payload)); - assert(ret); - current_node = renesendpay->route[i].node_id; - } - - const u32 final_cltv = renesendpay->final_cltv + renesendpay->blockheight; - if(renesendpay->blinded_path){ - sphinx_append_blinded_path(this_ctx, - sp, - renesendpay->blinded_path, - renesendpay->deliver_amount, - renesendpay->total_amount, - final_cltv); - }else{ - sphinx_append_final_hop(this_ctx, - sp, - renesendpay->payment_secret, - ¤t_node, - renesendpay->deliver_amount, - renesendpay->total_amount, - final_cltv, - renesendpay->metadata); - } - - struct secret *shared_secrets; - struct onionpacket *packet = create_onionpacket( - this_ctx, sp, ROUTING_INFO_SIZE, &shared_secrets); - renesendpay->shared_secrets = tal_steal(renesendpay, shared_secrets); - - const u8 *onion = serialize_onionpacket(ctx, packet); - tal_free(this_ctx); - return onion; -} - -static struct command_result *renesendpay_done(struct command *cmd, - const char *method UNUSED, - const char *buffer, - const jsmntok_t *toks, - struct renesendpay *renesendpay) -{ - const char *err; - u64 created_index; - u32 timestamp; - err = json_scan(tmpctx, buffer, toks, "{created_index:%,created_at:%}", - JSON_SCAN(json_to_u64, &created_index), - JSON_SCAN(json_to_u32, ×tamp)); - if (err) - return command_fail( - cmd, JSONRPC2_INVALID_PARAMS, - "renesendpay failed to read response from sendonion: %s", - err); - - struct json_stream *response = jsonrpc_stream_success(cmd); - json_add_string(response, "message", - "Monitor status with listpays or waitsendpay"); - - json_add_u64(response, "created_index", created_index); - json_add_u32(response, "created_at", timestamp); - json_add_sha256(response, "payment_hash", &renesendpay->payment_hash); - json_add_u64(response, "groupid", renesendpay->groupid); - json_add_u64(response, "partid", renesendpay->partid); - json_add_node_id(response, "destination", &renesendpay->destination); - json_add_amount_msat(response, "amount_sent_msat", - renesendpay->sent_amount); - json_add_amount_msat(response, "amount_delivered_msat", - renesendpay->deliver_amount); - json_add_amount_msat(response, "amount_total_msat", - renesendpay->total_amount); - json_add_string(response, "invoice", renesendpay->invoice); - json_add_string(response, "status", "pending"); - - const jsmntok_t *preimagetok = - json_get_member(buffer, toks, "payment_preimage"); - if (preimagetok) - json_add_tok(response, "payment_preimage", preimagetok, buffer); - - if (renesendpay->label) - json_add_string(response, "label", renesendpay->label); - if (renesendpay->description) - json_add_string(response, "description", - renesendpay->description); - if (renesendpay->metadata) - json_add_hex_talarr(response, "payment_metadata", - renesendpay->metadata); - - if (renesendpay->shared_secrets) { - json_array_start(response, "shared_secrets"); - for (size_t i = 0; i < tal_count(renesendpay->shared_secrets); - i++) { - json_add_secret(response, NULL, - &renesendpay->shared_secrets[i]); - } - json_array_end(response); - } - - /* FIXME: shall we report the blinded path, secret and route used? */ - return command_finished(cmd, response); -} - -static struct command_result *renesendpay_finished(struct command *cmd, - struct renesendpay *renesendpay) -{ - struct json_stream *response = jsonrpc_stream_success(cmd); - json_add_string(response, "message", - "Monitor status with listpays or waitsendpay"); - json_add_sha256(response, "payment_hash", &renesendpay->payment_hash); - json_add_u64(response, "groupid", renesendpay->groupid); - json_add_u64(response, "partid", renesendpay->partid); - json_add_node_id(response, "destination", &renesendpay->destination); - json_add_amount_msat(response, "amount_sent_msat", - renesendpay->sent_amount); - json_add_amount_msat(response, "amount_delivered_msat", - renesendpay->deliver_amount); - json_add_amount_msat(response, "amount_total_msat", - renesendpay->total_amount); - json_add_string(response, "invoice", renesendpay->invoice); - json_add_string(response, "status", "pending"); - - if (renesendpay->label) - json_add_string(response, "label", renesendpay->label); - if (renesendpay->description) - json_add_string(response, "description", - renesendpay->description); - if (renesendpay->metadata) - json_add_hex_talarr(response, "payment_metadata", - renesendpay->metadata); - - if (renesendpay->shared_secrets) { - json_array_start(response, "shared_secrets"); - for (size_t i = 0; i < tal_count(renesendpay->shared_secrets); - i++) { - json_add_secret(response, NULL, - &renesendpay->shared_secrets[i]); - } - json_array_end(response); - } - return command_finished(cmd, response); -} - -static u32 initial_cltv_delta(const struct renesendpay *renesendpay) -{ - if (tal_count(renesendpay->route) == 0) - return renesendpay->final_cltv; - return renesendpay->route[0].delay; -} - -static struct command_result *waitblockheight_done(struct command *cmd, - const char *method UNUSED, - const char *buffer, - const jsmntok_t *toks, - struct renesendpay *renesendpay) -{ - const char *err; - err = json_scan(tmpctx, buffer, toks, "{blockheight:%}", - JSON_SCAN(json_to_u32, &renesendpay->blockheight)); - renesendpay->blockheight += 1; - if (err) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "renesendpay failed to read blockheight " - "from waitblockheight response."); - - const u8 *onion; - struct out_req *req; - - if (tal_count(renesendpay->route) > 0) { - onion = create_onion(tmpctx, renesendpay, - renesendpay->route[0].node_id, 1); - req = jsonrpc_request_start(cmd, "sendonion", renesendpay_done, - rpc_fail, renesendpay); - json_add_hex_talarr(req->js, "onion", onion); - - json_add_amount_msat(req->js, "amount_msat", - renesendpay->deliver_amount); - - const struct route_hop *hop = &renesendpay->route[0]; - json_object_start(req->js, "first_hop"); - json_add_amount_msat(req->js, "amount_msat", hop->amount); - json_add_num(req->js, "delay", - hop->delay + renesendpay->blockheight); - json_add_node_id(req->js, "id", &hop->node_id); - json_add_short_channel_id(req->js, "channel", hop->scid); - json_object_end(req->js); - - json_array_start(req->js, "shared_secrets"); - for (size_t i = 0; i < tal_count(renesendpay->shared_secrets); - i++) { - json_add_secret(req->js, NULL, - &renesendpay->shared_secrets[i]); - } - json_array_end(req->js); - - json_add_node_id(req->js, "destination", - &renesendpay->destination); - json_add_sha256(req->js, "payment_hash", - &renesendpay->payment_hash); - json_add_u64(req->js, "partid", renesendpay->partid); - json_add_u64(req->js, "groupid", renesendpay->groupid); - if (renesendpay->label) - json_add_string(req->js, "label", renesendpay->label); - if (renesendpay->description) - json_add_string(req->js, "description", - renesendpay->description); - if (renesendpay->invoice) - json_add_string(req->js, "bolt11", - renesendpay->invoice); - } else { - /* This is either a self-payment or a payment through a blinded - * path that starts at our node. */ - // FIXME: do this with injectpaymentonion. - // FIXME: we could make all payments with injectpaymentonion but - // we need to make sure first that we don't lose older features, - // like for example to be able to show in listsendpays the - // recepient of the payment. - onion = create_onion(tmpctx, renesendpay, pay_plugin->my_id, 0); - req = jsonrpc_request_start(cmd, "injectpaymentonion", - renesendpay_done, rpc_fail, - renesendpay); - - json_add_hex_talarr(req->js, "onion", onion); - json_add_sha256(req->js, "payment_hash", - &renesendpay->payment_hash); - json_add_u64(req->js, "partid", renesendpay->partid); - json_add_u64(req->js, "groupid", renesendpay->groupid); - if (renesendpay->label) - json_add_string(req->js, "label", renesendpay->label); - if (renesendpay->invoice) - json_add_string(req->js, "invstring", - renesendpay->invoice); - json_add_amount_msat(req->js, "amount_msat", - renesendpay->sent_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); - } - send_outreq(req); - return renesendpay_finished(cmd, renesendpay); -} - -struct command_result *json_renesendpay(struct command *cmd, - const char *buf, - const jsmntok_t *params) -{ - struct route_hop *route; - struct sha256 *payment_hash; - const char *invoice, *label, *description; - struct amount_msat *amount, *total_amount; - u64 *groupid, *partid; - u32 *final_cltv; - struct node_id *destination; - u8 *metadata; - - /* only used in the case of BOLT11 */ - struct secret *payment_secret; - - /* only used in the case of BOLT12 */ - struct blinded_path *blinded_path; - - if (!param(cmd, buf, params, - p_req("route", param_route_hops, &route), - p_req("payment_hash", param_sha256, &payment_hash), - p_req("groupid", param_u64, &groupid), - p_req("partid", param_u64, &partid), - p_req("amount_msat", param_msat, &amount), - p_req("total_amount_msat", param_msat, &total_amount), - p_req("destination", param_node_id, &destination), - p_req("final_cltv", param_u32, &final_cltv), - p_opt("payment_secret", param_secret, &payment_secret), - p_opt("blinded_path", param_blinded_path, &blinded_path), - p_opt("invoice", param_invstring, &invoice), - p_opt("label", param_string, &label), - p_opt("description", param_string, &description), - p_opt("metadata", param_bin_from_hex, &metadata), - NULL)) - return command_param_failed(); - - if (payment_secret && blinded_path) - return command_fail( - cmd, JSONRPC2_INVALID_PARAMS, - "A payment cannot have both a secret and a blinded path."); - if (!payment_secret && !blinded_path) - return command_fail( - cmd, JSONRPC2_INVALID_PARAMS, - "For a BOLT11 payment a payment_secret " - "must be specified and for a BOLT12 " - "payment a blinded_path must be specified."); - - plugin_log(cmd->plugin, LOG_DBG, "renesendpay called: %.*s", - json_tok_full_len(params), json_tok_full(buf, params)); - - struct renesendpay *renesendpay = tal(cmd, struct renesendpay); - renesendpay->route = tal_steal(renesendpay, route); - renesendpay->payment_hash = *payment_hash; - renesendpay->partid = *partid; - renesendpay->groupid = *groupid; - - if (tal_count(renesendpay->route) > 0) - renesendpay->sent_amount = renesendpay->route[0].amount; - else /* this might be a self pay */ - renesendpay->sent_amount = *amount; - - renesendpay->total_amount = *total_amount; - renesendpay->deliver_amount = *amount; - renesendpay->final_cltv = *final_cltv; - - renesendpay->destination = *destination; - - renesendpay->payment_secret = tal_steal(renesendpay, payment_secret); - renesendpay->blinded_path = tal_steal(renesendpay, blinded_path); - - renesendpay->invoice = tal_steal(renesendpay, invoice); - renesendpay->label = tal_steal(renesendpay, label); - renesendpay->description = tal_steal(renesendpay, description); - renesendpay->metadata = tal_steal(renesendpay, metadata); - renesendpay->shared_secrets = NULL; - - struct out_req *req = - jsonrpc_request_start(cmd, "waitblockheight", waitblockheight_done, - rpc_fail, renesendpay); - json_add_num(req->js, "blockheight", 0); - return send_outreq(req); -} diff --git a/plugins/renepay/sendpay.h b/plugins/renepay/sendpay.h deleted file mode 100644 index 1012063368fe..000000000000 --- a/plugins/renepay/sendpay.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef LIGHTNING_PLUGINS_RENEPAY_SENDPAY_H -#define LIGHTNING_PLUGINS_RENEPAY_SENDPAY_H - -#include "config.h" - -struct command_result *json_renesendpay(struct command *cmd, - const char *buf, - const jsmntok_t *params); - -#endif /* LIGHTNING_PLUGINS_RENEPAY_SENDPAY_H */