Skip to content
Merged
10 changes: 7 additions & 3 deletions doc/lightning-getroute.7
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
.\" Title: lightning-getroute
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
.\" 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
.\" -----------------------------------------------------------------
Expand All @@ -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)\&.
Expand All @@ -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\&.
Expand Down
15 changes: 14 additions & 1 deletion doc/lightning-getroute.7.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
-----------
Expand All @@ -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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default is 75.0, or up to 75% fee distortion.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops!


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,
Expand Down
8 changes: 5 additions & 3 deletions doc/lightning-pay.7
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
.\" Title: lightning-pay
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
.\" 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
.\" -----------------------------------------------------------------
Expand Down Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions doc/lightning-pay.7.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
Expand Down
23 changes: 19 additions & 4 deletions gossipd/gossip.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <ccan/cast/cast.h>
#include <ccan/container_of/container_of.h>
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
#include <ccan/crypto/siphash24/siphash24.h>
#include <ccan/endian/endian.h>
#include <ccan/fdpass/fdpass.h>
#include <ccan/io/fdpass/fdpass.h>
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions gossipd/gossip_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 24 additions & 6 deletions gossipd/routing.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <bitcoin/block.h>
#include <bitcoin/script.h>
#include <ccan/array_size/array_size.h>
#include <ccan/crypto/siphash24/siphash24.h>
#include <ccan/endian/endian.h>
#include <ccan/structeq/structeq.h>
#include <ccan/tal/str/str.h>
Expand Down Expand Up @@ -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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure we can just make h an s64 and divide by INT64_MAX: fee_scale = 1.0 + fuzz * h / INT64_MAX

But it's trivial and unlikely to be any faster, so I'll just leave this as a comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Negative number absolute range is one higher than positive range, so getting INT64_MIN by chance would break that. Of course very unlikely but well.

}

assert(c->dst == node);
for (h = 0; h < ROUTING_MAX_HOPS; h++) {
Expand All @@ -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);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
}
}
Expand Down Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
5 changes: 4 additions & 1 deletion gossipd/routing.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define LIGHTNING_LIGHTNINGD_GOSSIP_ROUTING_H
#include "config.h"
#include <bitcoin/pubkey.h>
#include <ccan/crypto/siphash24/siphash24.h>
#include <ccan/htable/htable_type.h>
#include <ccan/time/time.h>
#include <gossipd/broadcast.h>
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions gossipd/test/run-bench-find_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);

Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion gossipd/test/run-find_route-specific.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
10 changes: 5 additions & 5 deletions gossipd/test/run-find_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -138,22 +138,22 @@ 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));
assert(fee == 1 + 3);

/* 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));
Expand Down
Loading