mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-03 18:57:06 +01:00
pay: remove route when a payment fails partway.
It's a bit harsh, but I'm assuming they'll get refreshed eventually. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
d8af789bbb
commit
7bebfe265c
4 changed files with 103 additions and 9 deletions
62
daemon/pay.c
62
daemon/pay.c
|
@ -16,7 +16,7 @@ struct pay_command {
|
||||||
struct list_node list;
|
struct list_node list;
|
||||||
struct sha256 rhash;
|
struct sha256 rhash;
|
||||||
u64 msatoshis;
|
u64 msatoshis;
|
||||||
struct pubkey id;
|
struct pubkey *ids;
|
||||||
/* Set if this is in progress. */
|
/* Set if this is in progress. */
|
||||||
struct htlc *htlc;
|
struct htlc *htlc;
|
||||||
/* Preimage if this succeeded. */
|
/* Preimage if this succeeded. */
|
||||||
|
@ -35,9 +35,9 @@ static void json_pay_success(struct command *cmd, const struct rval *rval)
|
||||||
command_success(cmd, response);
|
command_success(cmd, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_json(struct command *cmd, const struct htlc *htlc)
|
static void handle_json(struct command *cmd, const struct htlc *htlc,
|
||||||
|
const FailInfo *f)
|
||||||
{
|
{
|
||||||
FailInfo *f;
|
|
||||||
struct pubkey id;
|
struct pubkey id;
|
||||||
const char *idstr = "INVALID";
|
const char *idstr = "INVALID";
|
||||||
|
|
||||||
|
@ -46,7 +46,6 @@ static void handle_json(struct command *cmd, const struct htlc *htlc)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
f = failinfo_unwrap(cmd, htlc->fail, tal_count(htlc->fail));
|
|
||||||
if (!f) {
|
if (!f) {
|
||||||
command_fail(cmd, "failed (bad message)");
|
command_fail(cmd, "failed (bad message)");
|
||||||
return;
|
return;
|
||||||
|
@ -60,6 +59,40 @@ static void handle_json(struct command *cmd, const struct htlc *htlc)
|
||||||
f->error_code, idstr, f->reason ? f->reason : "unknown");
|
f->error_code, idstr, f->reason ? f->reason : "unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void check_routing_failure(struct lightningd_state *dstate,
|
||||||
|
const struct pay_command *pc,
|
||||||
|
const FailInfo *f)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
struct pubkey id;
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* FIXME: We remove route on *any* failure. */
|
||||||
|
log_debug(dstate->base_log, "Seeking route for fail code %u",
|
||||||
|
f->error_code);
|
||||||
|
if (!proto_to_pubkey(dstate->secpctx, f->id, &id)) {
|
||||||
|
log_add(dstate->base_log, " - bad node");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_add_struct(dstate->base_log, " node %s", struct pubkey, &id);
|
||||||
|
|
||||||
|
/* Don't remove route if it's last node (obviously) */
|
||||||
|
for (i = 0; i+1 < tal_count(pc->ids); i++) {
|
||||||
|
if (structeq(&pc->ids[i], &id)) {
|
||||||
|
remove_connection(dstate, &pc->ids[i], &pc->ids[i+1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (structeq(&pc->ids[i], &id))
|
||||||
|
log_debug(dstate->base_log, "Final node: ignoring");
|
||||||
|
else
|
||||||
|
log_debug(dstate->base_log, "Node not on route: ignoring");
|
||||||
|
}
|
||||||
|
|
||||||
void complete_pay_command(struct lightningd_state *dstate,
|
void complete_pay_command(struct lightningd_state *dstate,
|
||||||
const struct htlc *htlc)
|
const struct htlc *htlc)
|
||||||
{
|
{
|
||||||
|
@ -67,13 +100,21 @@ void complete_pay_command(struct lightningd_state *dstate,
|
||||||
|
|
||||||
list_for_each(&dstate->pay_commands, i, list) {
|
list_for_each(&dstate->pay_commands, i, list) {
|
||||||
if (i->htlc == htlc) {
|
if (i->htlc == htlc) {
|
||||||
|
FailInfo *f = NULL;
|
||||||
if (htlc->r)
|
if (htlc->r)
|
||||||
i->rval = tal_dup(i, struct rval, htlc->r);
|
i->rval = tal_dup(i, struct rval, htlc->r);
|
||||||
|
else {
|
||||||
|
f = failinfo_unwrap(i->cmd, htlc->fail,
|
||||||
|
tal_count(htlc->fail));
|
||||||
|
check_routing_failure(dstate, i, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No longer connected to live HTLC. */
|
||||||
i->htlc = NULL;
|
i->htlc = NULL;
|
||||||
|
|
||||||
/* Can be NULL if JSON RPC goes away. */
|
/* Can be NULL if JSON RPC goes away. */
|
||||||
if (i->cmd)
|
if (i->cmd)
|
||||||
handle_json(i->cmd, htlc);
|
handle_json(i->cmd, htlc, f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -305,6 +346,7 @@ static void json_sendpay(struct command *cmd,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (pc->rval) {
|
if (pc->rval) {
|
||||||
|
size_t old_nhops = tal_count(pc->ids);
|
||||||
log_add(cmd->dstate->base_log, "... succeeded");
|
log_add(cmd->dstate->base_log, "... succeeded");
|
||||||
/* Must match successful payment parameters. */
|
/* Must match successful payment parameters. */
|
||||||
if (pc->msatoshis != amounts[n_hops-1]) {
|
if (pc->msatoshis != amounts[n_hops-1]) {
|
||||||
|
@ -313,11 +355,11 @@ static void json_sendpay(struct command *cmd,
|
||||||
PRIu64, pc->msatoshis);
|
PRIu64, pc->msatoshis);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!structeq(&pc->id, &ids[n_hops-1])) {
|
if (!structeq(&pc->ids[old_nhops-1], &ids[n_hops-1])) {
|
||||||
char *previd;
|
char *previd;
|
||||||
previd = pubkey_to_hexstr(cmd,
|
previd = pubkey_to_hexstr(cmd,
|
||||||
cmd->dstate->secpctx,
|
cmd->dstate->secpctx,
|
||||||
&pc->id);
|
&pc->ids[old_nhops-1]);
|
||||||
command_fail(cmd,
|
command_fail(cmd,
|
||||||
"already succeeded to %s",
|
"already succeeded to %s",
|
||||||
previd);
|
previd);
|
||||||
|
@ -339,12 +381,14 @@ static void json_sendpay(struct command *cmd,
|
||||||
onion = onion_create(cmd, cmd->dstate->secpctx, ids+1, amounts+1,
|
onion = onion_create(cmd, cmd->dstate->secpctx, ids+1, amounts+1,
|
||||||
n_hops-1);
|
n_hops-1);
|
||||||
|
|
||||||
if (!pc)
|
if (pc)
|
||||||
|
pc->ids = tal_free(pc->ids);
|
||||||
|
else
|
||||||
pc = tal(cmd->dstate, struct pay_command);
|
pc = tal(cmd->dstate, struct pay_command);
|
||||||
pc->cmd = cmd;
|
pc->cmd = cmd;
|
||||||
pc->rhash = rhash;
|
pc->rhash = rhash;
|
||||||
pc->rval = NULL;
|
pc->rval = NULL;
|
||||||
pc->id = ids[n_hops-1];
|
pc->ids = tal_steal(pc, ids);
|
||||||
pc->msatoshis = amounts[n_hops-1];
|
pc->msatoshis = amounts[n_hops-1];
|
||||||
|
|
||||||
/* Expiry for HTLCs is absolute. And add one to give some margin. */
|
/* Expiry for HTLCs is absolute. And add one to give some margin. */
|
||||||
|
|
|
@ -154,6 +154,39 @@ struct node_connection *add_connection(struct lightningd_state *dstate,
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void remove_connection(struct lightningd_state *dstate,
|
||||||
|
const struct pubkey *src, const struct pubkey *dst)
|
||||||
|
{
|
||||||
|
struct node *from, *to;
|
||||||
|
size_t i, num_edges;
|
||||||
|
|
||||||
|
log_debug_struct(dstate->base_log, "Removing route from %s",
|
||||||
|
struct pubkey, src);
|
||||||
|
log_add_struct(dstate->base_log, " to %s", struct pubkey, dst);
|
||||||
|
|
||||||
|
from = get_node(dstate, src);
|
||||||
|
to = get_node(dstate, dst);
|
||||||
|
if (!from || !to) {
|
||||||
|
log_debug(dstate->base_log, "Not found: src=%p dst=%p",
|
||||||
|
from, to);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_edges = tal_count(from->out);
|
||||||
|
|
||||||
|
for (i = 0; i < num_edges; i++) {
|
||||||
|
if (from->out[i]->dst != to)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
log_add(dstate->base_log, " Matched route %zu of %zu",
|
||||||
|
i, num_edges);
|
||||||
|
/* Destructor makes it delete itself */
|
||||||
|
tal_free(from->out[i]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log_add(dstate->base_log, " None of %zu routes matched", num_edges);
|
||||||
|
}
|
||||||
|
|
||||||
/* Too big to reach, but don't overflow if added. */
|
/* Too big to reach, but don't overflow if added. */
|
||||||
#define INFINITE 0x3FFFFFFFFFFFFFFFULL
|
#define INFINITE 0x3FFFFFFFFFFFFFFFULL
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,9 @@ struct node_connection *add_connection(struct lightningd_state *dstate,
|
||||||
u32 base_fee, s32 proportional_fee,
|
u32 base_fee, s32 proportional_fee,
|
||||||
u32 delay, u32 min_blocks);
|
u32 delay, u32 min_blocks);
|
||||||
|
|
||||||
|
void remove_connection(struct lightningd_state *dstate,
|
||||||
|
const struct pubkey *src, const struct pubkey *dst);
|
||||||
|
|
||||||
struct peer *find_route(struct lightningd_state *dstate,
|
struct peer *find_route(struct lightningd_state *dstate,
|
||||||
const struct pubkey *to,
|
const struct pubkey *to,
|
||||||
u64 msatoshi, s64 *fee,
|
u64 msatoshi, s64 *fee,
|
||||||
|
|
|
@ -1048,6 +1048,20 @@ if [ ! -n "$MANUALCOMMIT" ]; then
|
||||||
lcli1 sendpay "$SHORTROUTE" $RHASH5 | $FGREP "already succeeded to $ID3"
|
lcli1 sendpay "$SHORTROUTE" $RHASH5 | $FGREP "already succeeded to $ID3"
|
||||||
lcli1 sendpay "$UNDERPAY" $RHASH5 | $FGREP "already succeeded with amount $HTLC_AMOUNT"
|
lcli1 sendpay "$UNDERPAY" $RHASH5 | $FGREP "already succeeded with amount $HTLC_AMOUNT"
|
||||||
|
|
||||||
|
# Now node2 should fail to route.
|
||||||
|
if lcli1 sendpay "$ROUTE" $RHASH4 | $FGREP "failed: error code 404 node $ID2 reason Unknown peer"; then : ;
|
||||||
|
else
|
||||||
|
echo "Pay to node3 didn't give 404" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Now node1 should fail to route (route deleted)
|
||||||
|
if lcli1 getroute $ID3 $HTLC_AMOUNT | $FGREP "no route found"; then : ;
|
||||||
|
else
|
||||||
|
echo "Pay to node3 didn't fail instantly second time" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
DO_RECONNECT=$RECONNECT
|
DO_RECONNECT=$RECONNECT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue