pay: Also report local channel failures to gossipd.

This commit is contained in:
ZmnSCPxj 2018-01-29 15:04:33 +00:00 committed by Rusty Russell
parent 131283af6e
commit a043d1063a

View File

@ -34,13 +34,11 @@ static void json_pay_success(struct command *cmd, const struct preimage *rval)
} }
static void json_pay_failed(struct command *cmd, static void json_pay_failed(struct command *cmd,
const struct pubkey *sender,
enum onion_type failure_code, enum onion_type failure_code,
const char *details) const char *details)
{ {
/* Can be NULL if JSON RPC goes away. */ /* Can be NULL if JSON RPC goes away. */
if (cmd) { if (cmd) {
/* FIXME: Report sender! */
command_fail(cmd, "failed: %s (%s)", command_fail(cmd, "failed: %s (%s)",
onion_type_name(failure_code), details); onion_type_name(failure_code), details);
} }
@ -92,17 +90,47 @@ static u8 *channel_update_from_onion_error(const tal_t *ctx,
return channel_update; return channel_update;
} }
struct routing_failure {
enum onion_type failcode;
struct pubkey erring_node;
struct short_channel_id erring_channel;
u8 *channel_update;
};
/* Return a struct routing_failure for a local failure allocated
* from the given context. */
static struct routing_failure*
local_routing_failure(const tal_t *ctx,
const struct lightningd *ld,
const struct htlc_out *hout,
const struct wallet_payment *payment)
{
struct routing_failure *routing_failure;
assert(hout->failcode);
routing_failure = tal(ctx, struct routing_failure);
routing_failure->failcode = hout->failcode;
routing_failure->erring_node = ld->id;
routing_failure->erring_channel = payment->route_channels[0];
routing_failure->channel_update = NULL;
return routing_failure;
}
/* Return false if permanent failure at the destination, true if /* Return false if permanent failure at the destination, true if
* retrying is plausible. */ * retrying is plausible. Fill *routing_failure with NULL if
static bool report_routing_failure(struct subd *gossip, * we cannot report the remote failure, or with the routing
* failure to report (allocated from ctx) otherwise. */
static bool remote_routing_failure(const tal_t *ctx,
struct routing_failure **routing_failure,
const struct wallet_payment *payment, const struct wallet_payment *payment,
const struct onionreply *failure) const struct onionreply *failure)
{ {
enum onion_type failcode = fromwire_peektype(failure->msg); enum onion_type failcode = fromwire_peektype(failure->msg);
const tal_t *tmpctx;
u8 *channel_update; u8 *channel_update;
u8 *gossip_msg;
const struct pubkey *route_nodes; const struct pubkey *route_nodes;
const struct pubkey *erring_node;
const struct short_channel_id *route_channels; const struct short_channel_id *route_channels;
const struct short_channel_id *erring_channel; const struct short_channel_id *erring_channel;
static const struct short_channel_id dummy_channel = { 0, 0, 0 }; static const struct short_channel_id dummy_channel = { 0, 0, 0 };
@ -110,12 +138,13 @@ static bool report_routing_failure(struct subd *gossip,
bool retry_plausible; bool retry_plausible;
bool report_to_gossipd; bool report_to_gossipd;
tmpctx = tal_tmpctx(payment); *routing_failure = tal(ctx, struct routing_failure);
route_nodes = payment->route_nodes; route_nodes = payment->route_nodes;
route_channels = payment->route_channels; route_channels = payment->route_channels;
origin_index = failure->origin_index; origin_index = failure->origin_index;
channel_update channel_update
= channel_update_from_onion_error(tmpctx, failure->msg); = channel_update_from_onion_error(*routing_failure,
failure->msg);
retry_plausible = true; retry_plausible = true;
report_to_gossipd = true; report_to_gossipd = true;
@ -145,21 +174,33 @@ static bool report_routing_failure(struct subd *gossip,
/* Report the *next* channel as failing. */ /* Report the *next* channel as failing. */
erring_channel = &route_channels[origin_index + 1]; erring_channel = &route_channels[origin_index + 1];
/* Report the error to gossipd if appropriate. */ erring_node = &route_nodes[origin_index];
if (report_to_gossipd) {
gossip_msg if (report_to_gossipd) {
= towire_gossip_routing_failure(tmpctx, (*routing_failure)->failcode = failcode;
&route_nodes[origin_index], (*routing_failure)->erring_node = *erring_node;
erring_channel, (*routing_failure)->erring_channel = *erring_channel;
(u16) failcode, (*routing_failure)->channel_update = channel_update;
channel_update); } else
subd_send_msg(gossip, gossip_msg); *routing_failure = tal_free(*routing_failure);
}
tal_free(tmpctx);
return retry_plausible; return retry_plausible;
} }
static void report_routing_failure(struct subd *gossip,
struct routing_failure *fail)
{
u8 *gossip_msg
= towire_gossip_routing_failure(gossip,
&fail->erring_node,
&fail->erring_channel,
(u16) fail->failcode,
fail->channel_update);
subd_send_msg(gossip, gossip_msg);
tal_free(gossip_msg);
}
void payment_failed(struct lightningd *ld, const struct htlc_out *hout, void payment_failed(struct lightningd *ld, const struct htlc_out *hout,
const char *localfail) const char *localfail)
{ {
@ -168,39 +209,52 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout,
struct secret *path_secrets; struct secret *path_secrets;
struct wallet_payment *payment; struct wallet_payment *payment;
const tal_t *tmpctx = tal_tmpctx(ld); const tal_t *tmpctx = tal_tmpctx(ld);
struct routing_failure* fail = NULL;
const char *failmsg;
bool retry_plausible;
payment = wallet_payment_by_hash(tmpctx, ld->wallet,
&hout->payment_hash);
/* This gives more details than a generic failure message */ /* This gives more details than a generic failure message */
if (localfail) { if (localfail) {
wallet_payment_set_status(ld->wallet, &hout->payment_hash, fail = local_routing_failure(tmpctx, ld, hout, payment);
PAYMENT_FAILED, NULL); failcode = fail->failcode;
json_pay_failed(hout->cmd, NULL, hout->failcode, localfail); failmsg = localfail;
tal_free(tmpctx); retry_plausible = true;
return;
}
/* Must be remote fail. */
assert(!hout->failcode);
payment = wallet_payment_by_hash(tmpctx, ld->wallet,
&hout->payment_hash);
path_secrets = payment->path_secrets;
reply = unwrap_onionreply(tmpctx, path_secrets, tal_count(path_secrets),
hout->failuremsg);
if (!reply) {
log_info(hout->key.peer->log,
"htlc %"PRIu64" failed with bad reply (%s)",
hout->key.id,
tal_hex(ltmp, hout->failuremsg));
failcode = WIRE_PERMANENT_NODE_FAILURE;
} else { } else {
failcode = fromwire_peektype(reply->msg); /* Must be remote fail. */
log_info(hout->key.peer->log, assert(!hout->failcode);
"htlc %"PRIu64" failed from %ith node with code 0x%04x (%s)", failmsg = "reply from remote";
hout->key.id, /* Try to parse reply. */
reply->origin_index, path_secrets = payment->path_secrets;
failcode, onion_type_name(failcode)); reply = unwrap_onionreply(tmpctx, path_secrets,
tal_count(path_secrets),
/* FIXME: Retry if routing failure thinks so. */ hout->failuremsg);
report_routing_failure(ld->gossip, payment, reply); if (!reply) {
log_info(hout->key.peer->log,
"htlc %"PRIu64" failed with bad reply (%s)",
hout->key.id,
tal_hex(ltmp, hout->failuremsg));
/* Cannot report failure. */
fail = NULL;
failcode = WIRE_PERMANENT_NODE_FAILURE;
/* Not safe to retry, not know what failed. */
/* FIXME: some mitigation for this branch. */
retry_plausible = false;
} else {
failcode = fromwire_peektype(reply->msg);
log_info(hout->key.peer->log,
"htlc %"PRIu64" "
"failed from %ith node "
"with code 0x%04x (%s)",
hout->key.id,
reply->origin_index,
failcode, onion_type_name(failcode));
retry_plausible
= remote_routing_failure(tmpctx, &fail,
payment, reply);
}
} }
/* This may invalidated the payment structure returned, so /* This may invalidated the payment structure returned, so
@ -209,12 +263,26 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout,
wallet_payment_set_status(ld->wallet, &hout->payment_hash, wallet_payment_set_status(ld->wallet, &hout->payment_hash,
PAYMENT_FAILED, NULL); PAYMENT_FAILED, NULL);
/* FIXME: save ids we can turn reply->origin_index into sender. */ /* Report to gossipd if there is something we can report. */
if (fail) {
log_debug(ld->log,
"Reporting route failure to gossipd: 0x%04x (%s) "
"node %s channel %s update %s",
fail->failcode, onion_type_name(fail->failcode),
type_to_string(tmpctx, struct pubkey,
&fail->erring_node),
type_to_string(tmpctx, struct short_channel_id,
&fail->erring_channel),
tal_hex(tmpctx, fail->channel_update));
report_routing_failure(ld->gossip, fail);
}
/* FIXME: check for routing failure / perm fail. */ /* FIXME(ZmnSCPxj): if retrying is plausible, and we are
/* check_for_routing_failure(i, sender, failure_code); */ * using pay command rather than sendpay, retry routing
* and payment again. */
(void) retry_plausible;
json_pay_failed(hout->cmd, NULL, failcode, "reply from remote"); json_pay_failed(hout->cmd, failcode, failmsg);
tal_free(tmpctx); tal_free(tmpctx);
} }