Skip to content

Commit

Permalink
payalgo: Add maximum delay.
Browse files Browse the repository at this point in the history
  • Loading branch information
ZmnSCPxj authored and rustyrussell committed Apr 3, 2018
1 parent 1133820 commit 26f7014
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 20 deletions.
18 changes: 12 additions & 6 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: 03/16/2018
.\" Date: 03/21/2018
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
.TH "LIGHTNING\-PAY" "7" "03/16/2018" "\ \&" "\ \&"
.TH "LIGHTNING\-PAY" "7" "03/21/2018" "\ \&" "\ \&"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
Expand All @@ -31,12 +31,12 @@
lightning-pay \- Protocol for sending a payment to a BOLT11 invoice
.SH "SYNOPSIS"
.sp
\fBpay\fR \fIbolt11\fR [\fImsatoshi\fR] [\fIdescription\fR] [\fIriskfactor\fR] [\fImaxfeepercent\fR] [\fIretry_for\fR]
\fBpay\fR \fIbolt11\fR [\fImsatoshi\fR] [\fIdescription\fR] [\fIriskfactor\fR] [\fImaxfeepercent\fR] [\fIretry_for\fR] [\fImaxdelay\fR]
.SH "DESCRIPTION"
.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\&.
The \fBpay\fR RPC command will randomize routes slightly, as long as the route achieves the targeted \fImaxfeepercent\fR and \fImaxdelay\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
Expand Down Expand Up @@ -122,13 +122,19 @@ field of the error will be routing failure object\&.
.sp -1
.IP \(bu 2.3
.\}
206\&. Route too expensive\&. The
206\&. Route too expensive\&. Either the fee or the needed total locktime for the route exceeds your
\fImaxfeepercent\fR
or
\fImaxdelay\fR
settings, respectively\&. The
\fIdata\fR
field of the error will indicate the actual
\fIfee\fR
as well as the
\fIfeepercent\fR
percentage that the fee has of the destination payment amount\&.
percentage that the fee has of the destination payment amount\&. It will also indicate the actual
\fIdelay\fR
along the route\&.
.RE
.sp
.RS 4
Expand Down
16 changes: 10 additions & 6 deletions doc/lightning-pay.7.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ lightning-pay - Protocol for sending a payment to a BOLT11 invoice

SYNOPSIS
--------
*pay* 'bolt11' ['msatoshi'] ['description'] ['riskfactor'] ['maxfeepercent'] ['retry_for']
*pay* 'bolt11' ['msatoshi'] ['description'] ['riskfactor'] ['maxfeepercent'] ['retry_for'] ['maxdelay']

DESCRIPTION
-----------
Expand All @@ -24,7 +24,7 @@ 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'.
route achieves the targeted 'maxfeepercent' and 'maxdelay'.

