diff --git a/gossipd/Makefile b/gossipd/Makefile index 6875f1baa..fa68afb0e 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -19,7 +19,8 @@ LIGHTNINGD_GOSSIP_HEADERS_WSRC := gossipd/gen_gossip_wire.h \ gossipd/gossip_store.h \ gossipd/queries.h \ gossipd/gossip_generation.h \ - gossipd/routing.h + gossipd/routing.h \ + gossipd/seeker.h LIGHTNINGD_GOSSIP_HEADERS := $(LIGHTNINGD_GOSSIP_HEADERS_WSRC) gossipd/broadcast.h LIGHTNINGD_GOSSIP_SRC := $(LIGHTNINGD_GOSSIP_HEADERS_WSRC:.h=.c) LIGHTNINGD_GOSSIP_OBJS := $(LIGHTNINGD_GOSSIP_SRC:.c=.o) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index e6ffa7835..14f6cb61d 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -178,7 +179,7 @@ static u32 gossip_start(const struct routing_state *rstate, * - if the `gossip_queries` feature is negotiated: * - MUST NOT relay any gossip messages unless explicitly requested. */ -static void setup_gossip_range(struct peer *peer) +void setup_gossip_range(struct peer *peer) { u8 *msg; @@ -557,8 +558,8 @@ done: } /* 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) +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)]; @@ -580,7 +581,7 @@ static enum gossip_level peer_gossip_level(const struct daemon *daemon, gossip_levels[peer->gossip_level]++; /* If we're missing gossip, try to fill GOSSIP_HIGH */ - if (daemon->gossip_missing != NULL) + if (seeker_gossip(daemon->seeker)) glevel = GOSSIP_HIGH; else glevel = GOSSIP_MEDIUM; @@ -854,8 +855,8 @@ static void gossip_disable_local_channels(struct daemon *daemon) local_disable_chan(daemon->rstate, c); } -static struct peer *random_peer(struct daemon *daemon, - bool (*check_peer)(const struct peer *peer)) +struct peer *random_peer(struct daemon *daemon, + bool (*check_peer)(const struct peer *peer)) { u64 target = UINT64_MAX; struct peer *best = NULL, *i; @@ -876,70 +877,6 @@ static struct peer *random_peer(struct daemon *daemon, return best; } -/* Mutual recursion, so we pre-declare this. */ -static void gossip_not_missing(struct daemon *daemon); - -static bool peer_is_not_gossip_high(const struct peer *peer) -{ - return peer->gossip_level != GOSSIP_HIGH; -} - -/*~ We've found gossip is missing. */ -static void gossip_missing(struct daemon *daemon) -{ - if (!daemon->gossip_missing) { - status_info("We seem to be missing gossip messages"); - /* FIXME: we could use query_channel_range. */ - /* Make some peers gossip harder. */ - for (size_t i = 0; i < gossip_level_targets[GOSSIP_HIGH]; i++) { - struct peer *peer = random_peer(daemon, - peer_is_not_gossip_high); - - if (!peer) - break; - - status_info("%s: gossip harder!", - type_to_string(tmpctx, struct node_id, - &peer->id)); - peer->gossip_level = GOSSIP_HIGH; - setup_gossip_range(peer); - } - } - - tal_free(daemon->gossip_missing); - /* Check again in 10 minutes. */ - daemon->gossip_missing = new_reltimer(&daemon->timers, daemon, - time_from_sec(600), - gossip_not_missing, daemon); -} - -/*~ This is a timer, which goes off 10 minutes after the last time we noticed - * that gossip was missing. */ -static void gossip_not_missing(struct daemon *daemon) -{ - /* Corner case: no peers, try again! */ - if (list_empty(&daemon->peers)) - gossip_missing(daemon); - else { - struct peer *peer; - - daemon->gossip_missing = tal_free(daemon->gossip_missing); - status_info("We seem to be caught up on gossip messages"); - /* Free any lagging/stale unknown scids. */ - daemon->unknown_scids = tal_free(daemon->unknown_scids); - - /* Reset peers we marked as HIGH */ - list_for_each(&daemon->peers, peer, list) { - if (peer->gossip_level != GOSSIP_HIGH) - continue; - if (!peer->gossip_queries_feature) - continue; - peer->gossip_level = peer_gossip_level(daemon, true); - setup_gossip_range(peer); - } - } -} - /*~ Parse init message from lightningd: starts the daemon properly. */ static struct io_plan *gossip_init(struct io_conn *conn, struct daemon *daemon, @@ -976,7 +913,7 @@ static struct io_plan *gossip_init(struct io_conn *conn, timestamp = gossip_store_load(daemon->rstate, daemon->rstate->gs); /* If gossip_store less than 24 hours old, say we're OK. */ if (timestamp < gossip_time_now(daemon->rstate).ts.tv_sec - 24*3600) - gossip_missing(daemon); + gossip_missing(daemon, daemon->seeker); /* Now disable all local channels, they can't be connected yet. */ gossip_disable_local_channels(daemon); @@ -1523,21 +1460,14 @@ static struct io_plan *handle_txout_reply(struct io_conn *conn, master_badmsg(WIRE_GOSSIP_GET_TXOUT_REPLY, msg); /* Were we looking specifically for this? */ - was_unknown = false; - for (size_t i = 0; i < tal_count(daemon->unknown_scids); i++) { - if (short_channel_id_eq(&daemon->unknown_scids[i], &scid)) { - was_unknown = true; - tal_arr_remove(&daemon->unknown_scids, i); - break; - } - } + was_unknown = remove_unknown_scid(daemon->seeker, &scid); /* Outscript is NULL if it's not an unspent output */ if (handle_pending_cannouncement(daemon, daemon->rstate, &scid, sat, outscript) && was_unknown) { /* It was real: we're missing gossip. */ - gossip_missing(daemon); + gossip_missing(daemon, daemon->seeker); } /* Anywhere we might have announced a channel, we check if it's time to @@ -1794,9 +1724,8 @@ int main(int argc, char *argv[]) daemon = tal(NULL, struct daemon); list_head_init(&daemon->peers); - daemon->unknown_scids = tal_arr(daemon, struct short_channel_id, 0); + daemon->seeker = new_seeker(daemon); daemon->deferred_txouts = tal_arr(daemon, struct short_channel_id, 0); - daemon->gossip_missing = NULL; daemon->node_announce_timer = NULL; daemon->current_blockheight = 0; /* i.e. unknown */ diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index 92ab5af6d..393276b93 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -16,6 +16,7 @@ struct chan; struct broadcastable; +struct seeker; /*~ The core daemon structure: */ struct daemon { @@ -53,17 +54,14 @@ struct daemon { /* What addresses we can actually announce. */ struct wireaddr *announcable; - /* Do we think we're missing gossip? Contains timer to re-check */ - struct oneshot *gossip_missing; - - /* Channels we've heard about, but don't know. */ - struct short_channel_id *unknown_scids; - /* Timer until we can send a new node_announcement */ struct oneshot *node_announce_timer; /* Channels we have an announce for, but aren't deep enough. */ struct short_channel_id *deferred_txouts; + + /* What, if any, gossip we're seeker from peers. */ + struct seeker *seeker; }; /*~ How gossipy do we ask a peer to be? */ @@ -135,6 +133,14 @@ struct peer *find_peer(struct daemon *daemon, const struct node_id *id); /* This peer (may be NULL) gave is valid gossip. */ void peer_supplied_good_gossip(struct peer *peer); +/* Pick a random peer which passes check_peer */ +struct peer *random_peer(struct daemon *daemon, + bool (*check_peer)(const struct peer *peer)); + +/* Extract gossip level for this peer */ +enum gossip_level peer_gossip_level(const struct daemon *daemon, + bool gossip_queries_feature); + /* Queue a gossip message for the peer: the subdaemon on the other end simply * forwards it to the peer. */ void queue_peer_msg(struct peer *peer, const u8 *msg TAKES); @@ -144,4 +150,7 @@ void queue_peer_msg(struct peer *peer, const u8 *msg TAKES); void queue_peer_from_store(struct peer *peer, const struct broadcastable *bcast); +/* Reset gossip range for this peer. */ +void setup_gossip_range(struct peer *peer); + #endif /* LIGHTNING_GOSSIPD_GOSSIPD_H */ diff --git a/gossipd/queries.c b/gossipd/queries.c index 4878b716c..ec3239340 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -143,10 +144,10 @@ static UNNEEDED bool encoding_end_external_type(u8 **encoded, u8 *type, size_t m } /* Query this peer for these short-channel-ids. */ -static bool query_short_channel_ids(struct daemon *daemon, - struct peer *peer, - const struct short_channel_id *scids, - void (*cb)(struct peer *peer, bool complete)) +bool query_short_channel_ids(struct daemon *daemon, + struct peer *peer, + const struct short_channel_id *scids, + void (*cb)(struct peer *peer, bool complete)) { u8 *encoded, *msg; @@ -197,27 +198,6 @@ static bool query_short_channel_ids(struct daemon *daemon, return true; } -/* This peer told us about an update to an unknown channel. Ask it for a - * channel_announcement. */ -void query_unknown_channel(struct daemon *daemon, - struct peer *peer, - const struct short_channel_id *id) -{ - /* Don't go overboard if we're already asking for a lot. */ - if (tal_count(daemon->unknown_scids) > 1000) - return; - - /* Check we're not already getting this one. */ - for (size_t i = 0; i < tal_count(daemon->unknown_scids); i++) - if (short_channel_id_eq(&daemon->unknown_scids[i], id)) - return; - - tal_arr_expand(&daemon->unknown_scids, *id); - - /* This is best effort: if peer is busy, we'll try next time. */ - query_short_channel_ids(daemon, peer, daemon->unknown_scids, NULL); -} - /* The peer can ask about an array of short channel ids: we don't assemble the * reply immediately but process them one at a time in dump_gossip which is * called when there's nothing more important to send. */ diff --git a/gossipd/queries.h b/gossipd/queries.h index 7f442df6d..309277815 100644 --- a/gossipd/queries.h +++ b/gossipd/queries.h @@ -15,10 +15,6 @@ const u8 *handle_reply_short_channel_ids_end(struct peer *peer, const u8 *msg); const u8 *handle_query_channel_range(struct peer *peer, const u8 *msg); const u8 *handle_reply_channel_range(struct peer *peer, const u8 *msg); -void query_unknown_channel(struct daemon *daemon, - struct peer *peer, - const struct short_channel_id *id); - /* This called when the peer is idle. */ void maybe_send_query_responses(struct peer *peer); @@ -32,6 +28,12 @@ bool query_channel_range(struct daemon *daemon, const struct short_channel_id *scids, bool complete)); +/* Ask this peer for info about an array of scids */ +bool query_short_channel_ids(struct daemon *daemon, + struct peer *peer, + const struct short_channel_id *scids, + void (*cb)(struct peer *peer, bool complete)); + #if DEVELOPER struct io_plan *query_scids_req(struct io_conn *conn, struct daemon *daemon, diff --git a/gossipd/seeker.c b/gossipd/seeker.c new file mode 100644 index 000000000..a3d6991f5 --- /dev/null +++ b/gossipd/seeker.c @@ -0,0 +1,140 @@ +/* This contains the code which actively seeks out gossip from peers */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Gossip we're seeking at the moment. */ +struct seeker { + /* Do we think we're missing gossip? Contains timer to re-check */ + struct oneshot *gossip_missing; + + /* Channels we've heard about, but don't know. */ + struct short_channel_id *unknown_scids; +}; + +struct seeker *new_seeker(struct daemon *daemon) +{ + struct seeker *seeker = tal(daemon, struct seeker); + seeker->gossip_missing = NULL; + seeker->unknown_scids = tal_arr(seeker, struct short_channel_id, 0); + + return seeker; +} + + +/*~ This is a timer, which goes off 10 minutes after the last time we noticed + * that gossip was missing. */ +static void gossip_not_missing(struct daemon *daemon) +{ + struct seeker *seeker = daemon->seeker; + + /* Corner case: no peers, try again! */ + if (list_empty(&daemon->peers)) + gossip_missing(daemon, daemon->seeker); + else { + struct peer *peer; + + seeker->gossip_missing = tal_free(seeker->gossip_missing); + status_info("We seem to be caught up on gossip messages"); + /* Free any lagging/stale unknown scids. */ + seeker->unknown_scids = tal_free(seeker->unknown_scids); + + /* Reset peers we marked as HIGH */ + list_for_each(&daemon->peers, peer, list) { + if (peer->gossip_level != GOSSIP_HIGH) + continue; + if (!peer->gossip_queries_feature) + continue; + peer->gossip_level = peer_gossip_level(daemon, true); + setup_gossip_range(peer); + } + } +} + +static bool peer_is_not_gossip_high(const struct peer *peer) +{ + return peer->gossip_level != GOSSIP_HIGH; +} + +/* We've found gossip is missing. */ +void gossip_missing(struct daemon *daemon, struct seeker *seeker) +{ + if (!seeker->gossip_missing) { + status_info("We seem to be missing gossip messages"); + /* FIXME: we could use query_channel_range. */ + /* Make some peers gossip harder. */ + for (size_t i = 0; i < 3; i++) { + struct peer *peer = random_peer(daemon, + peer_is_not_gossip_high); + + if (!peer) + break; + + status_info("%s: gossip harder!", + type_to_string(tmpctx, struct node_id, + &peer->id)); + peer->gossip_level = GOSSIP_HIGH; + setup_gossip_range(peer); + } + } + + tal_free(seeker->gossip_missing); + /* Check again in 10 minutes. */ + seeker->gossip_missing = new_reltimer(&daemon->timers, daemon, + time_from_sec(600), + gossip_not_missing, daemon); +} + +bool remove_unknown_scid(struct seeker *seeker, + const struct short_channel_id *scid) +{ + for (size_t i = 0; i < tal_count(seeker->unknown_scids); i++) { + if (short_channel_id_eq(&seeker->unknown_scids[i], scid)) { + tal_arr_remove(&seeker->unknown_scids, i); + return true; + } + } + return false; +} + +bool seeker_gossip(const struct seeker *seeker) +{ + return seeker->gossip_missing != NULL; +} + +bool add_unknown_scid(struct seeker *seeker, + const struct short_channel_id *scid) +{ + /* Don't go overboard if we're already asking for a lot. */ + if (tal_count(seeker->unknown_scids) > 1000) + return false; + + /* Check we're not already getting this one. */ + for (size_t i = 0; i < tal_count(seeker->unknown_scids); i++) + if (short_channel_id_eq(&seeker->unknown_scids[i], scid)) + return false; + + tal_arr_expand(&seeker->unknown_scids, *scid); + return true; +} + +/* This peer told us about an update to an unknown channel. Ask it for a + * channel_announcement. */ +void query_unknown_channel(struct daemon *daemon, + struct peer *peer, + const struct short_channel_id *id) +{ + /* Too many, or duplicate? */ + if (!add_unknown_scid(daemon->seeker, id)) + return; + + /* This is best effort: if peer is busy, we'll try next time. */ + query_short_channel_ids(daemon, peer, daemon->seeker->unknown_scids, + NULL); +} diff --git a/gossipd/seeker.h b/gossipd/seeker.h new file mode 100644 index 000000000..3f25208d4 --- /dev/null +++ b/gossipd/seeker.h @@ -0,0 +1,24 @@ +#ifndef LIGHTNING_GOSSIPD_SEEKER_H +#define LIGHTNING_GOSSIPD_SEEKER_H +#include "config.h" + +struct daemon; +struct peer; +struct short_channel_id; + +struct seeker *new_seeker(struct daemon *daemon); + +void gossip_missing(struct daemon *daemon, struct seeker *seeker); + +void query_unknown_channel(struct daemon *daemon, + struct peer *peer, + const struct short_channel_id *id); + +bool remove_unknown_scid(struct seeker *seeker, + const struct short_channel_id *scid); +bool add_unknown_scid(struct seeker *seeker, + const struct short_channel_id *scid); + +bool seeker_gossip(const struct seeker *seeker); + +#endif /* LIGHTNING_GOSSIPD_SEEKER_H */