From d93ce12afa56e80df4265d7c9d9554ffa0195504 Mon Sep 17 00:00:00 2001 From: Christian Decker <decker.christian@gmail.com> Date: Wed, 21 Dec 2016 17:15:35 +0100 Subject: [PATCH] gossip: Broadcasting and handling gossip messages --- daemon/Makefile | 2 + daemon/lightningd.c | 4 + daemon/p2p_announce.c | 322 ++++++++++++++++++++++++++++++++++++++++++ daemon/p2p_announce.h | 17 +++ daemon/peer.c | 1 + 5 files changed, 346 insertions(+) create mode 100644 daemon/p2p_announce.c create mode 100644 daemon/p2p_announce.h diff --git a/daemon/Makefile b/daemon/Makefile index 06cced233..fa7aca974 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -31,6 +31,7 @@ DAEMON_SRC := \ daemon/netaddr.c \ daemon/opt_time.c \ daemon/output_to_htlc.c \ + daemon/p2p_announce.c \ daemon/packets.c \ daemon/pay.c \ daemon/peer.c \ @@ -81,6 +82,7 @@ DAEMON_HEADERS := \ daemon/netaddr.h \ daemon/opt_time.h \ daemon/output_to_htlc.h \ + daemon/p2p_announce.h \ daemon/packets.h \ daemon/pay.h \ daemon/peer.h \ diff --git a/daemon/lightningd.c b/daemon/lightningd.c index f1aab72a2..dd73facfa 100644 --- a/daemon/lightningd.c +++ b/daemon/lightningd.c @@ -7,6 +7,7 @@ #include "lightningd.h" #include "log.h" #include "opt_time.h" +#include "p2p_announce.h" #include "peer.h" #include "routing.h" #include "secrets.h" @@ -552,6 +553,9 @@ int main(int argc, char *argv[]) if (dstate->config.use_irc) setup_irc_connection(dstate); + /* set up P2P gossip protocol */ + setup_p2p_announce(dstate); + log_info(dstate->base_log, "Hello world!"); /* If we loaded peers from database, reconnect now. */ diff --git a/daemon/p2p_announce.c b/daemon/p2p_announce.c new file mode 100644 index 000000000..1fcc85744 --- /dev/null +++ b/daemon/p2p_announce.c @@ -0,0 +1,322 @@ +#include "daemon/chaintopology.h" +#include "daemon/log.h" +#include "daemon/p2p_announce.h" +#include "daemon/packets.h" +#include "daemon/peer.h" +#include "daemon/routing.h" +#include "daemon/secrets.h" +#include "daemon/timeout.h" + +#include <arpa/inet.h> +#include <ccan/tal/str/str.h> +#include <ccan/tal/tal.h> +#include <secp256k1.h> + + +u8 ipv4prefix[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF +}; + +/* Read an IP from `srcip` and convert it into the dotted + * notation. Handles both IPv4 and IPv6 addresses and converts + * accordingly. We differentiate the two by using the RFC 4291 + * IPv4-mapped IPv6 format */ +static char* read_ip(const tal_t *ctx, const struct ipv6 *srcip) +{ + char tempaddr[INET6_ADDRSTRLEN]; + + if (memcmp(srcip, ipv4prefix, sizeof(ipv4prefix)) == 0) { + inet_ntop(AF_INET, srcip + 12, tempaddr, sizeof(tempaddr)); + }else{ + inet_ntop(AF_INET6, srcip, tempaddr, sizeof(tempaddr)); + } + return tal_strdup(ctx, tempaddr); +} + +/* Serialize the IP address in `srcip` into a 16 byte + * representation. It handles both IPv6 and IPv4 addresses, prefixing + * IPv4 addresses with the prefix described in RFC 4291. */ +static void write_ip(struct ipv6 *dstip, char *srcip) +{ + if (!strchr(srcip, ':')) { + memcpy(dstip, ipv4prefix, sizeof(ipv4prefix)); + inet_pton(AF_INET, srcip, dstip); + } else { + inet_pton(AF_INET6, srcip, dstip); + } +} + +static void broadcast(struct lightningd_state *dstate, + int type, u8 *pkt, + struct peer *origin) +{ + struct peer *p; + list_for_each(&dstate->peers, p, list) { + if (state_is_normal(p->state) && origin != p) + queue_pkt_nested(p, type, pkt); + } +} + +static bool add_channel_direction(struct lightningd_state *dstate, + const struct pubkey *from, + const struct pubkey *to, + const int direction, + const struct channel_id *channel_id + ) +{ + struct node_connection *c = get_connection(dstate, from, to); + if (c){ + /* Do not clobber connections added otherwise */ + memcpy(&c->channel_id, channel_id, sizeof(c->channel_id)); + c->flags = direction; + printf("Found node_connection via get_connection"); + return false; + }else if(get_connection_by_cid(dstate, channel_id, direction)) { + return false; + } + half_add_connection(dstate, + from, + to, + channel_id, direction); + return true; +} + +void handle_channel_announcement( + struct peer *peer, + const struct msg_channel_announcement *msg) +{ + u8 *serialized; + bool forward = false; + if (!msg) + return; + + //FIXME(cdecker) Check signatures, when the spec is settled + //FIXME(cdecker) Check chain topology for the anchor TX + + log_debug(peer->log, "Received channel_announcement for channel %d:%d:%d", + msg->channel_id.blocknum, + msg->channel_id.txnum, + msg->channel_id.outnum + ); + forward |= add_channel_direction(peer->dstate, &msg->node_id_1, + &msg->node_id_2, 0, &msg->channel_id); + forward |= add_channel_direction(peer->dstate, &msg->node_id_2, + &msg->node_id_1, 1, &msg->channel_id); + if (!forward){ + log_debug(peer->log, "Not forwarding channel_announcement"); + return; + } + + serialized = towire_channel_announcement(msg, msg); + broadcast(peer->dstate, WIRE_CHANNEL_ANNOUNCEMENT, serialized, peer); + tal_free(msg); +} + +void handle_channel_update(struct peer *peer, const struct msg_channel_update *msg) +{ + if (!msg) + return; + + u8 *serialized; + struct node_connection *c; + + log_debug(peer->log, "Received channel_update for channel %d:%d:%d(%d)", + msg->channel_id.blocknum, + msg->channel_id.txnum, + msg->channel_id.outnum, + msg->flags & 0x01 + ); + + c = get_connection_by_cid(peer->dstate, &msg->channel_id, msg->flags & 0x1); + + if (!c) { + log_debug(peer->log, "Ignoring update for unknown channel %d:%d:%d", + msg->channel_id.blocknum, + msg->channel_id.txnum, + msg->channel_id.outnum + ); + return; + } else if (c->last_timestamp >= msg->timestamp) { + log_debug(peer->log, "Ignoring outdated update."); + return; + } + + //FIXME(cdecker) Check signatures + serialized = towire_channel_update(msg, msg); + + c->last_timestamp = msg->timestamp; + c->delay = msg->expiry; + c->htlc_minimum_msat = msg->htlc_minimum_msat; + c->base_fee = msg->fee_base_msat; + c->proportional_fee = msg->fee_proportional_millionths; + c->active = true; + log_debug(peer->log, "Channel %d:%d:%d(%d) was updated.", + msg->channel_id.blocknum, + msg->channel_id.txnum, + msg->channel_id.outnum, + msg->flags + ); + + broadcast(peer->dstate, WIRE_CHANNEL_UPDATE, serialized, peer); + tal_free(msg); +} + +void handle_node_announcement( + struct peer *peer, const struct msg_node_announcement *msg) +{ + u8 *serialized; + struct sha256_double hash; + struct node *node; + + if (!msg) + return; + + log_debug_struct(peer->log, + "Received node_announcement for node %s", + struct pubkey, &msg->node_id); + + serialized = towire_node_announcement(msg, msg); + sha256_double(&hash, serialized + 64, tal_count(serialized) - 64); + if (!check_signed_hash(&hash, &msg->signature, &msg->node_id)) { + log_debug(peer->dstate->base_log, + "Ignoring node announcement, signature verification failed."); + return; + } + node = get_node(peer->dstate, &msg->node_id); + + if (!node) { + log_debug(peer->dstate->base_log, + "Node not found, was the node_announcement preceeded by at least channel_announcement?"); + return; + } else if (node->last_timestamp >= msg->timestamp) { + log_debug(peer->dstate->base_log, + "Ignoring node announcement, it's outdated."); + return; + } + + node->last_timestamp = msg->timestamp; + if (node->hostname) + node->hostname = tal_free(node->hostname); + node->hostname = read_ip(node, &msg->ipv6); + node->port = msg->port; + memcpy(node->rgb_color, msg->rgb_color, 3); + + broadcast(peer->dstate, WIRE_NODE_ANNOUNCEMENT, serialized, peer); + tal_free(msg); +} + +static void broadcast_channel_update(struct lightningd_state *dstate, struct peer *peer) +{ + struct msg_channel_update *msg; + struct txlocator *loc; + u8 *serialized; + + msg = tal(peer, struct msg_channel_update); + loc = locate_tx(msg, dstate, &peer->anchor.txid); + + msg->timestamp = timeabs_to_timeval(time_now()).tv_sec; + msg->channel_id.blocknum = loc->blkheight; + msg->channel_id.txnum = loc->index; + msg->channel_id.outnum = peer->anchor.index; + msg->flags = pubkey_cmp(&dstate->id, peer->id) > 0; + msg->expiry = dstate->config.min_htlc_expiry; + msg->htlc_minimum_msat = 1; + msg->fee_base_msat = dstate->config.fee_base; + msg->fee_proportional_millionths = dstate->config.fee_per_satoshi; + + /* Avoid triggering memcheck */ + memset(&msg->signature, 0, sizeof(msg->signature)); + serialized = towire_channel_update(msg, msg); + privkey_sign(dstate, serialized + 64, tal_count(serialized) - 64, &msg->signature); + serialized = towire_channel_update(msg, msg); + + broadcast(dstate, WIRE_CHANNEL_UPDATE, serialized, NULL); + tal_free(msg); +} + +static void broadcast_node_announcement(struct lightningd_state *dstate) +{ + u8 *serialized; + + /* Are we listeing for incoming connections at all? */ + if (!dstate->external_ip || !dstate->portnum) + return; + + struct msg_node_announcement *msg = tal(dstate, struct msg_node_announcement); + msg->timestamp = timeabs_to_timeval(time_now()).tv_sec; + msg->node_id = dstate->id; + write_ip(&msg->ipv6, dstate->external_ip); + msg->port = dstate->portnum; + memset(&msg->rgb_color, 0x00, 3); + + serialized = towire_node_announcement(msg, msg); + privkey_sign(dstate, serialized + 64, tal_count(serialized) - 64, &msg->signature); + serialized = towire_node_announcement(msg, msg); + broadcast(dstate, WIRE_NODE_ANNOUNCEMENT, serialized, NULL); + tal_free(msg); + +} + +static void broadcast_channel_announcement(struct lightningd_state *dstate, struct peer *peer) +{ + struct msg_channel_announcement *msg = tal(peer, struct msg_channel_announcement); + struct txlocator *loc; + u8 *ser; + + loc = locate_tx(msg, dstate, &peer->anchor.txid); + + msg->channel_id.blocknum = loc->blkheight; + msg->channel_id.txnum = loc->index; + msg->channel_id.outnum = peer->anchor.index; + + if (pubkey_cmp(&dstate->id, peer->id) > 0) { + msg->node_id_1 = *peer->id; + msg->node_id_2 = dstate->id; + msg->bitcoin_key_1 = *peer->id; + msg->bitcoin_key_2 = dstate->id; + } else { + msg->node_id_2 = *peer->id; + msg->node_id_1 = dstate->id; + msg->bitcoin_key_2 = *peer->id; + msg->bitcoin_key_1 = dstate->id; + } + + //FIXME(cdecker) actually sign this packet, currently not pinned down in spec + ser = towire_channel_announcement(msg, msg); + broadcast(dstate, WIRE_CHANNEL_ANNOUNCEMENT, ser, NULL); + tal_free(msg); +} + +static void announce(struct lightningd_state *dstate) +{ + struct peer *p; + int nchan = 0; + + new_reltimer(dstate, dstate, time_from_sec(6), announce, dstate); + + list_for_each(&dstate->peers, p, list) { + if (state_is_normal(p->state)) { + broadcast_channel_announcement(dstate, p); + broadcast_channel_update(dstate, p); + nchan += 1; + } + } + + /* No point in broadcasting our node if we don't have a channel */ + if (nchan > 0) + broadcast_node_announcement(dstate); +} + +void announce_channel(struct lightningd_state *dstate, struct peer *peer) +{ + broadcast_channel_announcement(dstate, peer); + broadcast_channel_update(dstate, peer); + broadcast_node_announcement(dstate); +} + +void setup_p2p_announce(struct lightningd_state *dstate) +{ + new_reltimer(dstate, dstate, time_from_sec(30), announce, dstate); +} diff --git a/daemon/p2p_announce.h b/daemon/p2p_announce.h new file mode 100644 index 000000000..bc8848828 --- /dev/null +++ b/daemon/p2p_announce.h @@ -0,0 +1,17 @@ +#ifndef LIGHTNING_DAEMON_P2P_ANNOUNCE_H +#define LIGHTNING_DAEMON_P2P_ANNOUNCE_H +#include "config.h" +#include "lightningd.h" +#include "wire/gen_wire.h" + +void setup_p2p_announce(struct lightningd_state *dstate); + +/* Handlers for incoming messages */ +void handle_channel_announcement(struct peer *peer, const struct msg_channel_announcement *announce); +void handle_channel_update(struct peer *peer, const struct msg_channel_update *update); +void handle_node_announcement(struct peer *peer, const struct msg_node_announcement *node); + +/* Used to announce the existence of a channel and the endpoints */ +void announce_channel(struct lightningd_state *dstate, struct peer *peer); + +#endif /* LIGHTNING_DAEMON_P2P_ANNOUNCE_H */ diff --git a/daemon/peer.c b/daemon/peer.c index 063abd984..8d04241a1 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -13,6 +13,7 @@ #include "names.h" #include "netaddr.h" #include "output_to_htlc.h" +#include "p2p_announce.h" #include "packets.h" #include "pay.h" #include "peer.h"