From 474887512d4e01dc917e8050b2c9b136b930d0d1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Oct 2017 20:39:49 +1030 Subject: [PATCH] gossipd: rewrite to do the handshake internally. Now the flow is much simpler from a lightningd POV: 1. If we want to connect to a peer, just send gossipd `gossipctl_reach_peer`. 2. Every new peer, gossipd hands up to lightningd, with global/local features and the peer fd and a gossip fd using `gossip_peer_connected` 3. If lightningd doesn't want it, it just hands the peerfd and global/local features back to gossipd using `gossipctl_handle_peer` 4. If a peer sends a non-gossip msg (eg `open_channel`) the gossipd sends it up using `gossip_peer_nongossip`. 5. If lightningd wants to fund a channel, it simply calls `release_channel`. Notes: * There's no more "unique_id": we use the peer id. * For the moment, we don't ask gossipd when we're told to list peers, so connected peers without a channel don't appear in the JSON getpeers API. * We add a `gossipctl_peer_addrhint` for the moment, so you can connect to a specific ip/port, but using other sources is a TODO. * We now (correctly) only give up on reaching a peer after we exchange init messages, which changes the test_disconnect case. Signed-off-by: Rusty Russell --- gossipd/Makefile | 7 +- gossipd/gossip.c | 981 +++++++++++++++------- gossipd/gossip_wire.csv | 124 +-- lightningd/Makefile | 3 +- lightningd/dev_ping.c | 52 +- lightningd/dns.c | 247 ------ lightningd/dns.h | 53 -- lightningd/gossip_control.c | 106 +-- lightningd/gossip_control.h | 1 + lightningd/jsonrpc.c | 1 + lightningd/lightningd.c | 25 +- lightningd/lightningd.h | 7 +- lightningd/netaddr.h | 1 + lightningd/new_connection.c | 283 ------- lightningd/new_connection.h | 25 - lightningd/peer_control.c | 1206 ++++++++++++++-------------- lightningd/peer_control.h | 39 +- lightningd/peer_state.h | 3 - lightningd/test/run-find_my_path.c | 3 - tests/test_lightningd.py | 41 +- 20 files changed, 1497 insertions(+), 1711 deletions(-) delete mode 100644 lightningd/dns.c delete mode 100644 lightningd/dns.h delete mode 100644 lightningd/new_connection.c delete mode 100644 lightningd/new_connection.h diff --git a/gossipd/Makefile b/gossipd/Makefile index e9d9c02c4..7f91f0407 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -13,6 +13,7 @@ LIGHTNINGD_GOSSIP_CONTROL_OBJS := $(LIGHTNINGD_GOSSIP_CONTROL_SRC:.c=.o) # gossipd needs these: LIGHTNINGD_GOSSIP_HEADERS := gossipd/gen_gossip_wire.h \ + gossipd/handshake.h \ gossipd/routing.h \ gossipd/broadcast.h LIGHTNINGD_GOSSIP_SRC := gossipd/gossip.c \ @@ -48,7 +49,11 @@ GOSSIPD_COMMON_OBJS := \ common/type_to_string.o \ common/utils.o \ common/version.o \ - lightningd/gossip_msg.o + common/wire_error.o \ + hsmd/client.o \ + hsmd/gen_hsm_client_wire.o \ + lightningd/gossip_msg.o \ + lightningd/netaddr.o $(LIGHTNINGD_GOSSIP_OBJS) $(LIGHTNINGD_GOSSIP_CLIENT_OBJS): $(LIGHTNINGD_HEADERS) diff --git a/gossipd/gossip.c b/gossipd/gossip.c index cfa7571cc..235b03f58 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -20,13 +21,18 @@ #include #include #include +#include #include #include #include #include +#include #include +#include #include #include +#include +#include #include #include #include @@ -39,25 +45,59 @@ #define HSM_FD 3 struct daemon { + /* Who am I? */ + struct pubkey id; + + /* Peers we have directly or indirectly */ struct list_head peers; + /* Peers we are trying to reach */ + struct list_head reaching; + /* Connection to main daemon. */ struct daemon_conn master; /* Routing information */ struct routing_state *rstate; + /* Hacky list of known address hints. */ + struct list_head addrhints; + struct timers timers; u32 broadcast_interval; + + /* Local and global features to offer to peers. */ + u8 *localfeatures, *globalfeatures; +}; + +/* Peers we're trying to reach. */ +struct reaching { + struct daemon *daemon; + + /* daemon->reaching */ + struct list_node list; + + /* The ID of the peer (not necessarily unique, in transit!) */ + struct pubkey id; + + /* Did we succeed? */ + bool succeeded; }; struct peer { struct daemon *daemon; + /* daemon->peers */ struct list_node list; - u64 unique_id; + /* The ID of the peer (not necessarily unique, in transit!) */ + struct pubkey id; + + /* Feature bitmaps. */ + u8 *gfeatures, *lfeatures; + + /* Cryptostate */ struct peer_crypto_state pcs; /* File descriptor corresponding to conn. */ @@ -66,9 +106,6 @@ struct peer { /* Our connection (and owner) */ struct io_conn *conn; - /* If this is non-NULL, it means we failed. */ - const char *error; - /* High water mark for the staggered broadcast */ u64 broadcast_index; @@ -86,64 +123,222 @@ struct peer { /* Are we the owner of the peer? */ bool local; + + /* If we die, should we reach again? */ + bool reach_again; }; +struct addrhint { + /* Off ld->addrhints */ + struct list_node list; + + struct pubkey id; + /* FIXME: use array... */ + struct ipaddr addr; +}; + +/* FIXME: Reorder */ +static struct io_plan *peer_start_gossip(struct io_conn *conn, + struct peer *peer); +static void send_peer_with_fds(struct peer *peer, const u8 *msg); static void wake_pkt_out(struct peer *peer); +static void try_reach_peer(struct daemon *daemon, const struct pubkey *id); static void destroy_peer(struct peer *peer) { list_del_from(&peer->daemon->peers, &peer->list); - if (peer->error) { - u8 *msg = towire_gossipstatus_peer_bad_msg(peer, - peer->unique_id, - (u8 *)peer->error); - daemon_conn_send(&peer->daemon->master, take(msg)); - } + if (peer->reach_again) + try_reach_peer(peer->daemon, &peer->id); } -static struct peer *setup_new_peer(struct daemon *daemon, - u64 unique_id, - const struct crypto_state *cs) +static struct peer *find_peer(struct daemon *daemon, const struct pubkey *id) { - struct peer *peer = tal(daemon, struct peer); + struct peer *peer; + + list_for_each(&daemon->peers, peer, list) + if (pubkey_eq(&peer->id, id)) + return peer; + return NULL; +} + +static void destroy_addrhint(struct addrhint *a) +{ + list_del(&a->list); +} + +static struct addrhint *find_addrhint(struct daemon *daemon, + const struct pubkey *id) +{ + struct addrhint *a; + + list_for_each(&daemon->addrhints, a, list) { + if (pubkey_eq(&a->id, id)) + return a; + } + return NULL; +} + +static struct peer *new_peer(const tal_t *ctx, + struct daemon *daemon, + const struct pubkey *their_id, + const struct crypto_state *cs) +{ + struct peer *peer = tal(ctx, struct peer); init_peer_crypto_state(peer, &peer->pcs); peer->pcs.cs = *cs; - peer->unique_id = unique_id; + peer->id = *their_id; peer->daemon = daemon; - peer->error = NULL; peer->local = true; + peer->reach_again = false; peer->num_pings_outstanding = 0; peer->broadcast_index = 0; msg_queue_init(&peer->peer_out, peer); - list_add_tail(&daemon->peers, &peer->list); - tal_add_destructor(peer, destroy_peer); - wake_pkt_out(peer); return peer; } -static struct peer *setup_new_remote_peer(struct daemon *daemon, - u64 unique_id, bool sync) +static void peer_finalized(struct peer *peer) { - struct peer *peer = tal(daemon, struct peer); + /* No longer tied to peer->conn's lifetime. */ + tal_steal(peer->daemon, peer); - peer->daemon = daemon; - peer->error = NULL; - peer->local = false; - peer->num_pings_outstanding = 0; - peer->fd = -1; - peer->unique_id = unique_id; - if (sync) - peer->broadcast_index = 0; - else - peer->broadcast_index = daemon->rstate->broadcasts->next_index; - - msg_queue_init(&peer->peer_out, peer); - list_add_tail(&daemon->peers, &peer->list); + /* Now we can put this in the list of peers */ + list_add_tail(&peer->daemon->peers, &peer->list); tal_add_destructor(peer, destroy_peer); +} + +static void destroy_reaching(struct reaching *reach) +{ + list_del_from(&reach->daemon->reaching, &reach->list); +} + +static struct reaching *find_reaching(struct daemon *daemon, + const struct pubkey *id) +{ + struct reaching *r; + + list_for_each(&daemon->reaching, r, list) + if (pubkey_eq(id, &r->id)) + return r; + return NULL; +} + +static void reached_peer(struct daemon *daemon, const struct pubkey *id, + struct io_conn *conn) +{ + struct reaching *r = find_reaching(daemon, id); + + if (!r) + return; + + /* OK, we've reached the peer successfully, stop retrying. */ + + /* Don't free conn with reach. */ + tal_steal(daemon, conn); + /* Don't call connect_failed */ + io_set_finish(conn, NULL, NULL); + + tal_free(r); +} + +static void peer_error(struct peer *peer, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + status_trace("peer %s: %s", + type_to_string(trc, struct pubkey, &peer->id), + tal_vfmt(trc, fmt, ap)); + va_end(ap); + + /* Send error: we'll close after writing this. */ + va_start(ap, fmt); + msg_enqueue(&peer->peer_out, + take(towire_errorfmtv(peer, NULL, fmt, ap))); + va_end(ap); +} + +static bool is_all_channel_error(const u8 *msg) +{ + struct channel_id channel_id; + u8 *data; + + if (!fromwire_error(msg, msg, NULL, &channel_id, &data)) + return false; + tal_free(data); + return is_all_channels(&channel_id); +} + +static struct io_plan *peer_close_after_error(struct io_conn *conn, + struct peer *peer) +{ + status_trace("%s: we sent them a fatal error, closing", + type_to_string(trc, struct pubkey, &peer->id)); + return io_close(conn); +} + +static struct io_plan *peer_init_received(struct io_conn *conn, + struct peer *peer, + u8 *msg) +{ + if (!fromwire_init(peer, msg, NULL, &peer->gfeatures, &peer->lfeatures)){ + status_trace("peer %s bad fromwire_init '%s', closing", + type_to_string(trc, struct pubkey, &peer->id), + tal_hex(trc, msg)); + return io_close(conn); + } + + reached_peer(peer->daemon, &peer->id, conn); + + /* This is a full peer now; we keep it around until its + * gossipfd closed (forget_peer) or reconnect. */ + peer_finalized(peer); + + msg = towire_gossip_peer_connected(peer, &peer->id, &peer->pcs.cs, + peer->gfeatures, peer->lfeatures); + send_peer_with_fds(peer, msg); + + /* Start the gossip flowing. */ + /* FIXME: This is a bit wasteful in the common case where master + * simply hands it straight back to us and we restart the peer and + * restart gossip broadcast... */ wake_pkt_out(peer); - return peer; + + return io_close_taken_fd(conn); +} + +static struct io_plan *read_init(struct io_conn *conn, struct peer *peer) +{ + /* BOLT #1: + * + * Each node MUST wait to receive `init` before sending any other + * messages. + */ + return peer_read_message(conn, &peer->pcs, peer_init_received); +} + +/* This creates a temporary peer which is not in the list and is owner + * by the connection; it's placed in the list and owned by daemon once + * we have the features. */ +static struct io_plan *init_new_peer(struct io_conn *conn, + const struct pubkey *their_id, + const struct crypto_state *cs, + struct daemon *daemon) +{ + struct peer *peer = new_peer(conn, daemon, their_id, cs); + u8 *initmsg; + + peer->fd = io_conn_fd(conn); + + /* BOLT #1: + * + * Each node MUST send `init` as the first lightning message for any + * connection. + */ + initmsg = towire_init(peer, + daemon->globalfeatures, daemon->localfeatures); + return peer_write_message(conn, &peer->pcs, take(initmsg), read_init); } static struct io_plan *owner_msg_in(struct io_conn *conn, @@ -151,43 +346,6 @@ static struct io_plan *owner_msg_in(struct io_conn *conn, static struct io_plan *nonlocal_dump_gossip(struct io_conn *conn, struct daemon_conn *dc); -/* When a peer is to be owned by another daemon, we create a socket - * pair to send/receive gossip from it */ -static void send_peer_with_fds(struct peer *peer, const u8 *msg) -{ - int fds[2]; - u8 *out; - - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { - out = towire_gossipstatus_peer_failed(msg, - peer->unique_id, - (u8 *)tal_fmt(msg, - "Failed to create socketpair: %s", - strerror(errno))); - daemon_conn_send(&peer->daemon->master, take(out)); - - /* FIXME: Send error to peer? */ - /* Peer will be freed when caller closes conn. */ - return; - } - - /* Now we talk to socket to get to peer's owner daemon. */ - peer->local = false; - /* FIXME: Forget peer if other end is closed. */ - daemon_conn_init(peer, &peer->owner_conn, fds[0], owner_msg_in, NULL); - peer->owner_conn.msg_queue_cleared_cb = nonlocal_dump_gossip; - - /* Peer stays around, even though we're going to free conn. */ - tal_steal(peer->daemon, peer); - - daemon_conn_send(&peer->daemon->master, msg); - daemon_conn_send_fd(&peer->daemon->master, peer->fd); - daemon_conn_send_fd(&peer->daemon->master, fds[1]); - - /* Don't get confused: we can't use this any more. */ - peer->fd = -1; -} - static void handle_gossip_msg(struct routing_state *rstate, u8 *msg) { int t = fromwire_peektype(msg); @@ -206,39 +364,38 @@ static void handle_gossip_msg(struct routing_state *rstate, u8 *msg) } } -static bool handle_ping(struct peer *peer, u8 *ping) +static void handle_ping(struct peer *peer, u8 *ping) { u8 *pong; if (!check_ping_make_pong(peer, ping, &pong)) { - peer->error = "Bad ping"; - return false; + peer_error(peer, "Bad ping"); + return; } if (pong) msg_enqueue(&peer->peer_out, take(pong)); - return true; } -static bool handle_pong(struct peer *peer, const u8 *pong) +static void handle_pong(struct peer *peer, const u8 *pong) { u8 *ignored; status_trace("Got pong!"); if (!fromwire_pong(pong, pong, NULL, &ignored)) { - peer->error = "pad pong"; - return false; + peer_error(peer, "Bad pong"); + return; } if (!peer->num_pings_outstanding) { - peer->error = "unexpected pong"; - return false; + peer_error(peer, "Unexpected pong"); + return; } peer->num_pings_outstanding--; daemon_conn_send(&peer->daemon->master, - take(towire_gossip_ping_reply(pong, tal_len(pong)))); - return true; + take(towire_gossip_ping_reply(pong, true, + tal_len(pong)))); } static struct io_plan *peer_msgin(struct io_conn *conn, @@ -249,8 +406,9 @@ static struct io_plan *peer_msgin(struct io_conn *conn, switch (t) { case WIRE_ERROR: - /* FIXME: Report error from msg. */ - peer->error = "ERROR message received"; + status_trace("%s sent ERROR %s", + type_to_string(trc, struct pubkey, &peer->id), + sanitize_error(trc, msg, NULL)); return io_close(conn); case WIRE_CHANNEL_ANNOUNCEMENT: @@ -260,13 +418,11 @@ static struct io_plan *peer_msgin(struct io_conn *conn, return peer_read_message(conn, &peer->pcs, peer_msgin); case WIRE_PING: - if (!handle_ping(peer, msg)) - return io_close(conn); + handle_ping(peer, msg); return peer_read_message(conn, &peer->pcs, peer_msgin); case WIRE_PONG: - if (!handle_pong(peer, msg)) - return io_close(conn); + handle_pong(peer, msg); return peer_read_message(conn, &peer->pcs, peer_msgin); case WIRE_OPEN_CHANNEL: @@ -287,8 +443,11 @@ static struct io_plan *peer_msgin(struct io_conn *conn, case WIRE_REVOKE_AND_ACK: case WIRE_INIT: /* Not our place to handle this, so we punt */ - s = towire_gossipstatus_peer_nongossip(msg, peer->unique_id, - &peer->pcs.cs, msg); + s = towire_gossip_peer_nongossip(msg, &peer->id, + &peer->pcs.cs, + peer->gfeatures, + peer->lfeatures, + msg); send_peer_with_fds(peer, take(s)); return io_close_taken_fd(conn); } @@ -299,12 +458,12 @@ static struct io_plan *peer_msgin(struct io_conn *conn, * odd-numbered types without ascertaining that the recipient * understands it. */ if (t & 1) { - status_trace("Peer %"PRIu64" sent unknown packet %u, ignoring", - peer->unique_id, t); - return peer_read_message(conn, &peer->pcs, peer_msgin); - } - peer->error = tal_fmt(peer, "Unknown packet %u", t); - return io_close(conn); + status_trace("Peer %s sent unknown packet %u, ignoring", + type_to_string(trc, struct pubkey, &peer->id), t); + } else + peer_error(peer, "Unknown packet %u", t); + + return peer_read_message(conn, &peer->pcs, peer_msgin); } /* Wake up the outgoing direction of the connection and write any @@ -328,9 +487,13 @@ static struct io_plan *peer_pkt_out(struct io_conn *conn, struct peer *peer) { /* First priority is queued packets, if any */ const u8 *out = msg_dequeue(&peer->peer_out); - if (out) + if (out) { + if (is_all_channel_error(out)) + return peer_write_message(conn, &peer->pcs, take(out), + peer_close_after_error); return peer_write_message(conn, &peer->pcs, take(out), peer_pkt_out); + } /* If we're supposed to be sending gossip, do so now. */ if (peer->gossip_sync) { @@ -350,6 +513,15 @@ static struct io_plan *peer_pkt_out(struct io_conn *conn, struct peer *peer) return msg_queue_wait(conn, &peer->peer_out, peer_pkt_out, peer); } +/* Now we're a fully-fledged peer. */ +static struct io_plan *peer_start_gossip(struct io_conn *conn, struct peer *peer) +{ + wake_pkt_out(peer); + return io_duplex(conn, + peer_read_message(conn, &peer->pcs, peer_msgin), + peer_pkt_out(conn, peer)); +} + /** * owner_msg_in - Called by the `peer->owner_conn` upon receiving a * message @@ -368,6 +540,51 @@ static struct io_plan *owner_msg_in(struct io_conn *conn, return daemon_conn_read_next(conn, dc); } +static void forget_peer(struct io_conn *conn, struct daemon_conn *dc) +{ + struct peer *peer = dc->ctx; + + status_trace("Forgetting %s peer %s", + peer->local ? "local" : "remote", + type_to_string(trc, struct pubkey, &peer->id)); + + /* Free peer. */ + tal_free(dc->ctx); +} + +/* When a peer is to be owned by another daemon, we create a socket + * pair to send/receive gossip from it */ +static void send_peer_with_fds(struct peer *peer, const u8 *msg) +{ + int fds[2]; + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { + status_trace("Failed to create socketpair: %s", + strerror(errno)); + + /* FIXME: Send error to peer? */ + /* Peer will be freed when caller closes conn. */ + return; + } + + /* Now we talk to socket to get to peer's owner daemon. */ + peer->local = false; + + daemon_conn_init(peer, &peer->owner_conn, fds[0], + owner_msg_in, forget_peer); + peer->owner_conn.msg_queue_cleared_cb = nonlocal_dump_gossip; + + /* Peer stays around, even though caller will close conn. */ + tal_steal(peer->daemon, peer); + + daemon_conn_send(&peer->daemon->master, msg); + daemon_conn_send_fd(&peer->daemon->master, peer->fd); + daemon_conn_send_fd(&peer->daemon->master, fds[1]); + + /* Don't get confused: we can't use this any more. */ + peer->fd = -1; +} + /** * nonlocal_dump_gossip - catch the nonlocal peer up with the latest gossip. * @@ -395,65 +612,12 @@ static struct io_plan *nonlocal_dump_gossip(struct io_conn *conn, struct daemon_ } } -static struct io_plan *peer_start_gossip(struct io_conn *conn, struct peer *peer) -{ - /* Need to go duplex here, otherwise backpressure would mean - * we both wait indefinitely */ - return io_duplex(conn, - peer_read_message(conn, &peer->pcs, peer_msgin), - peer_pkt_out(conn, peer)); -} - static struct io_plan *new_peer_got_fd(struct io_conn *conn, struct peer *peer) { peer->conn = io_new_conn(conn, peer->fd, peer_start_gossip, peer); if (!peer->conn) { - peer->error = "Could not create connection"; - tal_free(peer); - } else { - /* If conn dies, we forget peer. */ - tal_steal(peer->conn, peer); - } - return daemon_conn_read_next(conn,&peer->daemon->master); -} - -static struct io_plan *new_peer(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) -{ - struct peer *peer; - struct crypto_state cs; - u64 unique_id; - - if (!fromwire_gossipctl_new_peer(msg, NULL, &unique_id, &cs)) - master_badmsg(WIRE_GOSSIPCTL_NEW_PEER, msg); - - peer = setup_new_peer(daemon, unique_id, &cs); - return io_recv_fd(conn, &peer->fd, new_peer_got_fd, peer); -} - -static struct peer *find_peer(struct daemon *daemon, u64 unique_id) -{ - struct peer *peer; - - list_for_each(&daemon->peers, peer, list) - if (peer->unique_id == unique_id) - return peer; - return NULL; -} - -/* We send error, then close. */ -static struct io_plan *peer_send_error(struct io_conn *conn, struct peer *peer) -{ - const u8 *out = msg_dequeue(&peer->peer_out); - return peer_write_message(conn, &peer->pcs, take(out), - (void *)io_close_cb); -} - -static struct io_plan *fail_peer_got_fd(struct io_conn *conn, struct peer *peer) -{ - peer->conn = io_new_conn(conn, peer->fd, peer_send_error, peer); - if (!peer->conn) { - peer->error = "Could not create connection"; + status_trace("Could not create connection for peer: %s", + strerror(errno)); tal_free(peer); } else { /* If conn dies, we forget peer. */ @@ -462,115 +626,84 @@ static struct io_plan *fail_peer_got_fd(struct io_conn *conn, struct peer *peer) return daemon_conn_read_next(conn, &peer->daemon->master); } -static struct io_plan *fail_peer(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) +/* Read and close fd */ +static struct io_plan *discard_peer_fd(struct io_conn *conn, int *fd) +{ + struct daemon *daemon = tal_parent(fd); + close(*fd); + tal_free(fd); + return daemon_conn_read_next(conn, &daemon->master); +} + +static struct io_plan *handle_peer(struct io_conn *conn, struct daemon *daemon, + const u8 *msg) { struct peer *peer; struct crypto_state cs; - u64 unique_id; - u8 *failmsg; + struct pubkey id; + u8 *gfeatures, *lfeatures; + u8 *inner_msg; - if (!fromwire_gossipctl_fail_peer(msg, msg, NULL, &unique_id, &cs, - &failmsg)) - master_badmsg(WIRE_GOSSIPCTL_FAIL_PEER, msg); + if (!fromwire_gossipctl_handle_peer(msg, msg, NULL, &id, &cs, + &gfeatures, &lfeatures, &inner_msg)) + master_badmsg(WIRE_GOSSIPCTL_HANDLE_PEER, msg); - peer = setup_new_peer(daemon, unique_id, &cs); - msg_enqueue(&peer->peer_out, take(failmsg)); + /* If it already exists locally, that's probably a reconnect: + * drop this one. If it exists as remote, replace with this.*/ + peer = find_peer(daemon, &id); + if (peer) { + if (peer->local) { + int *fd = tal(daemon, int); + status_trace("handle_peer %s: duplicate, dropping", + type_to_string(trc, struct pubkey, &id)); + return io_recv_fd(conn, fd, discard_peer_fd, fd); + } + status_trace("handle_peer %s: found remote duplicate, dropping", + type_to_string(trc, struct pubkey, &id)); + tal_free(peer); + } - return io_recv_fd(conn, &peer->fd, fail_peer_got_fd, peer); + status_trace("handle_peer %s: new peer", + type_to_string(trc, struct pubkey, &id)); + peer = new_peer(daemon, daemon, &id, &cs); + peer->gfeatures = tal_steal(peer, gfeatures); + peer->lfeatures = tal_steal(peer, lfeatures); + peer_finalized(peer); + + if (tal_len(inner_msg)) + msg_enqueue(&peer->peer_out, take(inner_msg)); + + return io_recv_fd(conn, &peer->fd, new_peer_got_fd, peer); } static struct io_plan *release_peer(struct io_conn *conn, struct daemon *daemon, const u8 *msg) -{ - u64 unique_id; - struct peer *peer; + { + struct pubkey id; + struct peer *peer; - if (!fromwire_gossipctl_release_peer(msg, NULL, &unique_id)) + if (!fromwire_gossipctl_release_peer(msg, NULL, &id)) master_badmsg(WIRE_GOSSIPCTL_RELEASE_PEER, msg); - peer = find_peer(daemon, unique_id); + peer = find_peer(daemon, &id); if (!peer || !peer->local) { - /* This can happen with a reconnect vs connect race. - * See gossip_peer_released in master daemon. It may - * also happen if we asked to release just before - * failing the peer*/ - daemon_conn_send(&daemon->master, - take(towire_gossipctl_release_peer_replyfail(msg))); + status_trace("release_peer: peer %s not %s", + type_to_string(trc, struct pubkey, &id), + peer ? "local" : "found"); + /* This can happen with dying peers, or reconnect */ + msg = towire_gossipctl_release_peer_replyfail(msg); + daemon_conn_send(&daemon->master, take(msg)); } else { - send_peer_with_fds(peer, - take(towire_gossipctl_release_peer_reply(msg, - &peer->pcs.cs))); + msg = towire_gossipctl_release_peer_reply(msg, + &peer->pcs.cs, + peer->gfeatures, + peer->lfeatures); + send_peer_with_fds(peer, take(msg)); io_close_taken_fd(peer->conn); } return daemon_conn_read_next(conn, &daemon->master); } -static struct io_plan *drop_peer(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) -{ - u64 unique_id; - struct peer *peer; - - if (!fromwire_gossipctl_drop_peer(msg, NULL, &unique_id)) - master_badmsg(WIRE_GOSSIPCTL_DROP_PEER, msg); - - /* This may not find the peer, if we fail beforehand. */ - peer = find_peer(daemon, unique_id); - if (!peer) - status_trace("Unknown drop_peer %"PRIu64, unique_id); - else if (peer->local) { - status_trace("drop_peer %"PRIu64, unique_id); - /* This owns the peer, so we can free it */ - io_close(peer->conn); - } else { - status_trace("Could not drop_peer %"PRIu64", it's not local", - unique_id); - } - - return daemon_conn_read_next(conn, &daemon->master); -} - -static void forget_peer(struct io_conn *conn, struct daemon_conn *dc) -{ - /* Free peer. */ - tal_free(dc->ctx); -} - -static struct io_plan *new_peer_fd(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) -{ - int fds[2]; - u8 *out; - u64 unique_id; - bool sync; - struct peer *peer; - - if (!fromwire_gossipctl_get_peer_gossipfd(msg, NULL, - &unique_id, &sync)) - master_badmsg(WIRE_GOSSIPCTL_GET_PEER_GOSSIPFD, msg); - - peer = setup_new_remote_peer(daemon, unique_id, sync); - - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { - status_trace("Failed to create socketpair: %s", - strerror(errno)); - out = towire_gossipctl_get_peer_gossipfd_replyfail(msg); - daemon_conn_send(&peer->daemon->master, take(out)); - return daemon_conn_read_next(conn, &daemon->master); - } - - daemon_conn_init(peer, &peer->owner_conn, fds[0], owner_msg_in, - forget_peer); - peer->owner_conn.msg_queue_cleared_cb = nonlocal_dump_gossip; - - out = towire_gossipctl_get_peer_gossipfd_reply(msg); - daemon_conn_send(&peer->daemon->master, out); - daemon_conn_send_fd(&peer->daemon->master, fds[1]); - - return daemon_conn_read_next(conn, &daemon->master); -} - static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon, u8 *msg) { @@ -660,19 +793,20 @@ static struct io_plan *getnodes(struct io_conn *conn, struct daemon *daemon) static struct io_plan *ping_req(struct io_conn *conn, struct daemon *daemon, const u8 *msg) { - u64 unique_id; + struct pubkey id; u16 num_pong_bytes, len; struct peer *peer; u8 *ping; - if (!fromwire_gossip_ping(msg, NULL, &unique_id, &num_pong_bytes, &len)) + if (!fromwire_gossip_ping(msg, NULL, &id, &num_pong_bytes, &len)) master_badmsg(WIRE_GOSSIP_PING, msg); - /* FIXME: This is racy, but this op only for testing anywat. */ - peer = find_peer(daemon, unique_id); - if (!peer) - status_failed(STATUS_FAIL_MASTER_IO, - "gossip_ping: unknown peer %"PRIu64, unique_id); + peer = find_peer(daemon, &id); + if (!peer) { + daemon_conn_send(&daemon->master, + take(towire_gossip_ping_reply(peer, false, 0))); + goto out; + } ping = make_ping(peer, num_pong_bytes, len); if (tal_len(ping) > 65535) @@ -690,25 +824,142 @@ static struct io_plan *ping_req(struct io_conn *conn, struct daemon *daemon, */ if (num_pong_bytes >= 65532) daemon_conn_send(&daemon->master, - take(towire_gossip_ping_reply(peer, 0))); + take(towire_gossip_ping_reply(peer, true, 0))); else peer->num_pings_outstanding++; + +out: return daemon_conn_read_next(conn, &daemon->master); } +static int make_listen_fd(int domain, void *addr, socklen_t len) +{ + int fd = socket(domain, SOCK_STREAM, 0); + if (fd < 0) { + status_trace("Failed to create %u socket: %s", + domain, strerror(errno)); + return -1; + } + + if (addr) { + int on = 1; + + /* Re-use, please.. */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) + status_trace("Failed setting socket reuse: %s", + strerror(errno)); + + if (bind(fd, addr, len) != 0) { + status_trace("Failed to bind on %u socket: %s", + domain, strerror(errno)); + goto fail; + } + } + + if (listen(fd, 5) != 0) { + status_trace("Failed to listen on %u socket: %s", + domain, strerror(errno)); + goto fail; + } + return fd; + +fail: + close_noerr(fd); + return -1; +} + +static struct io_plan *connection_in(struct io_conn *conn, struct daemon *daemon) +{ + /* FIXME: Timeout */ + return responder_handshake(conn, &daemon->id, init_new_peer, daemon); +} + +static void setup_listeners(struct daemon *daemon, u16 portnum) +{ + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + socklen_t len; + int fd1, fd2; + + if (!portnum) { + status_trace("Zero portnum, not listening for incoming"); + return; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(portnum); + + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_addr = in6addr_any; + addr6.sin6_port = htons(portnum); + + /* IPv6, since on Linux that (usually) binds to IPv4 too. */ + fd1 = make_listen_fd(AF_INET6, &addr6, sizeof(addr6)); + if (fd1 >= 0) { + struct sockaddr_in6 in6; + + len = sizeof(in6); + if (getsockname(fd1, (void *)&in6, &len) != 0) { + status_trace("Failed get IPv6 sockname: %s", + strerror(errno)); + close_noerr(fd1); + fd1 = -1; + } else { + addr.sin_port = in6.sin6_port; + assert(portnum == ntohs(addr.sin_port)); + status_trace("Creating IPv6 listener on port %u", + portnum); + io_new_listener(daemon, fd1, connection_in, daemon); + } + } + + /* Just in case, aim for the same port... */ + fd2 = make_listen_fd(AF_INET, &addr, sizeof(addr)); + if (fd2 >= 0) { + len = sizeof(addr); + if (getsockname(fd2, (void *)&addr, &len) != 0) { + status_trace("Failed get IPv4 sockname: %s", + strerror(errno)); + close_noerr(fd2); + fd2 = -1; + } else { + assert(portnum == ntohs(addr.sin_port)); + status_trace("Creating IPv4 listener on port %u", + portnum); + io_new_listener(daemon, fd2, connection_in, daemon); + } + } + + if (fd1 < 0 && fd2 < 0) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Could not bind to a network address on port %u", + portnum); +} + + /* Parse an incoming gossip init message and assign config variables * to the daemon. */ static struct io_plan *gossip_init(struct daemon_conn *master, - struct daemon *daemon, u8 *msg) + struct daemon *daemon, + const u8 *msg) { struct sha256_double chain_hash; + u16 port; - if (!fromwire_gossipctl_init(msg, NULL, &daemon->broadcast_interval, - &chain_hash)) { + if (!fromwire_gossipctl_init(daemon, msg, NULL, + &daemon->broadcast_interval, + &chain_hash, &daemon->id, &port, + &daemon->localfeatures, + &daemon->globalfeatures)) { master_badmsg(WIRE_GOSSIPCTL_INIT, msg); } daemon->rstate = new_routing_state(daemon, &chain_hash); + + setup_listeners(daemon, port); return daemon_conn_read_next(master->conn, master); } @@ -750,6 +1001,200 @@ static void handle_forwarded_msg(struct io_conn *conn, struct daemon *daemon, co handle_gossip_msg(daemon->rstate, payload); } +static struct io_plan *handshake_out_success(struct io_conn *conn, + const struct pubkey *id, + const struct crypto_state *cs, + struct reaching *reach) +{ + return init_new_peer(conn, id, cs, reach->daemon); +} + + +static struct io_plan *connection_out(struct io_conn *conn, + struct reaching *reach) +{ + /* FIXME: Timeout */ + status_trace("Connected out for %s", + type_to_string(trc, struct pubkey, &reach->id)); + + return initiator_handshake(conn, &reach->daemon->id, &reach->id, + handshake_out_success, reach); +} + +static void try_connect(struct reaching *reach); + +static void connect_failed(struct io_conn *conn, struct reaching *reach) +{ + status_trace("Failed connected out for %s, will try again", + type_to_string(trc, struct pubkey, &reach->id)); + + /* FIXME: Configurable timer! */ + new_reltimer(&reach->daemon->timers, reach, + time_from_sec(5), + try_connect, reach); +} + +struct reach_addr { + struct reaching *reach; + struct ipaddr addr; +}; + +static struct io_plan *conn_init(struct io_conn *conn, struct reach_addr *r) +{ + struct reaching *reach = r->reach; + struct addrinfo ai; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + + /* FIXME: make generic */ + ai.ai_flags = 0; + ai.ai_socktype = SOCK_STREAM; + ai.ai_protocol = 0; + ai.ai_canonname = NULL; + ai.ai_next = NULL; + + switch (r->addr.type) { + case ADDR_TYPE_IPV4: + ai.ai_family = AF_INET; + sin.sin_family = AF_INET; + sin.sin_port = htons(r->addr.port); + memcpy(&sin.sin_addr, r->addr.addr, sizeof(sin.sin_addr)); + ai.ai_addrlen = sizeof(sin); + ai.ai_addr = (struct sockaddr *)&sin; + break; + case ADDR_TYPE_IPV6: + ai.ai_family = AF_INET6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(r->addr.port); + memcpy(&sin6.sin6_addr, r->addr.addr, sizeof(sin6.sin6_addr)); + ai.ai_addrlen = sizeof(sin6); + ai.ai_addr = (struct sockaddr *)&sin6; + break; + case ADDR_TYPE_PADDING: + /* Shouldn't happen. */ + return io_close(conn); + } + + io_set_finish(conn, connect_failed, reach); + return io_connect(conn, &ai, connection_out, reach); +} + +static void try_connect(struct reaching *reach) +{ + struct addrhint *a; + struct reach_addr r; + int fd; + + /* Already succeeded somehow? */ + if (find_peer(reach->daemon, &reach->id)) { + status_trace("Already reached %s, not retrying", + type_to_string(trc, struct pubkey, &reach->id)); + tal_free(reach); + return; + } + + a = find_addrhint(reach->daemon, &reach->id); + if (!a) { + /* FIXME: now try node table, dns lookups... */ + /* FIXME: add reach_failed message */ + status_trace("No address known for %s, giving up", + type_to_string(trc, struct pubkey, &reach->id)); + tal_free(reach); + return; + } + + /* Might not even be able to create eg. IPv6 sockets */ + switch (a->addr.type) { + case ADDR_TYPE_IPV4: + fd = socket(AF_INET, SOCK_STREAM, 0); + break; + case ADDR_TYPE_IPV6: + fd = socket(AF_INET6, SOCK_STREAM, 0); + break; + default: + fd = -1; + errno = EPROTONOSUPPORT; + break; + } + + if (fd < 0) { + status_trace("Can't open %i socket for %s (%s), giving up", + a->addr.type, + type_to_string(trc, struct pubkey, &reach->id), + strerror(errno)); + tal_free(reach); + return; + } + + r.reach = reach; + r.addr = a->addr; + io_new_conn(reach, fd, conn_init, &r); +} + +static void try_reach_peer(struct daemon *daemon, const struct pubkey *id) +{ + struct reaching *reach; + struct peer *peer; + + if (find_reaching(daemon, id)) { + /* FIXME: Perhaps kick timer in this case? */ + status_trace("try_reach_peer: already reaching %s", + type_to_string(trc, struct pubkey, id)); + return; + } + + /* Master might find out before we do that a peer is dead; if we + * seem to be connected just mark it for reconnect. */ + peer = find_peer(daemon, id); + if (peer) { + status_trace("reach_peer: have %s, will retry if it dies", + type_to_string(trc, struct pubkey, id)); + peer->reach_again = true; + return; + } + + reach = tal(daemon, struct reaching); + reach->succeeded = false; + reach->daemon = daemon; + reach->id = *id; + list_add_tail(&daemon->reaching, &reach->list); + tal_add_destructor(reach, destroy_reaching); + + try_connect(reach); +} + +/* This catches all kinds of failures, like network errors. */ +static struct io_plan *reach_peer(struct io_conn *conn, + struct daemon *daemon, const u8 *msg) +{ + struct pubkey id; + + if (!fromwire_gossipctl_reach_peer(msg, NULL, &id)) + master_badmsg(WIRE_GOSSIPCTL_REACH_PEER, msg); + + try_reach_peer(daemon, &id); + + return daemon_conn_read_next(conn, &daemon->master); +} + +static struct io_plan *addr_hint(struct io_conn *conn, + struct daemon *daemon, const u8 *msg) +{ + struct addrhint *a = tal(daemon, struct addrhint); + + if (!fromwire_gossipctl_peer_addrhint(msg, NULL, &a->id, &a->addr)) + master_badmsg(WIRE_GOSSIPCTL_PEER_ADDRHINT, msg); + + /* Replace any old one. */ + tal_free(find_addrhint(daemon, &a->id)); + + list_add_tail(&daemon->addrhints, &a->list); + tal_add_destructor(a, destroy_addrhint); + + return daemon_conn_read_next(conn, &daemon->master); +} + static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master) { struct daemon *daemon = container_of(master, struct daemon, master); @@ -762,14 +1207,8 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master case WIRE_GOSSIPCTL_INIT: return gossip_init(master, daemon, master->msg_in); - case WIRE_GOSSIPCTL_NEW_PEER: - return new_peer(conn, daemon, master->msg_in); case WIRE_GOSSIPCTL_RELEASE_PEER: return release_peer(conn, daemon, master->msg_in); - case WIRE_GOSSIPCTL_DROP_PEER: - return drop_peer(conn, daemon, master->msg_in); - case WIRE_GOSSIPCTL_GET_PEER_GOSSIPFD: - return new_peer_fd(conn, daemon, master->msg_in); case WIRE_GOSSIP_GETNODES_REQUEST: return getnodes(conn, daemon); @@ -790,22 +1229,25 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master handle_forwarded_msg(conn, daemon, daemon->master.msg_in); return daemon_conn_read_next(conn, &daemon->master); - case WIRE_GOSSIPCTL_FAIL_PEER: - return fail_peer(conn, daemon, master->msg_in); + case WIRE_GOSSIPCTL_HANDLE_PEER: + return handle_peer(conn, daemon, master->msg_in); + + case WIRE_GOSSIPCTL_REACH_PEER: + return reach_peer(conn, daemon, master->msg_in); + + case WIRE_GOSSIPCTL_PEER_ADDRHINT: + return addr_hint(conn, daemon, master->msg_in); case WIRE_GOSSIPCTL_RELEASE_PEER_REPLY: case WIRE_GOSSIPCTL_RELEASE_PEER_REPLYFAIL: - case WIRE_GOSSIPCTL_GET_PEER_GOSSIPFD_REPLY: - case WIRE_GOSSIPCTL_GET_PEER_GOSSIPFD_REPLYFAIL: case WIRE_GOSSIP_GETNODES_REPLY: case WIRE_GOSSIP_GETROUTE_REPLY: case WIRE_GOSSIP_GETCHANNELS_REPLY: case WIRE_GOSSIP_PING_REPLY: case WIRE_GOSSIP_RESOLVE_CHANNEL_REPLY: - case WIRE_GOSSIPSTATUS_PEER_BAD_MSG: - case WIRE_GOSSIPSTATUS_PEER_FAILED: - case WIRE_GOSSIPSTATUS_PEER_NONGOSSIP: - break; + case WIRE_GOSSIP_PEER_CONNECTED: + case WIRE_GOSSIP_PEER_NONGOSSIP: + break; } /* Master shouldn't give bad requests. */ @@ -837,6 +1279,8 @@ int main(int argc, char *argv[]) daemon = tal(NULL, struct daemon); list_head_init(&daemon->peers); + list_head_init(&daemon->reaching); + list_head_init(&daemon->addrhints); timers_init(&daemon->timers, time_mono()); daemon->broadcast_interval = 30000; @@ -844,6 +1288,7 @@ int main(int argc, char *argv[]) daemon_conn_init(daemon, &daemon->master, STDIN_FILENO, recv_req, master_gone); status_setup_async(&daemon->master); + hsm_setup(HSM_FD); /* When conn closes, everything is freed. */ tal_steal(daemon->master.conn, daemon); diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 3d77b4a97..2a2ce4c30 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -1,52 +1,71 @@ -# Peers can give a bad message, we close their fd, but no harm done. -gossipstatus_peer_bad_msg,13000 -gossipstatus_peer_bad_msg,,unique_id,u64 -gossipstatus_peer_bad_msg,,len,u16 -gossipstatus_peer_bad_msg,,err,len*u8 - -# Misc problems like opening control fd. -gossipstatus_peer_failed,13001 -gossipstatus_peer_failed,,unique_id,u64 -gossipstatus_peer_failed,,len,u16 -gossipstatus_peer_failed,,err,len*u8 - #include -# Initialize the gossip daemon +# Initialize the gossip daemon. gossipctl_init,3000 gossipctl_init,,broadcast_interval,u32 gossipctl_init,,chain_hash,struct sha256_double +gossipctl_init,,id,struct pubkey +# If non-zero, port to listen on. +gossipctl_init,,port,u16 +gossipctl_init,,gflen,u16 +gossipctl_init,,gfeatures,gflen*u8 +gossipctl_init,,lflen,u16 +gossipctl_init,,lfeatures,lflen*u8 -# These take an fd, but have no response -gossipctl_new_peer,3001 -gossipctl_new_peer,,unique_id,u64 -gossipctl_new_peer,,crypto_state,struct crypto_state +# Master -> gossipd: Optional hint for where to find peer. +gossipctl_peer_addrhint,3014 +gossipctl_peer_addrhint,,id,struct pubkey +gossipctl_peer_addrhint,,addr,struct ipaddr -# Tell it to release a peer which has initialized. -gossipctl_release_peer,3002 -gossipctl_release_peer,,unique_id,u64 +# Master -> gossipd: connect to a peer. We may get a peer_connected. +gossipctl_reach_peer,3001 +gossipctl_reach_peer,,id,struct pubkey -# This releases the peer and returns the cryptostate (followed two fds: peer and gossip) -gossipctl_release_peer_reply,3102 +# Gossipd -> master: we got a peer. Two fds: peer and gossip +gossip_peer_connected,3002 +gossip_peer_connected,,id,struct pubkey +gossip_peer_connected,,crypto_state,struct crypto_state +gossip_peer_connected,,gflen,u16 +gossip_peer_connected,,gfeatures,gflen*u8 +gossip_peer_connected,,lflen,u16 +gossip_peer_connected,,lfeatures,lflen*u8 + +# Gossipd -> master: peer sent non-gossip packet. Two fds: peer and gossip +gossip_peer_nongossip,3003 +gossip_peer_nongossip,,id,struct pubkey +gossip_peer_nongossip,,crypto_state,struct crypto_state +gossip_peer_nongossip,,gflen,u16 +gossip_peer_nongossip,,gfeatures,gflen*u8 +gossip_peer_nongossip,,lflen,u16 +gossip_peer_nongossip,,lfeatures,lflen*u8 +gossip_peer_nongossip,,len,u16 +gossip_peer_nongossip,,msg,len*u8 + +# Master -> gossipd: release a peer (so we can open a channel) +gossipctl_release_peer,3004 +gossipctl_release_peer,,id,struct pubkey + +# Gossipd -> master: reply to gossip_release_peer. Two fds: peer and gossip. +gossipctl_release_peer_reply,3104 gossipctl_release_peer_reply,,crypto_state,struct crypto_state +gossipctl_release_peer_reply,,gflen,u16 +gossipctl_release_peer_reply,,gfeatures,gflen*u8 +gossipctl_release_peer_reply,,lflen,u16 +gossipctl_release_peer_reply,,lfeatures,lflen*u8 -# This is if we couldn't find the peer. -gossipctl_release_peer_replyfail,3202 +# Gossipd -> master: reply to gossip_release_peer if we couldn't find the peer. +gossipctl_release_peer_replyfail,3204 -# This is where we save a peer's features. -#gossipstatus_peer_features,3001 -#gossipstatus_peer_features,,unique_id,u64 -#gossipstatus_peer_features,,gflen,u16 -#gossipstatus_peer_features,,globalfeatures,gflen*u8 -#gossipstatus_peer_features,,lflen,u16 -#gossipstatus_peer_features,,localfeatures,lflen*u8 - -# Peer can send non-gossip packet (usually an open_channel) (followed two fds: peer and gossip) -gossipstatus_peer_nongossip,3004 -gossipstatus_peer_nongossip,,unique_id,u64 -gossipstatus_peer_nongossip,,crypto_state,struct crypto_state -gossipstatus_peer_nongossip,,len,u16 -gossipstatus_peer_nongossip,,msg,len*u8 +# Gossipd -> master: take over peer, with optional msg. (+peer fd) +gossipctl_handle_peer,3013 +gossipctl_handle_peer,,id,struct pubkey +gossipctl_handle_peer,,crypto_state,struct crypto_state +gossipctl_handle_peer,,gflen,u16 +gossipctl_handle_peer,,gfeatures,gflen*u8 +gossipctl_handle_peer,,lflen,u16 +gossipctl_handle_peer,,lfeatures,lflen*u8 +gossipctl_handle_peer,,len,u16 +gossipctl_handle_peer,,msg,len*u8 # Pass JSON-RPC getnodes call through gossip_getnodes_request,3005 @@ -73,13 +92,16 @@ gossip_getchannels_reply,3107 gossip_getchannels_reply,,num_channels,u16 gossip_getchannels_reply,,nodes,num_channels*struct gossip_getchannels_entry -# Ping/pong test. +# Ping/pong test. Waits for a reply if it expects one. gossip_ping,3008 -gossip_ping,,unique_id,u64 +gossip_ping,,id,struct pubkey gossip_ping,,num_pong_bytes,u16 gossip_ping,,len,u16 gossip_ping_reply,3108 +# False if id in gossip_ping was unknown. +gossip_ping_reply,,sent,bool +# 0 == no pong expected gossip_ping_reply,,totlen,u16 # Given a short_channel_id, return the endpoints @@ -96,25 +118,3 @@ gossip_forwarded_msg,3010 gossip_forwarded_msg,,msglen,u16 gossip_forwarded_msg,,msg,msglen*u8 -# If peer is still connected, fail it (master does this for reconnect) -gossipctl_drop_peer,3011 -gossipctl_drop_peer,,unique_id,u64 - -# Get a gossip fd for this peer (it has reconnected) -gossipctl_get_peer_gossipfd,3012 -gossipctl_get_peer_gossipfd,,unique_id,u64 -# Does it want a full dump of gossip? -gossipctl_get_peer_gossipfd,,sync,bool - -# + fd. -gossipctl_get_peer_gossipfd_reply,3112 - -# Failure (can't make new socket) -gossipctl_get_peer_gossipfd_replyfail,3212 - -# Send canned message to peer and fail it. -gossipctl_fail_peer,3013 -gossipctl_fail_peer,,unique_id,u64 -gossipctl_fail_peer,,crypto_state,struct crypto_state -gossipctl_fail_peer,,len,u16 -gossipctl_fail_peer,,failmsg,len*u8 diff --git a/lightningd/Makefile b/lightningd/Makefile index b36eb0ff5..5668a3729 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -34,13 +34,13 @@ LIGHTNINGD_COMMON_OBJS := \ common/utils.o \ common/utxo.o \ common/version.o \ + common/wire_error.o \ common/withdraw_tx.o LIGHTNINGD_SRC := \ lightningd/bitcoind.c \ lightningd/build_utxos.c \ lightningd/chaintopology.c \ - lightningd/dns.c \ lightningd/gossip_control.c \ lightningd/gossip_msg.c \ lightningd/hsm_control.c \ @@ -50,7 +50,6 @@ LIGHTNINGD_SRC := \ lightningd/lightningd.c \ lightningd/log.c \ lightningd/netaddr.c \ - lightningd/new_connection.c \ lightningd/opt_time.c \ lightningd/options.c \ lightningd/pay.c \ diff --git a/lightningd/dev_ping.c b/lightningd/dev_ping.c index fc1c6e6ce..fc7af18f5 100644 --- a/lightningd/dev_ping.c +++ b/lightningd/dev_ping.c @@ -13,16 +13,18 @@ static bool ping_reply(struct subd *subd, const u8 *msg, const int *fds, struct command *cmd) { u16 totlen; - bool ok; + bool ok, sent = true; log_debug(subd->ld->log, "Got ping reply!"); if (streq(subd->name, "lightning_channeld")) ok = fromwire_channel_ping_reply(msg, NULL, &totlen); else - ok = fromwire_gossip_ping_reply(msg, NULL, &totlen); + ok = fromwire_gossip_ping_reply(msg, NULL, &sent, &totlen); if (!ok) command_fail(cmd, "Bad reply message"); + else if (!sent) + command_fail(cmd, "Unknown peer"); else { struct json_result *response = new_json_result(cmd); @@ -41,6 +43,8 @@ static void json_dev_ping(struct command *cmd, u8 *msg; jsmntok_t *peeridtok, *lentok, *pongbytestok; unsigned int len, pongbytes; + struct pubkey id; + struct subd *owner; if (!json_get_params(buffer, params, "peerid", &peeridtok, @@ -51,21 +55,8 @@ static void json_dev_ping(struct command *cmd, return; } - peer = peer_from_json(cmd->ld, buffer, peeridtok); - if (!peer) { - command_fail(cmd, "Could not find peer with that peerid"); - return; - } - /* FIXME: These checks are horrible, use a peer flag to say it's * ready to forward! */ - if (peer->owner && !streq(peer->owner->name, "lightning_channeld") - && !streq(peer->owner->name, "lightning_gossipd")) { - command_fail(cmd, "Peer in %s", - peer->owner ? peer->owner->name : "unattached"); - return; - } - if (!json_tok_number(buffer, lentok, &len)) { command_fail(cmd, "'%.*s' is not a valid number", (int)(lentok->end - lentok->start), @@ -80,13 +71,32 @@ static void json_dev_ping(struct command *cmd, return; } - if (streq(peer->owner->name, "lightning_channeld")) - msg = towire_channel_ping(cmd, pongbytes, len); - else - msg = towire_gossip_ping(cmd, peer->unique_id, pongbytes, len); + if (!json_tok_pubkey(buffer, peeridtok, &id)) { + command_fail(cmd, "'%.*s' is not a valid pubkey", + (int)(peeridtok->end - peeridtok->start), + buffer + peeridtok->start); + return; + } - /* FIXME: If subdaemon dies? */ - subd_req(peer->owner, peer->owner, take(msg), -1, 0, ping_reply, cmd); + /* First, see if it's in channeld. */ + peer = peer_by_id(cmd->ld, &id); + if (peer) { + if (!peer->owner || + !streq(peer->owner->name, "lightning_channeld")) { + command_fail(cmd, "Peer in %s", + peer->owner + ? peer->owner->name : "unattached"); + return; + } + msg = towire_channel_ping(cmd, pongbytes, len); + owner = peer->owner; + } else { + /* We assume it's in gossipd. */ + msg = towire_gossip_ping(cmd, &id, pongbytes, len); + owner = cmd->ld->gossip; + } + + subd_req(owner, owner, take(msg), -1, 0, ping_reply, cmd); } static const struct json_command dev_ping_command = { diff --git a/lightningd/dns.c b/lightningd/dns.c deleted file mode 100644 index 450fd4ea1..000000000 --- a/lightningd/dns.c +++ /dev/null @@ -1,247 +0,0 @@ -/* Async dns helper. */ -#include "dns.h" -#include "lightningd.h" -#include "log.h" -#include "netaddr.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct dns_async { - struct lightningd *ld; - struct io_plan *(*init)(struct io_conn *, struct lightningd *, - const struct netaddr *, - void *); - void (*fail)(struct lightningd *, void *arg); - const char *name; - void *arg; - int pid; - size_t num_addresses; - struct netaddr *addresses; -}; - -/* This runs in the child */ -static void lookup_and_write(int fd, const char *name, const char *port) -{ - struct addrinfo *addr, *i; - struct netaddr *addresses; - size_t num; - struct addrinfo hints; - - /* We don't want UDP sockets (yet?) */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - if (getaddrinfo(name, port, &hints, &addr) != 0) - return; - - num = 0; - for (i = addr; i; i = i->ai_next) - num++; - - addresses = tal_arr(NULL, struct netaddr, num); - num = 0; - for (i = addr; i; i = i->ai_next) { - addresses[num].type = i->ai_socktype; - addresses[num].protocol = i->ai_protocol; - addresses[num].addrlen = i->ai_addrlen; - memset(&addresses[num].saddr, 0, sizeof(addresses[num].saddr)); - /* Let parent report this error. */ - if (i->ai_addrlen <= sizeof(addresses[num].saddr)) - memcpy(&addresses[num].saddr, i->ai_addr, i->ai_addrlen); - num++; - } - - if (!num) { - tal_free(addresses); - return; - } - - if (write_all(fd, &num, sizeof(num))) - write_all(fd, addresses, num * sizeof(addresses[0])); - tal_free(addresses); -} - -static struct io_plan *connected(struct io_conn *conn, struct dns_async *d) -{ - struct io_plan *plan; - - /* No longer need to try more connections via connect_failed. */ - io_set_finish(conn, NULL, NULL); - - plan = d->init(conn, d->ld, &d->addresses[-1], d->arg); - tal_free(d); - - return plan; -} - -static void try_connect_one(struct dns_async *d); - -/* If this connection failed, try connecting to another address. */ -static void connect_failed(struct io_conn *conn, struct dns_async *d) -{ - try_connect_one(d); -} - -static struct io_plan *init_conn(struct io_conn *conn, struct dns_async *d) -{ - struct addrinfo a; - - netaddr_to_addrinfo(&a, &d->addresses[0]); - - /* Consume that address. */ - d->addresses++; - d->num_addresses--; - - io_set_finish(conn, connect_failed, d); - - /* That new connection owns d */ - return io_connect(conn, &a, connected, d); -} - -static void try_connect_one(struct dns_async *d) -{ - int fd; - - while (d->num_addresses) { - const struct netaddr *a = &d->addresses[0]; - - /* Now we can warn if it's overlength */ - if (a->addrlen > sizeof(a->saddr)) { - log_broken(d->ld->log, - "DNS lookup gave overlength address for %s" - " for family %u, len=%u", - d->name, a->saddr.s.sa_family, a->addrlen); - } else { - /* Might not even be able to create eg. IPv6 sockets */ - fd = socket(a->saddr.s.sa_family, a->type, a->protocol); - if (fd >= 0) { - io_new_conn(d->ld, fd, init_conn, d); - return; - } - } - - /* Consume that address. */ - d->addresses++; - d->num_addresses--; - } - - /* We're out of things to try. Fail. */ - d->fail(d->ld, d->arg); - tal_free(d); -} - -static struct io_plan *start_connecting(struct io_conn *conn, - struct dns_async *d) -{ - assert(d->num_addresses); - - /* OK, we've read all we want, child should exit. */ - waitpid(d->pid, NULL, 0); - - /* No need to call dns_lookup_failed now. */ - io_set_finish(conn, NULL, NULL); - - try_connect_one(d); - return io_close(conn); -} - -struct dns_async *multiaddress_connect_(struct lightningd *ld, - const struct netaddr *addresses, - struct io_plan *(*init)(struct io_conn *, - struct lightningd *, - const struct netaddr *, - void *arg), - void (*fail)(struct lightningd *, void *arg), - void *arg) -{ - struct dns_async *d = tal(ld, struct dns_async); - - d->ld = ld; - d->init = init; - d->fail = fail; - d->arg = arg; - d->name = "names from address list"; - d->num_addresses = tal_count(addresses); - d->addresses = tal_dup_arr(d, struct netaddr, addresses, - d->num_addresses, 0); - try_connect_one(d); - return d; -} - -static struct io_plan *read_addresses(struct io_conn *conn, struct dns_async *d) -{ - d->addresses = tal_arr(d, struct netaddr, d->num_addresses); - return io_read(conn, d->addresses, - d->num_addresses * sizeof(d->addresses[0]), - start_connecting, d); -} - -static struct io_plan *init_dns_conn(struct io_conn *conn, struct dns_async *d) -{ - return io_read(conn, &d->num_addresses, sizeof(d->num_addresses), - read_addresses, d); -} - -static void dns_lookup_failed(struct io_conn *conn, struct dns_async *d) -{ - waitpid(d->pid, NULL, 0); - d->fail(d->ld, d->arg); - tal_free(d); -} - -struct dns_async *dns_resolve_and_connect_(struct lightningd *ld, - const char *name, const char *port, - struct io_plan *(*init)(struct io_conn *, - struct lightningd *, - const struct netaddr *, - void *arg), - void (*fail)(struct lightningd *, void *arg), - void *arg) -{ - int pfds[2]; - struct dns_async *d = tal(ld, struct dns_async); - struct io_conn *conn; - - d->ld = ld; - d->init = init; - d->fail = fail; - d->arg = arg; - d->name = tal_fmt(d, "%s:%s", name, port); - - /* First fork child to get addresses. */ - if (pipe(pfds) != 0) { - log_unusual(ld->log, - "Creating pipes for dns lookup: %s", - strerror(errno)); - return NULL; - } - - fflush(stdout); - d->pid = fork(); - switch (d->pid) { - case -1: - log_unusual(ld->log, "forking for dns lookup: %s", - strerror(errno)); - close(pfds[0]); - close(pfds[1]); - return NULL; - case 0: - close(pfds[0]); - lookup_and_write(pfds[1], name, port); - exit(0); - } - - close(pfds[1]); - conn = io_new_conn(ld, pfds[0], init_dns_conn, d); - io_set_finish(conn, dns_lookup_failed, d); - return d; -} diff --git a/lightningd/dns.h b/lightningd/dns.h deleted file mode 100644 index b60996cc1..000000000 --- a/lightningd/dns.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef LIGHTNING_LIGHTNINGD_DNS_H -#define LIGHTNING_LIGHTNINGD_DNS_H -#include "config.h" -#include -#include -#include -#include - -struct lightningd; -struct netaddr; - -#define dns_resolve_and_connect(dstate, name, port, initfn, failfn, arg) \ - dns_resolve_and_connect_((dstate), (name), (port), \ - typesafe_cb_preargs(struct io_plan *, void *, \ - (initfn), (arg), \ - struct io_conn *, \ - struct lightningd *, \ - const struct netaddr *), \ - typesafe_cb_preargs(void, void *, (failfn), (arg), \ - struct lightningd *), \ - (arg)) - -struct dns_async *dns_resolve_and_connect_(struct lightningd *ld, - const char *name, const char *port, - struct io_plan *(*init)(struct io_conn *, - struct lightningd *, - const struct netaddr *, - void *arg), - void (*fail)(struct lightningd *, void *arg), - void *arg); - -/* Don't do lookup, just try to connect to these addresses. */ -#define multiaddress_connect(dstate, addresses, initfn, failfn, arg) \ - multiaddress_connect_((dstate), (addresses), \ - typesafe_cb_preargs(struct io_plan *, void *, \ - (initfn), (arg), \ - struct io_conn *, \ - struct lightningd *, \ - const struct netaddr *), \ - typesafe_cb_preargs(void, void *, (failfn), (arg), \ - struct lightningd *), \ - (arg)) - -struct dns_async *multiaddress_connect_(struct lightningd *ld, - const struct netaddr *addresses, - struct io_plan *(*init)(struct io_conn *, - struct lightningd *, - const struct netaddr *, - void *arg), - void (*fail)(struct lightningd *, void *arg), - void *arg); - -#endif /* LIGHTNING_LIGHTNINGD_DNS_H */ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index a4d23805e..a95bcf76d 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -27,69 +27,34 @@ static void gossip_finished(struct subd *gossip, int status) errx(1, "Gossip failed (signal %u), exiting.", WTERMSIG(status)); } -static void peer_bad_message(struct subd *gossip, const u8 *msg) -{ - u64 unique_id; - struct peer *peer; - u8 *err; - - if (!fromwire_gossipstatus_peer_bad_msg(msg, msg, NULL, - &unique_id, &err)) - fatal("Gossip gave bad PEER_BAD message %s", tal_hex(msg, msg)); - - peer = peer_by_unique_id(gossip->ld, unique_id); - if (!peer) - fatal("Gossip gave bad peerid %"PRIu64, unique_id); - - log_debug(gossip->log, "Peer %s gave bad msg %s", - type_to_string(msg, struct pubkey, &peer->id), - tal_hex(msg, msg)); - peer_fail_permanent(peer, msg); -} - -static void peer_failed(struct subd *gossip, const u8 *msg) -{ - u64 unique_id; - struct peer *peer; - u8 *err; - - if (!fromwire_gossipstatus_peer_failed(msg, msg, NULL, - &unique_id, &err)) - fatal("Gossip gave bad PEER_FAILED message %s", - tal_hex(msg, msg)); - - peer = peer_by_unique_id(gossip->ld, unique_id); - if (!peer) - fatal("Gossip gave bad peerid %"PRIu64, unique_id); - - peer_fail_permanent(peer, msg); -} - static void peer_nongossip(struct subd *gossip, const u8 *msg, int peer_fd, int gossip_fd) { - u64 unique_id; - struct peer *peer; - u8 *inner; + struct pubkey id; struct crypto_state cs; + u8 *gfeatures, *lfeatures, *in_pkt; - if (!fromwire_gossipstatus_peer_nongossip(msg, msg, NULL, - &unique_id, &cs, &inner)) - fatal("Gossip gave bad PEER_NONGOSSIP message %s", + if (!fromwire_gossip_peer_nongossip(msg, msg, NULL, + &id, &cs, + &gfeatures, + &lfeatures, + &in_pkt)) + fatal("Gossip gave bad GOSSIP_PEER_NONGOSSIP message %s", tal_hex(msg, msg)); - peer = peer_by_unique_id(gossip->ld, unique_id); - if (!peer) - fatal("Gossip gave bad peerid %"PRIu64, unique_id); + /* We already checked the features when it first connected. */ + if (unsupported_features(gfeatures, lfeatures)) { + log_unusual(gossip->log, + "Gossip gave unsupported features %s/%s", + tal_hex(msg, gfeatures), + tal_hex(msg, lfeatures)); + close(peer_fd); + close(gossip_fd); + return; + } - if (peer->owner != gossip) - fatal("Gossip gave bad peerid %"PRIu64" (owner %s)", - unique_id, peer->owner ? peer->owner->name : "(none)"); - - log_info(peer->log, "Gossip ended up receipt of %s", - wire_type_name(fromwire_peektype(inner))); - - peer_fundee_open(peer, inner, &cs, peer_fd, gossip_fd); + peer_sent_nongossip(gossip->ld, &id, &cs, gfeatures, lfeatures, + peer_fd, gossip_fd, in_pkt); } static int gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) @@ -99,35 +64,31 @@ static int gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) switch (t) { /* These are messages we send, not them. */ case WIRE_GOSSIPCTL_INIT: - case WIRE_GOSSIPCTL_NEW_PEER: - case WIRE_GOSSIPCTL_FAIL_PEER: - case WIRE_GOSSIPCTL_RELEASE_PEER: - case WIRE_GOSSIPCTL_DROP_PEER: - case WIRE_GOSSIPCTL_GET_PEER_GOSSIPFD: case WIRE_GOSSIP_GETNODES_REQUEST: case WIRE_GOSSIP_GETROUTE_REQUEST: case WIRE_GOSSIP_GETCHANNELS_REQUEST: case WIRE_GOSSIP_PING: case WIRE_GOSSIP_RESOLVE_CHANNEL_REQUEST: case WIRE_GOSSIP_FORWARDED_MSG: + case WIRE_GOSSIPCTL_REACH_PEER: + case WIRE_GOSSIPCTL_HANDLE_PEER: + case WIRE_GOSSIPCTL_RELEASE_PEER: + case WIRE_GOSSIPCTL_PEER_ADDRHINT: /* This is a reply, so never gets through to here. */ - case WIRE_GOSSIPCTL_RELEASE_PEER_REPLY: - case WIRE_GOSSIPCTL_RELEASE_PEER_REPLYFAIL: - case WIRE_GOSSIPCTL_GET_PEER_GOSSIPFD_REPLY: - case WIRE_GOSSIPCTL_GET_PEER_GOSSIPFD_REPLYFAIL: case WIRE_GOSSIP_GETNODES_REPLY: case WIRE_GOSSIP_GETROUTE_REPLY: case WIRE_GOSSIP_GETCHANNELS_REPLY: case WIRE_GOSSIP_PING_REPLY: case WIRE_GOSSIP_RESOLVE_CHANNEL_REPLY: + case WIRE_GOSSIPCTL_RELEASE_PEER_REPLY: + case WIRE_GOSSIPCTL_RELEASE_PEER_REPLYFAIL: break; - case WIRE_GOSSIPSTATUS_PEER_BAD_MSG: - peer_bad_message(gossip, msg); + case WIRE_GOSSIP_PEER_CONNECTED: + if (tal_count(fds) != 2) + return 2; + peer_connected(gossip->ld, msg, fds[0], fds[1]); break; - case WIRE_GOSSIPSTATUS_PEER_FAILED: - peer_failed(gossip, msg); - break; - case WIRE_GOSSIPSTATUS_PEER_NONGOSSIP: + case WIRE_GOSSIP_PEER_NONGOSSIP: if (tal_count(fds) != 2) return 2; peer_nongossip(gossip, msg, fds[0], fds[1]); @@ -164,7 +125,10 @@ void gossip_init(struct lightningd *ld) err(1, "Could not subdaemon gossip"); msg = towire_gossipctl_init(tmpctx, ld->broadcast_interval, - &get_chainparams(ld)->genesis_blockhash); + &get_chainparams(ld)->genesis_blockhash, + &ld->id, ld->portnum, + get_supported_global_features(tmpctx), + get_supported_local_features(tmpctx)); subd_send_msg(ld->gossip, msg); tal_free(tmpctx); } diff --git a/lightningd/gossip_control.h b/lightningd/gossip_control.h index d6b9be59b..8a4d6febc 100644 --- a/lightningd/gossip_control.h +++ b/lightningd/gossip_control.h @@ -1,6 +1,7 @@ #ifndef LIGHTNING_LIGHTNINGD_GOSSIP_CONTROL_H #define LIGHTNING_LIGHTNINGD_GOSSIP_CONTROL_H #include "config.h" +#include #include struct lightningd; diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 465d15f1b..ec6647e2c 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -655,5 +655,6 @@ void setup_jsonrpc(struct lightningd *ld, const char *rpc_filename) if (listen(fd, 1) != 0) err(1, "Listening on '%s'", rpc_filename); + log_debug(ld->log, "Listening on '%s'", rpc_filename); io_new_listener(ld, fd, incoming_jcon_connected, ld); } diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 25a8a1ea6..39544b87a 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -31,16 +31,6 @@ char *bitcoin_datadir; -struct peer *find_peer_by_unique_id(struct lightningd *ld, u64 unique_id) -{ - struct peer *peer; - list_for_each(&ld->peers, peer, list) { - if (peer->unique_id == unique_id) - return peer; - } - return NULL; -} - void notify_new_block(struct chain_topology *topo, u32 height); void notify_new_block(struct chain_topology *topo, u32 height) { @@ -81,7 +71,6 @@ static struct lightningd *new_lightningd(const tal_t *ctx, struct lightningd *ld = tal(ctx, struct lightningd); list_head_init(&ld->peers); - ld->peer_counter = 0; ld->dev_debug_subdaemon = NULL; htlc_in_map_init(&ld->htlcs_in); htlc_out_map_init(&ld->htlcs_out); @@ -90,6 +79,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx, ld->log = new_log(log_book, log_book, "lightningd(%u):", (int)getpid()); list_head_init(&ld->pay_commands); + list_head_init(&ld->connects); ld->portnum = DEFAULT_PORT; timers_init(&ld->timers, time_mono()); ld->topology = new_topology(ld, ld->log); @@ -260,15 +250,9 @@ int main(int argc, char *argv[]) /* Initialize wallet, now that we are in the correct directory */ ld->wallet = wallet_new(ld, ld->log); - /* Mark ourselves live. */ - log_info(ld->log, "Hello world from %s!", version()); - /* Set up HSM. */ hsm_init(ld, newdir); - /* Set up gossip daemon. */ - gossip_init(ld); - /* Initialize block topology. */ setup_topology(ld->topology, &ld->timers, @@ -281,6 +265,9 @@ int main(int argc, char *argv[]) err(1, "Could not load invoices from the database"); } + /* Set up gossip daemon. */ + gossip_init(ld); + /* Load peers from database */ wallet_channels_load_active(ld->wallet, &ld->peers); @@ -302,8 +289,8 @@ int main(int argc, char *argv[]) /* Create RPC socket (if any) */ setup_jsonrpc(ld, ld->rpc_filename); - /* Ready for connections from peers. */ - setup_listeners(ld); + /* Mark ourselves live. */ + log_info(ld->log, "Hello world from %s!", version()); #if 0 /* Load peers from database. */ diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 70cfe1c1f..eff6cda6a 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -103,8 +103,9 @@ struct lightningd { struct list_head peers; /* FIXME: This should stay in HSM */ struct secret peer_seed; - /* Used to give a unique seed to every peer. */ - u64 peer_counter; + + /* Outstanding connect commands. */ + struct list_head connects; /* Our chain topology. */ struct chain_topology *topology; @@ -150,7 +151,5 @@ struct lightningd { void derive_peer_seed(struct lightningd *ld, struct privkey *peer_seed, const struct pubkey *peer_id, const u64 channel_id); -struct peer *find_peer_by_unique_id(struct lightningd *ld, u64 unique_id); - struct chainparams *get_chainparams(const struct lightningd *ld); #endif /* LIGHTNING_LIGHTNINGD_LIGHTNINGD_H */ diff --git a/lightningd/netaddr.h b/lightningd/netaddr.h index 04345215f..342bbe132 100644 --- a/lightningd/netaddr.h +++ b/lightningd/netaddr.h @@ -1,3 +1,4 @@ +/* FIXME: We should deprecate this in favor of BOLT7 address descriptor */ #ifndef LIGHTNING_LIGHTNINGD_NETADDR_H #define LIGHTNING_LIGHTNINGD_NETADDR_H #include "config.h" diff --git a/lightningd/new_connection.c b/lightningd/new_connection.c deleted file mode 100644 index 20064ae0b..000000000 --- a/lightningd/new_connection.c +++ /dev/null @@ -1,283 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -const u8 supported_local_features[] = {LOCALFEATURES_INITIAL_ROUTING_SYNC}; -const u8 supported_global_features[] = {0x00}; - -/* Before we have identified the peer, we just have a connection object. */ -struct connection { - /* Lightning daemon, for when we're handed through callbacks. */ - struct lightningd *ld; - - /* Where we connected to/from. */ - struct netaddr netaddr; - - /* Unique identifier for handshaked. */ - u64 unique_id; - - /* Json command which made us connect (if any) */ - struct command *cmd; - - /* If we are initiating, we known their id. Otherwise NULL. */ - struct pubkey *known_id; -}; - -static void connection_destroy(struct connection *c) -{ - /* FIXME: better diagnostics. */ - if (c->cmd) - command_fail(c->cmd, "Failed to connect to peer"); -} - -static void -PRINTF_FMT(3,4) connection_failed(struct connection *c, struct log *log, - const char *fmt, ...) -{ - const char *msg; - va_list ap; - - va_start(ap, fmt); - msg = tal_vfmt(c, fmt, ap); - va_end(ap); - log_info(log, "%s", msg); - if (c->cmd) { - command_fail(c->cmd, "%s", msg); - /* Don't fail in destructor, too. */ - c->cmd = NULL; - } - tal_free(c); -} - -struct connection *new_connection(const tal_t *ctx, - struct lightningd *ld, - struct command *cmd, - const struct pubkey *known_id) -{ - static u64 id_counter = 1; - struct connection *c = tal(ctx, struct connection); - - c->ld = ld; - c->unique_id = id_counter++; - c->cmd = cmd; - if (known_id) - c->known_id = tal_dup(c, struct pubkey, known_id); - else - c->known_id = NULL; - tal_add_destructor(c, connection_destroy); - - return c; -} - -/** - * requires_unsupported_features - Check if we support what's being asked - * - * Given the features vector that the remote connection is expecting - * from us, we check to see if we support all even bit features, i.e., - * the required features. We do so by subtracting our own features in - * the provided positions and see if even bits remain. - * - * @bitmap: the features bitmap the peer is asking for - * @supportmap: what do we support - * @smlen: how long is our supportmap - */ -static bool requires_unsupported_features(const u8 *bitmap, - const u8 *supportmap, - size_t smlen) -{ - size_t len = tal_count(bitmap); - u8 support; - for (size_t i=0; i smlen) { - support = 0x00; - } else { - support = supportmap[smlen-1]; - } - - /* Cancel out supported bits, check for even bits */ - if ((~support & bitmap[i]) & 0x55) - return true; - } - return false; -} - -static bool handshake_succeeded(struct subd *handshaked, - const u8 *msg, const int *fds, - struct connection *c) -{ - struct crypto_state cs; - struct pubkey *id; - u8 *globalfeatures, *localfeatures; - - assert(tal_count(fds) == 1); - - /* FIXME: Look for peer duplicates! */ - - if (!c->known_id) { - id = tal(msg, struct pubkey); - if (!fromwire_handshake_responder_reply(c, msg, NULL, id, &cs, - &globalfeatures, - &localfeatures)) - goto err; - log_info(handshaked->log, "Peer in from %s", - type_to_string(ltmp, struct pubkey, id)); - } else { - id = c->known_id; - if (!fromwire_handshake_initiator_reply(c, msg, NULL, &cs, - &globalfeatures, - &localfeatures)) - goto err; - log_info(handshaked->log, "Peer out to %s", - type_to_string(ltmp, struct pubkey, id)); - } - - /* BOLT #1: - * - * For unknown feature bits which are non-zero, the receiver - * MUST ignore the bit if the bit number is odd, and MUST fail - * the connection if the bit number is even. - */ - if (requires_unsupported_features( - globalfeatures, supported_global_features, - ARRAY_SIZE(supported_global_features))) { - connection_failed(c, handshaked->log, - "peer %s: bad globalfeatures: %s", - type_to_string(c, struct pubkey, id), - tal_hex(msg, globalfeatures)); - return true; - } - - if (requires_unsupported_features( - localfeatures, supported_local_features, - ARRAY_SIZE(supported_local_features))) { - connection_failed(c, handshaked->log, - "peer %s: bad localfeatures: %s", - type_to_string(c, struct pubkey, id), - tal_hex(msg, localfeatures)); - return true; - } - - if (c->cmd) { - struct json_result *response; - response = new_json_result(c->cmd); - - json_object_start(response, NULL); - json_add_pubkey(response, "id", id); - json_object_end(response); - command_success(c->cmd, response); - c->cmd = NULL; - } - - add_peer(handshaked->ld, c->unique_id, fds[0], id, &cs); - /* Now shut handshaked down (frees c as well) */ - return false; - -err: - log_broken(handshaked->log, "Malformed resp: %s", tal_hex(c, msg)); - close(fds[0]); - return false; -} - -/* Same path for connecting in vs connecting out. */ -static struct io_plan *hsm_then_handshake(struct io_conn *conn, - struct lightningd *ld, - struct connection *c) -{ - const tal_t *tmpctx = tal_tmpctx(conn); - int connfd = io_conn_fd(conn), hsmfd; - struct subd *handshaked; - u8 *msg; - - /* Get HSM fd for this peer. */ - msg = towire_hsmctl_hsmfd_ecdh(tmpctx, c->unique_id); - if (!wire_sync_write(ld->hsm_fd, msg)) - fatal("Could not write to HSM: %s", strerror(errno)); - - msg = hsm_sync_read(tmpctx, ld); - if (!fromwire_hsmctl_hsmfd_ecdh_fd_reply(msg, NULL)) - fatal("Malformed hsmfd response: %s", tal_hex(msg, msg)); - - hsmfd = fdpass_recv(ld->hsm_fd); - if (hsmfd < 0) - fatal("Could not read fd from HSM: %s", strerror(errno)); - - /* Make sure connection fd is blocking */ - io_fd_block(connfd, true); - - /* Give handshake daemon the hsm fd. */ - handshaked = new_subd(ld, - "lightning_handshaked", NULL, - handshake_wire_type_name, - NULL, NULL, NULL, - take(&hsmfd), take(&connfd), NULL); - if (!handshaked) { - log_unusual(ld->log, "Could not subdaemon handshake: %s", - strerror(errno)); - goto error; - } - - /* If handshake daemon fails, we just drop connection. */ - tal_steal(handshaked, c); - - if (c->known_id) { - msg = towire_handshake_initiator(tmpctx, &ld->id, c->known_id); - } else { - msg = towire_handshake_responder(tmpctx, &ld->id); - } - - /* Now hand peer request to the handshake daemon: hands it - * back on success */ - subd_req(c, handshaked, take(msg), -1, 1, handshake_succeeded, c); - - tal_free(tmpctx); - - /* We don't need conn, we've passed fd to handshaked. */ - return io_close_taken_fd(conn); - -error: - close(hsmfd); - tal_free(tmpctx); - return io_close(conn); -} - -struct io_plan *connection_out(struct io_conn *conn, - struct lightningd *ld, - const struct netaddr *netaddr, - struct connection *c) -{ - c->netaddr = *netaddr; - return hsm_then_handshake(conn, ld, c); -} - -struct io_plan *connection_in(struct io_conn *conn, struct lightningd *ld) -{ - struct connection *c = new_connection(ld, ld, NULL, NULL); - - /* FIXME: Don't assume TCP here. */ - if (!netaddr_from_fd(io_conn_fd(conn), SOCK_STREAM, IPPROTO_TCP, - &c->netaddr)) { - log_unusual(ld->log, "Could not get address of incoming fd"); - return io_close(conn); - } - return hsm_then_handshake(conn, ld, c); -} - -const struct pubkey *connection_known_id(const struct connection *c) -{ - return c->known_id; -} diff --git a/lightningd/new_connection.h b/lightningd/new_connection.h deleted file mode 100644 index 0889f22d8..000000000 --- a/lightningd/new_connection.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef LIGHTNING_LIGHTNINGD_NEW_CONNECTION_H -#define LIGHTNING_LIGHTNINGD_NEW_CONNECTION_H -#include "config.h" -#include - -struct command; -struct io_conn; -struct lightningd; -struct netaddr; -struct pubkey; - -struct connection *new_connection(const tal_t *ctx, - struct lightningd *ld, - struct command *cmd, - const struct pubkey *known_id); - -struct io_plan *connection_out(struct io_conn *conn, - struct lightningd *dstate, - const struct netaddr *netaddr, - struct connection *c); - -struct io_plan *connection_in(struct io_conn *conn, struct lightningd *ld); - -const struct pubkey *connection_known_id(const struct connection *c); -#endif /* LIGHTNING_LIGHTNINGD_NEW_CONNECTION_H */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 8a0245314..fafc7e03f 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -25,25 +26,56 @@ #include #include #include -#include #include #include #include #include -#include +#include #include -#include #include #include #include -#include -#include #include #include #include #include #include +static const u8 supported_local_features[] += {LOCALFEATURES_INITIAL_ROUTING_SYNC}; +static const u8 supported_global_features[] += {}; + +struct connect { + struct list_node list; + struct pubkey id; + struct command *cmd; +}; + +/* FIXME: Reorder */ +struct funding_channel; +static void peer_owner_finished(struct subd *subd, int status); +static void peer_offer_channel(struct lightningd *ld, + struct funding_channel *fc, + const struct crypto_state *cs, + const u8 *gfeatures, const u8 *lfeatures, + int peer_fd, int gossip_fd); +static bool peer_start_channeld(struct peer *peer, + const struct crypto_state *cs, + int peer_fd, int gossip_fd, + const u8 *funding_signed, + bool reconnected); +static void peer_start_closingd(struct peer *peer, + struct crypto_state *cs, + int peer_fd, int gossip_fd, + bool reconnected); +static void peer_accept_channel(struct lightningd *ld, + const struct pubkey *peer_id, + const struct crypto_state *cs, + const u8 *gfeatures, const u8 *lfeatures, + int peer_fd, int gossip_fd, + const u8 *open_msg); + static void destroy_peer(struct peer *peer) { /* Don't leave owner pointer dangling. */ @@ -52,54 +84,16 @@ static void destroy_peer(struct peer *peer) list_del_from(&peer->ld->peers, &peer->list); } -/* Mutual recursion, sets timer. */ -static void peer_reconnect(struct peer *peer); - -static void reconnect_failed(struct lightningd *ld, - struct connection *c) +u8 *get_supported_global_features(const tal_t *ctx) { - /* Figure out what peer, set reconnect timer. */ - struct peer *peer = peer_by_id(ld, connection_known_id(c)); - - log_debug(peer->log, "reconnect_failed"); - - tal_free(c); - peer_reconnect(peer); + return tal_dup_arr(ctx, u8, supported_global_features, + sizeof(supported_global_features), 0); } -static void try_reconnect(struct peer *peer) +u8 *get_supported_local_features(const tal_t *ctx) { - struct connection *c; - struct netaddr *addrs; - - log_debug(peer->log, "try_reconnect: trying to reconnect"); - - /* We may already be reconnected (another incoming connection) */ - if (peer->owner) { - log_debug(peer->log, "try_reconnect: already reconnected (%s)", - peer->owner->name); - return; - } - - c = new_connection(peer, peer->ld, NULL, &peer->id); - - /* FIXME: Combine known address with gossip addresses and possibly - * DNS seed addresses. */ - addrs = tal_dup_arr(c, struct netaddr, &peer->netaddr, 1, 0); - multiaddress_connect(peer->ld, addrs, - connection_out, reconnect_failed, c); -} - -static void peer_reconnect(struct peer *peer) -{ - /* Don't schedule an attempt if we disabled reconnections with - * the `--no-reconnect` flag */ - if (peer->ld->config.no_reconnect) - return; - - new_reltimer(&peer->ld->timers, - peer, peer->ld->config.poll_time, - try_reconnect, peer); + return tal_dup_arr(ctx, u8, supported_local_features, + sizeof(supported_local_features), 0); } static void sign_last_tx(struct peer *peer) @@ -216,8 +210,13 @@ void peer_fail_transient(struct peer *peer, const char *fmt, ...) /* Reconnect unless we've dropped/are dropping to chain. */ if (!peer_on_chain(peer) && peer->state != CLOSINGD_COMPLETE) { - peer_reconnect(peer); - return; + /* Don't schedule an attempt if we disabled reconnections with + * the `--no-reconnect` flag */ + if (peer->ld->config.no_reconnect) + return; + + u8 *msg = towire_gossipctl_reach_peer(peer, &peer->id); + subd_send_msg(peer->ld->gossip, take(msg)); } } @@ -254,277 +253,173 @@ void peer_set_condition(struct peer *peer, enum peer_state old_state, } } -/* FIXME: Reshuffle. */ -static bool peer_start_channeld(struct peer *peer, - const struct crypto_state *cs, - int peer_fd, int gossip_fd, - const u8 *funding_signed, - bool reconnected); -static void peer_start_closingd(struct peer *peer, - struct crypto_state *cs, - int peer_fd, int gossip_fd, - bool reconnected); - -/* FIXME: - * - * This is a lot of code duplication! We should turn gossipd into welcomed(?): - * - * 1. gossipd listens to the socket for new connections, and also - * handles outgoing, absorbing handshaked. - * - * 2. gossipd thus knows who we're trying to connect to, and knows to - * return them immediately. - * - * That unifies the paths nicely and removes all this code. When - * gossipd hands a connection to us, it gives us the gossip fd for - * that peer, and it's all good. - */ -struct getting_gossip_fd { - struct pubkey id; - int peer_fd; - struct crypto_state cs; -}; - -static bool get_peer_gossipfd_channeld_reply(struct subd *subd, const u8 *msg, - const int *fds, - struct getting_gossip_fd *ggf) +static void destroy_connect(struct connect *c) { - struct peer *peer; - - if (!fromwire_gossipctl_get_peer_gossipfd_reply(msg, NULL)) { - if (!fromwire_gossipctl_get_peer_gossipfd_replyfail(msg, NULL)) - fatal("Gossipd gave bad get_peer_gossipfd reply %s", - tal_hex(subd, msg)); - - log_unusual(subd->log, "Gossipd could not get fds for peer %s", - type_to_string(ggf, struct pubkey, &ggf->id)); - - /* This is an internal error, but could be transient. - * Hang up and let them retry. */ - goto forget; - } - - /* Make sure it still needs gossipfd! */ - peer = peer_by_id(subd->ld, &ggf->id); - if (!peer) { - log_unusual(subd->log, "Gossipd gave fd, but peer %s gone", - type_to_string(ggf, struct pubkey, &ggf->id)); - goto close_gossipfd; - } - - if (peer->state != CHANNELD_AWAITING_LOCKIN - && peer->state != CHANNELD_NORMAL - && peer->state != CHANNELD_SHUTTING_DOWN) { - log_unusual(subd->log, "Gossipd gave fd, but peer %s %s", - type_to_string(ggf, struct pubkey, &ggf->id), - peer_state_name(peer->state)); - goto close_gossipfd; - } - - /* Kill off current channeld, if any */ - if (peer->owner) { - peer->owner->peer = NULL; - peer->owner = tal_free(peer->owner); - } - - /* We never re-transmit funding_signed. */ - peer_start_channeld(peer, &ggf->cs, ggf->peer_fd, fds[0], NULL, true); - goto out; - -close_gossipfd: - close(fds[0]); - -forget: - close(ggf->peer_fd); -out: - tal_free(ggf); - return true; + list_del(&c->list); } -static void get_gossip_fd_for_channeld_reconnect(struct lightningd *ld, - const struct pubkey *id, - u64 unique_id, - int peer_fd, - const struct crypto_state *cs) +static struct connect *new_connect(struct lightningd *ld, + const struct pubkey *id, + struct command *cmd) { - struct getting_gossip_fd *ggf = tal(ld, struct getting_gossip_fd); - u8 *req; - - ggf->peer_fd = peer_fd; - ggf->id = *id; - ggf->cs = *cs; - - /* FIXME: set sync to `initial_routing_sync` */ - req = towire_gossipctl_get_peer_gossipfd(ggf, unique_id, true); - subd_req(ggf, ld->gossip, take(req), -1, 1, - get_peer_gossipfd_channeld_reply, ggf); + struct connect *c = tal(cmd, struct connect); + c->id = *id; + c->cmd = cmd; + list_add(&ld->connects, &c->list); + tal_add_destructor(c, destroy_connect); + return c; } -static bool get_peer_gossipfd_closingd_reply(struct subd *subd, const u8 *msg, - const int *fds, - struct getting_gossip_fd *ggf) +static void connect_succeeded(struct lightningd *ld, const struct pubkey *id) { - struct peer *peer; + struct connect *i, *next; - if (!fromwire_gossipctl_get_peer_gossipfd_reply(msg, NULL)) { - if (!fromwire_gossipctl_get_peer_gossipfd_replyfail(msg, NULL)) - fatal("Gossipd gave bad get_peer_gossipfd reply %s", - tal_hex(subd, msg)); + /* Careful! Completing command frees connect. */ + list_for_each_safe(&ld->connects, i, next, list) { + struct json_result *response; - log_unusual(subd->log, "Gossipd could not get fds for peer %s", - type_to_string(ggf, struct pubkey, &ggf->id)); + if (!pubkey_eq(&i->id, id)) + continue; - /* This is an internal error, but could be transient. - * Hang up and let them retry. */ - goto forget; + response = new_json_result(i->cmd); + json_object_start(response, NULL); + json_add_pubkey(response, "id", id); + json_object_end(response); + command_success(i->cmd, response); } - - /* Make sure it still needs gossipfd! */ - peer = peer_by_id(subd->ld, &ggf->id); - if (!peer) { - log_unusual(subd->log, "Gossipd gave fd, but peer %s gone", - type_to_string(ggf, struct pubkey, &ggf->id)); - goto close_gossipfd; - } - - if (peer->state != CLOSINGD_SIGEXCHANGE - && peer->state != CLOSINGD_COMPLETE) { - log_unusual(subd->log, "Gossipd gave fd, but peer %s %s", - type_to_string(ggf, struct pubkey, &ggf->id), - peer_state_name(peer->state)); - goto close_gossipfd; - } - - /* Kill off current closingd, if any */ - if (peer->owner) { - peer->owner->peer = NULL; - peer->owner = tal_free(peer->owner); - } - - peer_start_closingd(peer, &ggf->cs, ggf->peer_fd, fds[0], true); - goto out; - -close_gossipfd: - close(fds[0]); - -forget: - close(ggf->peer_fd); -out: - tal_free(ggf); - return true; -} -static void get_gossip_fd_for_closingd_reconnect(struct lightningd *ld, - const struct pubkey *id, - u64 unique_id, - int peer_fd, - const struct crypto_state *cs) -{ - struct getting_gossip_fd *ggf = tal(ld, struct getting_gossip_fd); - u8 *req; - - ggf->peer_fd = peer_fd; - ggf->id = *id; - ggf->cs = *cs; - - /* FIXME: set sync to `initial_routing_sync` */ - req = towire_gossipctl_get_peer_gossipfd(ggf, unique_id, true); - subd_req(ggf, ld->gossip, take(req), -1, 1, - get_peer_gossipfd_closingd_reply, ggf); } +static void connect_failed(struct lightningd *ld, const struct pubkey *id, + const char *error) +{ + struct connect *i, *next; -/* Returns true if we consider this a reconnection. */ -static bool peer_reconnected(struct lightningd *ld, + /* Careful! Completing command frees connect. */ + list_for_each_safe(&ld->connects, i, next, list) { + if (pubkey_eq(&i->id, id)) + command_fail(i->cmd, "%s", error); + } +} + +static struct peer *new_peer(struct lightningd *ld, const struct pubkey *id, - int fd, - const struct crypto_state *cs) + const u8 *gfeatures, const u8 *lfeatures, + int peer_fd) { - struct peer *peer = peer_by_id(ld, id); - if (!peer) - return false; + struct peer *peer; - log_info(peer->log, "Peer has reconnected, state %s", - peer_state_name(peer->state)); + /* Need to memset since storing will access all fields */ + peer = talz(ld, struct peer); + peer->error = NULL; + peer->id = *id; + peer->funding_txid = NULL; + peer->remote_funding_locked = false; + peer->scid = NULL; + peer->seed = NULL; + peer->our_msatoshi = NULL; + peer->state = UNINITIALIZED; + peer->channel_info = NULL; + peer->last_tx = NULL; + peer->last_sig = NULL; + peer->last_htlc_sigs = NULL; + peer->last_was_revoke = false; + peer->last_sent_commit = NULL; + peer->remote_shutdown_scriptpubkey = NULL; + peer->local_shutdown_idx = -1; + peer->next_index[LOCAL] + = peer->next_index[REMOTE] = 0; + peer->next_htlc_id = 0; + wallet_shachain_init(ld->wallet, &peer->their_shachain); + + /* FIXME: db should be keyed by (peer_id, channel_id) */ + + /* If we have the peer in the DB, this'll populate the fields, + * failure just indicates that the peer wasn't found in the + * DB */ + wallet_peer_by_nodeid(ld->wallet, id, peer); + + /* peer->channel gets populated as soon as we start opening a channel */ + peer->channel = NULL; /* FIXME: Don't assume protocol here! */ - if (!netaddr_from_fd(fd, SOCK_STREAM, IPPROTO_TCP, &peer->netaddr)) { - log_unusual(ld->log, "Failed to get netaddr for peer: %s", + if (!netaddr_from_fd(peer_fd, SOCK_STREAM, IPPROTO_TCP, &peer->netaddr)) { + log_unusual(ld->log, "Failed to get netaddr for outgoing: %s", strerror(errno)); + return tal_free(peer); } - /* BOLT #2: - * - * On reconnection, if a channel is in an error state, the node SHOULD - * retransmit the error packet and ignore any other packets for that - * channel, and the following requirements do not apply. */ - if (peer->error) { - /* FIXME: we should do this in response to reestablish, unless - * global error */ - log_debug(peer->log, "Sending canned error"); - subd_send_msg(peer->ld->gossip, - take(towire_gossipctl_fail_peer(peer, - peer->unique_id, - cs, peer->error))); - subd_send_fd(peer->ld->gossip, fd); - return true; - } + list_add_tail(&ld->peers, &peer->list); + populate_peer(ld, peer); - switch (peer->state) { - /* This can't happen. */ - case UNINITIALIZED: - abort(); - - case GOSSIPD: - /* Tell gossipd to kick that one out, will call peer_fail */ - subd_send_msg(peer->ld->gossip, - take(towire_gossipctl_drop_peer(peer, - peer->unique_id))); - tal_free(peer); - /* Continue with a new peer. */ - return false; - - case OPENINGD: - /* Kill off openingd, which will free peer. */ - tal_free(peer->owner); - - /* A fresh start. */ - return false; - - case CHANNELD_AWAITING_LOCKIN: - case CHANNELD_NORMAL: - case CHANNELD_SHUTTING_DOWN: - /* We need the gossipfd now */ - get_gossip_fd_for_channeld_reconnect(ld, id, peer->unique_id, fd, cs); - return true; - - case CLOSINGD_SIGEXCHANGE: - case CLOSINGD_COMPLETE: - /* We need the gossipfd now */ - get_gossip_fd_for_closingd_reconnect(ld, id, peer->unique_id, fd, cs); - return true; - - case FUNDING_SPEND_SEEN: - case ONCHAIND_CHEATED: - case ONCHAIND_THEIR_UNILATERAL: - case ONCHAIND_OUR_UNILATERAL: - case ONCHAIND_MUTUAL: - ; /* FIXME: Implement! */ - } - abort(); + return peer; } -/* We copy per-peer entries above --log-level into the main log. */ -static void copy_to_parent_log(const char *prefix, - enum log_level level, - bool continued, - const char *str, - struct peer *peer) +/** + * requires_unsupported_features - Check if we support what's being asked + * + * Given the features vector that the remote connection is expecting + * from us, we check to see if we support all even bit features, i.e., + * the required features. We do so by subtracting our own features in + * the provided positions and see if even bits remain. + * + * @bitmap: the features bitmap the peer is asking for + * @supportmap: what do we support + * @smlen: how long is our supportmap + */ +static bool requires_unsupported_features(const u8 *bitmap, + const u8 *supportmap, + size_t smlen) { - const char *idstr = type_to_string(peer, struct pubkey, &peer->id); - if (continued) - log_add(peer->ld->log, "peer %s: ... %s", idstr, str); - else - log_(peer->ld->log, level, "peer %s: %s", idstr, str); - tal_free(idstr); + size_t len = tal_count(bitmap); + u8 support; + for (size_t i=0; i smlen) { + support = 0x00; + } else { + support = supportmap[smlen-1]; + } + + /* Cancel out supported bits, check for even bits */ + if ((~support & bitmap[i]) & 0x55) + return true; + } + return false; +} + +bool unsupported_features(const u8 *gfeatures, const u8 *lfeatures) +{ + return requires_unsupported_features(gfeatures, + supported_global_features, + sizeof(supported_global_features)) + || requires_unsupported_features(lfeatures, + supported_local_features, + sizeof(supported_local_features)); +} + +/* Extract channel_id from various packets, return true if possible. */ +static bool extract_channel_id(const u8 *in_pkt, struct channel_id *channel_id) +{ + u64 ignored_u64; + u32 ignored_u32; + u16 ignored_u16; + u8 ignored_u8; + struct pubkey ignored_pubkey; + struct sha256_double ignored_shadouble; + + if (fromwire_channel_reestablish(in_pkt, NULL, channel_id, + &ignored_u64, &ignored_u64)) + return true; + if (fromwire_open_channel(in_pkt, NULL, &ignored_shadouble, + channel_id, &ignored_u64, + &ignored_u64, &ignored_u64, + &ignored_u64, &ignored_u64, + &ignored_u64, &ignored_u32, + &ignored_u16, &ignored_u16, + &ignored_pubkey, &ignored_pubkey, + &ignored_pubkey, &ignored_pubkey, + &ignored_pubkey, &ignored_u8)) + return true; + return false; } /** @@ -557,6 +452,232 @@ static struct wallet_channel *peer_channel_new(struct wallet *w, return wc; } +static void channel_config(struct lightningd *ld, + struct channel_config *ours, + u32 *max_to_self_delay, + u32 *max_minimum_depth, + u64 *min_effective_htlc_capacity_msat) +{ + /* FIXME: depend on feerate. */ + *max_to_self_delay = ld->config.locktime_max; + *max_minimum_depth = ld->config.anchor_confirms_max; + /* This is 1c at $1000/BTC */ + *min_effective_htlc_capacity_msat = 1000000; + + /* BOLT #2: + * + * The sender SHOULD set `dust_limit_satoshis` to a sufficient + * value to allow commitment transactions to propagate through + * the Bitcoin network. + */ + ours->dust_limit_satoshis = 546; + ours->max_htlc_value_in_flight_msat = UINT64_MAX; + + /* Don't care */ + ours->htlc_minimum_msat = 0; + + /* BOLT #2: + * + * The sender SHOULD set `to_self_delay` sufficient to ensure + * the sender can irreversibly spend a commitment transaction + * output in case of misbehavior by the receiver. + */ + ours->to_self_delay = ld->config.locktime_blocks; + + /* BOLT #2: + * + * It MUST fail the channel if `max_accepted_htlcs` is greater than + * 483. + */ + ours->max_accepted_htlcs = 483; + + /* This is filled in by lightning_openingd, for consistency. */ + ours->channel_reserve_satoshis = 0; +}; + +/* Gossipd tells us a peer has connected */ +void peer_connected(struct lightningd *ld, const u8 *msg, + int peer_fd, int gossip_fd) +{ + struct pubkey id; + struct crypto_state cs; + u8 *gfeatures, *lfeatures; + u8 *error; + struct peer *peer; + + if (!fromwire_gossip_peer_connected(msg, msg, NULL, + &id, &cs, &gfeatures, &lfeatures)) + fatal("Gossip gave bad GOSSIP_PEER_CONNECTED message %s", + tal_hex(msg, msg)); + + if (unsupported_features(gfeatures, lfeatures)) { + log_unusual(ld->log, "peer %s offers unsupported features %s/%s", + type_to_string(msg, struct pubkey, &id), + tal_hex(msg, gfeatures), + tal_hex(msg, lfeatures)); + error = towire_errorfmt(msg, NULL, + "We only support globalfeatures %s" + " and localfeatures %s", + tal_hexstr(msg, + supported_global_features, + sizeof(supported_global_features)), + tal_hexstr(msg, + supported_local_features, + sizeof(supported_local_features))); + goto send_error; + } + + /* Now, do we already know this peer? */ + peer = peer_by_id(ld, &id); + if (peer) { + struct subd *owner; + + log_debug(peer->log, "Peer has reconnected, state %s", + peer_state_name(peer->state)); + + /* FIXME: We can have errors for multiple channels. */ + if (peer->error) { + error = peer->error; + goto send_error; + } + + switch (peer->state) { + /* This can't happen. */ + case UNINITIALIZED: + abort(); + + /* Reconnect: discard old one. */ + case OPENINGD: + /* This kills daemon (frees peer!) */ + tal_free(peer->owner); + peer = NULL; + goto return_to_gossipd; + + case ONCHAIND_CHEATED: + case ONCHAIND_THEIR_UNILATERAL: + case ONCHAIND_OUR_UNILATERAL: + case FUNDING_SPEND_SEEN: + case ONCHAIND_MUTUAL: + /* If they try to reestablish channel, we'll send + * error then */ + goto return_to_gossipd; + + case CHANNELD_AWAITING_LOCKIN: + case CHANNELD_NORMAL: + case CHANNELD_SHUTTING_DOWN: + /* Stop any existing daemon, without triggering error + * on this peer. */ + owner = peer->owner; + peer->owner = NULL; + tal_free(owner); + + peer_start_channeld(peer, &cs, peer_fd, gossip_fd, NULL, + true); + return; + + case CLOSINGD_SIGEXCHANGE: + case CLOSINGD_COMPLETE: + /* Stop any existing daemon, without triggering error + * on this peer. */ + owner = peer->owner; + peer->owner = NULL; + tal_free(owner); + + peer_start_closingd(peer, &cs, peer_fd, gossip_fd, + true); + return; + } + abort(); + } + +return_to_gossipd: + /* Otherwise, we hand back to gossipd, to continue. */ + msg = towire_gossipctl_handle_peer(msg, &id, &cs, + gfeatures, lfeatures, NULL); + subd_send_msg(ld->gossip, take(msg)); + subd_send_fd(ld->gossip, peer_fd); + close(gossip_fd); + + /* If we were waiting for connection, we succeeded. */ + connect_succeeded(ld, &id); + return; + +send_error: + /* Hand back to gossipd, with an error packet. */ + connect_failed(ld, &id, sanitize_error(msg, error, NULL)); + msg = towire_gossipctl_handle_peer(msg, &id, &cs, + gfeatures, lfeatures, error); + subd_send_msg(ld->gossip, take(msg)); + subd_send_fd(ld->gossip, peer_fd); + close(gossip_fd); +} + +void peer_sent_nongossip(struct lightningd *ld, + const struct pubkey *id, + const struct crypto_state *cs, + const u8 *gfeatures, + const u8 *lfeatures, + int peer_fd, int gossip_fd, + const u8 *in_msg) +{ + struct channel_id *channel_id, extracted_channel_id; + struct peer *peer; + u8 *error, *msg; + + if (!extract_channel_id(in_msg, &extracted_channel_id)) + channel_id = NULL; + else + channel_id = &extracted_channel_id; + + /* FIXME: match state too; we can have multiple onchain + * (ie. dead) channels for the same peer. */ + peer = peer_by_id(ld, id); + if (peer) { + error = towire_errorfmt(ld, channel_id, + "Unexpected message %i in state %s", + fromwire_peektype(in_msg), + peer_state_name(peer->state)); + goto send_error; + } + + /* Open request? */ + if (fromwire_peektype(in_msg) == WIRE_OPEN_CHANNEL) { + peer_accept_channel(ld, id, cs, gfeatures, lfeatures, + peer_fd, gossip_fd, in_msg); + return; + } + + /* Weird request. */ + error = towire_errorfmt(ld, channel_id, + "Unexpected message %i for unknown peer", + fromwire_peektype(in_msg)); + +send_error: + /* Hand back to gossipd, with an error packet. */ + connect_failed(ld, id, sanitize_error(error, error, NULL)); + msg = towire_gossipctl_handle_peer(error, id, cs, + gfeatures, lfeatures, error); + subd_send_msg(ld->gossip, take(msg)); + subd_send_fd(ld->gossip, peer_fd); + close(gossip_fd); + tal_free(error); +} + +/* We copy per-peer entries above --log-level into the main log. */ +static void copy_to_parent_log(const char *prefix, + enum log_level level, + bool continued, + const char *str, + struct peer *peer) +{ + const char *idstr = type_to_string(peer, struct pubkey, &peer->id); + if (continued) + log_add(peer->ld->log, "peer %s: ... %s", idstr, str); + else + log_(peer->ld->log, level, "peer %s: %s", idstr, str); + tal_free(idstr); +} + void populate_peer(struct lightningd *ld, struct peer *peer) { const char *idname; @@ -574,87 +695,6 @@ void populate_peer(struct lightningd *ld, struct peer *peer) tal_add_destructor(peer, destroy_peer); } -void add_peer(struct lightningd *ld, u64 unique_id, - int fd, const struct pubkey *id, - const struct crypto_state *cs) -{ - struct peer *peer; - const char *netname, *idname; - u8 *msg; - - /* It's a reconnect? */ - if (peer_reconnected(ld, id, fd, cs)) - return; - - /* Fresh peer. */ - /* Need to memset since storing will access all fields */ - peer = talz(ld, struct peer); - peer->ld = ld; - peer->error = NULL; - peer->unique_id = unique_id; - peer->id = *id; - peer->funding_txid = NULL; - peer->remote_funding_locked = false; - peer->scid = NULL; - peer->seed = NULL; - peer->our_msatoshi = NULL; - peer->state = UNINITIALIZED; - peer->channel_info = NULL; - peer->last_tx = NULL; - peer->last_sig = NULL; - peer->last_htlc_sigs = NULL; - peer->last_was_revoke = false; - peer->last_sent_commit = NULL; - peer->remote_shutdown_scriptpubkey = NULL; - peer->local_shutdown_idx = -1; - peer->next_index[LOCAL] - = peer->next_index[REMOTE] = 0; - peer->next_htlc_id = 0; - wallet_shachain_init(ld->wallet, &peer->their_shachain); - - /* If we have the peer in the DB, this'll populate the fields, - * failure just indicates that the peer wasn't found in the - * DB */ - wallet_peer_by_nodeid(ld->wallet, id, peer); - - /* peer->channel gets populated as soon as we start opening a channel */ - peer->channel = NULL; - - /* FIXME: Don't assume protocol here! */ - if (!netaddr_from_fd(fd, SOCK_STREAM, IPPROTO_TCP, &peer->netaddr)) { - log_unusual(ld->log, "Failed to get netaddr for outgoing: %s", - strerror(errno)); - tal_free(peer); - return; - } - list_add_tail(&ld->peers, &peer->list); - populate_peer(ld, peer); - - idname = type_to_string(peer, struct pubkey, id); - netname = netaddr_name(idname, &peer->netaddr); - log_info(peer->log, "Connected from %s", netname); - - tal_free(idname); - - /* Let gossip handle it from here. */ - peer->owner = peer->ld->gossip; - peer_set_condition(peer, UNINITIALIZED, GOSSIPD); - - msg = towire_gossipctl_new_peer(peer, peer->unique_id, cs); - subd_send_msg(peer->ld->gossip, take(msg)); - subd_send_fd(peer->ld->gossip, fd); -} - -struct peer *peer_by_unique_id(struct lightningd *ld, u64 unique_id) -{ - struct peer *p; - - list_for_each(&ld->peers, p, list) - if (p->unique_id == unique_id) - return p; - return NULL; -} - struct peer *peer_by_id(struct lightningd *ld, const struct pubkey *id) { struct peer *p; @@ -683,127 +723,20 @@ static void peer_owner_finished(struct subd *subd, int status) subd->name, status); } -static int make_listen_fd(struct lightningd *ld, - int domain, void *addr, socklen_t len) -{ - int fd = socket(domain, SOCK_STREAM, 0); - if (fd < 0) { - log_debug(ld->log, "Failed to create %u socket: %s", - domain, strerror(errno)); - return -1; - } - - if (addr) { - int on = 1; - - /* Re-use, please.. */ - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) - log_unusual(ld->log, "Failed setting socket reuse: %s", - strerror(errno)); - - if (bind(fd, addr, len) != 0) { - log_unusual(ld->log, "Failed to bind on %u socket: %s", - domain, strerror(errno)); - goto fail; - } - } - - if (listen(fd, 5) != 0) { - log_unusual(ld->log, "Failed to listen on %u socket: %s", - domain, strerror(errno)); - goto fail; - } - return fd; - -fail: - close_noerr(fd); - return -1; -} - -void setup_listeners(struct lightningd *ld) -{ - struct sockaddr_in addr; - struct sockaddr_in6 addr6; - socklen_t len; - int fd1, fd2; - - if (!ld->portnum) { - log_debug(ld->log, "Zero portnum, not listening for incoming"); - return; - } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(ld->portnum); - - memset(&addr6, 0, sizeof(addr6)); - addr6.sin6_family = AF_INET6; - addr6.sin6_addr = in6addr_any; - addr6.sin6_port = htons(ld->portnum); - - /* IPv6, since on Linux that (usually) binds to IPv4 too. */ - fd1 = make_listen_fd(ld, AF_INET6, &addr6, sizeof(addr6)); - if (fd1 >= 0) { - struct sockaddr_in6 in6; - - len = sizeof(in6); - if (getsockname(fd1, (void *)&in6, &len) != 0) { - log_unusual(ld->log, "Failed get IPv6 sockname: %s", - strerror(errno)); - close_noerr(fd1); - fd1 = -1; - } else { - addr.sin_port = in6.sin6_port; - assert(ld->portnum == ntohs(addr.sin_port)); - log_debug(ld->log, "Creating IPv6 listener on port %u", - ld->portnum); - io_new_listener(ld, fd1, connection_in, ld); - } - } - - /* Just in case, aim for the same port... */ - fd2 = make_listen_fd(ld, AF_INET, &addr, sizeof(addr)); - if (fd2 >= 0) { - len = sizeof(addr); - if (getsockname(fd2, (void *)&addr, &len) != 0) { - log_unusual(ld->log, "Failed get IPv4 sockname: %s", - strerror(errno)); - close_noerr(fd2); - fd2 = -1; - } else { - assert(ld->portnum == ntohs(addr.sin_port)); - log_debug(ld->log, "Creating IPv4 listener on port %u", - ld->portnum); - io_new_listener(ld, fd2, connection_in, ld); - } - } - - if (fd1 < 0 && fd2 < 0) - fatal("Could not bind to a network address on port %u", - ld->portnum); -} - -static void connect_failed(struct lightningd *ld, - struct connection *c) -{ - tal_free(c); -} - static void json_connect(struct command *cmd, const char *buffer, const jsmntok_t *params) { - struct connection *c; jsmntok_t *hosttok, *idtok; - const tal_t *tmpctx = tal_tmpctx(cmd); struct pubkey id; const char *name, *port, *colon; + struct ipaddr addr; + u8 *msg; if (!json_get_params(buffer, params, "id", &idtok, - "host", &hosttok, + "?host", &hosttok, NULL)) { - command_fail(cmd, "Need id and host to connect"); + command_fail(cmd, "Need id to connect"); return; } @@ -814,27 +747,34 @@ static void json_connect(struct command *cmd, return; } - colon = memchr(buffer + hosttok->start, ':', - hosttok->end - hosttok->start); - if (colon) { - name = tal_strndup(cmd, buffer + hosttok->start, - colon - (buffer + hosttok->start)); - port = tal_strndup(cmd, colon + 1, - (buffer + hosttok->end) - colon - 1); - } else { - name = tal_strndup(cmd, buffer + hosttok->start, - hosttok->end - hosttok->start); - port = tal_strdup(cmd, stringify(DEFAULT_PORT)); + if (hosttok) { + colon = memchr(buffer + hosttok->start, ':', + hosttok->end - hosttok->start); + if (colon) { + name = tal_strndup(cmd, buffer + hosttok->start, + colon - (buffer + hosttok->start)); + port = tal_strndup(cmd, colon + 1, + (buffer + hosttok->end) - colon - 1); + } else { + name = tal_strndup(cmd, buffer + hosttok->start, + hosttok->end - hosttok->start); + port = tal_strdup(cmd, stringify(DEFAULT_PORT)); + } + addr.port = atoi(port); + if (!parse_ipaddr(name, &addr) || !addr.port) + command_fail(cmd, "host %s:%s not valid", name, port); + + /* Tell it about the address. */ + msg = towire_gossipctl_peer_addrhint(cmd, &id, &addr); + subd_send_msg(cmd->ld->gossip, take(msg)); } - c = new_connection(cmd, cmd->ld, cmd, &id); - if (!dns_resolve_and_connect(cmd->ld, name, port, - connection_out, connect_failed, c)) { - command_fail(cmd, "DNS failed"); - return; - } + /* Now tell it to try reaching it. */ + msg = towire_gossipctl_reach_peer(cmd, &id); + subd_send_msg(cmd->ld->gossip, take(msg)); - tal_free(tmpctx); + /* Leave this here for gossip_peer_connected */ + new_connect(cmd->ld, &id, cmd); } static const struct json_command connect_command = { @@ -1028,13 +968,19 @@ struct peer *peer_from_json(struct lightningd *ld, } struct funding_channel { - struct peer *peer; - struct command *cmd; + struct command *cmd; /* Which also owns us. */ - /* Details we sent to openingd to create funding. */ + /* Peer we're trying to reach. */ + struct pubkey peerid; + + /* Details of how to make funding. */ const struct utxo **utxomap; u64 change; u32 change_keyindex; + u64 funding_satoshi, push_msat; + + /* Peer, once we have one. */ + struct peer *peer; /* Funding tx once we're ready to sign and send. */ struct bitcoin_tx *funding_tx; @@ -2255,7 +2201,7 @@ static bool opening_funder_finished(struct subd *opening, const u8 *resp, &channel_info->remote_fundingkey, &funding_txid, &channel_info->feerate_per_kw)) { - peer_internal_error(fc->peer, "bad shutdown_complete: %s", + peer_internal_error(fc->peer, "bad funder_reply: %s", tal_hex(resp, resp)); return false; } @@ -2410,82 +2356,45 @@ static bool opening_fundee_finished(struct subd *opening, return false; } -static void channel_config(struct lightningd *ld, - struct channel_config *ours, - u32 *max_to_self_delay, - u32 *max_minimum_depth, - u64 *min_effective_htlc_capacity_msat) +/* Peer has spontaneously exited from gossip due to open msg */ +static void peer_accept_channel(struct lightningd *ld, + const struct pubkey *peer_id, + const struct crypto_state *cs, + const u8 *gfeatures, const u8 *lfeatures, + int peer_fd, int gossip_fd, + const u8 *open_msg) { - /* FIXME: depend on feerate. */ - *max_to_self_delay = ld->config.locktime_max; - *max_minimum_depth = ld->config.anchor_confirms_max; - /* This is 1c at $1000/BTC */ - *min_effective_htlc_capacity_msat = 1000000; - - /* BOLT #2: - * - * The sender SHOULD set `dust_limit_satoshis` to a sufficient - * value to allow commitment transactions to propagate through - * the Bitcoin network. - */ - ours->dust_limit_satoshis = 546; - ours->max_htlc_value_in_flight_msat = UINT64_MAX; - - /* Don't care */ - ours->htlc_minimum_msat = 0; - - /* BOLT #2: - * - * The sender SHOULD set `to_self_delay` sufficient to ensure - * the sender can irreversibly spend a commitment transaction - * output in case of misbehavior by the receiver. - */ - ours->to_self_delay = ld->config.locktime_blocks; - - /* BOLT #2: - * - * It MUST fail the channel if `max_accepted_htlcs` is greater than - * 483. - */ - ours->max_accepted_htlcs = 483; - - /* This is filled in by lightning_openingd, for consistency. */ - ours->channel_reserve_satoshis = 0; -}; - -/* Peer has spontaneously exited from gossip due to msg */ -void peer_fundee_open(struct peer *peer, const u8 *from_peer, - const struct crypto_state *cs, - int peer_fd, int gossip_fd) -{ - struct lightningd *ld = peer->ld; u32 max_to_self_delay, max_minimum_depth; u64 min_effective_htlc_capacity_msat; + u8 *errmsg; u8 *msg; + struct peer *peer; + struct subd *opening; - /* Note: gossipd handles unknown packets, so we don't have to worry - * about ignoring odd ones here. */ - if (fromwire_peektype(from_peer) != WIRE_OPEN_CHANNEL) { - char *msg = tal_fmt(peer, "Bad message %i (%s) before opening", - fromwire_peektype(from_peer), - wire_type_name(fromwire_peektype(from_peer))); - log_unusual(peer->log, "Strange message to exit gossip: %u", - fromwire_peektype(from_peer)); - peer_fail_permanent_str(peer, take(msg)); - return; + assert(fromwire_peektype(open_msg) == WIRE_OPEN_CHANNEL); + + /* We make a new peer. */ + peer = new_peer(ld, peer_id, gfeatures, lfeatures, peer_fd); + + /* FIXME: Only happens due to netaddr fail. */ + if (!peer) { + errmsg = take(towire_errorfmt(ld, NULL, + "Can't resolve your address")); + goto peer_to_gossipd; } - peer_set_condition(peer, GOSSIPD, OPENINGD); - peer->owner = new_subd(ld, "lightning_openingd", peer, - opening_wire_type_name, - NULL, bad_peer, peer_owner_finished, - take(&peer_fd), take(&gossip_fd), - NULL); - if (!peer->owner) { + peer_set_condition(peer, UNINITIALIZED, OPENINGD); + opening = new_subd(ld, + "lightning_openingd", peer, + opening_wire_type_name, + NULL, bad_peer, peer_owner_finished, + take(&peer_fd), take(&gossip_fd), NULL); + if (!opening) { peer_fail_transient(peer, "Failed to subdaemon opening: %s", strerror(errno)); return; } + peer->owner = opening; /* They will open channel. */ peer->funder = REMOTE; @@ -2521,51 +2430,71 @@ void peer_fundee_open(struct peer *peer, const u8 *from_peer, subd_send_msg(peer->owner, take(msg)); /* FIXME: Expose the min_feerate_per_kw and max_feerate_per_kw in the config */ msg = towire_opening_fundee(peer, peer->minimum_depth, - 7500, 150000, from_peer); + 7500, 150000, open_msg); subd_req(peer, peer->owner, take(msg), -1, 2, opening_fundee_finished, peer); + return; + +peer_to_gossipd: + /* Return to gossipd, with optional error msg to send. */ + msg = towire_gossipctl_handle_peer(ld, peer_id, cs, + gfeatures, lfeatures, errmsg); + subd_send_msg(ld->gossip, take(msg)); + subd_send_fd(ld->gossip, peer_fd); + close(gossip_fd); + return; } -/* Peer has been released from gossip. Start opening. */ -static bool gossip_peer_released(struct subd *gossip, - const u8 *resp, - const int *fds, - struct funding_channel *fc) +static void peer_offer_channel(struct lightningd *ld, + struct funding_channel *fc, + const struct crypto_state *cs, + const u8 *gfeatures, const u8 *lfeatures, + int peer_fd, int gossip_fd) { - struct lightningd *ld = fc->peer->ld; - u32 max_to_self_delay, max_minimum_depth; - u64 min_effective_htlc_capacity_msat; u8 *msg; struct subd *opening; + u32 max_to_self_delay, max_minimum_depth; + u64 min_effective_htlc_capacity_msat; struct utxo *utxos; - struct crypto_state cs; - if (!fromwire_gossipctl_release_peer_reply(resp, NULL, &cs)) { - if (!fromwire_gossipctl_release_peer_replyfail(resp, NULL)) { - fatal("Gossip daemon gave invalid reply %s", - tal_hex(gossip, resp)); - } - tal_del_destructor(fc, fail_fundchannel_command); - command_fail(fc->cmd, "Peer reconnected, try again"); - return true; + /* We make a new peer. */ + fc->peer = new_peer(ld, &fc->peerid, gfeatures, lfeatures, peer_fd); + + /* FIXME: Only happens due to netaddr fail. */ + if (!fc->peer) { + command_fail(fc->cmd, + "Failed to make peer: Can't resolve address: %s", + strerror(errno)); + close(peer_fd); + close(gossip_fd); + return; } + fc->peer->funding_satoshi = fc->funding_satoshi; + fc->peer->push_msat = fc->push_msat; - assert(tal_count(fds) == 2); - - peer_set_condition(fc->peer, GOSSIPD, OPENINGD); + peer_set_condition(fc->peer, UNINITIALIZED, OPENINGD); opening = new_subd(ld, "lightning_openingd", fc->peer, opening_wire_type_name, NULL, bad_peer, peer_owner_finished, - take(&fds[0]), take(&fds[1]), NULL); + take(&peer_fd), take(&gossip_fd), NULL); if (!opening) { - peer_fail_transient(fc->peer, "Failed to subdaemon opening: %s", - strerror(errno)); - return true; + fc->peer = tal_free(fc->peer); + command_fail(fc->cmd, + "Failed to launch openingd: %s", + strerror(errno)); + return; } fc->peer->owner = opening; + /* FIXME: This is wrong in several ways. + * + * 1. We should set the temporary channel id *now*, so that's the + * key. + * 2. We don't need the peer or channel in db until peer_persists(). + */ + /* Store the channel in the database in order to get a channel * ID that is unique and which we can base the peer_seed on */ fc->peer->channel = peer_channel_new(ld->wallet, fc->peer); @@ -2590,8 +2519,7 @@ static bool gossip_peer_released(struct subd *gossip, &fc->peer->our_config, max_to_self_delay, min_effective_htlc_capacity_msat, - &cs, fc->peer->seed); - + cs, fc->peer->seed); subd_send_msg(opening, take(msg)); utxos = from_utxoptr_arr(fc, fc->utxomap); @@ -2603,7 +2531,54 @@ static bool gossip_peer_released(struct subd *gossip, fc->change, fc->change_keyindex, fc->peer->channel_flags, utxos, fc->peer->ld->wallet->bip32_base); + + /* Peer now owns fc; if it dies, we fail fc. */ + tal_steal(fc->peer, fc); + tal_add_destructor(fc, fail_fundchannel_command); + subd_req(fc, opening, take(msg), -1, 2, opening_funder_finished, fc); +} + +/* Peer has been released from gossip. Start opening. */ +static bool gossip_peer_released(struct subd *gossip, + const u8 *resp, + const int *fds, + struct funding_channel *fc) +{ + struct lightningd *ld = gossip->ld; + struct crypto_state cs; + u8 *gfeatures, *lfeatures; + + /* We could have raced with peer doing something else. */ + fc->peer = peer_by_id(ld, &fc->peerid); + + if (!fromwire_gossipctl_release_peer_reply(fc, resp, NULL, &cs, + &gfeatures, &lfeatures)) { + if (!fromwire_gossipctl_release_peer_replyfail(resp, NULL)) { + fatal("Gossip daemon gave invalid reply %s", + tal_hex(gossip, resp)); + } + if (fc->peer) + command_fail(fc->cmd, "Peer already %s", + peer_state_name(fc->peer->state)); + else + command_fail(fc->cmd, "Peer not connected"); + return true; + } + assert(tal_count(fds) == 2); + + /* We asked to release this peer, but another raced in? Corner case, + * close this is easiest. */ + if (fc->peer) { + command_fail(fc->cmd, "Peer already %s", + peer_state_name(fc->peer->state)); + close(fds[0]); + close(fds[1]); + return true; + } + + /* OK, offer peer a channel. */ + peer_offer_channel(ld, fc, &cs, gfeatures, lfeatures, fds[0], fds[1]); return true; } @@ -2618,43 +2593,36 @@ static void json_fund_channel(struct command *cmd, "id", &peertok, "satoshi", &satoshitok, NULL)) { - command_fail(cmd, "Need peerid and satoshi"); + command_fail(cmd, "Need id and satoshi"); return; } fc->cmd = cmd; - fc->peer = peer_from_json(cmd->ld, buffer, peertok); - if (!fc->peer) { - command_fail(cmd, "Could not find peer with that peerid"); - return; - } - if (fc->peer->owner != cmd->ld->gossip) { - command_fail(cmd, "Peer not ready for connection"); + + if (!pubkey_from_hexstr(buffer + peertok->start, + peertok->end - peertok->start, &fc->peerid)) { + command_fail(cmd, "Could not parse id"); return; } - if (!json_tok_u64(buffer, satoshitok, &fc->peer->funding_satoshi)) { + if (!json_tok_u64(buffer, satoshitok, &fc->funding_satoshi)) { command_fail(cmd, "Invalid satoshis"); return; } /* FIXME: Support push_msat? */ - fc->peer->push_msat = 0; + fc->push_msat = 0; /* Try to do this now, so we know if insufficient funds. */ /* FIXME: Feerate & dustlimit */ - fc->utxomap = build_utxos(fc, cmd->ld, fc->peer->funding_satoshi, + fc->utxomap = build_utxos(fc, cmd->ld, fc->funding_satoshi, 15000, 600, &fc->change, &fc->change_keyindex); if (!fc->utxomap) { command_fail(cmd, "Cannot afford funding transaction"); return; } - msg = towire_gossipctl_release_peer(cmd, fc->peer->unique_id); - - /* Tie this fc lifetime (and hence utxo release) to the peer */ - tal_steal(fc->peer, fc); - tal_add_destructor(fc, fail_fundchannel_command); + msg = towire_gossipctl_release_peer(cmd, &fc->peerid); subd_req(fc, cmd->ld->gossip, msg, -1, 2, gossip_peer_released, fc); } diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 50c888eb7..d9de9b10f 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -29,6 +29,9 @@ struct peer { /* ID of peer */ struct pubkey id; + /* Global and local features bitfields. */ + const u8 *gfeatures, *lfeatures; + /* Error message (iff in error state) */ u8 *error; @@ -127,6 +130,12 @@ static inline bool peer_on_chain(const struct peer *peer) return peer_state_on_chain(peer->state); } +static inline bool peer_wants_reconnect(const struct peer *peer) +{ + return peer->state >= CHANNELD_AWAITING_LOCKIN + && peer->state <= CLOSINGD_COMPLETE; +} + /* BOLT #2: * * On disconnection, the funder MUST remember the channel for @@ -142,7 +151,6 @@ static inline bool peer_persists(const struct peer *peer) return peer->state >= CHANNELD_AWAITING_LOCKIN; } -struct peer *peer_by_unique_id(struct lightningd *ld, u64 unique_id); struct peer *peer_by_id(struct lightningd *ld, const struct pubkey *id); struct peer *peer_from_json(struct lightningd *ld, const char *buffer, @@ -151,13 +159,23 @@ struct peer *peer_from_json(struct lightningd *ld, void peer_last_tx(struct peer *peer, struct bitcoin_tx *tx, const secp256k1_ecdsa_signature *sig); -void peer_fundee_open(struct peer *peer, const u8 *msg, - const struct crypto_state *cs, - int peer_fd, int gossip_fd); +/* The three ways peers enter from the network: + * + * peer_connected - when it first connects to gossipd (after init exchange). + * peer_sent_nongossip - when it tries to fund a channel. + * gossip_peer_released - when we tell gossipd to release it so we can fund + * a channel. +*/ +void peer_connected(struct lightningd *ld, const u8 *msg, + int peer_fd, int gossip_fd); -void add_peer(struct lightningd *ld, u64 unique_id, - int fd, const struct pubkey *id, - const struct crypto_state *cs); +void peer_sent_nongossip(struct lightningd *ld, + const struct pubkey *id, + const struct crypto_state *cs, + const u8 *gfeatures, + const u8 *lfeatures, + int peer_fd, int gossip_fd, + const u8 *in_msg); /** * populate_peer -- Populate daemon fields in a peer @@ -171,6 +189,13 @@ void add_peer(struct lightningd *ld, u64 unique_id, */ void populate_peer(struct lightningd *ld, struct peer *peer); +/* Returns true if these contain any unsupported features. */ +bool unsupported_features(const u8 *gfeatures, const u8 *lfeatures); + +/* For sending our features: tal_len() returns length. */ +u8 *get_supported_global_features(const tal_t *ctx); +u8 *get_supported_local_features(const tal_t *ctx); + /* Could be configurable. */ #define OUR_CHANNEL_FLAGS CHANNEL_FLAGS_ANNOUNCE_CHANNEL diff --git a/lightningd/peer_state.h b/lightningd/peer_state.h index f2d228626..46227a340 100644 --- a/lightningd/peer_state.h +++ b/lightningd/peer_state.h @@ -5,9 +5,6 @@ enum peer_state { UNINITIALIZED, - /* In gossip daemon. */ - GOSSIPD, - /* Negotiating channel opening: in opening daemon */ OPENINGD, diff --git a/lightningd/test/run-find_my_path.c b/lightningd/test/run-find_my_path.c index 622f4f7ab..425a6de97 100644 --- a/lightningd/test/run-find_my_path.c +++ b/lightningd/test/run-find_my_path.c @@ -55,9 +55,6 @@ void register_opts(struct lightningd *ld UNNEEDED) /* Generated stub for setup_jsonrpc */ void setup_jsonrpc(struct lightningd *ld UNNEEDED, const char *rpc_filename UNNEEDED) { fprintf(stderr, "setup_jsonrpc called!\n"); abort(); } -/* Generated stub for setup_listeners */ -void setup_listeners(struct lightningd *ld UNNEEDED) -{ fprintf(stderr, "setup_listeners called!\n"); abort(); } /* Generated stub for setup_topology */ void setup_topology(struct chain_topology *topology UNNEEDED, struct timers *timers UNNEEDED, diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index c095b9c07..4e6229d0b 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -212,8 +212,8 @@ class LightningDTests(BaseLightningDTests): assert ret['id'] == l2.info['id'] - l1.daemon.wait_for_log('WIRE_GOSSIPCTL_NEW_PEER') - l2.daemon.wait_for_log('WIRE_GOSSIPCTL_NEW_PEER') + l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER') + l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER') return l1,l2 def fund_channel(self, l1, l2, amount): @@ -267,18 +267,13 @@ class LightningDTests(BaseLightningDTests): def test_connect(self): l1,l2 = self.connect() - p1 = l1.rpc.getpeer(l2.info['id'], 'info') - p2 = l2.rpc.getpeer(l1.info['id'], 'info') + # Main daemon has no idea about these peers; they're in gossipd. + assert l1.rpc.getpeer(l2.info['id'], 'info') == None + assert l2.rpc.getpeer(l1.info['id'], 'info') == None - assert p1['state'] == 'GOSSIPD' - assert p2['state'] == 'GOSSIPD' - - # It should have gone through these steps - assert 'state: UNINITIALIZED -> GOSSIPD' in p1['log'] - - # Both should still be owned by gossip - assert p1['owner'] == 'lightning_gossipd' - assert p2['owner'] == 'lightning_gossipd' + # Both gossipds will have them as new peers once handed back. + l1.daemon.wait_for_log('handle_peer {}: new peer'.format(l2.info['id'])) + l2.daemon.wait_for_log('handle_peer {}: new peer'.format(l1.info['id'])) def test_balance(self): l1,l2 = self.connect() @@ -403,8 +398,8 @@ class LightningDTests(BaseLightningDTests): assert ret['id'] == l2.info['id'] - l1.daemon.wait_for_log('WIRE_GOSSIPCTL_NEW_PEER') - l2.daemon.wait_for_log('WIRE_GOSSIPCTL_NEW_PEER') + l1.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER') + l2.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER') addr = l1.rpc.newaddr()['address'] txid = l1.bitcoin.rpc.sendtoaddress(addr, 10**6 / 10**8 + 0.01) @@ -1087,7 +1082,7 @@ class LightningDTests(BaseLightningDTests): assert ret['id'] == l3.info['id'] - l3.daemon.wait_for_log('WIRE_GOSSIPCTL_NEW_PEER') + l3.daemon.wait_for_log('WIRE_GOSSIPCTL_HANDLE_PEER') self.fund_channel(l1, l2, 10**6) self.fund_channel(l2, l3, 10**6) @@ -1138,20 +1133,20 @@ class LightningDTests(BaseLightningDTests): l1.rpc.sendpay(to_json(route), rhash) def test_disconnect(self): - # These should all make us fail. + # These should all make us fail, and retry. + # FIXME: Configure short timeout for reconnect! disconnects = ['-WIRE_INIT', '@WIRE_INIT', '+WIRE_INIT'] l1 = self.node_factory.get_node(disconnect=disconnects) l2 = self.node_factory.get_node() - for d in disconnects: - self.assertRaises(ValueError, l1.rpc.connect, - l2.info['id'], 'localhost:{}'.format(l2.info['port'])) - assert l1.rpc.getpeer(l2.info['id']) == None - - # Now we should connect normally. l1.rpc.connect(l2.info['id'], 'localhost:{}'.format(l2.info['port'])) + # Should have 3 connect fails. + for d in disconnects: + l1.daemon.wait_for_log('Failed connected out for {}, will try again' + .format(l2.info['id'])) + def test_disconnect_funder(self): # Now error on funder side duringchannel open. disconnects = ['-WIRE_OPEN_CHANNEL',