mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
paymod: Add a deadline to the pay command and retry modifier
This commit is contained in:
@@ -36,6 +36,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd,
|
|||||||
|
|
||||||
/* Re-establish the unmodified constraints for our sub-payment. */
|
/* Re-establish the unmodified constraints for our sub-payment. */
|
||||||
p->constraints = *parent->start_constraints;
|
p->constraints = *parent->start_constraints;
|
||||||
|
p->deadline = parent->deadline;
|
||||||
} else {
|
} else {
|
||||||
assert(cmd != NULL);
|
assert(cmd != NULL);
|
||||||
p->partid = 0;
|
p->partid = 0;
|
||||||
@@ -1317,10 +1318,21 @@ static inline void retry_step_cb(struct retry_mod_data *rd,
|
|||||||
{
|
{
|
||||||
struct payment *subpayment;
|
struct payment *subpayment;
|
||||||
struct retry_mod_data *rdata = payment_mod_retry_get_data(p);
|
struct retry_mod_data *rdata = payment_mod_retry_get_data(p);
|
||||||
|
struct timeabs now = time_now();
|
||||||
|
|
||||||
if (p->step != PAYMENT_STEP_FAILED)
|
if (p->step != PAYMENT_STEP_FAILED)
|
||||||
return payment_continue(p);
|
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
|
/* 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. */
|
* new one without any other changes, so it's time to give up. */
|
||||||
if (p->route == NULL)
|
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,
|
REGISTER_PAYMENT_MODIFIER(directpay, struct direct_pay_data *, direct_pay_init,
|
||||||
direct_pay_cb);
|
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);
|
||||||
|
|||||||
@@ -317,6 +317,7 @@ REGISTER_PAYMENT_MODIFIER_HEADER(routehints, struct routehints_data);
|
|||||||
REGISTER_PAYMENT_MODIFIER_HEADER(exemptfee, struct exemptfee_data);
|
REGISTER_PAYMENT_MODIFIER_HEADER(exemptfee, struct exemptfee_data);
|
||||||
REGISTER_PAYMENT_MODIFIER_HEADER(shadowroute, struct shadow_route_data);
|
REGISTER_PAYMENT_MODIFIER_HEADER(shadowroute, struct shadow_route_data);
|
||||||
REGISTER_PAYMENT_MODIFIER_HEADER(directpay, struct direct_pay_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
|
/* For the root payment we can seed the channel_hints with the result from
|
||||||
* `listpeers`, hence avoid channels that we know have insufficient capacity
|
* `listpeers`, hence avoid channels that we know have insufficient capacity
|
||||||
|
|||||||
@@ -1842,6 +1842,7 @@ struct payment_modifier *paymod_mods[] = {
|
|||||||
&shadowroute_pay_mod,
|
&shadowroute_pay_mod,
|
||||||
&exemptfee_pay_mod,
|
&exemptfee_pay_mod,
|
||||||
&routehints_pay_mod,
|
&routehints_pay_mod,
|
||||||
|
&waitblockheight_pay_mod,
|
||||||
&retry_pay_mod,
|
&retry_pay_mod,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
@@ -1861,6 +1862,7 @@ static struct command_result *json_paymod(struct command *cmd,
|
|||||||
u32 *maxdelay;
|
u32 *maxdelay;
|
||||||
struct amount_msat *exemptfee, *msat;
|
struct amount_msat *exemptfee, *msat;
|
||||||
const char *label;
|
const char *label;
|
||||||
|
unsigned int *retryfor;
|
||||||
#if DEVELOPER
|
#if DEVELOPER
|
||||||
bool *use_shadow;
|
bool *use_shadow;
|
||||||
#endif
|
#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("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)),
|
||||||
p_opt_def("maxdelay", param_number, &maxdelay,
|
p_opt_def("maxdelay", param_number, &maxdelay,
|
||||||
maxdelay_default),
|
maxdelay_default),
|
||||||
|
p_opt_def("retry_for", param_number, &retryfor, 60),
|
||||||
p_opt_def("maxfeepercent", param_millionths,
|
p_opt_def("maxfeepercent", param_millionths,
|
||||||
&maxfee_pct_millionths, 500000),
|
&maxfee_pct_millionths, 500000),
|
||||||
#if DEVELOPER
|
#if DEVELOPER
|
||||||
@@ -1933,6 +1936,7 @@ static struct command_result *json_paymod(struct command *cmd,
|
|||||||
p->bolt11 = tal_steal(p, b11str);
|
p->bolt11 = tal_steal(p, b11str);
|
||||||
p->why = "Initial attempt";
|
p->why = "Initial attempt";
|
||||||
p->constraints.cltv_budget = *maxdelay;
|
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,
|
if (!amount_msat_fee(&p->constraints.fee_budget, p->amount, 0,
|
||||||
*maxfee_pct_millionths / 100)) {
|
*maxfee_pct_millionths / 100)) {
|
||||||
|
|||||||
@@ -3077,7 +3077,7 @@ def test_pay_modifiers(node_factory):
|
|||||||
# Make sure that the dummy param is in the help (and therefore assigned to
|
# Make sure that the dummy param is in the help (and therefore assigned to
|
||||||
# the modifier data).
|
# the modifier data).
|
||||||
hlp = l1.rpc.help("paymod")['help'][0]
|
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']
|
inv = l2.rpc.invoice(123, 'lbl', 'desc')['bolt11']
|
||||||
r = l1.rpc.paymod(inv)
|
r = l1.rpc.paymod(inv)
|
||||||
|
|||||||
Reference in New Issue
Block a user