Browse Source

wire: remove towire_double()

Before this patch we used to send `double`s over the wire by just
copying them. This is not portable because the internal represenation
of a `double` is implementation specific.

Instead of this, multiply any floating-point numbers that come from
the outside (e.g. JSONs) by 1 million and round them to integers when
handling them.

* Introduce a new param_millionths() that expects a floating-point
  number and returns it multipled by 1000000 as an integer.

* Replace param_double() and param_percent() with param_millionths()

* Previously the riskfactor would be allowed to be negative, which must
  have been unintentional. This patch changes that to require a
  non-negative number.

Changelog-None
travis-debug
Vasil Dimov 5 years ago
committed by Rusty Russell
parent
commit
89ceb273f5
  1. 33
      common/json_tok.c
  2. 17
      common/json_tok.h
  3. 65
      common/test/run-param.c
  4. 8
      doc/lightning-getroute.7
  5. 8
      doc/lightning-getroute.7.md
  6. 5
      gossipd/gossip_wire.csv
  7. 22
      gossipd/gossipd.c
  8. 40
      lightningd/gossip_control.c
  9. 52
      plugins/pay.c
  10. 1
      tools/generate-wire.py
  11. 5
      wire/fromwire.c
  12. 5
      wire/towire.c
  13. 2
      wire/wire.h

33
common/json_tok.c

@ -35,17 +35,21 @@ struct command_result *param_bool(struct command *cmd, const char *name,
name, tok->end - tok->start, buffer + tok->start); name, tok->end - tok->start, buffer + tok->start);
} }
struct command_result *param_double(struct command *cmd, const char *name, struct command_result *param_millionths(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok, const char *buffer,
double **num) const jsmntok_t *tok, uint64_t **num)
{ {
*num = tal(cmd, double); double d;
if (json_to_double(buffer, tok, *num)) if (json_to_double(buffer, tok, &d) && d >= 0.0) {
*num = tal(cmd, uint64_t);
**num = (uint64_t)(d * 1000000);
return NULL; return NULL;
}
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, return command_fail(
"'%s' should be a double, not '%.*s'", cmd, JSONRPC2_INVALID_PARAMS,
name, tok->end - tok->start, buffer + tok->start); "'%s' should be a non-negative floating-point number, not '%.*s'",
name, tok->end - tok->start, buffer + tok->start);
} }
struct command_result *param_escaped_string(struct command *cmd, struct command_result *param_escaped_string(struct command *cmd,
@ -128,19 +132,6 @@ struct command_result *param_sha256(struct command *cmd, const char *name,
name, tok->end - tok->start, buffer + tok->start); name, tok->end - tok->start, buffer + tok->start);
} }
struct command_result *param_percent(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
double **num)
{
*num = tal(cmd, double);
if (json_to_double(buffer, tok, *num) && **num >= 0.0)
return NULL;
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be a positive double, not '%.*s'",
name, tok->end - tok->start, buffer + tok->start);
}
struct command_result *param_u64(struct command *cmd, const char *name, struct command_result *param_u64(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok, const char *buffer, const jsmntok_t *tok,
uint64_t **num) uint64_t **num)

17
common/json_tok.h

