diff --git a/doc/lightning-getroute.7 b/doc/lightning-getroute.7 index d5405be09e5e..e26a7b0604a1 100644 --- a/doc/lightning-getroute.7 +++ b/doc/lightning-getroute.7 @@ -2,12 +2,12 @@ .\" Title: lightning-getroute .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 01/16/2018 +.\" Date: 02/19/2018 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-GETROUTE" "7" "01/16/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-GETROUTE" "7" "02/19/2018" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -31,7 +31,7 @@ lightning-getroute \- Protocol for routing a payment (low\-level)\&. .SH "SYNOPSIS" .sp -\fBgetroute\fR \fIid\fR \fImsatoshi\fR \fIriskfactor\fR [\fIcltv\fR] +\fBgetroute\fR \fIid\fR \fImsatoshi\fR \fIriskfactor\fR [\fIcltv\fR] [\fIfuzzpercent\fR] [\fIseed\fR] .SH "DESCRIPTION" .sp The \fBgetroute\fR RPC command attempts to find the best route for the payment of \fImsatoshi\fR to lightning node \fIid\fR, such that the payment will arrive at \fIid\fR with \fIcltv\fR\-blocks to spare (default 9)\&. @@ -41,6 +41,10 @@ There are two considerations for how good a route is: how low the fees are, and For example, if you thought there was a 1% chance that a node would fail, and it would cost you 20% per annum if that happened, \fIriskfactor\fR would be 20\&. .sp If you didn\(cqt care about risk, \fIriskfactor\fR would be zero\&. +.sp +The \fIfuzzpercent\fR is a floating\-point number, between 0\&.0 to 100\&.0, unit of percent\&. The \fIfuzzpercent\fR is used to distort 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 100\&.0 means the fee used might be from 0 to twice the actual fee\&. The default is 5\&.0, or up to 5% fee distortion\&. +.sp +The \fIseed\fR is a string whose bytes are used to seed the RNG for the route randomization\&. If not specified, a random string is used\&. .SH "RISKFACTOR EFFECT ON ROUTING" .sp The risk factor is treated as if it were an additional fee on the route, for the purposes of comparing routes\&. diff --git a/doc/lightning-getroute.7.txt b/doc/lightning-getroute.7.txt index c321f62e5a86..8cdd6fadc238 100644 --- a/doc/lightning-getroute.7.txt +++ b/doc/lightning-getroute.7.txt @@ -9,7 +9,7 @@ lightning-getroute - Protocol for routing a payment (low-level). SYNOPSIS -------- -*getroute* 'id' 'msatoshi' 'riskfactor' ['cltv'] +*getroute* 'id' 'msatoshi' 'riskfactor' ['cltv'] ['fuzzpercent'] ['seed'] DESCRIPTION ----------- @@ -29,6 +29,19 @@ fail, and it would cost you 20% per annum if that happened, If you didn't care about risk, 'riskfactor' would be zero. +The 'fuzzpercent' is a floating-point number, between 0.0 to 100.0, +unit of percent. +The 'fuzzpercent' is used to distort 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 100.0 means the fee used might be from 0 to twice the actual fee. +The default is 5.0, or up to 5% fee distortion. + +The 'seed' is a string whose bytes are used to seed the RNG for +the route randomization. +If not specified, a random string is used. + + RISKFACTOR EFFECT ON ROUTING ---------------------------- The risk factor is treated as if it were an additional fee on the route, diff --git a/doc/lightning-pay.7 b/doc/lightning-pay.7 index c0cbfc09928e..39d169ba3318 100644 --- a/doc/lightning-pay.7 +++ b/doc/lightning-pay.7 @@ -2,12 +2,12 @@ .\" Title: lightning-pay .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 02/17/2018 +.\" Date: 02/19/2018 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-PAY" "7" "02/17/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-PAY" "7" "02/19/2018" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -36,12 +36,14 @@ lightning-pay \- Protocol for sending a payment to a BOLT11 invoice .sp The \fBpay\fR RPC command attempts to find a route to the given destination, and send the funds it asks for\&. If the \fIbolt11\fR does not contain an amount, \fImsatoshi\fR is required, otherwise if it is specified it must be \fInull\fR\&. If \fIbolt11\fR contains a description hash (\fIh\fR field) \fIdescription\fR is required, otherwise it is unused\&. The \fIriskfactor\fR is described in detail in lightning\-getroute(7), and defaults to 1\&.0\&. The \fImaxfeepercent\fR limits the money paid in fees, and defaults to 0\&.5\&. The \(oqmaxfeepercent\(cq is a percentage of the amount that is to be paid\&. .sp +The \fBpay\fR RPC command will randomize routes slightly, as long as the route achieves the targeted \fImaxfeepercent\fR\&. +.sp The response will occur when the payment fails or succeeds\&. Once a payment has succeeded, calls to \fBpay\fR with the same \fIbolt11\fR will succeed immediately\&. .sp When using \fIlightning\-cli\fR, you may skip optional parameters by using \fInull\fR\&. Alternatively, use \fB\-k\fR option to provide parameters by name\&. .SH "RETURN VALUE" .sp -On success, this returns the payment \fIpreimage\fR which hashes to the \fIpayment_hash\fR to prove that the payment was successful\&. +On success, this returns the payment \fIpreimage\fR which hashes to the \fIpayment_hash\fR to prove that the payment was successful\&. It will also return, a \fIgetroute_tries\fR and a \fIsendpay_tries\fR statistics for the number of times it internally called \fBgetroute\fR and \fBsendpay\fR\&. .sp On error, if the error occurred from a node other than the final destination, the route table will be updated so that getroute(7) should return an alternate route (if any)\&. An error from the final destination implies the payment should not be retried\&. .sp diff --git a/doc/lightning-pay.7.txt b/doc/lightning-pay.7.txt index a9b019c395b0..73053a94dc3c 100644 --- a/doc/lightning-pay.7.txt +++ b/doc/lightning-pay.7.txt @@ -23,6 +23,9 @@ The 'maxfeepercent' limits the money paid in fees, and defaults to 0.5. The `maxfeepercent' is a percentage of the amount that is to be paid. +The *pay* RPC command will randomize routes slightly, as long as the +route achieves the targeted 'maxfeepercent'. + The response will occur when the payment fails or succeeds. Once a payment has succeeded, calls to *pay* with the same 'bolt11' will succeed immediately. @@ -36,6 +39,9 @@ RETURN VALUE On success, this returns the payment 'preimage' which hashes to the 'payment_hash' to prove that the payment was successful. +It will also return, a 'getroute_tries' and a 'sendpay_tries' +statistics for the number of times it internally called *getroute* +and *sendpay*. On error, if the error occurred from a node other than the final destination, the route table will be updated so that getroute(7) diff --git a/gossipd/gossip.c b/gossipd/gossip.c index 22fed49d7e2e..26779fb0f1be 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -1047,15 +1048,29 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon, u16 riskfactor; u8 *out; struct route_hop *hops; - - fromwire_gossip_getroute_request(msg, &source, &destination, - &msatoshi, &riskfactor, &final_cltv); + double fuzz; + u8 *rawseed; + struct siphash_seed seed; + size_t seedbytes; + + fromwire_gossip_getroute_request(tmpctx, msg, + &source, &destination, + &msatoshi, &riskfactor, &final_cltv, + &fuzz, &rawseed); status_trace("Trying to find a route from %s to %s for %d msatoshi", pubkey_to_hexstr(tmpctx, &source), pubkey_to_hexstr(tmpctx, &destination), msatoshi); + /* Initialize siphash */ + memset(&seed, 0, sizeof(seed)); + seedbytes = + (tal_len(rawseed) > sizeof(seed)) ? sizeof(seed) : + /*otherwise*/ tal_len(rawseed) ; + memcpy(&seed, rawseed, seedbytes); + hops = get_route(tmpctx, daemon->rstate, &source, &destination, - msatoshi, 1, final_cltv); + msatoshi, 1, final_cltv, + fuzz, &seed); out = towire_gossip_getroute_reply(msg, hops); tal_free(tmpctx); diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 8f8d6f2654c8..469a041f2e99 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -107,6 +107,9 @@ gossip_getroute_request,,destination,struct pubkey gossip_getroute_request,,msatoshi,u32 gossip_getroute_request,,riskfactor,u16 gossip_getroute_request,,final_cltv,u32 +gossip_getroute_request,,fuzz,double +gossip_getroute_request,,seedlen,u16 +gossip_getroute_request,,seed,seedlen*u8 gossip_getroute_reply,3106 gossip_getroute_reply,,num_hops,u16 diff --git a/gossipd/routing.c b/gossipd/routing.c index 6392b43e053b..9007c102d5e6 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -331,10 +330,24 @@ static u64 risk_fee(u64 amount, u32 delay, double riskfactor) /* We track totals, rather than costs. That's because the fee depends * on the current amount passing through. */ -static void bfg_one_edge(struct node *node, size_t edgenum, double riskfactor) +static void bfg_one_edge(struct node *node, size_t edgenum, double riskfactor, + double fuzz, const struct siphash_seed *base_seed) { struct node_connection *c = node->in[edgenum]; size_t h; + double fee_scale = 1.0; + + if (fuzz != 0.0) { + u64 scid = short_channel_id_to_uint(&c->short_channel_id); + u64 h = siphash24(base_seed, &scid, sizeof(scid)); + + /* Scale fees for this channel */ + /* rand = (h / UINT64_MAX) random number between 0.0 -> 1.0 + * 2*fuzz*rand random number between 0.0 -> 2*fuzz + * 2*fuzz*rand - fuzz random number between -fuzz -> +fuzz + */ + fee_scale = 1.0 + (2.0 * fuzz * h / UINT64_MAX) - fuzz; + } assert(c->dst == node); for (h = 0; h < ROUTING_MAX_HOPS; h++) { @@ -345,7 +358,7 @@ static void bfg_one_edge(struct node *node, size_t edgenum, double riskfactor) if (node->bfg[h].total == INFINITE) continue; - fee = connection_fee(c, node->bfg[h].total); + fee = connection_fee(c, node->bfg[h].total) * fee_scale; risk = node->bfg[h].risk + risk_fee(node->bfg[h].total + fee, c->delay, riskfactor); @@ -380,7 +393,9 @@ static bool nc_is_routable(const struct node_connection *nc, time_t now) static struct node_connection * find_route(const tal_t *ctx, struct routing_state *rstate, const struct pubkey *from, const struct pubkey *to, u64 msatoshi, - double riskfactor, u64 *fee, struct node_connection ***route) + double riskfactor, + double fuzz, const struct siphash_seed *base_seed, + u64 *fee, struct node_connection ***route) { struct node *n, *src, *dst; struct node_map_iter it; @@ -440,7 +455,8 @@ find_route(const tal_t *ctx, struct routing_state *rstate, SUPERVERBOSE("...unroutable"); continue; } - bfg_one_edge(n, i, riskfactor); + bfg_one_edge(n, i, riskfactor, + fuzz, base_seed); SUPERVERBOSE("...done"); } } @@ -1118,7 +1134,8 @@ struct route_hop *get_route(tal_t *ctx, struct routing_state *rstate, const struct pubkey *source, const struct pubkey *destination, const u32 msatoshi, double riskfactor, - u32 final_cltv) + u32 final_cltv, + double fuzz, const struct siphash_seed *base_seed) { struct node_connection **route; u64 total_amount; @@ -1130,6 +1147,7 @@ struct route_hop *get_route(tal_t *ctx, struct routing_state *rstate, first_conn = find_route(ctx, rstate, source, destination, msatoshi, riskfactor / BLOCKS_PER_YEAR / 10000, + fuzz, base_seed, &fee, &route); if (!first_conn) { diff --git a/gossipd/routing.h b/gossipd/routing.h index 214985a3b462..ab713a3e828a 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -2,6 +2,7 @@ #define LIGHTNING_LIGHTNINGD_GOSSIP_ROUTING_H #include "config.h" #include +#include #include #include #include @@ -186,7 +187,9 @@ struct route_hop *get_route(tal_t *ctx, struct routing_state *rstate, const struct pubkey *source, const struct pubkey *destination, const u32 msatoshi, double riskfactor, - u32 final_cltv); + u32 final_cltv, + double fuzz, + const struct siphash_seed *base_seed); /* Disable channel(s) based on the given routing failure. */ void routing_failure(struct routing_state *rstate, const struct pubkey *erring_node, diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c index 376f4e9fa665..c091e7eff0c2 100644 --- a/gossipd/test/run-bench-find_route.c +++ b/gossipd/test/run-bench-find_route.c @@ -177,6 +177,7 @@ int main(int argc, char *argv[]) struct pubkey me = nodeid(0); bool perfme = false; const double riskfactor = 0.01 / BLOCKS_PER_YEAR / 10000; + struct siphash_seed base_seed; secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); @@ -194,6 +195,7 @@ int main(int argc, char *argv[]) if (argc > 3) opt_usage_and_exit("[num_nodes [num_runs]]"); + memset(&base_seed, 0, sizeof(base_seed)); for (size_t i = 0; i < num_nodes; i++) populate_random_node(rstate, i); @@ -212,6 +214,7 @@ int main(int argc, char *argv[]) nc = find_route(ctx, rstate, &from, &to, pseudorand(100000), riskfactor, + 0.75, &base_seed, &fee, &route); num_success += (nc != NULL); tal_free(route); diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c index eac0ae87889a..ae638bc07790 100644 --- a/gossipd/test/run-find_route-specific.c +++ b/gossipd/test/run-find_route-specific.c @@ -127,7 +127,7 @@ int main(void) nc->flags = 1; nc->last_timestamp = 1504064344; - nc = find_route(ctx, rstate, &a, &c, 100000, riskfactor, &fee, &route); + nc = find_route(ctx, rstate, &a, &c, 100000, riskfactor, 0.0, NULL, &fee, &route); assert(nc); assert(tal_count(route) == 1); assert(pubkey_eq(&route[0]->src->id, &b)); diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c index 6a83fb158d70..e25bd48bd0aa 100644 --- a/gossipd/test/run-find_route.c +++ b/gossipd/test/run-find_route.c @@ -108,7 +108,7 @@ int main(void) /* A<->B */ add_connection(rstate, &a, &b, 1, 1, 1); - nc = find_route(ctx, rstate, &a, &b, 1000, riskfactor, &fee, &route); + nc = find_route(ctx, rstate, &a, &b, 1000, riskfactor, 0.0, NULL, &fee, &route); assert(nc); assert(tal_count(route) == 0); assert(fee == 0); @@ -123,7 +123,7 @@ int main(void) status_trace("C = %s", type_to_string(trc, struct pubkey, &c)); add_connection(rstate, &b, &c, 1, 1, 1); - nc = find_route(ctx, rstate, &a, &c, 1000, riskfactor, &fee, &route); + nc = find_route(ctx, rstate, &a, &c, 1000, riskfactor, 0.0, NULL, &fee, &route); assert(nc); assert(tal_count(route) == 1); assert(fee == 1); @@ -138,14 +138,14 @@ int main(void) add_connection(rstate, &d, &c, 0, 2, 1); /* Will go via D for small amounts. */ - nc = find_route(ctx, rstate, &a, &c, 1000, riskfactor, &fee, &route); + nc = find_route(ctx, rstate, &a, &c, 1000, riskfactor, 0.0, NULL, &fee, &route); assert(nc); assert(tal_count(route) == 1); assert(pubkey_eq(&route[0]->src->id, &d)); assert(fee == 0); /* Will go via B for large amounts. */ - nc = find_route(ctx, rstate, &a, &c, 3000000, riskfactor, &fee, &route); + nc = find_route(ctx, rstate, &a, &c, 3000000, riskfactor, 0.0, NULL, &fee, &route); assert(nc); assert(tal_count(route) == 1); assert(pubkey_eq(&route[0]->src->id, &b)); @@ -153,7 +153,7 @@ int main(void) /* Make B->C inactive, force it back via D */ get_connection(rstate, &b, &c)->active = false; - nc = find_route(ctx, rstate, &a, &c, 3000000, riskfactor, &fee, &route); + nc = find_route(ctx, rstate, &a, &c, 3000000, riskfactor, 0.0, NULL, &fee, &route); assert(nc); assert(tal_count(route) == 1); assert(pubkey_eq(&route[0]->src->id, &d)); diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index a38c78eca703..dba69075223a 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -5,6 +5,7 @@ #include "peer_control.h" #include "subd.h" #include +#include #include #include #include @@ -22,6 +23,8 @@ #include #include #include +#include +#include #include #include @@ -299,9 +302,18 @@ static void json_getroute(struct command *cmd, const char *buffer, const jsmntok struct lightningd *ld = cmd->ld; struct pubkey source = ld->id, destination; jsmntok_t *idtok, *msatoshitok, *riskfactortok, *cltvtok, *fromidtok; + jsmntok_t *fuzztok; + jsmntok_t *seedtok; u64 msatoshi; unsigned cltv = 9; double riskfactor; + /* Higher fuzz means that some high-fee paths can be discounted + * for an even larger value, increasing the scope for route + * randomization (the higher-fee paths become more likely to + * be selected) at the cost of increasing the probability of + * selecting the higher-fee paths. */ + double fuzz = 75.0; + u8 *seed = tal_arrz(cmd, u8, sizeof(struct siphash_seed)); if (!json_get_params(cmd, buffer, params, "id", &idtok, @@ -309,6 +321,8 @@ static void json_getroute(struct command *cmd, const char *buffer, const jsmntok "riskfactor", &riskfactortok, "?cltv", &cltvtok, "?fromid", &fromidtok, + "?fuzzpercent", &fuzztok, + "?seed", &seedtok, NULL)) { return; } @@ -342,7 +356,30 @@ static void json_getroute(struct command *cmd, const char *buffer, const jsmntok return; } - u8 *req = towire_gossip_getroute_request(cmd, &source, &destination, msatoshi, riskfactor*1000, cltv); + if (fuzztok && + !json_tok_double(buffer, fuzztok, &fuzz)) { + command_fail(cmd, "'%.*s' is not a valid double", + (int)(fuzztok->end - fuzztok->start), + buffer + fuzztok->start); + return; + } + if (!(0.0 <= fuzz && fuzz <= 100.0)) { + command_fail(cmd, + "fuzz must be in range 0.0 <= %f <= 100.0", + fuzz); + return; + } + /* Convert from percentage */ + fuzz = fuzz / 100.0; + + if (seedtok) { + tal_resize(&seed, seedtok->end - seedtok->start); + memcpy(seed, buffer + seedtok->start, + seedtok->end - seedtok->start); + } else + randombytes_buf(seed, tal_len(seed)); + + u8 *req = towire_gossip_getroute_request(cmd, &source, &destination, msatoshi, riskfactor*1000, cltv, &fuzz, seed); subd_req(ld->gossip, ld->gossip, req, -1, 0, json_getroute_reply, cmd); command_still_pending(cmd); } @@ -350,7 +387,10 @@ static void json_getroute(struct command *cmd, const char *buffer, const jsmntok static const struct json_command getroute_command = { "getroute", json_getroute, - "Show route to {id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9), if specified search from {source} otherwise use this node as source." + "Show route to {id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9). " + "If specified search from {fromid} otherwise use this node as source. " + "Randomize the route with up to {fuzzpercent} (0.0 -> 100.0, default 5.0) " + "using {seed} as an arbitrary-size string seed." }; AUTODATA(json_command, &getroute_command); diff --git a/lightningd/payalgo.c b/lightningd/payalgo.c index 852d5bf544ed..87fb404670f3 100644 --- a/lightningd/payalgo.c +++ b/lightningd/payalgo.c @@ -1,5 +1,6 @@ #include "pay.h" #include "payalgo.h" +#include #include #include #include @@ -9,6 +10,7 @@ #include #include #include +#include struct pay { /* Parent command. */ @@ -25,17 +27,24 @@ struct pay { double riskfactor; double maxfeepercent; - /* Number of payment tries */ - unsigned int tries; + /* Number of getroute and sendpay tries */ + unsigned int getroute_tries; + unsigned int sendpay_tries; - /* Parent of the sendpay object. */ - char *sendpay_parent; + /* Current fuzz we pass into getroute. */ + double fuzz; + + /* Parent of the current pay attempt. This object is + * freed, then allocated at the start of each pay + * attempt to ensure no leaks across long pay attempts */ + char *try_parent; }; static void json_pay_success(struct command *cmd, const struct preimage *payment_preimage, - unsigned int tries) + unsigned int getroute_tries, + unsigned int sendpay_tries) { struct json_result *response; @@ -43,7 +52,8 @@ json_pay_success(struct command *cmd, json_object_start(response, NULL); json_add_hex(response, "preimage", payment_preimage, sizeof(*payment_preimage)); - json_add_num(response, "tries", tries); + json_add_num(response, "getroute_tries", getroute_tries); + json_add_num(response, "sendpay_tries", sendpay_tries); json_object_end(response); command_success(cmd, response); } @@ -124,7 +134,8 @@ static void json_pay_sendpay_resolve(const struct sendpay_result *r, /* If we succeed, hurray */ if (r->succeeded) { - json_pay_success(pay->cmd, &r->preimage, pay->tries); + json_pay_success(pay->cmd, &r->preimage, + pay->getroute_tries, pay->sendpay_tries); return; } @@ -147,6 +158,7 @@ static void json_pay_getroute_reply(struct subd *gossip UNUSED, u64 msatoshi_sent; u64 fee; double feepercent; + bool fee_too_high; struct json_result *data; fromwire_gossip_getroute_reply(reply, reply, &route); @@ -167,7 +179,9 @@ static void json_pay_getroute_reply(struct subd *gossip UNUSED, * 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) { + fee_too_high = (feepercent > pay->maxfeepercent); + /* compare fuzz to range */ + if (fee_too_high && pay->fuzz < 0.01) { data = new_json_result(pay); json_object_start(data, NULL); json_add_u64(data, "fee", fee); @@ -186,8 +200,18 @@ static void json_pay_getroute_reply(struct subd *gossip UNUSED, pay->maxfeepercent); return; } + if (fee_too_high) { + /* Retry with lower fuzz */ + pay->fuzz -= 0.15; + if (pay->fuzz <= 0.0) + pay->fuzz = 0.0; + json_pay_try(pay); + return; + } - send_payment(pay->sendpay_parent, + ++pay->sendpay_tries; + + send_payment(pay->try_parent, pay->cmd->ld, &pay->payment_hash, route, &json_pay_sendpay_resolve, pay); } @@ -196,6 +220,7 @@ static void json_pay_getroute_reply(struct subd *gossip UNUSED, * false if resolved now. */ static bool json_pay_try(struct pay *pay) { + u8 *seed; u8 *req; struct command *cmd = pay->cmd; struct timeabs now = time_now(); @@ -213,19 +238,26 @@ static bool json_pay_try(struct pay *pay) return false; } - /* Clear previous sendpay. */ - pay->sendpay_parent = tal_free(pay->sendpay_parent); - pay->sendpay_parent = tal(pay, char); + /* Clear previous try memory. */ + pay->try_parent = tal_free(pay->try_parent); + pay->try_parent = tal(pay, char); + + /* Generate random seed */ + seed = tal_arr(pay->try_parent, u8, sizeof(struct siphash_seed)); + randombytes_buf(seed, tal_len(seed)); - ++pay->tries; + ++pay->getroute_tries; /* FIXME: use b11->routes */ - req = towire_gossip_getroute_request(cmd, &cmd->ld->id, + req = towire_gossip_getroute_request(pay->try_parent, + &cmd->ld->id, &pay->receiver_id, pay->msatoshi, pay->riskfactor, - pay->min_final_cltv_expiry); - subd_req(pay, cmd->ld->gossip, req, -1, 0, json_pay_getroute_reply, pay); + pay->min_final_cltv_expiry, + &pay->fuzz, + seed); + subd_req(pay->try_parent, cmd->ld->gossip, req, -1, 0, json_pay_getroute_reply, pay); return true; } @@ -322,8 +354,18 @@ static void json_pay(struct command *cmd, } pay->maxfeepercent = maxfeepercent; - pay->tries = 0; - pay->sendpay_parent = NULL; + pay->getroute_tries = 0; + pay->sendpay_tries = 0; + /* Higher fuzz increases the potential fees we will pay, since + * higher fuzz makes it more likely that high-fee paths get + * selected. We start with very high fuzz, but if the + * returned route is too expensive for the given + * `maxfeepercent` we reduce the fuzz. Starting with high + * fuzz means, if the user allows high fee, we can take + * advantage of that to increase randomization and + * improve privacy somewhat. */ + pay->fuzz = 0.75; + pay->try_parent = NULL; /* Initiate payment */ if (json_pay_try(pay)) diff --git a/wire/fromwire.c b/wire/fromwire.c index 80147b69c520..ad3f5094216a 100644 --- a/wire/fromwire.c +++ b/wire/fromwire.c @@ -82,6 +82,11 @@ u64 fromwire_u64(const u8 **cursor, size_t *max) return be64_to_cpu(ret); } +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) { u8 ret; diff --git a/wire/towire.c b/wire/towire.c index 229e585bfc60..5e1162aee1c9 100644 --- a/wire/towire.c +++ b/wire/towire.c @@ -39,6 +39,11 @@ void towire_u64(u8 **pptr, u64 v) towire(pptr, &l, sizeof(l)); } +void towire_double(u8 **pptr, const double *v) +{ + towire(pptr, v, sizeof(*v)); +} + void towire_bool(u8 **pptr, bool v) { u8 val = v; diff --git a/wire/wire.h b/wire/wire.h index 5712324927ed..9769829c9a65 100644 --- a/wire/wire.h +++ b/wire/wire.h @@ -52,6 +52,7 @@ void towire_u8(u8 **pptr, u8 v); void towire_u16(u8 **pptr, u16 v); void towire_u32(u8 **pptr, u32 v); void towire_u64(u8 **pptr, u64 v); +void towire_double(u8 **pptr, const double *v); void towire_pad(u8 **pptr, size_t num); void towire_bool(u8 **pptr, bool v); @@ -65,6 +66,7 @@ u8 fromwire_u8(const u8 **cursor, size_t *max); u16 fromwire_u16(const u8 **cursor, size_t *max); u32 fromwire_u32(const u8 **cursor, size_t *max); u64 fromwire_u64(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); void fromwire_secret(const u8 **cursor, size_t *max, struct secret *secret); void fromwire_privkey(const u8 **cursor, size_t *max, struct privkey *privkey);