Browse Source

paymod: Add a deadline to the pay command and retry modifier

paymod-03
Christian Decker 5 years ago
parent
commit
d90664c409
  1. 77
      plugins/libplugin-pay.c
  2. 1
      plugins/libplugin-pay.h
  3. 4
      plugins/pay.c
  4. 2
      tests/test_pay.py

77
plugins/libplugin-pay.c

@ -36,6 +36,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd,
/* Re-establish the unmodified constraints for our sub-payment. */
p->constraints = *parent->start_constraints;
p->deadline = parent->deadline;
} else {
assert(cmd != NULL);
p->partid = 0;
@ -1317,10 +1318,21 @@ static inline void retry_step_cb(struct retry_mod_data *rd,
{
struct payment *subpayment;
struct retry_mod_data *rdata = payment_mod_retry_get_data(p);
struct timeabs now = time_now();
if (p->step != PAYMENT_STEP_FAILED)
return payment_continue(p);
if (time_after(now, p->deadline)) {
plugin_log(
p->plugin, LOG_INFORM,
"Payment deadline expired, not retrying (partial-)payment "
"%s/%d",
type_to_string(tmpctx, struct sha256, p->payment_hash),
p->partid);
return payment_continue(p);
}
/* If we failed to find a route, it's unlikely we can suddenly find a
* new one without any other changes, so it's time to give up. */
if (p->route == NULL)
@ -1928,3 +1940,68 @@ static struct direct_pay_data *direct_pay_init(struct payment *p)
REGISTER_PAYMENT_MODIFIER(directpay, struct direct_pay_data *, direct_pay_init,
direct_pay_cb);
static struct command_result *waitblockheight_rpc_cb(struct command *cmd,
const char *buffer,
const jsmntok_t *toks,
struct payment *p)
{
struct payment *subpayment;
subpayment = payment_new(p, NULL, p, p->modifiers);
payment_start(subpayment);
payment_set_step(p, PAYMENT_STEP_RETRY);
subpayment->why =
tal_fmt(subpayment, "Retrying after waiting for blockchain sync.");
payment_continue(p);
return command_still_pending(cmd);
}
static void waitblockheight_cb(void *d, struct payment *p)
{
struct out_req *req;
struct timeabs now = time_now();
struct timerel remaining;
u32 blockheight;
int failcode;
const u8 *raw_message;
if (p->step != PAYMENT_STEP_FAILED)
return payment_continue(p);
/* If we don't have an error message to parse we can't wait for blockheight. */
if (p->result == NULL)
return payment_continue(p);
if (time_after(now, p->deadline))
return payment_continue(p);
failcode = p->result->failcode;
raw_message = p->result->raw_message;
remaining = time_between(p->deadline, now);
if (failcode != 17 /* Former final_expiry_too_soon */) {
blockheight = p->start_block + 1;
} else {
/* If it's incorrect_or_unknown_payment_details, that tells us
* what height they're at */
struct amount_msat unused;
const void *ptr = raw_message;
if (!fromwire_incorrect_or_unknown_payment_details(
ptr, &unused, &blockheight))
return payment_continue(p);
}
plugin_log(p->plugin, LOG_INFORM,
"Remote node appears to be on a longer chain, which causes "
"CLTV timeouts to be incorrect. Waiting up to %" PRIu64
" seconds to catch up to block %d before retrying.",
time_to_sec(remaining), blockheight);
req = jsonrpc_request_start(p->plugin, NULL, "waitblockheight",
waitblockheight_rpc_cb,
waitblockheight_rpc_cb, p);
json_add_u32(req->js, "blockheight", blockheight);
json_add_u32(req->js, "timeout", time_to_sec(remaining));
send_outreq(p->plugin, req);
}
REGISTER_PAYMENT_MODIFIER(waitblockheight, void *, NULL, waitblockheight_cb);

1
plugins/libplugin-pay.h

@ -317,6 +317,7 @@ REGISTER_PAYMENT_MODIFIER_HEADER(routehints, struct routehints_data);
REGISTER_PAYMENT_MODIFIER_HEADER(exemptfee, struct exemptfee_data);
REGISTER_PAYMENT_MODIFIER_HEADER(shadowroute, struct shadow_route_data);
REGISTER_PAYMENT_MODIFIER_HEADER(directpay, struct direct_pay_data);
extern struct payment_modifier waitblockheight_pay_mod;
/* For the root payment we can seed the channel_hints with the result from
* `listpeers`, hence avoid channels that we know have insufficient capacity

4
plugins/pay.c

@ -1842,6 +1842,7 @@ struct payment_modifier *paymod_mods[] = {
&shadowroute_pay_mod,
&exemptfee_pay_mod,
&routehints_pay_mod,
&waitblockheight_pay_mod,
&retry_pay_mod,
NULL,
};
@ -1861,6 +1862,7 @@ static struct command_result *json_paymod(struct command *cmd,
u32 *maxdelay;
struct amount_msat *exemptfee, *msat;
const char *label;
unsigned int *retryfor;
#if DEVELOPER
bool *use_shadow;
#endif
@ -1876,6 +1878,7 @@ static struct command_result *json_paymod(struct command *cmd,
p_opt_def("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)),
p_opt_def("maxdelay", param_number, &maxdelay,
maxdelay_default),
p_opt_def("retry_for", param_number, &retryfor, 60),
p_opt_def("maxfeepercent", param_millionths,
&maxfee_pct_millionths, 500000),
#if DEVELOPER
@ -1933,6 +1936,7 @@ static struct command_result *json_paymod(struct command *cmd,
p->bolt11 = tal_steal(p, b11str);
p->why = "Initial attempt";
p->constraints.cltv_budget = *maxdelay;
p->deadline = timeabs_add(time_now(), time_from_sec(*retryfor));
if (!amount_msat_fee(&p->constraints.fee_budget, p->amount, 0,
*maxfee_pct_millionths / 100)) {

2
tests/test_pay.py

@ -3077,7 +3077,7 @@ def test_pay_modifiers(node_factory):
# Make sure that the dummy param is in the help (and therefore assigned to
# the modifier data).
hlp = l1.rpc.help("paymod")['help'][0]
assert(hlp['command'] == 'paymod bolt11 [msatoshi] [label] [exemptfee] [maxdelay] [maxfeepercent] [use_shadow]')
assert(hlp['command'] == 'paymod bolt11 [msatoshi] [label] [exemptfee] [maxdelay] [retry_for] [maxfeepercent] [use_shadow]')
inv = l2.rpc.invoice(123, 'lbl', 'desc')['bolt11']
r = l1.rpc.paymod(inv)

Loading…
Cancel
Save