Browse Source

payalgo: Repeat pay command if possible.

Fixes: #863
ppa-0.6.1
ZmnSCPxj 7 years ago
committed by Christian Decker
parent
commit
4ad1021c2c
  1. 5
      lightningd/pay.c
  2. 217
      lightningd/payalgo.c

5
lightningd/pay.c

@ -442,11 +442,6 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout,
if (report_to_gossipd)
report_routing_failure(ld->log, ld->gossip, fail);
/* FIXME(ZmnSCPxj): if retrying is plausible, and we are
* using pay command rather than sendpay, retry routing
* and payment again. */
(void) retry_plausible;
/* Report to client. */
sendpay_route_failure(ld, &hout->payment_hash,
retry_plausible, fail, hout->failuremsg,

217
lightningd/payalgo.c

@ -1,6 +1,7 @@
#include "pay.h"
#include "payalgo.h"
#include <ccan/tal/str/str.h>
#include <ccan/time/time.h>
#include <common/bolt11.h>
#include <gossipd/gen_gossip_wire.h>
#include <gossipd/routing.h>
@ -10,17 +11,31 @@
#include <lightningd/subd.h>
struct pay {
struct sha256 payment_hash;
/* Parent command. */
struct command *cmd;
/* Bolt11 details */
struct sha256 payment_hash;
struct pubkey receiver_id;
struct timeabs expiry;
u32 min_final_cltv_expiry;
/* Command details */
u64 msatoshi;
double riskfactor;
double maxfeepercent;
/* Number of payment tries */
unsigned int tries;
/* Parent of the sendpay object. */
char *sendpay_parent;
};
/* Duplicated here from lightningd/pay.c, but will be modified
* in a later commit. */
static void
json_sendpay_success(struct command *cmd,
const struct preimage *payment_preimage)
json_pay_success(struct command *cmd,
const struct preimage *payment_preimage,
unsigned int tries)
{
struct json_result *response;
@ -28,76 +43,107 @@ json_sendpay_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_object_end(response);
command_success(cmd, response);
}
/* Duplicated here from lightningd/pay.c, but will be modified
* in a later commit. */
static void json_sendpay_on_resolve(const struct sendpay_result *r,
void *vcmd)
static void json_pay_failure(struct command *cmd,
const struct sendpay_result *r)
{
struct command *cmd = (struct command*) vcmd;
struct json_result *data;
const char *msg;
struct routing_failure *fail;
if (r->succeeded)
json_sendpay_success(cmd, &r->preimage);
else {
switch (r->errorcode) {
case PAY_IN_PROGRESS:
case PAY_RHASH_ALREADY_USED:
data = NULL;
msg = r->details;
break;
case PAY_UNPARSEABLE_ONION:
data = new_json_result(cmd);
json_object_start(data, NULL);
json_add_hex(data, "onionreply",
r->onionreply, tal_len(r->onionreply));
json_object_end(data);
msg = tal_fmt(cmd,
"failed: WIRE_PERMANENT_NODE_FAILURE "
"(%s)",
r->details);
break;
case PAY_DESTINATION_PERM_FAIL:
case PAY_TRY_OTHER_ROUTE:
fail = r->routing_failure;
data = new_json_result(cmd);
json_object_start(data, NULL);
json_add_num(data, "erring_index",
fail->erring_index);
json_add_num(data, "failcode",
(unsigned) fail->failcode);
json_add_hex(data, "erring_node",
&fail->erring_node,
sizeof(fail->erring_node));
json_add_short_channel_id(data, "erring_channel",
&fail->erring_channel);
if (fail->channel_update)
json_add_hex(data, "channel_update",
fail->channel_update,
tal_len(fail->channel_update));
json_object_end(data);
msg = tal_fmt(cmd,
"failed: %s (%s)",
onion_type_name(fail->failcode),
r->details);
break;
}
assert(!r->succeeded);
/* FIXME: can probably be factored out with similar code
* in lightningd/pay.c */
switch (r->errorcode) {
case PAY_IN_PROGRESS:
case PAY_RHASH_ALREADY_USED:
data = NULL;
msg = r->details;
break;
case PAY_UNPARSEABLE_ONION:
data = new_json_result(cmd);
json_object_start(data, NULL);
json_add_hex(data, "onionreply",
r->onionreply, tal_len(r->onionreply));
json_object_end(data);
msg = tal_fmt(cmd,
"failed: WIRE_PERMANENT_NODE_FAILURE "
"(%s)",
r->details);
command_fail_detailed(cmd, r->errorcode, data, "%s", msg);
break;
case PAY_DESTINATION_PERM_FAIL:
case PAY_TRY_OTHER_ROUTE:
fail = r->routing_failure;
data = new_json_result(cmd);
json_object_start(data, NULL);
json_add_num(data, "erring_index",
fail->erring_index);
json_add_num(data, "failcode",
(unsigned) fail->failcode);
json_add_hex(data, "erring_node",
&fail->erring_node,
sizeof(fail->erring_node));
json_add_short_channel_id(data, "erring_channel",
&fail->erring_channel);
if (fail->channel_update)
json_add_hex(data, "channel_update",
fail->channel_update,
tal_len(fail->channel_update));
json_object_end(data);
msg = tal_fmt(cmd,
"failed: %s (%s)",
onion_type_name(fail->failcode),
r->details);
break;
}
command_fail_detailed(cmd, r->errorcode, data, "%s", msg);
}
/* Start a payment attempt. */
static void json_pay_try(struct pay *pay);
/* Call when sendpay returns to us. */
static void json_pay_sendpay_resolve(const struct sendpay_result *r,
void *vpay)
{
struct pay *pay = (struct pay *) vpay;
struct timeabs now = time_now();
/* If we succeed, hurray */
if (r->succeeded) {
json_pay_success(pay->cmd, &r->preimage, pay->tries);
return;
}
/* We can retry only if it is one of the retryable errors
* below. If it is not, fail now. */
if (r->errorcode != PAY_UNPARSEABLE_ONION &&
r->errorcode != PAY_TRY_OTHER_ROUTE) {
json_pay_failure(pay->cmd, r);
return;
}
/* If too late anyway, fail now. */
if (time_after(now, pay->expiry)) {
/* FIXME: maybe another error kind? */
json_pay_failure(pay->cmd, r);
return;
}
json_pay_try(pay);
}
static void json_pay_getroute_reply(struct subd *gossip,
@ -148,8 +194,30 @@ static void json_pay_getroute_reply(struct subd *gossip,
return;
}
send_payment(pay->cmd, pay->cmd->ld, &pay->payment_hash, route,
&json_sendpay_on_resolve, pay->cmd);
send_payment(pay->sendpay_parent,
pay->cmd->ld, &pay->payment_hash, route,
&json_pay_sendpay_resolve, pay);
}
/* Start a payment attempt */
static void json_pay_try(struct pay *pay)
{
u8 *req;
struct command *cmd = pay->cmd;
/* Clear previous sendpay. */
pay->sendpay_parent = tal_free(pay->sendpay_parent);
pay->sendpay_parent = tal(pay, char);
++pay->tries;
/* FIXME: use b11->routes */
req = towire_gossip_getroute_request(cmd, &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);
}
static void json_pay(struct command *cmd,
@ -162,7 +230,6 @@ static void json_pay(struct command *cmd,
struct pay *pay = tal(cmd, struct pay);
struct bolt11 *b11;
char *fail, *b11str, *desc;
u8 *req;
if (!json_get_params(cmd, buffer, params,
"bolt11", &bolt11tok,
@ -190,6 +257,10 @@ static void json_pay(struct command *cmd,
pay->cmd = cmd;
pay->payment_hash = b11->payment_hash;
pay->receiver_id = b11->receiver_id;
memset(&pay->expiry, 0, sizeof(pay->expiry));
pay->expiry.ts.tv_sec = b11->timestamp + b11->expiry;
pay->min_final_cltv_expiry = b11->min_final_cltv_expiry;
if (b11->msatoshi) {
msatoshi = *b11->msatoshi;
@ -219,6 +290,7 @@ static void json_pay(struct command *cmd,
buffer + riskfactortok->start);
return;
}
pay->riskfactor = riskfactor * 1000;
if (maxfeetok
&& !json_tok_double(buffer, maxfeetok, &maxfeepercent)) {
@ -240,12 +312,11 @@ static void json_pay(struct command *cmd,
}
pay->maxfeepercent = maxfeepercent;
/* FIXME: use b11->routes */
req = towire_gossip_getroute_request(cmd, &cmd->ld->id,
&b11->receiver_id,
msatoshi, riskfactor*1000,
b11->min_final_cltv_expiry);
subd_req(pay, cmd->ld->gossip, req, -1, 0, json_pay_getroute_reply, pay);
pay->tries = 0;
pay->sendpay_parent = NULL;
/* Initiate payment */
json_pay_try(pay);
command_still_pending(cmd);
}

Loading…
Cancel
Save