diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c1e50da3..77edc8f17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ changes. ### Fixed - `--bind-addr=` fixed for nodes using local sockets (eg. testing). +- Unannounced local channels were forgotten for routing on restart until reconnection occurred. ### Security diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index e48fe673d..d3af56205 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -150,6 +150,46 @@ static bool gossip_store_append(int fd, struct routing_state *rstate, const u8 * write(fd, msg, msglen) == msglen); } +/* Local unannounced channels don't appear in broadcast map, but we need to + * remember them anyway, so we manually append to the store. + * + * Note these do *not* add to gs->count, since that's compared with + * the broadcast map count. +*/ +static bool add_local_unnannounced(int fd, + struct routing_state *rstate, + struct node *self) +{ + struct chan_map_iter i; + struct chan *c; + + for (c = chan_map_first(&self->chans, &i); + c; + c = chan_map_next(&self->chans, &i)) { + struct node *peer = other_node(self, c); + const u8 *msg; + + /* Ignore already announced. */ + if (c->channel_announce) + continue; + + msg = towire_gossipd_local_add_channel(tmpctx, &c->scid, + &peer->id, c->sat); + if (!gossip_store_append(fd, rstate, msg)) + return false; + + for (size_t i = 0; i < 2; i++) { + msg = c->half[i].channel_update; + if (!msg) + continue; + if (!gossip_store_append(fd, rstate, msg)) + return false; + } + } + + return true; +} + /** * Rewrite the on-disk gossip store, compacting it along the way * @@ -162,6 +202,7 @@ bool gossip_store_compact(struct gossip_store *gs) u64 index = 0; int fd; const u8 *msg; + struct node *self; assert(gs->broadcast); status_trace( @@ -187,11 +228,18 @@ bool gossip_store_compact(struct gossip_store *gs) status_broken("Failed writing to gossip store: %s", strerror(errno)); goto unlink_disable; - } count++; } + /* Local unannounced channels are not in the store! */ + self = get_node(gs->rstate, &gs->rstate->local_id); + if (self && !add_local_unnannounced(fd, gs->rstate, self)) { + status_broken("Failed writing unannounced to gossip store: %s", + strerror(errno)); + goto unlink_disable; + } + if (rename(GOSSIP_STORE_TEMP_FILENAME, GOSSIP_STORE_FILENAME) == -1) { status_broken( "Error swapping compacted gossip_store into place: %s", diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c index f947c3e59..cfdab2285 100644 --- a/gossipd/test/run-bench-find_route.c +++ b/gossipd/test/run-bench-find_route.c @@ -95,6 +95,9 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_errorfmt called!\n"); abort(); } +/* Generated stub for towire_gossipd_local_add_channel */ +u8 *towire_gossipd_local_add_channel(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED, const struct pubkey *remote_node_id UNNEEDED, struct amount_sat satoshis UNNEEDED) +{ fprintf(stderr, "towire_gossipd_local_add_channel called!\n"); abort(); } /* Generated stub for towire_gossip_store_channel_announcement */ u8 *towire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED, struct amount_sat satoshis UNNEEDED) { fprintf(stderr, "towire_gossip_store_channel_announcement called!\n"); abort(); } diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c index 9335b437c..d39bf64f7 100644 --- a/gossipd/test/run-find_route-specific.c +++ b/gossipd/test/run-find_route-specific.c @@ -84,6 +84,9 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_errorfmt called!\n"); abort(); } +/* Generated stub for towire_gossipd_local_add_channel */ +u8 *towire_gossipd_local_add_channel(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED, const struct pubkey *remote_node_id UNNEEDED, struct amount_sat satoshis UNNEEDED) +{ fprintf(stderr, "towire_gossipd_local_add_channel called!\n"); abort(); } /* Generated stub for towire_gossip_store_channel_announcement */ u8 *towire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED, struct amount_sat satoshis UNNEEDED) { fprintf(stderr, "towire_gossip_store_channel_announcement called!\n"); abort(); } diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c index 6b90ae789..6d644c482 100644 --- a/gossipd/test/run-find_route.c +++ b/gossipd/test/run-find_route.c @@ -82,6 +82,9 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_errorfmt called!\n"); abort(); } +/* Generated stub for towire_gossipd_local_add_channel */ +u8 *towire_gossipd_local_add_channel(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED, const struct pubkey *remote_node_id UNNEEDED, struct amount_sat satoshis UNNEEDED) +{ fprintf(stderr, "towire_gossipd_local_add_channel called!\n"); abort(); } /* Generated stub for towire_gossip_store_channel_announcement */ u8 *towire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED, struct amount_sat satoshis UNNEEDED) { fprintf(stderr, "towire_gossip_store_channel_announcement called!\n"); abort(); } diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 082d87f5b..220fb4344 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1033,7 +1033,6 @@ def test_getroute_exclude(node_factory, bitcoind): l1.rpc.getroute(l4.info['id'], 1, 1, exclude=[chan_l2l3, chan_l2l4]) -@pytest.mark.xfail(strict=True) @unittest.skipIf(not DEVELOPER, "need dev-compact-gossip-store") def test_gossip_store_local_channels(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2, wait_for_announce=False) @@ -1059,7 +1058,6 @@ def test_gossip_store_local_channels(node_factory, bitcoind): assert len(chans) == 2 -@pytest.mark.xfail(strict=True) @unittest.skipIf(not DEVELOPER, "need dev-compact-gossip-store") def test_gossip_store_private_channels(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2, announce_channels=False)