diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 09d851194..adc594920 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -121,6 +121,25 @@ struct daemon { struct short_channel_id *unknown_scids; }; +/*~ How gossipy do we ask a peer to be? */ +enum gossip_level { + /* Give us everything since epoch */ + GOSSIP_HIGH, + /* Give us everything from 24 hours ago. */ + GOSSIP_MEDIUM, + /* Give us everything from now. */ + GOSSIP_LOW, + /* Give us nothing. */ + GOSSIP_NONE, +}; + +/* What are our targets for each gossip level? (including levels above). + * + * If we're missing gossip: 3 high. + * Otherwise, 2 medium, and 8 low. Rest no limit.. + */ +static const size_t gossip_level_targets[] = { 3, 2, 8, SIZE_MAX }; + /* This represents each peer we're gossiping with */ struct peer { /* daemon->peers */ @@ -157,6 +176,9 @@ struct peer { u32 range_blocks_remaining; struct short_channel_id *query_channel_scids; + /* Are we asking this peer to give us lot of gossip? */ + enum gossip_level gossip_level; + /* The daemon_conn used to queue messages to/from the peer. */ struct daemon_conn *dc; }; @@ -312,6 +334,22 @@ check_length: return tal_count(*encoded) <= max_bytes; } +/*~ We have different levels of gossipiness, depending on our needs. */ +static u32 gossip_start(enum gossip_level gossip_level) +{ + switch (gossip_level) { + case GOSSIP_HIGH: + return 0; + case GOSSIP_MEDIUM: + return time_now().ts.tv_sec - 24 * 3600; + case GOSSIP_LOW: + return time_now().ts.tv_sec; + case GOSSIP_NONE: + return UINT32_MAX; + } + abort(); +} + /* BOLT #7: * * A node: @@ -323,16 +361,23 @@ static void setup_gossip_range(struct peer *peer) u8 *msg; /*~ Without the `gossip_queries` feature, gossip flows automatically. */ - if (!peer->gossip_queries_feature) + if (!peer->gossip_queries_feature) { + /* This peer is gossipy whether we want it or not! */ return; + } - /*~ We need to ask for something to start the gossip flowing: we ask - * for everything from 1970 to 2106; this is horribly naive. We - * should be much smarter about requesting only what we don't already - * have. */ + status_trace("Setting peer %s to gossip level %s", + type_to_string(tmpctx, struct node_id, &peer->id), + peer->gossip_level == GOSSIP_HIGH ? "HIGH" + : peer->gossip_level == GOSSIP_MEDIUM ? "MEDIUM" + : peer->gossip_level == GOSSIP_LOW ? "LOW" + : peer->gossip_level == GOSSIP_NONE ? "NONE" + : "INVALID"); + /*~ We need to ask for something to start the gossip flowing. */ msg = towire_gossip_timestamp_filter(peer, &peer->daemon->chain_hash, - 0, UINT32_MAX); + gossip_start(peer->gossip_level), + UINT32_MAX); queue_peer_msg(peer, take(msg)); } @@ -1669,6 +1714,35 @@ done: return daemon_conn_read_next(conn, peer->dc); } +/* What gossip level do we set for this to meet our target? */ +static enum gossip_level peer_gossip_level(const struct daemon *daemon, + bool gossip_queries_feature) +{ + struct peer *peer; + size_t gossip_levels[ARRAY_SIZE(gossip_level_targets)]; + enum gossip_level glevel; + + /* Old peers always give us a flood. */ + if (!gossip_queries_feature) + return GOSSIP_HIGH; + + /* Figure out how many we have at each level. */ + memset(gossip_levels, 0, sizeof(gossip_levels)); + list_for_each(&daemon->peers, peer, list) + gossip_levels[peer->gossip_level]++; + + /* If we're missing gossip, try to fill GOSSIP_HIGH */ + if (daemon->gossip_missing != NULL) + glevel = GOSSIP_HIGH; + else + glevel = GOSSIP_MEDIUM; + + while (gossip_levels[glevel] >= gossip_level_targets[glevel]) + glevel++; + + return glevel; +} + /*~ This is where connectd tells us about a new peer, and we hand back an fd for * it to send us messages via peer_msg_in above */ static struct io_plan *connectd_new_peer(struct io_conn *conn, @@ -1723,6 +1797,8 @@ static struct io_plan *connectd_new_peer(struct io_conn *conn, peer->scid_query_outstanding = false; peer->query_channel_blocks = NULL; peer->num_pings_outstanding = 0; + peer->gossip_level = peer_gossip_level(daemon, + peer->gossip_queries_feature); /* We keep a list so we can find peer by id */ list_add_tail(&peer->daemon->peers, &peer->list);