@ -25,10 +25,14 @@ struct command_result *param_bool(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok, const char *buffer, const jsmntok_t *tok,
bool **b); bool **b);
/* Extract double from this (must be a number literal) */ /*
struct command_result *param_double(struct command *cmd, const char *name, * Extract a non-negative (either 0 or positive) floating-point number from this
const char *buffer, const jsmntok_t *tok, * (must be a number literal), multiply it by 1 million and return it as an
double **num); * integer.
*/
struct command_result *param_millionths(struct command *cmd, const char *name,
const char *buffer,
const jsmntok_t *tok, uint64_t **num);
/* Extract an escaped string (and unescape it) */ /* Extract an escaped string (and unescape it) */
struct command_result *param_escaped_string(struct command *cmd, struct command_result *param_escaped_string(struct command *cmd,
@ -57,11 +61,6 @@ struct command_result *param_sha256(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok, const char *buffer, const jsmntok_t *tok,
struct sha256 **hash); struct sha256 **hash);
/* Extract double in range [0.0, 100.0] */
struct command_result *param_percent(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
double **num);
/* Extract number from this (may be a string, or a number literal) */ /* Extract number from this (may be a string, or a number literal) */
struct command_result *param_u64(struct command *cmd, const char *name, struct command_result *param_u64(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok, const char *buffer, const jsmntok_t *tok,

65
common/test/run-param.c

@ -131,17 +131,17 @@ struct sanity {
char *str; char *str;
bool failed; bool failed;
int ival; int ival;
double dval; u64 fpval; /* floating-point, multiplied by 1000000 */
char *fail_str; char *fail_str;
}; };
struct sanity buffers[] = { struct sanity buffers[] = {
// pass // pass
{"['42', '3.15']", false, 42, 3.15, NULL}, {"['42', '3.15']", false, 42, 3150000, NULL},
{"{ 'u64' : '42', 'double' : '3.15' }", false, 42, 3.15, NULL}, {"{ 'u64' : '42', 'fp' : '3.15' }", false, 42, 3150000, NULL},
// fail // fail
{"{'u64':'42', 'double':'3.15', 'extra':'stuff'}", true, 0, 0, {"{'u64':'42', 'fp':'3.15', 'extra':'stuff'}", true, 0, 0,
"unknown parameter"}, "unknown parameter"},
{"['42', '3.15', 'stuff']", true, 0, 0, "too many"}, {"['42', '3.15', 'stuff']", true, 0, 0, "too many"},
{"['42', '3.15', 'null']", true, 0, 0, "too many"}, {"['42', '3.15', 'null']", true, 0, 0, "too many"},
@ -151,17 +151,17 @@ struct sanity buffers[] = {
{"['42']", true, 0, 0, "missing required"}, {"['42']", true, 0, 0, "missing required"},
// fail wrong type // fail wrong type
{"{'u64':'hello', 'double':'3.15'}", true, 0, 0, "be an unsigned 64"}, {"{'u64':'hello', 'fp':'3.15'}", true, 0, 0, "be an unsigned 64"},
{"['3.15', '3.15', 'stuff']", true, 0, 0, "integer"}, {"['3.15', '3.15', 'stuff']", true, 0, 0, "integer"},
}; };
static void stest(const struct json *j, struct sanity *b) static void stest(const struct json *j, struct sanity *b)
{ {
u64 *ival; u64 *ival;
double *dval; u64 *fpval;
if (!param(cmd, j->buffer, j->toks, if (!param(cmd, j->buffer, j->toks,
p_req("u64", param_u64, &ival), p_req("u64", param_u64, &ival),
p_req("double", param_double, &dval), NULL)) { p_req("fp", param_millionths, &fpval), NULL)) {
assert(check_fail()); assert(check_fail());
assert(b->failed == true); assert(b->failed == true);
if (!strstr(fail_msg, b->fail_str)) { if (!strstr(fail_msg, b->fail_str)) {
@ -172,7 +172,7 @@ static void stest(const struct json *j, struct sanity *b)
assert(!check_fail()); assert(!check_fail());
assert(b->failed == false); assert(b->failed == false);
assert(*ival == 42); assert(*ival == 42);
assert(*dval > 3.1499 && b->dval < 3.1501); assert(*fpval > 3149900 && b->fpval < 3150100);
} }
} }
@ -222,13 +222,13 @@ static void dup_names(void)
{ {
struct json *j = struct json *j =
json_parse(cmd, json_parse(cmd,
"{ 'u64' : '42', 'u64' : '43', 'double' : '3.15' }"); "{ 'u64' : '42', 'u64' : '43', 'fp' : '3.15' }");
u64 *i; u64 *i;
double *d; u64 *fp;
assert(!param(cmd, j->buffer, j->toks, assert(!param(cmd, j->buffer, j->toks,
p_req("u64", param_u64, &i), p_req("u64", param_u64, &i),
p_req("double", param_double, &d), NULL)); p_req("fp", param_millionths, &fp), NULL));
} }
static void null_params(void) static void null_params(void)
@ -290,28 +290,28 @@ static void bad_programmer(void)
{ {
u64 *ival; u64 *ival;
u64 *ival2; u64 *ival2;
double *dval; u64 *fpval;
struct json *j = json_parse(cmd, "[ '25', '546', '26' ]"); struct json *j = json_parse(cmd, "[ '25', '546', '26' ]");
/* check for repeated names */ /* check for repeated names */
assert(!param(cmd, j->buffer, j->toks, assert(!param(cmd, j->buffer, j->toks,
p_req("repeat", param_u64, &ival), p_req("repeat", param_u64, &ival),
p_req("double", param_double, &dval), p_req("fp", param_millionths, &fpval),
p_req("repeat", param_u64, &ival2), NULL)); p_req("repeat", param_u64, &ival2), NULL));
assert(check_fail()); assert(check_fail());
assert(strstr(fail_msg, "developer error")); assert(strstr(fail_msg, "developer error"));
assert(!param(cmd, j->buffer, j->toks, assert(!param(cmd, j->buffer, j->toks,
p_req("repeat", param_u64, &ival), p_req("repeat", param_u64, &ival),
p_req("double", param_double, &dval), p_req("fp", param_millionths, &fpval),
p_req("repeat", param_u64, &ival), NULL)); p_req("repeat", param_u64, &ival), NULL));
assert(check_fail()); assert(check_fail());
assert(strstr(fail_msg, "developer error")); assert(strstr(fail_msg, "developer error"));
assert(!param(cmd, j->buffer, j->toks, assert(!param(cmd, j->buffer, j->toks,
p_req("u64", param_u64, &ival), p_req("u64", param_u64, &ival),
p_req("repeat", param_double, &dval), p_req("repeat", param_millionths, &fpval),
p_req("repeat", param_double, &dval), NULL)); p_req("repeat", param_millionths, &fpval), NULL));
assert(check_fail()); assert(check_fail());
assert(strstr(fail_msg, "developer error")); assert(strstr(fail_msg, "developer error"));
@ -330,12 +330,13 @@ static void bad_programmer(void)
/* Add required param after optional */ /* Add required param after optional */
j = json_parse(cmd, "[ '25', '546', '26', '1.1' ]"); j = json_parse(cmd, "[ '25', '546', '26', '1.1' ]");
unsigned int *msatoshi; unsigned int *msatoshi;
double *riskfactor; u64 *riskfactor_millionths;
assert(!param(cmd, j->buffer, j->toks, assert(!param(
p_req("u64", param_u64, &ival), cmd, j->buffer, j->toks, p_req("u64", param_u64, &ival),
p_req("double", param_double, &dval), p_req("fp", param_millionths, &fpval),
p_opt_def("msatoshi", param_number, &msatoshi, 100), p_opt_def("msatoshi", param_number, &msatoshi, 100),
p_req("riskfactor", param_double, &riskfactor), NULL)); p_req("riskfactor", param_millionths, &riskfactor_millionths),
NULL));
assert(*msatoshi); assert(*msatoshi);
assert(*msatoshi == 100); assert(*msatoshi == 100);
assert(check_fail()); assert(check_fail());
@ -525,16 +526,16 @@ static void param_tests(void)
test_cb(param_bool, bool, "[ tru ]", false, false); test_cb(param_bool, bool, "[ tru ]", false, false);
test_cb(param_bool, bool, "[ 1 ]", false, false); test_cb(param_bool, bool, "[ 1 ]", false, false);
test_cb(param_percent, double, "[ -0.01 ]", 0, false); test_cb(param_millionths, u64, "[ -0.01 ]", 0, false);
test_cb(param_percent, double, "[ 0.00 ]", 0, true); test_cb(param_millionths, u64, "[ 0.00 ]", 0, true);
test_cb(param_percent, double, "[ 1 ]", 1, true); test_cb(param_millionths, u64, "[ 1 ]", 1000000, true);
test_cb(param_percent, double, "[ 1.1 ]", 1.1, true); test_cb(param_millionths, u64, "[ 1.1 ]", 1100000, true);
test_cb(param_percent, double, "[ 1.01 ]", 1.01, true); test_cb(param_millionths, u64, "[ 1.01 ]", 1010000, true);
test_cb(param_percent, double, "[ 99.99 ]", 99.99, true); test_cb(param_millionths, u64, "[ 99.99 ]", 99990000, true);
test_cb(param_percent, double, "[ 100.0 ]", 100, true); test_cb(param_millionths, u64, "[ 100.0 ]", 100000000, true);
test_cb(param_percent, double, "[ 100.001 ]", 100.001, true); test_cb(param_millionths, u64, "[ 100.001 ]", 100001000, true);
test_cb(param_percent, double, "[ 1000 ]", 1000, true); test_cb(param_millionths, u64, "[ 1000 ]", 1000000000, true);
test_cb(param_percent, double, "[ 'wow' ]", 0, false); test_cb(param_millionths, u64, "[ 'wow' ]", 0, false);
} }
static void test_invoice(struct command *cmd, static void test_invoice(struct command *cmd,

8
doc/lightning-getroute.7

@ -21,9 +21,9 @@ in \fIbtc\fR\.
There are two considerations for how good a route is: how low the fees There are two considerations for how good a route is: how low the fees
are, and how long your payment will get stuck in a delayed output if a are, and how long your payment will get stuck in a delayed output if a
node goes down during the process\. The \fIriskfactor\fR floating-point field node goes down during the process\. The \fIriskfactor\fR non-negative
controls this tradeoff; it is the annual cost of your funds being stuck floating-point field controls this tradeoff; it is the annual cost of
(as a percentage)\. your funds being stuck (as a percentage)\.
For example, if you thought the convenience of keeping your funds liquid For example, if you thought the convenience of keeping your funds liquid
@ -36,7 +36,7 @@ If you didn’t care about risk, \fIriskfactor\fR would be zero\.
\fIfromid\fR is the node to start the route from: default is this node\. \fIfromid\fR is the node to start the route from: default is this node\.
The \fIfuzzpercent\fR is a positive floating-point number, representing a The \fIfuzzpercent\fR is a non-negative floating-point number, representing a
percentage of the actual fee\. The \fIfuzzpercent\fR is used to distort percentage of the actual fee\. The \fIfuzzpercent\fR is used to distort
computed fees along each channel, to provide some randomization to the computed fees along each channel, to provide some randomization to the
route generated\. 0\.0 means the exact fee of that channel is used, while route generated\. 0\.0 means the exact fee of that channel is used, while

8
doc/lightning-getroute.7.md

@ -21,9 +21,9 @@ in *btc*.
There are two considerations for how good a route is: how low the fees There are two considerations for how good a route is: how low the fees
are, and how long your payment will get stuck in a delayed output if a are, and how long your payment will get stuck in a delayed output if a
node goes down during the process. The *riskfactor* floating-point field node goes down during the process. The *riskfactor* non-negative
controls this tradeoff; it is the annual cost of your funds being stuck floating-point field controls this tradeoff; it is the annual cost of
(as a percentage). your funds being stuck (as a percentage).
For example, if you thought the convenience of keeping your funds liquid For example, if you thought the convenience of keeping your funds liquid
(not stuck) was worth 20% per annum interest, *riskfactor* would be 20. (not stuck) was worth 20% per annum interest, *riskfactor* would be 20.
@ -32,7 +32,7 @@ If you didn’t care about risk, *riskfactor* would be zero.
*fromid* is the node to start the route from: default is this node. *fromid* is the node to start the route from: default is this node.
The *fuzzpercent* is a positive floating-point number, representing a The *fuzzpercent* is a non-negative floating-point number, representing a
percentage of the actual fee. The *fuzzpercent* is used to distort percentage of the actual fee. The *fuzzpercent* is used to distort
computed fees along each channel, to provide some randomization to the computed fees along each channel, to provide some randomization to the
route generated. 0.0 means the exact fee of that channel is used, while route generated. 0.0 means the exact fee of that channel is used, while

5
gossipd/gossip_wire.csv

@ -35,10 +35,9 @@ msgtype,gossip_getroute_request,3006
msgdata,gossip_getroute_request,source,?node_id, msgdata,gossip_getroute_request,source,?node_id,
msgdata,gossip_getroute_request,destination,node_id, msgdata,gossip_getroute_request,destination,node_id,
msgdata,gossip_getroute_request,msatoshi,amount_msat, msgdata,gossip_getroute_request,msatoshi,amount_msat,
# We don't pass doubles, so pass riskfactor 1000000. msgdata,gossip_getroute_request,riskfactor_millionths,u64,
msgdata,gossip_getroute_request,riskfactor_by_million,u64,
msgdata,gossip_getroute_request,final_cltv,u32, msgdata,gossip_getroute_request,final_cltv,u32,
msgdata,gossip_getroute_request,fuzz,double, msgdata,gossip_getroute_request,fuzz_millionths,u64,
msgdata,gossip_getroute_request,num_excluded,u16, msgdata,gossip_getroute_request,num_excluded,u16,
msgdata,gossip_getroute_request,excluded,exclude_entry,num_excluded msgdata,gossip_getroute_request,excluded,exclude_entry,num_excluded
msgdata,gossip_getroute_request,max_hops,u32, msgdata,gossip_getroute_request,max_hops,u32,

Can't render this file because it has a wrong number of fields in line 6.

22
gossipd/gossipd.c

@ -883,11 +883,13 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon,
struct node_id *source, destination; struct node_id *source, destination;
struct amount_msat msat; struct amount_msat msat;
u32 final_cltv; u32 final_cltv;
u64 riskfactor_by_million; /* risk factor 12.345% -> riskfactor_millionths = 12345000 */
u64 riskfactor_millionths;
u32 max_hops; u32 max_hops;
u8 *out; u8 *out;
struct route_hop *hops; struct route_hop *hops;
double fuzz; /* fuzz 12.345% -> fuzz_millionths = 12345000 */
u64 fuzz_millionths;
struct exclude_entry **excluded; struct exclude_entry **excluded;
/* To choose between variations, we need to know how much we're /* To choose between variations, we need to know how much we're
@ -901,12 +903,9 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon,
* for a route from ourselves (the usual case): in that case, * for a route from ourselves (the usual case): in that case,
* we don't have to consider fees on our own outgoing channels. * we don't have to consider fees on our own outgoing channels.
*/ */
if (!fromwire_gossip_getroute_request(msg, msg, if (!fromwire_gossip_getroute_request(
&source, &destination, msg, msg, &source, &destination, &msat, &riskfactor_millionths,
&msat, &riskfactor_by_million, &final_cltv, &fuzz_millionths, &excluded, &max_hops))
&final_cltv, &fuzz,
&excluded,
&max_hops))
master_badmsg(WIRE_GOSSIP_GETROUTE_REQUEST, msg); master_badmsg(WIRE_GOSSIP_GETROUTE_REQUEST, msg);
status_debug("Trying to find a route from %s to %s for %s", status_debug("Trying to find a route from %s to %s for %s",
@ -916,9 +915,10 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon,
type_to_string(tmpctx, struct amount_msat, &msat)); type_to_string(tmpctx, struct amount_msat, &msat));
/* routing.c does all the hard work; can return NULL. */ /* routing.c does all the hard work; can return NULL. */
hops = get_route(tmpctx, daemon->rstate, source, &destination, hops = get_route(tmpctx, daemon->rstate, source, &destination, msat,
msat, riskfactor_by_million / 1000000.0, final_cltv, riskfactor_millionths / 1000000.0, final_cltv,
fuzz, pseudorand_u64(), excluded, max_hops); fuzz_millionths / 1000000.0, pseudorand_u64(),
excluded, max_hops);
out = towire_gossip_getroute_reply(NULL, hops); out = towire_gossip_getroute_reply(NULL, hops);
daemon_conn_send(daemon->master, take(out)); daemon_conn_send(daemon->master, take(out));

40
lightningd/gossip_control.c

@ -381,7 +381,8 @@ static struct command_result *json_getroute(struct command *cmd,
const jsmntok_t *excludetok; const jsmntok_t *excludetok;
struct amount_msat *msat; struct amount_msat *msat;
u32 *cltv; u32 *cltv;
double *riskfactor; /* risk factor 12.345% -> riskfactor_millionths = 12345000 */
u64 *riskfactor_millionths;
const struct exclude_entry **excluded; const struct exclude_entry **excluded;
u32 *max_hops; u32 *max_hops;
@ -390,23 +391,23 @@ static struct command_result *json_getroute(struct command *cmd,
* randomization (the higher-fee paths become more likely to * randomization (the higher-fee paths become more likely to
* be selected) at the cost of increasing the probability of * be selected) at the cost of increasing the probability of
* selecting the higher-fee paths. */ * selecting the higher-fee paths. */
double *fuzz; u64 *fuzz_millionths; /* fuzz 12.345% -> fuzz_millionths = 12345000 */
if (!param(cmd, buffer, params, if (!param(
p_req("id", param_node_id, &destination), cmd, buffer, params, p_req("id", param_node_id, &destination),
p_req("msatoshi", param_msat, &msat), p_req("msatoshi", param_msat, &msat),
p_req("riskfactor", param_double, &riskfactor), p_req("riskfactor", param_millionths, &riskfactor_millionths),
p_opt_def("cltv", param_number, &cltv, 9), p_opt_def("cltv", param_number, &cltv, 9),
p_opt("fromid", param_node_id, &source), p_opt("fromid", param_node_id, &source),
p_opt_def("fuzzpercent", param_percent, &fuzz, 5.0), p_opt_def("fuzzpercent", param_millionths, &fuzz_millionths,
p_opt("exclude", param_array, &excludetok), 5000000),
p_opt_def("maxhops", param_number, &max_hops, p_opt("exclude", param_array, &excludetok),
ROUTING_MAX_HOPS), p_opt_def("maxhops", param_number, &max_hops, ROUTING_MAX_HOPS),
NULL)) NULL))
return command_param_failed(); return command_param_failed();
/* Convert from percentage */ /* Convert from percentage */
*fuzz = *fuzz / 100.0; *fuzz_millionths /= 100;
if (excludetok) { if (excludetok) {
const jsmntok_t *t; const jsmntok_t *t;
@ -442,12 +443,9 @@ static struct command_result *json_getroute(struct command *cmd,
excluded = NULL; excluded = NULL;
} }
u8 *req = towire_gossip_getroute_request(cmd, source, destination, u8 *req = towire_gossip_getroute_request(
*msat, cmd, source, destination, *msat, *riskfactor_millionths, *cltv,
*riskfactor * 1000000.0, *fuzz_millionths, excluded, *max_hops);
*cltv, fuzz,
excluded,
*max_hops);
subd_req(ld->gossip, ld->gossip, req, -1, 0, json_getroute_reply, cmd); subd_req(ld->gossip, ld->gossip, req, -1, 0, json_getroute_reply, cmd);
return command_still_pending(cmd); return command_still_pending(cmd);
} }

52
plugins/pay.c

@ -13,7 +13,9 @@
#include <common/json_stream.h> #include <common/json_stream.h>
#include <common/pseudorand.h> #include <common/pseudorand.h>
#include <common/type_to_string.h> #include <common/type_to_string.h>
#include <inttypes.h>
#include <plugins/libplugin.h> #include <plugins/libplugin.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <wire/onion_defs.h> #include <wire/onion_defs.h>
#include <wire/wire.h> #include <wire/wire.h>
@ -83,11 +85,13 @@ struct pay_command {
/* How much we're paying, and what riskfactor for routing. */ /* How much we're paying, and what riskfactor for routing. */
struct amount_msat msat; struct amount_msat msat;
double riskfactor; /* riskfactor 12.345% -> riskfactor_millionths = 12345000 */
u64 riskfactor_millionths;
unsigned int final_cltv; unsigned int final_cltv;
/* Limits on what routes we'll accept. */ /* Limits on what routes we'll accept. */
double maxfeepercent; /* 12.345% -> maxfee_pct_millionths = 12345000 */
u64 maxfee_pct_millionths;
unsigned int maxdelay; unsigned int maxdelay;
struct amount_msat exemptfee; struct amount_msat exemptfee;
@ -719,8 +723,8 @@ static struct command_result *getroute_done(struct command *cmd,
struct pay_attempt *attempt = current_attempt(pc); struct pay_attempt *attempt = current_attempt(pc);
const jsmntok_t *t = json_get_member(buf, result, "route"); const jsmntok_t *t = json_get_member(buf, result, "route");
struct amount_msat fee; struct amount_msat fee;
struct amount_msat max_fee;
u32 delay; u32 delay;
double feepercent;
struct out_req *req; struct out_req *req;
if (!t) if (!t)
@ -752,14 +756,19 @@ static struct command_result *getroute_done(struct command *cmd,
plugin_err(cmd->plugin, "getroute with invalid delay? %.*s", plugin_err(cmd->plugin, "getroute with invalid delay? %.*s",
result->end - result->start, buf); result->end - result->start, buf);
/* Casting u64 to double will lose some precision. The loss of precision if (pc->maxfee_pct_millionths / 100 > UINT32_MAX)
* in feepercent will be like 3.0000..(some dots)..1 % - 3.0 %. plugin_err(cmd->plugin, "max fee percent too large: %lf",
* That loss will not be representable in double. So, it's Okay to pc->maxfee_pct_millionths / 1000000.0);
* cast u64 to double for feepercent calculation. */
feepercent = ((double)fee.millisatoshis) * 100.0 / ((double) pc->msat.millisatoshis); /* Raw: fee double manipulation */
if (amount_msat_greater(fee, pc->exemptfee) if (!amount_msat_fee(&max_fee, pc->msat, 0,
&& feepercent > pc->maxfeepercent) { (u32)(pc->maxfee_pct_millionths / 100)))
plugin_err(
cmd->plugin, "max fee too large: %s * %lf%%",
type_to_string(tmpctx, struct amount_msat, &pc->msat),
pc->maxfee_pct_millionths / 1000000.0);
if (amount_msat_greater(fee, pc->exemptfee) &&
amount_msat_greater(fee, max_fee)) {
const jsmntok_t *charger; const jsmntok_t *charger;
struct json_out *failed; struct json_out *failed;
char *feemsg; char *feemsg;
@ -916,7 +925,8 @@ static struct command_result *execute_getroute(struct command *cmd,
type_to_string(tmpctx, struct amount_msat, &msat)); type_to_string(tmpctx, struct amount_msat, &msat));
json_add_u32(req->js, "cltv", cltv); json_add_u32(req->js, "cltv", cltv);
json_add_u32(req->js, "maxhops", max_hops); json_add_u32(req->js, "maxhops", max_hops);
json_add_member(req->js, "riskfactor", false, "%f", pc->riskfactor); json_add_member(req->js, "riskfactor", false, "%lf",
pc->riskfactor_millionths / 1000000.0);
if (tal_count(pc->excludes) != 0) { if (tal_count(pc->excludes) != 0) {
json_array_start(req->js, "exclude"); json_array_start(req->js, "exclude");
for (size_t i = 0; i < tal_count(pc->excludes); i++) for (size_t i = 0; i < tal_count(pc->excludes); i++)
@ -1245,10 +1255,10 @@ static struct command_result *json_pay(struct command *cmd,
struct bolt11 *b11; struct bolt11 *b11;
const char *b11str; const char *b11str;
char *fail; char *fail;
double *riskfactor; u64 *riskfactor_millionths;
unsigned int *retryfor; unsigned int *retryfor;
struct pay_command *pc = tal(cmd, struct pay_command); struct pay_command *pc = tal(cmd, struct pay_command);
double *maxfeepercent; u64 *maxfee_pct_millionths;
unsigned int *maxdelay; unsigned int *maxdelay;
struct amount_msat *exemptfee; struct amount_msat *exemptfee;
struct out_req *req; struct out_req *req;
@ -1256,16 +1266,18 @@ static struct command_result *json_pay(struct command *cmd,
bool *use_shadow; bool *use_shadow;
#endif #endif
if (!param(cmd, buf, params, if (!param(cmd, buf, params, p_req("bolt11", param_string, &b11str),
p_req("bolt11", param_string, &b11str),
p_opt("msatoshi", param_msat, &msat), p_opt("msatoshi", param_msat, &msat),
p_opt("label", param_string, &pc->label), p_opt("label", param_string, &pc->label),
p_opt_def("riskfactor", param_double, &riskfactor, 10), p_opt_def("riskfactor", param_millionths,
p_opt_def("maxfeepercent", param_percent, &maxfeepercent, 0.5), &riskfactor_millionths, 10000000),
p_opt_def("maxfeepercent", param_millionths,
&maxfee_pct_millionths, 500000),
p_opt_def("retry_for", param_number, &retryfor, 60), p_opt_def("retry_for", param_number, &retryfor, 60),
p_opt_def("maxdelay", param_number, &maxdelay, p_opt_def("maxdelay", param_number, &maxdelay,
maxdelay_default), maxdelay_default),
p_opt_def("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)), p_opt_def("exemptfee", param_msat, &exemptfee,
AMOUNT_MSAT(5000)),
#if DEVELOPER #if DEVELOPER
p_opt_def("use_shadow", param_bool, &use_shadow, true), p_opt_def("use_shadow", param_bool, &use_shadow, true),
#endif #endif
@ -1312,10 +1324,10 @@ static struct command_result *json_pay(struct command *cmd,
" sets feature var_onion with no secret"); " sets feature var_onion with no secret");
} }
pc->maxfeepercent = *maxfeepercent; pc->maxfee_pct_millionths = *maxfee_pct_millionths;
pc->maxdelay = *maxdelay; pc->maxdelay = *maxdelay;
pc->exemptfee = *exemptfee; pc->exemptfee = *exemptfee;
pc->riskfactor = *riskfactor; pc->riskfactor_millionths = *riskfactor_millionths;
pc->final_cltv = b11->min_final_cltv_expiry; pc->final_cltv = b11->min_final_cltv_expiry;
pc->dest = type_to_string(cmd, struct node_id, &b11->receiver_id); pc->dest = type_to_string(cmd, struct node_id, &b11->receiver_id);
pc->shadow_dest = tal_strdup(pc, pc->dest); pc->shadow_dest = tal_strdup(pc, pc->dest);

1
tools/generate-wire.py

@ -199,7 +199,6 @@ class Type(FieldSet):
'secp256k1_ecdsa_signature', 'secp256k1_ecdsa_signature',
'secp256k1_ecdsa_recoverable_signature', 'secp256k1_ecdsa_recoverable_signature',
'wirestring', 'wirestring',
'double',
'errcode_t', 'errcode_t',
'bigsize', 'bigsize',
'varint', 'varint',

5
wire/fromwire.c

@ -151,11 +151,6 @@ u64 fromwire_tu64(const u8 **cursor, size_t *max)
return fromwire_tlv_uint(cursor, max, 8); return fromwire_tlv_uint(cursor, max, 8);
} }
void fromwire_double(const u8 **cursor, size_t *max, double *ret)
{
fromwire(cursor, max, ret, sizeof(*ret));
}
bool fromwire_bool(const u8 **cursor, size_t *max) bool fromwire_bool(const u8 **cursor, size_t *max)
{ {
u8 ret; u8 ret;

5
wire/towire.c

@ -77,11 +77,6 @@ void towire_tu64(u8 **pptr, u64 v)
return towire_tlv_uint(pptr, v); return towire_tlv_uint(pptr, v);
} }
void towire_double(u8 **pptr, const double *v)
{
towire(pptr, v, sizeof(*v));
}
void towire_bool(u8 **pptr, bool v) void towire_bool(u8 **pptr, bool v)
{ {
u8 val = v; u8 val = v;

2
wire/wire.h

@ -78,7 +78,6 @@ void towire_u64(u8 **pptr, u64 v);
void towire_tu16(u8 **pptr, u16 v); void towire_tu16(u8 **pptr, u16 v);
void towire_tu32(u8 **pptr, u32 v); void towire_tu32(u8 **pptr, u32 v);
void towire_tu64(u8 **pptr, u64 v); void towire_tu64(u8 **pptr, u64 v);
void towire_double(u8 **pptr, const double *v);
void towire_pad(u8 **pptr, size_t num); void towire_pad(u8 **pptr, size_t num);
void towire_bool(u8 **pptr, bool v); void towire_bool(u8 **pptr, bool v);
void towire_errcode_t(u8 **pptr, errcode_t v); void towire_errcode_t(u8 **pptr, errcode_t v);
@ -103,7 +102,6 @@ u64 fromwire_u64(const u8 **cursor, size_t *max);
u16 fromwire_tu16(const u8 **cursor, size_t *max); u16 fromwire_tu16(const u8 **cursor, size_t *max);
u32 fromwire_tu32(const u8 **cursor, size_t *max); u32 fromwire_tu32(const u8 **cursor, size_t *max);
u64 fromwire_tu64(const u8 **cursor, size_t *max); u64 fromwire_tu64(const u8 **cursor, size_t *max);
void fromwire_double(const u8 **cursor, size_t *max, double *v);
bool fromwire_bool(const u8 **cursor, size_t *max); bool fromwire_bool(const u8 **cursor, size_t *max);
errcode_t fromwire_errcode_t(const u8 **cursor, size_t *max); errcode_t fromwire_errcode_t(const u8 **cursor, size_t *max);
bigsize_t fromwire_bigsize(const u8 **cursor, size_t *max); bigsize_t fromwire_bigsize(const u8 **cursor, size_t *max);

Loading…
Cancel
Save