diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index 194837b3e..2c7c8ab52 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -39,9 +39,6 @@ struct gossip_store { * should it be needed */ struct routing_state *rstate; - /* This is daemon->peers for handling to update_peers_broadcast_index */ - struct list_head *peers; - /* Disable compaction if we encounter an error during a prior * compaction */ bool disable_compaction; @@ -263,8 +260,7 @@ close_old: return 0; } -struct gossip_store *gossip_store_new(struct routing_state *rstate, - struct list_head *peers) +struct gossip_store *gossip_store_new(struct routing_state *rstate) { struct gossip_store *gs = tal(rstate, struct gossip_store); gs->count = gs->deleted = 0; @@ -278,7 +274,6 @@ struct gossip_store *gossip_store_new(struct routing_state *rstate, gs->rstate = rstate; gs->disable_compaction = false; gs->len = sizeof(gs->version); - gs->peers = peers; tal_add_destructor(gs, gossip_store_destroy); diff --git a/gossipd/gossip_store.h b/gossipd/gossip_store.h index 36d29d216..3d27df5ba 100644 --- a/gossipd/gossip_store.h +++ b/gossipd/gossip_store.h @@ -16,8 +16,7 @@ struct gossip_store; struct routing_state; -struct gossip_store *gossip_store_new(struct routing_state *rstate, - struct list_head *peers); +struct gossip_store *gossip_store_new(struct routing_state *rstate); /** * Load the initial gossip store, if any. diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 35808aa99..97e18d046 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -37,6 +37,16 @@ #include #include +const struct node_id *peer_node_id(const struct peer *peer) +{ + return &peer->id; +} + +bool peer_node_id_eq(const struct peer *peer, const struct node_id *node_id) +{ + return node_id_eq(&peer->id, node_id); +} + /*~ A channel consists of a `struct half_chan` for each direction, each of * which has a `flags` word from the `channel_update`; bit 1 is * ROUTING_FLAGS_DISABLED in the `channel_update`. But we also keep a local @@ -83,8 +93,8 @@ static void destroy_peer(struct peer *peer) { struct node *node; - /* Remove it from the peers list */ - list_del_from(&peer->daemon->peers, &peer->list); + /* Remove it from the peers table */ + peer_node_id_map_del(peer->daemon->peers, peer);; /* If we have a channel with this peer, disable it. */ node = get_node(peer->daemon->rstate, &peer->id); @@ -95,12 +105,7 @@ static void destroy_peer(struct peer *peer) /* Search for a peer. */ struct peer *find_peer(struct daemon *daemon, const struct node_id *id) { - struct peer *peer; - - list_for_each(&daemon->peers, peer, list) - if (node_id_eq(&peer->id, id)) - return peer; - return NULL; + return peer_node_id_map_get(daemon->peers, id); } /* Increase a peer's gossip_counter, if peer not NULL */ @@ -486,8 +491,8 @@ static void connectd_new_peer(struct daemon *daemon, const u8 *msg) peer->range_replies = NULL; peer->query_channel_range_cb = NULL; - /* We keep a list so we can find peer by id */ - list_add_tail(&peer->daemon->peers, &peer->list); + /* We keep a htable so we can find peer by id */ + peer_node_id_map_add(daemon->peers, peer); tal_add_destructor(peer, destroy_peer); node = get_node(daemon->rstate, &peer->id); @@ -754,26 +759,26 @@ static void gossip_disable_local_channels(struct daemon *daemon) local_disable_chan(daemon, c, half_chan_idx(local_node, c)); } -struct peer *random_peer(struct daemon *daemon, - bool (*check_peer)(const struct peer *peer)) +struct peer *first_random_peer(struct daemon *daemon, + struct peer_node_id_map_iter *it) { - u64 target = UINT64_MAX; - struct peer *best = NULL, *i; + return peer_node_id_map_pick(daemon->peers, pseudorand_u64(), it); +} - /* Reservoir sampling */ - list_for_each(&daemon->peers, i, list) { - u64 r; +struct peer *next_random_peer(struct daemon *daemon, + const struct peer *first, + struct peer_node_id_map_iter *it) +{ + struct peer *p; - if (!check_peer(i)) - continue; + p = peer_node_id_map_next(daemon->peers, it); + if (!p) + p = peer_node_id_map_first(daemon->peers, it); - r = pseudorand_u64(); - if (r <= target) { - best = i; - target = r; - } - } - return best; + /* Full circle? */ + if (p == first) + return NULL; + return p; } /* This is called when lightningd or connectd closes its connection to @@ -808,7 +813,6 @@ static void gossip_init(struct daemon *daemon, const u8 *msg) daemon->rstate = new_routing_state(daemon, &daemon->id, - &daemon->peers, &daemon->timers, take(dev_gossip_time), dev_fast_gossip, @@ -887,6 +891,7 @@ static void dev_gossip_memleak(struct daemon *daemon, const u8 *msg) memleak_ptr(memtable, msg); /* Now delete daemon and those which it has pointers to. */ memleak_scan_obj(memtable, daemon); + memleak_scan_htable(memtable, &daemon->peers->raw); found_leak = dump_memleak(memtable, memleak_status_broken); daemon_conn_send(daemon->master, @@ -1163,7 +1168,8 @@ int main(int argc, char *argv[]) subdaemon_setup(argc, argv); daemon = tal(NULL, struct daemon); - list_head_init(&daemon->peers); + daemon->peers = tal(daemon, struct peer_node_id_map); + peer_node_id_map_init(daemon->peers); daemon->deferred_txouts = tal_arr(daemon, struct short_channel_id, 0); daemon->node_announce_timer = NULL; daemon->node_announce_regen_timer = NULL; diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index 5a1c9ce4a..442ff1892 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -14,19 +14,29 @@ #define CONNECTD2_FD 5 struct chan; +struct peer; struct channel_update_timestamps; struct broadcastable; struct lease_rates; struct seeker; struct dying_channel; +/* Helpers for htable */ +const struct node_id *peer_node_id(const struct peer *peer); +bool peer_node_id_eq(const struct peer *peer, const struct node_id *node_id); + +/* Defines struct peer_node_id_map */ +HTABLE_DEFINE_TYPE(struct peer, + peer_node_id, node_id_hash, peer_node_id_eq, + peer_node_id_map); + /*~ The core daemon structure: */ struct daemon { /* Who am I? Helps us find ourself in the routing map. */ struct node_id id; /* Peers we are gossiping to: id is unique */ - struct list_head peers; + struct peer_node_id_map *peers; /* Current blockheight: 0 means we're not up-to-date. */ u32 current_blockheight; @@ -127,9 +137,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, size_t amount); -/* Pick a random peer which passes check_peer */ -struct peer *random_peer(struct daemon *daemon, - bool (*check_peer)(const struct peer *peer)); +/* Get a random peer. NULL if no peers. */ +struct peer *first_random_peer(struct daemon *daemon, + struct peer_node_id_map_iter *it); + +/* Get another... return NULL when we're back at frist. */ +struct peer *next_random_peer(struct daemon *daemon, + const struct peer *first, + struct peer_node_id_map_iter *it); /* Queue a gossip message for the peer: the subdaemon on the other end simply * forwards it to the peer. */ diff --git a/gossipd/queries.c b/gossipd/queries.c index 4c894f436..76135d8d5 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -1052,17 +1052,16 @@ static bool maybe_send_query_responses_peer(struct peer *peer) void maybe_send_query_responses(struct daemon *daemon) { - /* Rotate through, so we don't favor a single peer. */ - struct list_head used; - struct peer *p; + struct peer *first, *p; + struct peer_node_id_map_iter it; - list_head_init(&used); - while ((p = list_pop(&daemon->peers, struct peer, list)) != NULL) { - list_add(&used, &p->list); + /* Rotate through, so we don't favor a single peer. */ + p = first = first_random_peer(daemon, &it); + while (p) { if (maybe_send_query_responses_peer(p)) break; + p = next_random_peer(daemon, first, &it); } - list_append_list(&daemon->peers, &used); } bool query_channel_range(struct daemon *daemon, diff --git a/gossipd/routing.c b/gossipd/routing.c index 17e4bcf35..eb3b1f87b 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -281,7 +281,6 @@ static bool in_txout_failures(struct routing_state *rstate, struct routing_state *new_routing_state(const tal_t *ctx, const struct node_id *local_id, - struct list_head *peers, struct timers *timers, const u32 *dev_gossip_time TAKES, bool dev_fast_gossip, @@ -291,7 +290,7 @@ struct routing_state *new_routing_state(const tal_t *ctx, rstate->nodes = new_node_map(rstate); rstate->timers = timers; rstate->local_id = *local_id; - rstate->gs = gossip_store_new(rstate, peers); + rstate->gs = gossip_store_new(rstate); rstate->local_channel_announced = false; rstate->last_timestamp = 0; rstate->dying_channels = tal_arr(rstate, struct dying_channel, 0); diff --git a/gossipd/routing.h b/gossipd/routing.h index d64e763c2..4eaca73ee 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -272,7 +272,6 @@ get_channel(const struct routing_state *rstate, struct routing_state *new_routing_state(const tal_t *ctx, const struct node_id *local_id, - struct list_head *peers, struct timers *timers, const u32 *dev_gossip_time TAKES, bool dev_fast_gossip, diff --git a/gossipd/seeker.c b/gossipd/seeker.c index c2ad30b42..513c55118 100644 --- a/gossipd/seeker.c +++ b/gossipd/seeker.c @@ -160,6 +160,8 @@ static struct peer *random_seeker(struct seeker *seeker, bool (*check_peer)(const struct peer *peer)) { struct peer *peer = seeker->preferred_peer_softref; + struct peer *first; + struct peer_node_id_map_iter it; /* 80% chance of immediately choosing a peer who reported the missing * stuff: they presumably can tell us more about it. We don't @@ -171,7 +173,14 @@ static struct peer *random_seeker(struct seeker *seeker, return peer; } - return random_peer(seeker->daemon, check_peer); + /* Rotate through, so we don't favor a single peer. */ + peer = first = first_random_peer(seeker->daemon, &it); + while (peer) { + if (check_peer(peer)) + break; + peer = next_random_peer(seeker->daemon, first, &it); + } + return peer; } static bool peer_made_progress(struct seeker *seeker) @@ -741,7 +750,8 @@ static void probe_many_random_scids(struct seeker *seeker) static void check_firstpeer(struct seeker *seeker) { - struct peer *peer = seeker->random_peer_softref, *p; + struct peer *peer = seeker->random_peer_softref; + struct peer_node_id_map_iter it; /* It might have died, pick another. */ if (!peer) { @@ -764,7 +774,10 @@ static void check_firstpeer(struct seeker *seeker) /* Other peers can gossip now. */ status_peer_debug(&peer->id, "seeker: startup peer finished"); clear_softref(seeker, &seeker->random_peer_softref); - list_for_each(&seeker->daemon->peers, p, list) { + + for (struct peer *p = peer_node_id_map_first(seeker->daemon->peers, &it); + p; + p = peer_node_id_map_next(seeker->daemon->peers, &it)) { if (p == peer) continue; diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 127d34135..684a8fb7e 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -87,8 +87,7 @@ void gossip_store_mark_channel_deleted(struct gossip_store *gs UNNEEDED, const struct short_channel_id *scid UNNEEDED) { fprintf(stderr, "gossip_store_mark_channel_deleted called!\n"); abort(); } /* Generated stub for gossip_store_new */ -struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED, - struct list_head *peers UNNEEDED) +struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED) { fprintf(stderr, "gossip_store_new called!\n"); abort(); } /* Generated stub for memleak_add_helper_ */ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 22b26c2d1..3e5efab43 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -48,6 +48,10 @@ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *e /* Generated stub for find_peer */ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "find_peer called!\n"); abort(); } +/* Generated stub for first_random_peer */ +struct peer *first_random_peer(struct daemon *daemon UNNEEDED, + struct peer_node_id_map_iter *it UNNEEDED) +{ fprintf(stderr, "first_random_peer called!\n"); abort(); } /* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */ bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } @@ -94,6 +98,11 @@ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, struct timerel expire UNNEEDED, void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "new_reltimer_ called!\n"); abort(); } +/* Generated stub for next_random_peer */ +struct peer *next_random_peer(struct daemon *daemon UNNEEDED, + const struct peer *first UNNEEDED, + struct peer_node_id_map_iter *it UNNEEDED) +{ fprintf(stderr, "next_random_peer called!\n"); abort(); } /* Generated stub for node_has_broadcastable_channels */ bool node_has_broadcastable_channels(const struct node *node UNNEEDED) { fprintf(stderr, "node_has_broadcastable_channels called!\n"); abort(); } diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index 835d8bfae..0290d2ab7 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -45,6 +45,10 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx UNNEEDED, /* Generated stub for decode_short_ids */ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *encoded UNNEEDED) { fprintf(stderr, "decode_short_ids called!\n"); abort(); } +/* Generated stub for first_random_peer */ +struct peer *first_random_peer(struct daemon *daemon UNNEEDED, + struct peer_node_id_map_iter *it UNNEEDED) +{ fprintf(stderr, "first_random_peer called!\n"); abort(); } /* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */ bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } @@ -65,6 +69,11 @@ const u8 *gossip_store_get(const tal_t *ctx UNNEEDED, /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } +/* Generated stub for next_random_peer */ +struct peer *next_random_peer(struct daemon *daemon UNNEEDED, + const struct peer *first UNNEEDED, + struct peer_node_id_map_iter *it UNNEEDED) +{ fprintf(stderr, "next_random_peer called!\n"); abort(); } /* Generated stub for peer_supplied_good_gossip */ void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) { fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index b83fa2bf9..dcee25621 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -26,12 +26,21 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, const struct sha256 *h UNNEEDED, struct pubkey *next UNNEEDED) { fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } +/* Generated stub for first_random_peer */ +struct peer *first_random_peer(struct daemon *daemon UNNEEDED, + struct peer_node_id_map_iter *it UNNEEDED) +{ fprintf(stderr, "first_random_peer called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, struct timerel expire UNNEEDED, void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "new_reltimer_ called!\n"); abort(); } +/* Generated stub for next_random_peer */ +struct peer *next_random_peer(struct daemon *daemon UNNEEDED, + const struct peer *first UNNEEDED, + struct peer_node_id_map_iter *it UNNEEDED) +{ fprintf(stderr, "next_random_peer called!\n"); abort(); } /* Generated stub for query_channel_range */ bool query_channel_range(struct daemon *daemon UNNEEDED, struct peer *peer UNNEEDED, @@ -52,10 +61,6 @@ bool query_short_channel_ids(struct daemon *daemon UNNEEDED, /* Generated stub for queue_peer_msg */ void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) { fprintf(stderr, "queue_peer_msg called!\n"); abort(); } -/* Generated stub for random_peer */ -struct peer *random_peer(struct daemon *daemon UNNEEDED, - bool (*check_peer)(const struct peer *peer)) -{ fprintf(stderr, "random_peer called!\n"); abort(); } /* Generated stub for random_select */ bool random_select(double weight UNNEEDED, double *tot_weight UNNEEDED) { fprintf(stderr, "random_select called!\n"); abort(); } diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 3cb97cbee..9f5782ed8 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -100,8 +100,7 @@ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, /* AUTOGENERATED MOCKS END */ /* NOOP stub for gossip_store_new */ -struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED, - struct list_head *peers UNNEEDED) +struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED) { return NULL; } @@ -118,7 +117,7 @@ int main(int argc, char *argv[]) timers_init(&timers, time_mono()); /* Random uninitalized node_id, we don't reference it. */ rstate = new_routing_state(tmpctx, tal(tmpctx, struct node_id), - NULL, &timers, NULL, false, false); + &timers, NULL, false, false); scid1.u64 = 100; scid2.u64 = 200;