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);