diff --git a/gossipd/Makefile b/gossipd/Makefile index 5740cc93eadc..0a8b3f93f02d 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -56,7 +56,8 @@ GOSSIPD_COMMON_OBJS := \ common/wire_error.o \ hsmd/client.o \ hsmd/gen_hsm_client_wire.o \ - lightningd/gossip_msg.o + lightningd/gossip_msg.o \ + wire/gen_onion_wire.o $(LIGHTNINGD_GOSSIP_OBJS) $(LIGHTNINGD_GOSSIP_CLIENT_OBJS): $(LIGHTNINGD_HEADERS) $(LIGHTNINGD_GOSSIP_HEADERS) diff --git a/gossipd/gossip.c b/gossipd/gossip.c index c4d9c58eb7e1..76a647003ce3 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -1883,6 +1883,31 @@ static struct io_plan *handle_disable_channel(struct io_conn *conn, tal_free(tmpctx); return daemon_conn_read_next(conn, &daemon->master); } +static struct io_plan *handle_routing_failure(struct io_conn *conn, + struct daemon *daemon, + const u8 *msg) +{ + struct pubkey erring_node; + struct short_channel_id erring_channel; + u16 failcode; + u8 *channel_update; + + if (!fromwire_gossip_routing_failure(msg, + msg, NULL, + &erring_node, + &erring_channel, + &failcode, + &channel_update)) + master_badmsg(WIRE_GOSSIP_ROUTING_FAILURE, msg); + + routing_failure(daemon->rstate, + &erring_node, + &erring_channel, + (enum onion_type) failcode, + channel_update); + + return daemon_conn_read_next(conn, &daemon->master); +} static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master) { @@ -1932,6 +1957,9 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master case WIRE_GOSSIP_DISABLE_CHANNEL: return handle_disable_channel(conn, daemon, master->msg_in); + case WIRE_GOSSIP_ROUTING_FAILURE: + return handle_routing_failure(conn, daemon, master->msg_in); + /* We send these, we don't receive them */ case WIRE_GOSSIPCTL_RELEASE_PEER_REPLY: case WIRE_GOSSIPCTL_RELEASE_PEER_REPLYFAIL: diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 41e183c915b1..756953e9ad9c 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -1,5 +1,6 @@ #include #include +#include # Initialize the gossip daemon. gossipctl_init,3000 @@ -195,3 +196,11 @@ gossip_disable_channel,3019 gossip_disable_channel,,short_channel_id,struct short_channel_id gossip_disable_channel,,direction,u8 gossip_disable_channel,,active,bool + +# master->gossipd a routing failure occurred +gossip_routing_failure,3021 +gossip_routing_failure,,erring_node,struct pubkey +gossip_routing_failure,,erring_channel,struct short_channel_id +gossip_routing_failure,,failcode,u16 +gossip_routing_failure,,len,u16 +gossip_routing_failure,,channel_update,len*u8 diff --git a/gossipd/routing.c b/gossipd/routing.c index 1cbfdc991bd7..22e49b8d8846 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #ifndef SUPERVERBOSE @@ -238,6 +239,12 @@ get_or_make_connection(struct routing_state *rstate, return nc; } +static void delete_connection(struct routing_state *rstate, + const struct node_connection *connection) +{ + tal_free(connection); +} + struct node_connection *half_add_connection( struct routing_state *rstate, const struct pubkey *from, @@ -994,3 +1001,129 @@ struct route_hop *get_route(tal_t *ctx, struct routing_state *rstate, /* FIXME: Shadow route! */ return hops; } + +/* Get the struct node_connection matching the short_channel_id, + * which must be an out connection of the given node. */ +static struct node_connection * +get_out_node_connection_of(struct routing_state *rstate, + const struct node *node, + const struct short_channel_id *short_channel_id) +{ + int i; + + for (i = 0; i < tal_count(node->out); ++i) { + if (short_channel_id_eq(&node->out[i]->short_channel_id, short_channel_id)) + return node->out[i]; + } + + return NULL; +} + +/** + * routing_failure_on_nc - Handle routing failure on a specific + * node_connection. + */ +static void routing_failure_on_nc(struct routing_state *rstate, + enum onion_type failcode, + struct node_connection *nc) +{ + /* BOLT #4: + * + * - if the PERM bit is NOT set: + * - SHOULD restore the channels as it receives new `channel_update`s. + */ + if (failcode & PERM) + nc->active = false; + else + delete_connection(rstate, nc); +} + +void routing_failure(struct routing_state *rstate, + const struct pubkey *erring_node_pubkey, + const struct short_channel_id *scid, + enum onion_type failcode, + const u8 *channel_update) +{ + const tal_t *tmpctx = tal_tmpctx(rstate); + struct node *node; + struct node_connection *nc; + int i; + enum wire_type t; + + status_trace("Received routing failure 0x%04x (%s), " + "erring node %s, " + "channel %s", + (int) failcode, onion_type_name(failcode), + type_to_string(tmpctx, struct pubkey, erring_node_pubkey), + type_to_string(tmpctx, struct short_channel_id, scid)); + + node = get_node(rstate, erring_node_pubkey); + if (!node) { + status_trace("UNUSUAL routing_failure: " + "Erring node %s not in map", + type_to_string(tmpctx, struct pubkey, + erring_node_pubkey)); + /* No node, so no channel, so any channel_update + * can also be ignored. */ + goto out; + } + + /* BOLT #4: + * + * - if the NODE bit is set: + * - SHOULD remove all channels connected with the erring node from + * consideration. + * + */ + if (failcode & NODE) { + for (i = 0; i < tal_count(node->in); ++i) + routing_failure_on_nc(rstate, failcode, node->in[i]); + for (i = 0; i < tal_count(node->out); ++i) + routing_failure_on_nc(rstate, failcode, node->out[i]); + } else { + nc = get_out_node_connection_of(rstate, node, scid); + if (nc) + routing_failure_on_nc(rstate, failcode, nc); + else + status_trace("UNUSUAL routing_failure: " + "Channel %s not an out channel " + "of node %s", + type_to_string(tmpctx, + struct short_channel_id, + scid), + type_to_string(tmpctx, struct pubkey, + erring_node_pubkey)); + } + + /* Update the channel if UPDATE failcode. Do + * this after deactivating, so that if the + * channel_update is newer it will be + * reactivated. */ + if (failcode & UPDATE) { + if (tal_len(channel_update) == 0) { + status_trace("UNUSUAL routing_failure: " + "UPDATE bit set, no channel_update. " + "failcode: 0x%04x", + (int) failcode); + goto out; + } + t = fromwire_peektype(channel_update); + if (t != WIRE_CHANNEL_UPDATE) { + status_trace("UNUSUAL routing_failure: " + "not a channel_update. " + "type: %d", + (int) t); + goto out; + } + handle_channel_update(rstate, channel_update); + } else { + if (tal_len(channel_update) != 0) + status_trace("UNUSUAL routing_failure: " + "UPDATE bit clear, channel_update given. " + "failcode: 0x%04x", + (int) failcode); + } + +out: + tal_free(tmpctx); +} diff --git a/gossipd/routing.h b/gossipd/routing.h index c3c794961437..68d590bfa6af 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #define ROUTING_MAX_HOPS 20 @@ -148,6 +149,12 @@ struct route_hop *get_route(tal_t *ctx, struct routing_state *rstate, const struct pubkey *destination, const u32 msatoshi, double riskfactor, u32 final_cltv); +/* Disable channel(s) based on the given routing failure. */ +void routing_failure(struct routing_state *rstate, + const struct pubkey *erring_node, + const struct short_channel_id *erring_channel, + enum onion_type failcode, + const u8 *channel_update); /* Utility function that, given a source and a destination, gives us * the direction bit the matching channel should get */ diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c index adaed335a400..9be41701e1b2 100644 --- a/gossipd/test/run-bench-find_route.c +++ b/gossipd/test/run-bench-find_route.c @@ -57,12 +57,18 @@ bool fromwire_channel_update(const void *p UNNEEDED, size_t *plen UNNEEDED, secp /* Generated stub for fromwire_node_announcement */ bool fromwire_node_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, size_t *plen UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, u8 **features UNNEEDED, u32 *timestamp UNNEEDED, struct pubkey *node_id UNNEEDED, u8 rgb_color[3] UNNEEDED, u8 alias[32] UNNEEDED, u8 **addresses UNNEEDED) { fprintf(stderr, "fromwire_node_announcement called!\n"); abort(); } +/* Generated stub for fromwire_peektype */ +int fromwire_peektype(const u8 *cursor UNNEEDED) +{ fprintf(stderr, "fromwire_peektype called!\n"); abort(); } /* Generated stub for fromwire_u8 */ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u8 called!\n"); abort(); } /* Generated stub for fromwire_wireaddr */ bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for onion_type_name */ +const char *onion_type_name(int e UNNEEDED) +{ fprintf(stderr, "onion_type_name called!\n"); abort(); } /* Generated stub for queue_broadcast */ bool queue_broadcast(struct broadcast_state *bstate UNNEEDED, const int type UNNEEDED, diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c index 889b2a5300b7..29c89485fc94 100644 --- a/gossipd/test/run-find_route-specific.c +++ b/gossipd/test/run-find_route-specific.c @@ -28,12 +28,18 @@ bool fromwire_channel_update(const void *p UNNEEDED, size_t *plen UNNEEDED, secp /* Generated stub for fromwire_node_announcement */ bool fromwire_node_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, size_t *plen UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, u8 **features UNNEEDED, u32 *timestamp UNNEEDED, struct pubkey *node_id UNNEEDED, u8 rgb_color[3] UNNEEDED, u8 alias[32] UNNEEDED, u8 **addresses UNNEEDED) { fprintf(stderr, "fromwire_node_announcement called!\n"); abort(); } +/* Generated stub for fromwire_peektype */ +int fromwire_peektype(const u8 *cursor UNNEEDED) +{ fprintf(stderr, "fromwire_peektype called!\n"); abort(); } /* Generated stub for fromwire_u8 */ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u8 called!\n"); abort(); } /* Generated stub for fromwire_wireaddr */ bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for onion_type_name */ +const char *onion_type_name(int e UNNEEDED) +{ fprintf(stderr, "onion_type_name called!\n"); abort(); } /* Generated stub for queue_broadcast */ bool queue_broadcast(struct broadcast_state *bstate UNNEEDED, const int type UNNEEDED, diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c index bcb24d683470..0883e480618e 100644 --- a/gossipd/test/run-find_route.c +++ b/gossipd/test/run-find_route.c @@ -21,12 +21,18 @@ bool fromwire_channel_update(const void *p UNNEEDED, size_t *plen UNNEEDED, secp /* Generated stub for fromwire_node_announcement */ bool fromwire_node_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, size_t *plen UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, u8 **features UNNEEDED, u32 *timestamp UNNEEDED, struct pubkey *node_id UNNEEDED, u8 rgb_color[3] UNNEEDED, u8 alias[32] UNNEEDED, u8 **addresses UNNEEDED) { fprintf(stderr, "fromwire_node_announcement called!\n"); abort(); } +/* Generated stub for fromwire_peektype */ +int fromwire_peektype(const u8 *cursor UNNEEDED) +{ fprintf(stderr, "fromwire_peektype called!\n"); abort(); } /* Generated stub for fromwire_u8 */ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u8 called!\n"); abort(); } /* Generated stub for fromwire_wireaddr */ bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for onion_type_name */ +const char *onion_type_name(int e UNNEEDED) +{ fprintf(stderr, "onion_type_name called!\n"); abort(); } /* Generated stub for queue_broadcast */ bool queue_broadcast(struct broadcast_state *bstate UNNEEDED, const int type UNNEEDED, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 992bc9f7cdba..f5a256f9a1d1 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -110,6 +110,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIP_SEND_GOSSIP: case WIRE_GOSSIP_GET_TXOUT_REPLY: case WIRE_GOSSIP_DISABLE_CHANNEL: + case WIRE_GOSSIP_ROUTING_FAILURE: /* This is a reply, so never gets through to here. */ case WIRE_GOSSIP_GET_UPDATE_REPLY: case WIRE_GOSSIP_GETNODES_REPLY: diff --git a/lightningd/pay.c b/lightningd/pay.c index 44724f6d62f9..616bd674034d 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -34,13 +34,11 @@ static void json_pay_success(struct command *cmd, const struct preimage *rval) } static void json_pay_failed(struct command *cmd, - const struct pubkey *sender, enum onion_type failure_code, const char *details) { /* Can be NULL if JSON RPC goes away. */ if (cmd) { - /* FIXME: Report sender! */ command_fail(cmd, "failed: %s (%s)", onion_type_name(failure_code), details); } @@ -57,51 +55,234 @@ void payment_succeeded(struct lightningd *ld, struct htlc_out *hout, hout->cmd = NULL; } +/* Return NULL if the wrapped onion error message has no + * channel_update field, or return the embedded + * channel_update message otherwise. */ +static u8 *channel_update_from_onion_error(const tal_t *ctx, + const u8 *onion_message) +{ + u8 *channel_update = NULL; + u64 unused64; + u32 unused32; + + /* Identify failcodes that have some channel_update. + * + * TODO > BOLT 1.0: Add new failcodes when updating to a + * new BOLT version. */ + if (!fromwire_temporary_channel_failure(ctx, + onion_message, NULL, + &channel_update) && + !fromwire_amount_below_minimum(ctx, + onion_message, NULL, &unused64, + &channel_update) && + !fromwire_fee_insufficient(ctx, + onion_message, NULL, &unused64, + &channel_update) && + !fromwire_incorrect_cltv_expiry(ctx, + onion_message, NULL, &unused32, + &channel_update) && + !fromwire_expiry_too_soon(ctx, + onion_message, NULL, + &channel_update)) + /* No channel update. */ + channel_update = NULL; + + 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 + * retrying is plausible. Fill *routing_failure with NULL if + * 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 onionreply *failure) +{ + enum onion_type failcode = fromwire_peektype(failure->msg); + u8 *channel_update; + const struct pubkey *route_nodes; + const struct pubkey *erring_node; + const struct short_channel_id *route_channels; + const struct short_channel_id *erring_channel; + static const struct short_channel_id dummy_channel = { 0, 0, 0 }; + int origin_index; + bool retry_plausible; + bool report_to_gossipd; + + *routing_failure = tal(ctx, struct routing_failure); + route_nodes = payment->route_nodes; + route_channels = payment->route_channels; + origin_index = failure->origin_index; + channel_update + = channel_update_from_onion_error(*routing_failure, + failure->msg); + retry_plausible = true; + report_to_gossipd = true; + + assert(origin_index < tal_count(route_nodes)); + + /* Check if at destination. */ + if (origin_index == tal_count(route_nodes) - 1) { + /* BOLT #4: + * + * - if the _final node_ is returning the error: + * - if the PERM bit is set: + * - SHOULD fail the payment. + * */ + if (failcode & PERM) + retry_plausible = false; + else + retry_plausible = true; + /* Only send message to gossipd if NODE error; + * there is no "next" channel to report as + * failing if this is the last node. */ + if (failcode & NODE) { + erring_channel = &dummy_channel; + report_to_gossipd = true; + } else + report_to_gossipd = false; + } else + /* Report the *next* channel as failing. */ + erring_channel = &route_channels[origin_index + 1]; + + erring_node = &route_nodes[origin_index]; + + if (report_to_gossipd) { + (*routing_failure)->failcode = failcode; + (*routing_failure)->erring_node = *erring_node; + (*routing_failure)->erring_channel = *erring_channel; + (*routing_failure)->channel_update = channel_update; + } else + *routing_failure = tal_free(*routing_failure); + + 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, const char *localfail) { struct onionreply *reply; enum onion_type failcode; struct secret *path_secrets; + struct wallet_payment *payment; const tal_t *tmpctx = tal_tmpctx(ld); + struct routing_failure* fail = NULL; + const char *failmsg; + bool retry_plausible; - wallet_payment_set_status(ld->wallet, &hout->payment_hash, - PAYMENT_FAILED, NULL); + payment = wallet_payment_by_hash(tmpctx, ld->wallet, + &hout->payment_hash); /* This gives more details than a generic failure message */ if (localfail) { - json_pay_failed(hout->cmd, NULL, hout->failcode, localfail); - tal_free(tmpctx); - return; - } - - /* Must be remote fail. */ - assert(!hout->failcode); - path_secrets = wallet_payment_get_secrets(tmpctx, ld->wallet, - &hout->payment_hash); - 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; + fail = local_routing_failure(tmpctx, ld, hout, payment); + failcode = fail->failcode; + failmsg = localfail; + retry_plausible = true; } 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)); + /* Must be remote fail. */ + assert(!hout->failcode); + failmsg = "reply from remote"; + /* Try to parse reply. */ + 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)); + /* 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); + } } - /* FIXME: save ids we can turn reply->origin_index into sender. */ + /* This may invalidated the payment structure returned, so + * access to payment object should not be done after the + * below call. */ + wallet_payment_set_status(ld->wallet, &hout->payment_hash, + PAYMENT_FAILED, NULL); - /* FIXME: check for routing failure / perm fail. */ - /* check_for_routing_failure(i, sender, failure_code); */ + /* 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(ZmnSCPxj): if retrying is plausible, and we are + * 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); } @@ -131,6 +312,7 @@ static bool send_payment(struct command *cmd, struct pubkey *ids = tal_arr(tmpctx, struct pubkey, n_hops); struct wallet_payment *payment = NULL; struct htlc_out *hout; + struct short_channel_id *channels; /* Expiry for HTLCs is absolute. And add one to give some margin. */ base_expiry = get_block_height(cmd->ld->topology) + 1; @@ -213,6 +395,11 @@ static bool send_payment(struct command *cmd, return false; } + /* Copy channels used along the route. */ + channels = tal_arr(tmpctx, struct short_channel_id, n_hops); + for (i = 0; i < n_hops; ++i) + channels[i] = route[i].channel_id; + /* If hout fails, payment should be freed too. */ payment = tal(hout, struct wallet_payment); payment->id = 0; @@ -223,6 +410,8 @@ static bool send_payment(struct command *cmd, payment->timestamp = time_now().ts.tv_sec; payment->payment_preimage = NULL; payment->path_secrets = tal_steal(payment, path_secrets); + payment->route_nodes = tal_steal(payment, ids); + payment->route_channels = tal_steal(payment, channels); /* We write this into db when HTLC is actually sent. */ wallet_payment_setup(cmd->ld->wallet, payment); diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 6a883a07ac47..f295937bc241 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -339,6 +339,7 @@ static void rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds, } if (failure_code) { + hout->failcode = (enum onion_type) failure_code; if (!hout->in) { char *localfail = tal_fmt(msg, "%s: %.*s", onion_type_name(failure_code), @@ -348,6 +349,8 @@ static void rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds, } else local_fail_htlc(hout->in, failure_code, hout->key.peer->scid); + /* Prevent hout from being failed twice. */ + tal_del_destructor(hout, hout_subd_died); tal_free(hout); return; } diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 0fb6528183de..70bb003e0249 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -776,6 +776,21 @@ def test_sendpay_cant_afford(self): # But this should work. self.pay(l2, l1, available - reserve*2) + def test_pay0(self): + """Test paying 0 amount + """ + l1,l2 = self.connect() + # Set up channel. + chanid = self.fund_channel(l1, l2, 10**6) + self.wait_for_routes(l1, [chanid]) + + # Get any-amount invoice + inv = l2.rpc.invoice("any", "any", 'description')['bolt11'] + + # Amount must be nonzero! + self.assertRaisesRegex(ValueError, 'WIRE_AMOUNT_BELOW_MINIMUM', + l1.rpc.pay, inv, 0) + def test_pay(self): l1,l2 = self.connect() @@ -811,9 +826,6 @@ def test_pay(self): # Must provide an amount! self.assertRaises(ValueError, l1.rpc.pay, inv2) self.assertRaises(ValueError, l1.rpc.pay, inv2, None) - # Amount must be nonzero! - self.assertRaisesRegex(ValueError, 'WIRE_AMOUNT_BELOW_MINIMUM', - l1.rpc.pay, inv2, 0) l1.rpc.pay(inv2, random.randint(1000, 999999)) # Should see 6 completed payments @@ -823,6 +835,71 @@ def test_pay(self): assert len(l1.rpc.listpayments(inv)['payments']) == 1 assert l1.rpc.listpayments(inv)['payments'][0]['payment_preimage'] == preimage['preimage'] + # Long test involving 4 lightningd instances. + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") + def test_report_routing_failure(self): + """Test routing failure and retrying of routing. + """ + # The setup is as follows: + # l3-->l4 + # ^ / | + # | / | + # | L v + # l2<--l1 + # + # l1 wants to pay to l4. + # The shortest route is l1-l4, but l1 cannot + # afford to pay to l1 because l4 has all the + # funds. + # This is a local failure. + # The next shortest route is l1-l2-l4, but + # l2 cannot afford to pay l4 for same reason. + # This is a remote failure. + # Finally the only possible path is + # l1-l2-l3-l4. + + def fund_from_to_payer(lsrc, ldst, lpayer): + lsrc.rpc.connect(ldst.info['id'], 'localhost', ldst.info['port']) + c = self.fund_channel(lsrc, ldst, 10000000) + self.wait_for_routes(lpayer, [c]) + + # FIXME: This repeated retrying should get implemented + # in lightningd `pay` command directly. + def pay(l, inv): + start_time = time.time() + retry = True + err = None + while retry: + if time.time() > start_time + 10: + raise err + try: + l.rpc.pay(inv) + retry = False + except Exception as errx: + err = errx + + ## Setup + # Construct lightningd + l1 = self.node_factory.get_node() + l2 = self.node_factory.get_node() + l3 = self.node_factory.get_node() + l4 = self.node_factory.get_node() + + # Wire them up + # The ordering below matters! + # Particularly, l1 is payer and we will + # wait for l1 to receive gossip for the + # channel being made. + fund_from_to_payer(l1, l2, l1) + fund_from_to_payer(l2, l3, l1) + fund_from_to_payer(l3, l4, l1) + fund_from_to_payer(l4, l1, l1) + fund_from_to_payer(l4, l2, l1) + + ## Test + inv = l4.rpc.invoice(1234567, 'inv', 'for testing')['bolt11'] + pay(l1, inv) + def test_bad_opening(self): # l1 asks for a too-long locktime l1 = self.node_factory.get_node(options=['--locktime-blocks=100']) diff --git a/wallet/db.c b/wallet/db.c index d25e8ef15f4e..183b2e5496d4 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -183,6 +183,12 @@ char *dbmigrations[] = { "UPDATE invoices" " SET paid_timestamp = strftime('%s', 'now')" " WHERE state = 1;", + /* We need to keep the route node pubkeys and short channel ids to + * correctly mark routing failures. We separate short channel ids + * because we cannot safely save them as blobs due to byteorder + * concerns. */ + "ALTER TABLE payments ADD COLUMN route_nodes BLOB;", + "ALTER TABLE payments ADD COLUMN route_channels TEXT;", NULL, }; @@ -454,6 +460,56 @@ bool sqlite3_column_short_channel_id(sqlite3_stmt *stmt, int col, size_t sourcelen = sqlite3_column_bytes(stmt, col); return short_channel_id_from_str(source, sourcelen, dest); } +bool sqlite3_bind_short_channel_id_array(sqlite3_stmt *stmt, int col, + const struct short_channel_id *id) +{ + u8 *ser; + size_t num; + size_t i; + + /* Handle nulls early. */ + if (!id) { + sqlite3_bind_null(stmt, col); + return true; + } + + ser = tal_arr(NULL, u8, 0); + num = tal_count(id); + + for (i = 0; i < num; ++i) + towire_short_channel_id(&ser, &id[i]); + + sqlite3_bind_blob(stmt, col, ser, tal_len(ser), SQLITE_TRANSIENT); + + tal_free(ser); + return true; +} +struct short_channel_id * +sqlite3_column_short_channel_id_array(const tal_t *ctx, + sqlite3_stmt *stmt, int col) +{ + const u8 *ser; + size_t len; + struct short_channel_id *ret; + size_t n; + + /* Handle nulls early. */ + if (sqlite3_column_type(stmt, col) == SQLITE_NULL) + return NULL; + + ser = sqlite3_column_blob(stmt, col); + len = sqlite3_column_bytes(stmt, col); + ret = tal_arr(ctx, struct short_channel_id, 0); + n = 0; + + while (len != 0) { + tal_resize(&ret, n + 1); + fromwire_short_channel_id(&ser, &len, &ret[n]); + ++n; + } + + return ret; +} bool sqlite3_bind_tx(sqlite3_stmt *stmt, int col, const struct bitcoin_tx *tx) { @@ -503,6 +559,52 @@ bool sqlite3_bind_pubkey(sqlite3_stmt *stmt, int col, const struct pubkey *pk) return true; } +bool sqlite3_bind_pubkey_array(sqlite3_stmt *stmt, int col, + const struct pubkey *pks) +{ + size_t n; + size_t i; + u8 *ders; + + if (!pks) { + sqlite3_bind_null(stmt, col); + return true; + } + + n = tal_count(pks); + ders = tal_arr(NULL, u8, n * PUBKEY_DER_LEN); + + for (i = 0; i < n; ++i) + pubkey_to_der(&ders[i * PUBKEY_DER_LEN], &pks[i]); + sqlite3_bind_blob(stmt, col, ders, tal_len(ders), SQLITE_TRANSIENT); + + tal_free(ders); + return true; +} +struct pubkey *sqlite3_column_pubkey_array(const tal_t *ctx, + sqlite3_stmt *stmt, int col) +{ + size_t i; + size_t n; + struct pubkey *ret; + const u8 *ders; + + if (sqlite3_column_type(stmt, col) == SQLITE_NULL) + return NULL; + + n = sqlite3_column_bytes(stmt, col) / PUBKEY_DER_LEN; + assert(n * PUBKEY_DER_LEN == sqlite3_column_bytes(stmt, col)); + ret = tal_arr(ctx, struct pubkey, n); + ders = sqlite3_column_blob(stmt, col); + + for (i = 0; i < n; ++i) { + if (!pubkey_from_der(&ders[i * PUBKEY_DER_LEN], PUBKEY_DER_LEN, &ret[i])) + return tal_free(ret); + } + + return ret; +} + bool sqlite3_column_preimage(sqlite3_stmt *stmt, int col, struct preimage *dest) { assert(sqlite3_column_bytes(stmt, col) == sizeof(struct preimage)); diff --git a/wallet/db.h b/wallet/db.h index 21f135913628..0c8151bfb09f 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -117,6 +117,11 @@ bool sqlite3_bind_short_channel_id(sqlite3_stmt *stmt, int col, const struct short_channel_id *id); bool sqlite3_column_short_channel_id(sqlite3_stmt *stmt, int col, struct short_channel_id *dest); +bool sqlite3_bind_short_channel_id_array(sqlite3_stmt *stmt, int col, + const struct short_channel_id *id); +struct short_channel_id * +sqlite3_column_short_channel_id_array(const tal_t *ctx, + sqlite3_stmt *stmt, int col); bool sqlite3_bind_tx(sqlite3_stmt *stmt, int col, const struct bitcoin_tx *tx); struct bitcoin_tx *sqlite3_column_tx(const tal_t *ctx, sqlite3_stmt *stmt, int col); @@ -126,6 +131,11 @@ bool sqlite3_column_signature(sqlite3_stmt *stmt, int col, secp256k1_ecdsa_signa bool sqlite3_column_pubkey(sqlite3_stmt *stmt, int col, struct pubkey *dest); bool sqlite3_bind_pubkey(sqlite3_stmt *stmt, int col, const struct pubkey *pk); +bool sqlite3_bind_pubkey_array(sqlite3_stmt *stmt, int col, + const struct pubkey *pks); +struct pubkey *sqlite3_column_pubkey_array(const tal_t *ctx, + sqlite3_stmt *stmt, int col); + bool sqlite3_column_preimage(sqlite3_stmt *stmt, int col, struct preimage *dest); bool sqlite3_bind_preimage(sqlite3_stmt *stmt, int col, const struct preimage *p); diff --git a/wallet/wallet.c b/wallet/wallet.c index 220d80e029e4..c881728d81a5 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1295,8 +1295,10 @@ void wallet_payment_store(struct wallet *wallet, " destination," " msatoshi," " timestamp," - " path_secrets" - ") VALUES (?, ?, ?, ?, ?, ?);"); + " path_secrets," + " route_nodes," + " route_channels" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?);"); sqlite3_bind_int(stmt, 1, payment->status); sqlite3_bind_sha256(stmt, 2, &payment->payment_hash); @@ -1306,6 +1308,9 @@ void wallet_payment_store(struct wallet *wallet, sqlite3_bind_blob(stmt, 6, payment->path_secrets, tal_len(payment->path_secrets), SQLITE_TRANSIENT); + sqlite3_bind_pubkey_array(stmt, 7, payment->route_nodes); + sqlite3_bind_short_channel_id_array(stmt, 8, + payment->route_channels); db_exec_prepared(wallet->db, stmt); @@ -1353,6 +1358,11 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, /* Can be NULL for old db! */ payment->path_secrets = sqlite3_column_secrets(payment, stmt, 7); + + payment->route_nodes = sqlite3_column_pubkey_array(payment, stmt, 8); + payment->route_channels + = sqlite3_column_short_channel_id_array(payment, stmt, 9); + return payment; } @@ -1371,7 +1381,7 @@ wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet, stmt = db_prepare(wallet->db, "SELECT id, status, destination," "msatoshi, payment_hash, timestamp, payment_preimage, " - "path_secrets " + "path_secrets, route_nodes, route_channels " "FROM payments " "WHERE payment_hash = ?"); @@ -1463,7 +1473,7 @@ wallet_payment_list(const tal_t *ctx, wallet->db, "SELECT id, status, destination, " "msatoshi, payment_hash, timestamp, payment_preimage, " - "path_secrets " + "path_secrets, route_nodes, route_channels " "FROM payments;"); } diff --git a/wallet/wallet.h b/wallet/wallet.h index 1741b04dbe44..837a776ce123 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -93,7 +93,10 @@ struct wallet_payment { u64 msatoshi; /* If and only if PAYMENT_COMPLETE */ struct preimage *payment_preimage; + /* Needed for recovering from routing failures. */ struct secret *path_secrets; + struct pubkey *route_nodes; + struct short_channel_id *route_channels; }; /**