From daf8866eb586b3285f3de6471e6bdf9b9241d24e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sun, 7 May 2017 01:59:48 +0200 Subject: [PATCH] gossip: Implement the basic node_announcement Rather a big commit, but I couldn't figure out how to split it nicely. It introduces a new message from the channel to the master signaling that the channel has been announced, so that the master can take care of announcing the node itself. A provisorial announcement is created and passed to the HSM, which signs it and passes it back to the master. Finally the master injects it into gossipd which will take care of broadcasting it. --- daemon/routing.c | 1 + lightningd/channel/channel.c | 7 ++++ lightningd/channel/channel_wire.csv | 2 + lightningd/gossip/gossip.c | 12 ++++++ lightningd/gossip/gossip_wire.csv | 6 +++ lightningd/gossip_control.c | 1 + lightningd/hsm/hsm.c | 36 ++++++++++++++++ lightningd/hsm/hsm_wire.csv | 8 ++++ lightningd/hsm_control.c | 2 + lightningd/peer_control.c | 64 +++++++++++++++++++++++++++++ 10 files changed, 139 insertions(+) diff --git a/daemon/routing.c b/daemon/routing.c index b8460504c..f1d90ea8d 100644 --- a/daemon/routing.c +++ b/daemon/routing.c @@ -78,6 +78,7 @@ struct node *new_node(struct routing_state *rstate, n->alias = NULL; n->hostname = NULL; n->node_announcement = NULL; + n->last_timestamp = 0; node_map_add(rstate->nodes, n); tal_add_destructor(n, destroy_node); diff --git a/lightningd/channel/channel.c b/lightningd/channel/channel.c index 48be0c9be..266fcce7d 100644 --- a/lightningd/channel/channel.c +++ b/lightningd/channel/channel.c @@ -305,6 +305,9 @@ static void handle_peer_announcement_signatures(struct peer *peer, const u8 *msg if (peer->funding_locked[LOCAL] && peer->have_sigs[LOCAL]) { send_channel_announcement(peer); send_channel_update(peer, false); + /* Tell the master that we just announced the channel, + * so it may announce the node */ + daemon_conn_send(&peer->master, take(towire_channel_announced(msg))); } } @@ -1105,6 +1108,9 @@ static void handle_funding_announce_depth(struct peer *peer, const u8 *msg) if (peer->have_sigs[REMOTE]) { send_channel_announcement(peer); send_channel_update(peer, false); + /* Tell the master that we just announced the channel, + * so it may announce the node */ + daemon_conn_send(&peer->master, take(towire_channel_announced(msg))); } } @@ -1326,6 +1332,7 @@ static struct io_plan *req_in(struct io_conn *conn, struct daemon_conn *master) case WIRE_CHANNEL_MALFORMED_HTLC: case WIRE_CHANNEL_PING_REPLY: case WIRE_CHANNEL_PEER_BAD_MESSAGE: + case WIRE_CHANNEL_ANNOUNCED: break; } status_failed(WIRE_CHANNEL_BAD_COMMAND, "%u %s", t, diff --git a/lightningd/channel/channel_wire.csv b/lightningd/channel/channel_wire.csv index 9c31a6b84..4ecaf657c 100644 --- a/lightningd/channel/channel_wire.csv +++ b/lightningd/channel/channel_wire.csv @@ -117,3 +117,5 @@ channel_ping,0,len,u16 channel_ping_reply,111 channel_ping_reply,0,totlen,u16 +# Channeld tells the master that the channel has been announced +channel_announced,12 \ No newline at end of file diff --git a/lightningd/gossip/gossip.c b/lightningd/gossip/gossip.c index a4dbbbabb..d60b9bdef 100644 --- a/lightningd/gossip/gossip.c +++ b/lightningd/gossip/gossip.c @@ -660,6 +660,15 @@ static struct io_plan *resolve_channel_req(struct io_conn *conn, return daemon_conn_read_next(conn, &daemon->master); } +static void handle_forwarded_msg(struct io_conn *conn, struct daemon *daemon, const u8 *msg) +{ + u8 *payload; + if (!fromwire_gossip_forwarded_msg(msg, msg, NULL, &payload)) { + status_trace("Malformed forwarded message: %s", tal_hex(trc, msg)); + return; + } + handle_gossip_msg(daemon->rstate, payload); +} static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master) { struct daemon *daemon = container_of(master, struct daemon, master); @@ -692,6 +701,9 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master case WIRE_GOSSIP_RESOLVE_CHANNEL_REQUEST: return resolve_channel_req(conn, daemon, daemon->master.msg_in); + case WIRE_GOSSIP_FORWARDED_MSG: + handle_forwarded_msg(conn, daemon, daemon->master.msg_in); + return daemon_conn_read_next(conn, &daemon->master); case WIRE_GOSSIPCTL_RELEASE_PEER_REPLY: case WIRE_GOSSIP_GETNODES_REPLY: case WIRE_GOSSIP_GETROUTE_REPLY: diff --git a/lightningd/gossip/gossip_wire.csv b/lightningd/gossip/gossip_wire.csv index 66656c1c7..4b44384bb 100644 --- a/lightningd/gossip/gossip_wire.csv +++ b/lightningd/gossip/gossip_wire.csv @@ -98,3 +98,9 @@ gossip_resolve_channel_request,0,channel_id,struct short_channel_id gossip_resolve_channel_reply,109 gossip_resolve_channel_reply,0,num_keys,u16 gossip_resolve_channel_reply,0,keys,num_keys*struct pubkey + +# The main daemon forward some gossip message to gossipd, allows injecting +# arbitrary gossip messages. +gossip_forwarded_msg,10 +gossip_forwarded_msg,0,msglen,2 +gossip_forwarded_msg,2,msg,msglen diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 776d854a0..a7e7c4b7c 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -148,6 +148,7 @@ static int gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIP_GETCHANNELS_REQUEST: case WIRE_GOSSIP_PING: case WIRE_GOSSIP_RESOLVE_CHANNEL_REQUEST: + case WIRE_GOSSIP_FORWARDED_MSG: /* This is a reply, so never gets through to here. */ case WIRE_GOSSIPCTL_RELEASE_PEER_REPLY: case WIRE_GOSSIP_GETNODES_REPLY: diff --git a/lightningd/hsm/hsm.c b/lightningd/hsm/hsm.c index 0298b1374..490dd149c 100644 --- a/lightningd/hsm/hsm.c +++ b/lightningd/hsm/hsm.c @@ -511,6 +511,37 @@ static void sign_funding_tx(struct daemon_conn *master, const u8 *msg) tal_free(tmpctx); } +static void sign_node_announcement(struct daemon_conn *master, const u8 *msg) +{ + /* 2 bytes msg type + 64 bytes signature */ + size_t offset = 66; + struct sha256_double hash; + struct privkey node_pkey; + secp256k1_ecdsa_signature sig; + u8 *reply; + u8 *ann; + + if (!fromwire_hsmctl_node_announcement_sig_req(msg, msg, NULL, &ann)) { + status_trace("Failed to parse node_announcement_sig_req: %s", + tal_hex(trc, msg)); + return; + } + + if (tal_len(ann) < offset) { + status_trace("Node announcement too short: %s", tal_hex(trc, msg)); + return; + } + + /* FIXME(cdecker) Check the node announcement's content */ + node_key(&node_pkey, NULL); + sha256_double(&hash, ann + offset, tal_len(ann) - offset); + + sign_hash(&node_pkey, &hash, &sig); + + reply = towire_hsmctl_node_announcement_sig_reply(msg, &sig); + daemon_conn_send(master, take(reply)); +} + static struct io_plan *control_received_req(struct io_conn *conn, struct daemon_conn *master) { @@ -533,6 +564,10 @@ static struct io_plan *control_received_req(struct io_conn *conn, sign_funding_tx(master, master->msg_in); return daemon_conn_read_next(conn, master); + case WIRE_HSMCTL_NODE_ANNOUNCEMENT_SIG_REQ: + sign_node_announcement(master, master->msg_in); + return daemon_conn_read_next(conn, master); + case WIRE_HSMCTL_INIT_REPLY: case WIRE_HSMCTL_HSMFD_ECDH_FD_REPLY: case WIRE_HSMCTL_HSMFD_CHANNELD_REPLY: @@ -543,6 +578,7 @@ static struct io_plan *control_received_req(struct io_conn *conn, case WIRE_HSMSTATUS_FD_FAILED: case WIRE_HSMSTATUS_KEY_FAILED: case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: + case WIRE_HSMCTL_NODE_ANNOUNCEMENT_SIG_REPLY: break; } diff --git a/lightningd/hsm/hsm_wire.csv b/lightningd/hsm/hsm_wire.csv index aefe3aeb6..4ce802f46 100644 --- a/lightningd/hsm/hsm_wire.csv +++ b/lightningd/hsm/hsm_wire.csv @@ -50,3 +50,11 @@ hsmctl_hsmfd_channeld,0,unique_id,8 # Empty reply, just an fd hsmctl_hsmfd_channeld_reply,105 + +# Master asks the HSM to sign a node_announcement +hsmctl_node_announcement_sig_req,6 +hsmctl_node_announcement_sig_req,0,annlen,2 +hsmctl_node_announcement_sig_req,2,announcement,annlen*u8 + +hsmctl_node_announcement_sig_reply,106 +hsmctl_node_announcement_sig_reply,0,signature,secp256k1_ecdsa_signature diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index 95b0d03a8..5d021383b 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -73,12 +73,14 @@ static int hsm_msg(struct subd *hsm, const u8 *msg, const int *fds) case WIRE_HSMCTL_HSMFD_ECDH: case WIRE_HSMCTL_HSMFD_CHANNELD: case WIRE_HSMCTL_SIGN_FUNDING: + case WIRE_HSMCTL_NODE_ANNOUNCEMENT_SIG_REQ: /* Replies should be paired to individual requests. */ case WIRE_HSMCTL_INIT_REPLY: case WIRE_HSMCTL_HSMFD_CHANNELD_REPLY: case WIRE_HSMCTL_HSMFD_ECDH_FD_REPLY: case WIRE_HSMCTL_SIGN_FUNDING_REPLY: + case WIRE_HSMCTL_NODE_ANNOUNCEMENT_SIG_REPLY: errx(1, "HSM gave invalid message %s", hsm_wire_type_name(t)); } return 0; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 01b90c0b5..cc9bb2edd 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1249,6 +1249,67 @@ static int peer_failed_malformed_htlc(struct peer *peer, const u8 *msg) return 0; } +/* Create a node_announcement with the given signature. It may be NULL + * in the case we need to create a provisional announcement for the + * HSM to sign. */ +static u8 *create_node_announcement(const tal_t *ctx, struct lightningd *ld, + secp256k1_ecdsa_signature *sig) +{ + u32 timestamp = time_now().ts.tv_sec; + u8 rgb[3] = {0x77, 0x88, 0x99}; + u8 alias[32]; + u8 *features = NULL; + u8 *addresses = NULL; + u8 *announcement; + if (!sig) { + sig = tal(ctx, secp256k1_ecdsa_signature); + memset(sig, 0, sizeof(*sig)); + } + memset(alias, 0, sizeof(alias)); + announcement = + towire_node_announcement(ctx, sig, timestamp, &ld->dstate.id, rgb, + alias, features, addresses); + return announcement; +} + +/* We got the signature for out provisional node_announcement back + * from the HSM, create the real announcement and forward it to + * gossipd so it can take care of forwarding it. */ +static bool send_node_announcement_got_sig(struct subd *hsm, const u8 *msg, + const int *fds, + struct lightningd *ld) +{ + tal_t *tmpctx = tal_tmpctx(hsm); + secp256k1_ecdsa_signature sig; + u8 *announcement, *wrappedmsg; + if (!fromwire_hsmctl_node_announcement_sig_reply(msg, NULL, &sig)) { + log_debug(ld->log, + "HSM returned an invalid node_announcement sig"); + return false; + } + announcement = create_node_announcement(tmpctx, ld, &sig); + wrappedmsg = towire_gossip_forwarded_msg(tmpctx, announcement); + subd_send_msg(ld->gossip, take(wrappedmsg)); + tal_free(tmpctx); + return true; +} + +/* We were informed by channeld that it announced the channel and sent + * an update, so we can now start sending a node_announcement. The + * first step is to build the provisional announcement and ask the HSM + * to sign it. */ +static void peer_channel_announced(struct peer *peer, const u8 *msg) +{ + struct lightningd *ld = peer->ld; + tal_t *tmpctx = tal_tmpctx(ld); + u8 *req; + req = towire_hsmctl_node_announcement_sig_req( + tmpctx, create_node_announcement(tmpctx, ld, NULL)); + subd_req(ld, ld->hsm, take(req), -1, 0, + send_node_announcement_got_sig, ld); + tal_free(tmpctx); +} + static int channel_msg(struct subd *sd, const u8 *msg, const int *unused) { enum channel_wire_type t = fromwire_peektype(msg); @@ -1268,6 +1329,9 @@ static int channel_msg(struct subd *sd, const u8 *msg, const int *unused) return peer_failed_htlc(sd->peer, msg); case WIRE_CHANNEL_MALFORMED_HTLC: return peer_failed_malformed_htlc(sd->peer, msg); + case WIRE_CHANNEL_ANNOUNCED: + peer_channel_announced(sd->peer, msg); + break; /* We never see fatal ones. */ case WIRE_CHANNEL_BAD_COMMAND: