From fda26bdcdaa0df03817ba01c75f622bcd399ab6f Mon Sep 17 00:00:00 2001 From: ZmnSCPxj Date: Thu, 15 Feb 2018 03:32:03 +0000 Subject: [PATCH] payalgo: New file for pay command. --- lightningd/Makefile | 1 + lightningd/pay.c | 210 +--------------------------------- lightningd/pay.h | 43 ++++++- lightningd/payalgo.c | 261 +++++++++++++++++++++++++++++++++++++++++++ lightningd/payalgo.h | 5 + 5 files changed, 314 insertions(+), 206 deletions(-) create mode 100644 lightningd/payalgo.c create mode 100644 lightningd/payalgo.h diff --git a/lightningd/Makefile b/lightningd/Makefile index 42846bce7..4280a6434 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -63,6 +63,7 @@ LIGHTNINGD_SRC := \ lightningd/opt_time.c \ lightningd/options.c \ lightningd/pay.c \ + lightningd/payalgo.c \ lightningd/peer_control.c \ lightningd/peer_htlcs.c \ lightningd/subd.c \ diff --git a/lightningd/pay.c b/lightningd/pay.c index b535cf537..8157560f4 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -3,11 +3,8 @@ #include #include #include -#include #include #include -#include -#include #include #include #include @@ -23,34 +20,6 @@ Internal sendpay interface -----------------------------------------------------------------------------*/ -struct routing_failure { - unsigned int erring_index; - enum onion_type failcode; - struct pubkey erring_node; - struct short_channel_id erring_channel; - u8 *channel_update; -}; - -/* Result of sendpay */ -struct sendpay_result { - /* Did the payment succeed? */ - bool succeeded; - /* Preimage. Only loaded if payment succeeded. */ - struct preimage preimage; - /* Error code, one of the PAY_* macro in jsonrpc_errors.h. - * Only loaded if payment failed. */ - int errorcode; - /* Unparseable onion reply. Only loaded if payment failed, - * and errorcode == PAY_UNPARSEABLE_ONION. */ - const u8* onionreply; - /* Routing failure object. Only loaded if payment failed, - * and errorcode == PAY_DESTINATION_PERM_FAIL or - * errorcode == PAY_TRY_OTHER_ROUTE */ - struct routing_failure* routing_failure; - /* Error message. Only loaded if payment failed. */ - const char *details; -}; - /* sendpay command */ struct sendpay_command { struct list_node list; @@ -490,12 +459,12 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout, * * This call expects that if it calls the callback, then * the given context should have been freed. */ -static bool send_payment(const tal_t *ctx, - struct lightningd* ld, - const struct sha256 *rhash, - const struct route_hop *route, - void (*cb)(const struct sendpay_result*, void*), - void *cbarg) +bool send_payment(const tal_t *ctx, + struct lightningd* ld, + const struct sha256 *rhash, + const struct route_hop *route, + void (*cb)(const struct sendpay_result*, void*), + void *cbarg) { const u8 *onion; u8 sessionkey[32]; @@ -831,173 +800,6 @@ static const struct json_command sendpay_command = { }; AUTODATA(json_command, &sendpay_command); -struct pay { - struct sha256 payment_hash; - struct command *cmd; - u64 msatoshi; - double maxfeepercent; -}; - -static void json_pay_getroute_reply(struct subd *gossip, - const u8 *reply, const int *fds, - struct pay *pay) -{ - struct route_hop *route; - u64 msatoshi_sent; - u64 fee; - double feepercent; - struct json_result *data; - - fromwire_gossip_getroute_reply(reply, reply, NULL, &route); - - if (tal_count(route) == 0) { - command_fail_detailed(pay->cmd, PAY_ROUTE_NOT_FOUND, NULL, - "Could not find a route"); - return; - } - - msatoshi_sent = route[0].amount; - fee = msatoshi_sent - pay->msatoshi; - /* FIXME: IEEE Double-precision floating point has only 53 bits - * of precision. Total satoshis that can ever be created is - * slightly less than 2100000000000000. Total msatoshis that - * can ever be created is 1000 times that or - * 2100000000000000000, requiring 60.865 bits of precision, - * and thus losing precision in the below. Currently, OK, as, - * payments are limited to 4294967295 msatoshi. */ - feepercent = ((double) fee) * 100.0 / ((double) pay->msatoshi); - if (feepercent > pay->maxfeepercent) { - data = new_json_result(pay); - json_object_start(data, NULL); - json_add_u64(data, "fee", fee); - json_add_double(data, "feepercent", feepercent); - json_add_u64(data, "msatoshi", pay->msatoshi); - json_add_double(data, "maxfeepercent", pay->maxfeepercent); - json_object_end(data); - - command_fail_detailed(pay->cmd, PAY_ROUTE_TOO_EXPENSIVE, - data, - "Fee %"PRIu64" is %f%% " - "of payment %"PRIu64"; " - "max fee requested is %f%%", - fee, feepercent, - pay->msatoshi, - pay->maxfeepercent); - return; - } - - send_payment(pay->cmd, pay->cmd->ld, &pay->payment_hash, route, - &json_sendpay_on_resolve, pay->cmd); -} - -static void json_pay(struct command *cmd, - const char *buffer, const jsmntok_t *params) -{ - jsmntok_t *bolt11tok, *msatoshitok, *desctok, *riskfactortok, *maxfeetok; - double riskfactor = 1.0; - double maxfeepercent = 0.5; - u64 msatoshi; - struct pay *pay = tal(cmd, struct pay); - struct bolt11 *b11; - char *fail, *b11str, *desc; - u8 *req; - - if (!json_get_params(cmd, buffer, params, - "bolt11", &bolt11tok, - "?msatoshi", &msatoshitok, - "?description", &desctok, - "?riskfactor", &riskfactortok, - "?maxfeepercent", &maxfeetok, - NULL)) { - return; - } - - b11str = tal_strndup(cmd, buffer + bolt11tok->start, - bolt11tok->end - bolt11tok->start); - if (desctok) - desc = tal_strndup(cmd, buffer + desctok->start, - desctok->end - desctok->start); - else - desc = NULL; - - b11 = bolt11_decode(pay, b11str, desc, &fail); - if (!b11) { - command_fail(cmd, "Invalid bolt11: %s", fail); - return; - } - - pay->cmd = cmd; - pay->payment_hash = b11->payment_hash; - - if (b11->msatoshi) { - msatoshi = *b11->msatoshi; - if (msatoshitok) { - command_fail(cmd, "msatoshi parameter unnecessary"); - return; - } - } else { - if (!msatoshitok) { - command_fail(cmd, "msatoshi parameter required"); - return; - } - if (!json_tok_u64(buffer, msatoshitok, &msatoshi)) { - command_fail(cmd, - "msatoshi '%.*s' is not a valid number", - (int)(msatoshitok->end-msatoshitok->start), - buffer + msatoshitok->start); - return; - } - } - pay->msatoshi = msatoshi; - - if (riskfactortok - && !json_tok_double(buffer, riskfactortok, &riskfactor)) { - command_fail(cmd, "'%.*s' is not a valid double", - (int)(riskfactortok->end - riskfactortok->start), - buffer + riskfactortok->start); - return; - } - - if (maxfeetok - && !json_tok_double(buffer, maxfeetok, &maxfeepercent)) { - command_fail(cmd, "'%.*s' is not a valid double", - (int)(maxfeetok->end - maxfeetok->start), - buffer + maxfeetok->start); - return; - } - /* Ensure it is in range 0.0 <= maxfeepercent <= 100.0 */ - if (!(0.0 <= maxfeepercent)) { - command_fail(cmd, "%f maxfeepercent must be non-negative", - maxfeepercent); - return; - } - if (!(maxfeepercent <= 100.0)) { - command_fail(cmd, "%f maxfeepercent must be <= 100.0", - maxfeepercent); - return; - } - pay->maxfeepercent = maxfeepercent; - - /* FIXME: use b11->routes */ - req = towire_gossip_getroute_request(cmd, &cmd->ld->id, - &b11->receiver_id, - msatoshi, riskfactor*1000, - b11->min_final_cltv_expiry); - subd_req(pay, cmd->ld->gossip, req, -1, 0, json_pay_getroute_reply, pay); - command_still_pending(cmd); -} - -static const struct json_command pay_command = { - "pay", - json_pay, - "Send payment specified by {bolt11} with optional {msatoshi} " - "(if and only if {bolt11} does not have amount), " - "{description} (required if {bolt11} uses description hash), " - "{riskfactor} (default 1.0), and " - "{maxfeepercent} (default 0.5) the maximum acceptable fee as a percentage (e.g. 0.5 => 0.5%)" -}; -AUTODATA(json_command, &pay_command); - static void json_listpayments(struct command *cmd, const char *buffer, const jsmntok_t *params) { diff --git a/lightningd/pay.h b/lightningd/pay.h index b8fe64d7f..152e5c885 100644 --- a/lightningd/pay.h +++ b/lightningd/pay.h @@ -1,12 +1,51 @@ #ifndef LIGHTNING_LIGHTNINGD_PAY_H #define LIGHTNING_LIGHTNINGD_PAY_H #include "config.h" +#include +#include +#include #include struct htlc_out; struct lightningd; -struct preimage; -struct pubkey; +struct route_hop; +struct sha256; + +/* Routing failure object */ +struct routing_failure { + unsigned int erring_index; + enum onion_type failcode; + struct pubkey erring_node; + struct short_channel_id erring_channel; + u8 *channel_update; +}; + +/* Result of send_payment */ +struct sendpay_result { + /* Did the payment succeed? */ + bool succeeded; + /* Preimage. Only loaded if payment succeeded. */ + struct preimage preimage; + /* Error code, one of the PAY_* macro in jsonrpc_errors.h. + * Only loaded if payment failed. */ + int errorcode; + /* Unparseable onion reply. Only loaded if payment failed, + * and errorcode == PAY_UNPARSEABLE_ONION. */ + const u8* onionreply; + /* Routing failure object. Only loaded if payment failed, + * and errorcode == PAY_DESTINATION_PERM_FAIL or + * errorcode == PAY_TRY_OTHER_ROUTE */ + struct routing_failure* routing_failure; + /* Error message. Only loaded if payment failed. */ + const char *details; +}; + +bool send_payment(const tal_t *ctx, + struct lightningd* ld, + const struct sha256 *rhash, + const struct route_hop *route, + void (*cb)(const struct sendpay_result*, void*), + void *cbarg); void payment_succeeded(struct lightningd *ld, struct htlc_out *hout, const struct preimage *rval); diff --git a/lightningd/payalgo.c b/lightningd/payalgo.c new file mode 100644 index 000000000..4eb3ec68a --- /dev/null +++ b/lightningd/payalgo.c @@ -0,0 +1,261 @@ +#include "pay.h" +#include "payalgo.h" +#include +#include +#include +#include +#include +#include +#include +#include + +struct pay { + struct sha256 payment_hash; + struct command *cmd; + u64 msatoshi; + double maxfeepercent; +}; + +/* Duplicated here from lightningd/pay.c, but will be modified + * in a later commit. */ +static void +json_sendpay_success(struct command *cmd, + const struct preimage *payment_preimage) +{ + struct json_result *response; + + response = new_json_result(cmd); + json_object_start(response, NULL); + json_add_hex(response, "preimage", + payment_preimage, sizeof(*payment_preimage)); + json_object_end(response); + command_success(cmd, response); +} + +/* Duplicated here from lightningd/pay.c, but will be modified + * in a later commit. */ +static void json_sendpay_on_resolve(const struct sendpay_result *r, + void *vcmd) +{ + struct command *cmd = (struct command*) vcmd; + + struct json_result *data; + const char *msg; + struct routing_failure *fail; + + if (r->succeeded) + json_sendpay_success(cmd, &r->preimage); + else { + switch (r->errorcode) { + case PAY_IN_PROGRESS: + case PAY_RHASH_ALREADY_USED: + data = NULL; + msg = r->details; + break; + + case PAY_UNPARSEABLE_ONION: + data = new_json_result(cmd); + json_object_start(data, NULL); + json_add_hex(data, "onionreply", + r->onionreply, tal_len(r->onionreply)); + json_object_end(data); + + msg = tal_fmt(cmd, + "failed: WIRE_PERMANENT_NODE_FAILURE " + "(%s)", + r->details); + + break; + + case PAY_DESTINATION_PERM_FAIL: + case PAY_TRY_OTHER_ROUTE: + fail = r->routing_failure; + data = new_json_result(cmd); + + json_object_start(data, NULL); + json_add_num(data, "erring_index", + fail->erring_index); + json_add_num(data, "failcode", + (unsigned) fail->failcode); + json_add_hex(data, "erring_node", + &fail->erring_node, + sizeof(fail->erring_node)); + json_add_short_channel_id(data, "erring_channel", + &fail->erring_channel); + if (fail->channel_update) + json_add_hex(data, "channel_update", + fail->channel_update, + tal_len(fail->channel_update)); + json_object_end(data); + + msg = tal_fmt(cmd, + "failed: %s (%s)", + onion_type_name(fail->failcode), + r->details); + + break; + } + + command_fail_detailed(cmd, r->errorcode, data, "%s", msg); + } +} + +static void json_pay_getroute_reply(struct subd *gossip, + const u8 *reply, const int *fds, + struct pay *pay) +{ + struct route_hop *route; + u64 msatoshi_sent; + u64 fee; + double feepercent; + struct json_result *data; + + fromwire_gossip_getroute_reply(reply, reply, NULL, &route); + + if (tal_count(route) == 0) { + command_fail_detailed(pay->cmd, PAY_ROUTE_NOT_FOUND, NULL, + "Could not find a route"); + return; + } + + msatoshi_sent = route[0].amount; + fee = msatoshi_sent - pay->msatoshi; + /* FIXME: IEEE Double-precision floating point has only 53 bits + * of precision. Total satoshis that can ever be created is + * slightly less than 2100000000000000. Total msatoshis that + * can ever be created is 1000 times that or + * 2100000000000000000, requiring 60.865 bits of precision, + * and thus losing precision in the below. Currently, OK, as, + * payments are limited to 4294967295 msatoshi. */ + feepercent = ((double) fee) * 100.0 / ((double) pay->msatoshi); + if (feepercent > pay->maxfeepercent) { + data = new_json_result(pay); + json_object_start(data, NULL); + json_add_u64(data, "fee", fee); + json_add_double(data, "feepercent", feepercent); + json_add_u64(data, "msatoshi", pay->msatoshi); + json_add_double(data, "maxfeepercent", pay->maxfeepercent); + json_object_end(data); + + command_fail_detailed(pay->cmd, PAY_ROUTE_TOO_EXPENSIVE, + data, + "Fee %"PRIu64" is %f%% " + "of payment %"PRIu64"; " + "max fee requested is %f%%", + fee, feepercent, + pay->msatoshi, + pay->maxfeepercent); + return; + } + + send_payment(pay->cmd, pay->cmd->ld, &pay->payment_hash, route, + &json_sendpay_on_resolve, pay->cmd); +} + +static void json_pay(struct command *cmd, + const char *buffer, const jsmntok_t *params) +{ + jsmntok_t *bolt11tok, *msatoshitok, *desctok, *riskfactortok, *maxfeetok; + double riskfactor = 1.0; + double maxfeepercent = 0.5; + u64 msatoshi; + struct pay *pay = tal(cmd, struct pay); + struct bolt11 *b11; + char *fail, *b11str, *desc; + u8 *req; + + if (!json_get_params(cmd, buffer, params, + "bolt11", &bolt11tok, + "?msatoshi", &msatoshitok, + "?description", &desctok, + "?riskfactor", &riskfactortok, + "?maxfeepercent", &maxfeetok, + NULL)) { + return; + } + + b11str = tal_strndup(cmd, buffer + bolt11tok->start, + bolt11tok->end - bolt11tok->start); + if (desctok) + desc = tal_strndup(cmd, buffer + desctok->start, + desctok->end - desctok->start); + else + desc = NULL; + + b11 = bolt11_decode(pay, b11str, desc, &fail); + if (!b11) { + command_fail(cmd, "Invalid bolt11: %s", fail); + return; + } + + pay->cmd = cmd; + pay->payment_hash = b11->payment_hash; + + if (b11->msatoshi) { + msatoshi = *b11->msatoshi; + if (msatoshitok) { + command_fail(cmd, "msatoshi parameter unnecessary"); + return; + } + } else { + if (!msatoshitok) { + command_fail(cmd, "msatoshi parameter required"); + return; + } + if (!json_tok_u64(buffer, msatoshitok, &msatoshi)) { + command_fail(cmd, + "msatoshi '%.*s' is not a valid number", + (int)(msatoshitok->end-msatoshitok->start), + buffer + msatoshitok->start); + return; + } + } + pay->msatoshi = msatoshi; + + if (riskfactortok + && !json_tok_double(buffer, riskfactortok, &riskfactor)) { + command_fail(cmd, "'%.*s' is not a valid double", + (int)(riskfactortok->end - riskfactortok->start), + buffer + riskfactortok->start); + return; + } + + if (maxfeetok + && !json_tok_double(buffer, maxfeetok, &maxfeepercent)) { + command_fail(cmd, "'%.*s' is not a valid double", + (int)(maxfeetok->end - maxfeetok->start), + buffer + maxfeetok->start); + return; + } + /* Ensure it is in range 0.0 <= maxfeepercent <= 100.0 */ + if (!(0.0 <= maxfeepercent)) { + command_fail(cmd, "%f maxfeepercent must be non-negative", + maxfeepercent); + return; + } + if (!(maxfeepercent <= 100.0)) { + command_fail(cmd, "%f maxfeepercent must be <= 100.0", + maxfeepercent); + return; + } + pay->maxfeepercent = maxfeepercent; + + /* FIXME: use b11->routes */ + req = towire_gossip_getroute_request(cmd, &cmd->ld->id, + &b11->receiver_id, + msatoshi, riskfactor*1000, + b11->min_final_cltv_expiry); + subd_req(pay, cmd->ld->gossip, req, -1, 0, json_pay_getroute_reply, pay); + command_still_pending(cmd); +} + +static const struct json_command pay_command = { + "pay", + json_pay, + "Send payment specified by {bolt11} with optional {msatoshi} " + "(if and only if {bolt11} does not have amount), " + "{description} (required if {bolt11} uses description hash), " + "{riskfactor} (default 1.0), and " + "{maxfeepercent} (default 0.5) the maximum acceptable fee as a percentage (e.g. 0.5 => 0.5%)" +}; +AUTODATA(json_command, &pay_command); diff --git a/lightningd/payalgo.h b/lightningd/payalgo.h new file mode 100644 index 000000000..971b80ba2 --- /dev/null +++ b/lightningd/payalgo.h @@ -0,0 +1,5 @@ +#ifndef LIGHTNING_LIGHTNINGD_PAYALGO_H +#define LIGHTNING_LIGHTNINGD_PAYALGO_H +#include "config.h" + +#endif /* LIGHTNING_LIGHTNINGD_PAYALGO_H */