The response will occur when the payment fails or succeeds. Once a
payment has succeeded, calls to *pay* with the same 'bolt11' will
Expand Down Expand Up @@ -65,10 +65,14 @@ The following error codes may occur:
* 203. Permanent failure at destination. The 'data' field of
the error will be routing failure object.
* 205. Unable to find a route.
* 206. Route too expensive. The 'data' field of the error will
indicate the actual 'fee' as well as the 'feepercent'
percentage that the fee has of the destination payment
amount.
* 206. Route too expensive.
Either the fee or the needed total locktime for the route
exceeds your 'maxfeepercent' or 'maxdelay' settings,
respectively.
The 'data' field of the error will indicate the actual 'fee'
as well as the 'feepercent' percentage that the fee has of the
destination payment amount.
It will also indicate the actual 'delay' along the route.
* 207. Invoice expired. Payment took too long before expiration,
or already expired at the time you initiated payment.
The 'data' field of the error indicates 'now' (the current time)
Expand Down
58 changes: 50 additions & 8 deletions lightningd/payalgo.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ struct pay {
u64 msatoshi;
double riskfactor;
double maxfeepercent;
u32 maxdelay;

/* Number of getroute and sendpay tries */
unsigned int getroute_tries;
Expand Down Expand Up @@ -407,7 +408,9 @@ static void json_pay_getroute_reply(struct subd *gossip UNUSED,
u64 fee;
double feepercent;
bool fee_too_high;
bool delay_too_high;
struct json_result *data;
char const *err;

fromwire_gossip_getroute_reply(reply, reply, &route);

Expand All @@ -434,30 +437,48 @@ static void json_pay_getroute_reply(struct subd *gossip UNUSED,
* payments are limited to 4294967295 msatoshi. */
feepercent = ((double) fee) * 100.0 / ((double) pay->msatoshi);
fee_too_high = (feepercent > pay->maxfeepercent);
delay_too_high = (route[0].delay > pay->maxdelay);
/* compare fuzz to range */
if (fee_too_high && pay->fuzz < 0.01) {
if ((fee_too_high || delay_too_high) && pay->fuzz < 0.01) {
data = new_json_result(pay);
json_object_start(data, NULL);
json_add_u64(data, "msatoshi", pay->msatoshi);
json_add_u64(data, "fee", fee);
json_add_double(data, "feepercent", feepercent);
json_add_u64(data, "msatoshi", pay->msatoshi);
json_add_double(data, "maxfeepercent", pay->maxfeepercent);
json_add_u64(data, "delay", (u64) route[0].delay);
json_add_num(data, "maxdelay", pay->maxdelay);
json_add_num(data, "getroute_tries", pay->getroute_tries);
json_add_num(data, "sendpay_tries", pay->sendpay_tries);
json_add_route(data, "route",
route, tal_count(route));
json_add_failures(data, "failures", &pay->pay_failures);
json_object_end(data);

command_fail_detailed(pay->cmd, PAY_ROUTE_TOO_EXPENSIVE,
data,
err = "";
if (fee_too_high)
err = tal_fmt(pay,
"Fee %"PRIu64" is %f%% "
"of payment %"PRIu64"; "
"max fee requested is %f%%",
"max fee requested is %f%%.",
fee, feepercent,
pay->msatoshi,
pay->maxfeepercent);
if (fee_too_high && delay_too_high)
err = tal_fmt(pay, "%s ", err);
if (delay_too_high)
err = tal_fmt(pay,
"%s"
"Delay (locktime) is %"PRIu32" blocks; "
"max delay requested is %u.",
err, route[0].delay, pay->maxdelay);


command_fail_detailed(pay->cmd, PAY_ROUTE_TOO_EXPENSIVE,
data, "%s", err);
return;
}
if (fee_too_high) {
if (fee_too_high || delay_too_high) {
/* Retry with lower fuzz */
pay->fuzz -= 0.15;
if (pay->fuzz <= 0.0)
Expand Down Expand Up @@ -575,13 +596,15 @@ static void json_pay(struct command *cmd,
{
jsmntok_t *bolt11tok, *msatoshitok, *desctok, *riskfactortok, *maxfeetok;
jsmntok_t *retryfortok;
jsmntok_t *maxdelaytok;
double riskfactor = 1.0;
double maxfeepercent = 0.5;
u64 msatoshi;
struct pay *pay = tal(cmd, struct pay);
struct bolt11 *b11;
char *fail, *b11str, *desc;
unsigned int retryfor = 60;
unsigned int maxdelay = 500;

if (!json_get_params(cmd, buffer, params,
"bolt11", &bolt11tok,
Expand All @@ -590,6 +613,7 @@ static void json_pay(struct command *cmd,
"?riskfactor", &riskfactortok,
"?maxfeepercent", &maxfeetok,
"?retry_for", &retryfortok,
"?maxdelay", &maxdelaytok,
NULL)) {
return;
}
Expand Down Expand Up @@ -672,14 +696,32 @@ static void json_pay(struct command *cmd,
}
pay->maxfeepercent = maxfeepercent;

if (maxdelaytok
&& !json_tok_number(buffer, maxdelaytok, &maxdelay)) {
command_fail(cmd, "'%.*s' is not a valid double",
maxdelaytok->end - maxdelaytok->start,
buffer + maxdelaytok->start);
return;
}
if (maxdelay < pay->min_final_cltv_expiry) {
command_fail(cmd,
"maxdelay (%u) must be greater than "
"min_final_cltv_expiry (%"PRIu32") of "
"invoice",
maxdelay, pay->min_final_cltv_expiry);
return;
}
pay->maxdelay = maxdelay;

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
* `maxfeepercent` or `maxdelay` we reduce the fuzz.
* Starting with high
* fuzz means, if the user allows high fee/locktime, we can take
* advantage of that to increase randomization and
* improve privacy somewhat. */
pay->fuzz = 0.75;
Expand Down

0 comments on commit 26f7014

Please sign in to comment.