mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 01:43:36 +01:00
xpay: add deadline.
We promised this in the schema originally, now support it. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
06a9aa8907
commit
47318a7875
@ -1,4 +1,3 @@
|
||||
/* FIXME: Timeout! */
|
||||
#include "config.h"
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/crypto/siphash24/siphash24.h>
|
||||
@ -51,6 +50,8 @@ static struct gossmap *get_gossmap(struct xpay *xpay)
|
||||
/* The unifies bolt11 and bolt12 handling */
|
||||
struct payment {
|
||||
struct plugin *plugin;
|
||||
/* Stop sending new payments after this */
|
||||
struct timemono deadline;
|
||||
/* This is the command which is expecting the success/fail. When
|
||||
* it's NULL, that means we're just cleaning up */
|
||||
struct command *cmd;
|
||||
@ -576,6 +577,10 @@ static void update_knowledge_from_error(struct command *aux_cmd,
|
||||
|
||||
case WIRE_MPP_TIMEOUT:
|
||||
/* Not actually an error at all, nothing to do. */
|
||||
add_result_summary(attempt, LOG_DBG,
|
||||
"Payment of %s reached destination,"
|
||||
" but timed out before the rest arrived.",
|
||||
fmt_amount_msat(tmpctx, attempt->delivers));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -928,6 +933,15 @@ static struct command_result *getroutes_done(struct command *aux_cmd,
|
||||
return getroutes_for(aux_cmd, payment, needs_routing);
|
||||
}
|
||||
|
||||
/* Even if we're amazingly slow, we should make one attempt. */
|
||||
if (payment->total_num_attempts > 0
|
||||
&& time_greater_(time_mono().ts, payment->deadline.ts)) {
|
||||
payment_failed(aux_cmd, payment, PAY_UNSPECIFIED_ERROR,
|
||||
"Timed out after after %"PRIu64" attempts. %s",
|
||||
payment->total_num_attempts,
|
||||
payment->prior_results);
|
||||
return command_still_pending(aux_cmd);
|
||||
}
|
||||
|
||||
routes = json_get_member(buf, result, "routes");
|
||||
payment_log(payment, LOG_DBG, "routes for %s = %.*s",
|
||||
@ -1321,6 +1335,7 @@ static struct command_result *json_xpay(struct command *cmd,
|
||||
struct xpay *xpay = xpay_of(cmd->plugin);
|
||||
struct amount_msat *msat, *maxfee;
|
||||
struct payment *payment = tal(cmd, struct payment);
|
||||
unsigned int *retryfor;
|
||||
char *err;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
@ -1328,6 +1343,7 @@ static struct command_result *json_xpay(struct command *cmd,
|
||||
p_opt("amount_msat", param_msat, &msat),
|
||||
p_opt("maxfee", param_msat, &maxfee),
|
||||
p_opt("layers", param_string_array, &payment->layers),
|
||||
p_opt_def("retry_for", param_number, &retryfor, 60),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
@ -1343,6 +1359,7 @@ static struct command_result *json_xpay(struct command *cmd,
|
||||
payment->total_num_attempts = payment->num_failures = 0;
|
||||
payment->requests = tal_arr(payment, struct out_req *, 0);
|
||||
payment->prior_results = tal_strdup(payment, "");
|
||||
payment->deadline = timemono_add(time_mono(), time_from_sec(*retryfor));
|
||||
|
||||
if (bolt12_has_prefix(payment->invstring)) {
|
||||
struct gossmap *gossmap = get_gossmap(xpay);
|
||||
|
@ -1,8 +1,9 @@
|
||||
from fixtures import * # noqa: F401,F403
|
||||
from fixtures import TEST_NETWORK
|
||||
from pyln.client import RpcError
|
||||
from pyln.testing.utils import FUNDAMOUNT
|
||||
from utils import (
|
||||
TIMEOUT, first_scid, GenChannel, generate_gossip_store
|
||||
TIMEOUT, first_scid, GenChannel, generate_gossip_store, wait_for
|
||||
)
|
||||
|
||||
import os
|
||||
@ -246,3 +247,25 @@ def test_xpay_fake_channeld(node_factory, bitcoind, chainparams):
|
||||
f"amount={AMOUNT}msat"]).decode('utf-8').strip()
|
||||
assert l1.rpc.decode(inv)['payee'] == nodeids[n]
|
||||
l1.rpc.xpay(inv)
|
||||
|
||||
|
||||
def test_xpay_timeout(node_factory, executor):
|
||||
# ->l3->
|
||||
# l1->l2< >l4
|
||||
# ->l5->
|
||||
l1, l2, l3, l4, l5 = node_factory.get_nodes(5, opts={'dev-no-reconnect': None})
|
||||
|
||||
node_factory.join_nodes([l1, l2, l3, l4], wait_for_announce=True)
|
||||
node_factory.join_nodes([l2, l5, l4], fundamount=FUNDAMOUNT // 2, wait_for_announce=True)
|
||||
|
||||
# Make sure l1 sees both paths.
|
||||
wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 5 * 2)
|
||||
|
||||
# Break l3->l4
|
||||
l3.rpc.disconnect(l4.info['id'], force=True)
|
||||
|
||||
b11 = l4.rpc.invoice('100000sat', 'test_xpay_timeout', 'test_xpay_timeout')['bolt11']
|
||||
fut = executor.submit(l1.rpc.xpay, invstring=b11, retry_for=0)
|
||||
|
||||
with pytest.raises(RpcError, match=r"Timed out after after 1 attempts"):
|
||||
fut.result(TIMEOUT)
|
||||
|
Loading…
Reference in New Issue
Block a user