From 354fafbf60090c3b5ddebe80a7f690796b7cee74 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj Date: Wed, 7 Feb 2018 13:17:58 +0000 Subject: [PATCH] pay: Add a limit to the fee. Fixes: #717 --- lightningd/pay.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/lightningd/pay.c b/lightningd/pay.c index b817bc806..6bed5650a 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -701,6 +701,8 @@ 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, @@ -708,6 +710,10 @@ static void json_pay_getroute_reply(struct subd *gossip, 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); @@ -717,14 +723,45 @@ static void json_pay_getroute_reply(struct subd *gossip, 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->payment_hash, route); } static void json_pay(struct command *cmd, const char *buffer, const jsmntok_t *params) { - jsmntok_t *bolt11tok, *msatoshitok, *desctok, *riskfactortok; + 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; @@ -736,6 +773,7 @@ static void json_pay(struct command *cmd, "?msatoshi", &msatoshitok, "?description", &desctok, "?riskfactor", &riskfactortok, + "?maxfeepercent", &maxfeetok, NULL)) { return; } @@ -776,6 +814,7 @@ static void json_pay(struct command *cmd, return; } } + pay->msatoshi = msatoshi; if (riskfactortok && !json_tok_double(buffer, riskfactortok, &riskfactor)) { @@ -785,6 +824,26 @@ static void json_pay(struct command *cmd, 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, @@ -797,7 +856,11 @@ static void json_pay(struct command *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) and {riskfactor} (default 1.0)" + "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);