From db30411ba5760ea8e1ed605cb7d48ac97697ef9d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 4 Jan 2018 22:10:58 +1030 Subject: [PATCH] gossipd: look up short_channel_id when we see a channel_announcement. This is done it two parts, since we have to ask the main daemon to do the lookup for us. If this becomes a bottleneck, we can have a separate daemon, or even an RPC pipe to bitcoind ourselves. Fixes: #403 Signed-off-by: Rusty Russell --- gossipd/gossip.c | 34 +++++++-- gossipd/routing.c | 183 +++++++++++++++++++++++++++++++++------------- gossipd/routing.h | 25 +++++-- 3 files changed, 177 insertions(+), 65 deletions(-) diff --git a/gossipd/gossip.c b/gossipd/gossip.c index df033977d..255af2620 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -452,15 +452,18 @@ static void handle_gossip_msg(struct daemon *daemon, u8 *msg) { struct routing_state *rstate = daemon->rstate; int t = fromwire_peektype(msg); + switch(t) { - case WIRE_CHANNEL_ANNOUNCEMENT: - /* Add the channel_announcement to the routing state, - * it'll tell us whether this is local and signed, so - * we can hand in a node_announcement as well. */ - if(handle_channel_announcement(rstate, msg)) { - send_node_announcement(daemon); - } + case WIRE_CHANNEL_ANNOUNCEMENT: { + const struct short_channel_id *scid; + /* If it's OK, tells us the short_channel_id to lookup */ + scid = handle_channel_announcement(rstate, msg); + if (scid) + daemon_conn_send(&daemon->master, + take(towire_gossip_get_txout(daemon, + scid))); break; + } case WIRE_NODE_ANNOUNCEMENT: handle_node_announcement(rstate, msg); @@ -1559,6 +1562,21 @@ static struct io_plan *get_peers(struct io_conn *conn, return daemon_conn_read_next(conn, &daemon->master); } +static struct io_plan *handle_txout_reply(struct io_conn *conn, + struct daemon *daemon, const u8 *msg) +{ + struct short_channel_id scid; + u8 *outscript; + + if (!fromwire_gossip_get_txout_reply(msg, msg, NULL, &scid, &outscript)) + master_badmsg(WIRE_GOSSIP_GET_TXOUT_REPLY, msg); + + if (handle_pending_cannouncement(daemon->rstate, &scid, outscript)) + send_node_announcement(daemon); + + 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); @@ -1602,7 +1620,7 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master return get_peers(conn, daemon, master->msg_in); case WIRE_GOSSIP_GET_TXOUT_REPLY: - /* FIXME */ + return handle_txout_reply(conn, daemon, master->msg_in); /* We send these, we don't receive them */ case WIRE_GOSSIPCTL_RELEASE_PEER_REPLY: diff --git a/gossipd/routing.c b/gossipd/routing.c index 09d18fc83..d33a6750f 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1,6 +1,7 @@ #include "routing.h" #include #include +#include #include #include #include @@ -26,6 +27,22 @@ /* Proportional fee must be less than 24 bits, so never overflows. */ #define MAX_PROPORTIONAL_FEE (1 << 24) +/* We've unpacked and checked its signatures, now we wait for master to tell + * us the txout to check */ +struct pending_cannouncement { + struct list_node list; + + /* Unpacked fields here */ + struct short_channel_id short_channel_id; + struct pubkey node_id_1; + struct pubkey node_id_2; + struct pubkey bitcoin_key_1; + struct pubkey bitcoin_key_2; + + /* The raw bits */ + const u8 *announce; +}; + static struct node_map *empty_node_map(const tal_t *ctx) { struct node_map *map = tal(ctx, struct node_map); @@ -43,6 +60,7 @@ struct routing_state *new_routing_state(const tal_t *ctx, rstate->broadcasts = new_broadcast_state(rstate); rstate->chain_hash = *chain_hash; rstate->local_id = *local_id; + list_head_init(&rstate->pending_cannouncement); return rstate; } @@ -496,44 +514,39 @@ static bool check_channel_announcement( check_signed_hash(&hash, bitcoin2_sig, bitcoin2_key); } -bool handle_channel_announcement( +const struct short_channel_id *handle_channel_announcement( struct routing_state *rstate, - const u8 *announce) + const u8 *announce TAKES) { - u8 *serialized; - bool forward = false, local, sigfail; - secp256k1_ecdsa_signature node_signature_1; - secp256k1_ecdsa_signature node_signature_2; - struct short_channel_id short_channel_id; - secp256k1_ecdsa_signature bitcoin_signature_1; - secp256k1_ecdsa_signature bitcoin_signature_2; - struct pubkey node_id_1; - struct pubkey node_id_2; - struct pubkey bitcoin_key_1; - struct pubkey bitcoin_key_2; + struct pending_cannouncement *pending; struct bitcoin_blkid chain_hash; - struct node_connection *c0, *c1; - const tal_t *tmpctx = tal_tmpctx(rstate); u8 *features; - char *tag; - size_t len = tal_len(announce); + const char *tag; + secp256k1_ecdsa_signature node_signature_1, node_signature_2; + secp256k1_ecdsa_signature bitcoin_signature_1, bitcoin_signature_2; - serialized = tal_dup_arr(tmpctx, u8, announce, len, 0); - if (!fromwire_channel_announcement(tmpctx, serialized, NULL, - &node_signature_1, &node_signature_2, + pending = tal(rstate, struct pending_cannouncement); + pending->announce = tal_dup_arr(pending, u8, + announce, tal_len(announce), 0); + + if (!fromwire_channel_announcement(pending, pending->announce, NULL, + &node_signature_1, + &node_signature_2, &bitcoin_signature_1, &bitcoin_signature_2, &features, &chain_hash, - &short_channel_id, - &node_id_1, &node_id_2, - &bitcoin_key_1, &bitcoin_key_2)) { - tal_free(tmpctx); - return false; + &pending->short_channel_id, + &pending->node_id_1, + &pending->node_id_2, + &pending->bitcoin_key_1, + &pending->bitcoin_key_2)) { + tal_free(pending); + return NULL; } - tag = type_to_string(tmpctx, struct short_channel_id, - &short_channel_id); + tag = type_to_string(pending, struct short_channel_id, + &pending->short_channel_id); /* BOLT #7: * @@ -544,54 +557,120 @@ bool handle_channel_announcement( status_trace( "Received channel_announcement %s for unknown chain %s", tag, - type_to_string(tmpctx, struct bitcoin_blkid, &chain_hash)); - tal_free(tmpctx); - return false; + type_to_string(pending, struct bitcoin_blkid, &chain_hash)); + tal_free(pending); + return NULL; } // FIXME: Check features! - //FIXME(cdecker) Check chain topology for the anchor TX - local = pubkey_eq(&node_id_1, &rstate->local_id) || - pubkey_eq(&node_id_2, &rstate->local_id); + if (!check_channel_announcement(&pending->node_id_1, &pending->node_id_2, + &pending->bitcoin_key_1, + &pending->bitcoin_key_2, + &node_signature_1, + &node_signature_2, + &bitcoin_signature_1, + &bitcoin_signature_2, + pending->announce)) { + status_trace("Signature verification of channel_announcement" + " for %s failed", tag); + tal_free(pending); + return NULL; + } + + status_trace("Received channel_announcement for channel %s", tag); + tal_free(tag); - sigfail = !check_channel_announcement( - &node_id_1, &node_id_2, &bitcoin_key_1, &bitcoin_key_2, - &node_signature_1, &node_signature_2, &bitcoin_signature_1, - &bitcoin_signature_2, serialized); + list_add_tail(&rstate->pending_cannouncement, &pending->list); + return &pending->short_channel_id; +} - status_trace("Received channel_announcement for channel %s, local=%d, sigfail=%d", - tag, local, sigfail); +/* While master always processes in order, bitcoind is async, so they could + * theoretically return out of order. */ +static struct pending_cannouncement * +find_pending_cannouncement(struct routing_state *rstate, + const struct short_channel_id *scid) +{ + struct pending_cannouncement *i; - if (sigfail) { - status_trace( - "Signature verification of channel_announcement for %s failed", tag); - tal_free(tmpctx); + list_for_each(&rstate->pending_cannouncement, i, list) { + if (short_channel_id_eq(scid, &i->short_channel_id)) + return i; + } + return NULL; +} + +bool handle_pending_cannouncement(struct routing_state *rstate, + const struct short_channel_id *scid, + const u8 *outscript) +{ + bool forward, local; + struct node_connection *c0, *c1; + const char *tag; + const u8 *s; + struct pending_cannouncement *pending; + + pending = find_pending_cannouncement(rstate, scid); + assert(pending); + list_del_from(&rstate->pending_cannouncement, &pending->list); + + tag = type_to_string(pending, struct short_channel_id, scid); + + /* BOLT #7: + * + * The receiving node MUST ignore the message if this output is spent. + */ + if (tal_len(outscript) == 0) { + status_trace("channel_announcement: no unspent txout %s", tag); + tal_free(pending); + return false; + } + + /* BOLT #7: + * + * The receiving node MUST ignore the message if the output + * specified by `short_channel_id` does not correspond to a + * P2WSH using `bitcoin_key_1` and `bitcoin_key_2` as + * specified in [BOLT + * #3](03-transactions.md#funding-transaction-output). + */ + s = scriptpubkey_p2wsh(pending, + bitcoin_redeem_2of2(pending, + &pending->bitcoin_key_1, + &pending->bitcoin_key_2)); + + if (!scripteq(s, outscript)) { + status_trace("channel_announcement: txout %s expectes %s, got %s", + tag, tal_hex(trc, s), tal_hex(trc, outscript)); + tal_free(pending); return false; } /* Is this a new connection? It is if we don't know the * channel yet, or do not have a matching announcement in the * case of side-loaded channels*/ - c0 = get_connection(rstate, &node_id_2, &node_id_1); - c1 = get_connection(rstate, &node_id_1, &node_id_2); + c0 = get_connection(rstate, &pending->node_id_2, &pending->node_id_1); + c1 = get_connection(rstate, &pending->node_id_1, &pending->node_id_2); forward = !c0 || !c1 || !c0->channel_announcement || !c1->channel_announcement; - add_channel_direction(rstate, &node_id_1, &node_id_2, &short_channel_id, - serialized); - add_channel_direction(rstate, &node_id_2, &node_id_1, &short_channel_id, - serialized); + add_channel_direction(rstate, &pending->node_id_1, &pending->node_id_2, + &pending->short_channel_id, pending->announce); + add_channel_direction(rstate, &pending->node_id_2, &pending->node_id_1, + &pending->short_channel_id, pending->announce); if (forward) { if (queue_broadcast(rstate->broadcasts, WIRE_CHANNEL_ANNOUNCEMENT, - (u8*)tag, serialized)) + (u8*)tag, pending->announce)) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Announcement %s was replaced?", - tal_hex(trc, serialized)); + tal_hex(trc, pending->announce)); } - tal_free(tmpctx); + local = pubkey_eq(&pending->node_id_1, &rstate->local_id) || + pubkey_eq(&pending->node_id_2, &rstate->local_id); + + tal_free(pending); return local && forward; } diff --git a/gossipd/routing.h b/gossipd/routing.h index 88fb23aee..c3c794961 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -80,6 +80,9 @@ struct routing_state { /* All known nodes. */ struct node_map *nodes; + /* channel_announcement which are pending short_channel_id lookup */ + struct list_head pending_cannouncement; + struct broadcast_state *broadcasts; struct bitcoin_blkid chain_hash; @@ -117,13 +120,25 @@ struct node_connection *get_connection_by_scid(const struct routing_state *rstat /* Handlers for incoming messages */ /** - * handle_channel_announcement -- Add channel announcement to state + * handle_channel_announcement -- Check channel announcement is valid + * + * Returns a short_channel_id to look up if signatures pass. + */ +const struct short_channel_id * +handle_channel_announcement(struct routing_state *rstate, + const u8 *announce TAKES); + +/** + * handle_pending_cannouncement -- handle channel_announce once we've + * completed short_channel_id lookup. * - * Returns true if the channel was fully signed and is local. This - * means that if we haven't sent a node_announcement just yet, now - * would be a good time. + * Returns true if the channel was new and is local. This means that + * if we haven't sent a node_announcement just yet, now would be a + * good time. */ -bool handle_channel_announcement(struct routing_state *rstate, const u8 *announce); +bool handle_pending_cannouncement(struct routing_state *rstate, + const struct short_channel_id *scid, + const u8 *txscript); void handle_channel_update(struct routing_state *rstate, const u8 *update); void handle_node_announcement(struct routing_state *rstate, const u8 *node);