From 6bff10cd40a886594f16f3fe906ccc6bef7f567f Mon Sep 17 00:00:00 2001 From: Alex Myers Date: Fri, 16 Dec 2022 10:25:47 -0600 Subject: [PATCH] gossip_store: add a flag for zombie entries This will allow gossipd to store and persist gossip for channels rather than deleting them entirely when the channels are pruned from the network. --- common/gossip_store.h | 5 +++++ devtools/dump-gossipstore.c | 10 +++++---- gossipd/gossip_store.c | 22 ++++++++++--------- gossipd/gossip_store.h | 4 +++- gossipd/routing.c | 7 +++--- gossipd/test/run-check_channel_announcement.c | 3 ++- gossipd/test/run-txout_failure.c | 3 ++- tests/test_gossip.py | 10 ++++----- 8 files changed, 39 insertions(+), 25 deletions(-) diff --git a/common/gossip_store.h b/common/gossip_store.h index e74f7cba4718..4ddf4289eda9 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -39,6 +39,11 @@ struct gossip_rcvd_filter; */ #define GOSSIP_STORE_LEN_RATELIMIT_BIT 0x20000000U +/** + * Bit used to mark a channel announcement as inactive (needs channel updates.) + */ +#define GOSSIP_STORE_LEN_ZOMBIE_BIT 0x10000000U + /** * Full flags mask */ diff --git a/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c index 6d4d8523c40f..80a9fef823d0 100644 --- a/devtools/dump-gossipstore.c +++ b/devtools/dump-gossipstore.c @@ -12,7 +12,7 @@ /* Current versions we support */ #define GSTORE_MAJOR 0 -#define GSTORE_MINOR 10 +#define GSTORE_MINOR 12 int main(int argc, char *argv[]) { @@ -67,12 +67,13 @@ int main(int argc, char *argv[]) struct short_channel_id scid; u32 msglen = be32_to_cpu(hdr.len); u8 *msg, *inner; - bool deleted, push, ratelimit; + bool deleted, push, ratelimit, zombie; u32 blockheight; deleted = (msglen & GOSSIP_STORE_LEN_DELETED_BIT); push = (msglen & GOSSIP_STORE_LEN_PUSH_BIT); ratelimit = (msglen & GOSSIP_STORE_LEN_RATELIMIT_BIT); + zombie = (msglen & GOSSIP_STORE_LEN_ZOMBIE_BIT); msglen &= GOSSIP_STORE_LEN_MASK; msg = tal_arr(NULL, u8, msglen); @@ -83,10 +84,11 @@ int main(int argc, char *argv[]) != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) warnx("Checksum verification failed"); - printf("%zu: %s%s%s", off, + printf("%zu: %s%s%s%s", off, deleted ? "DELETED " : "", push ? "PUSH " : "", - ratelimit ? "RATE-LIMITED " : ""); + ratelimit ? "RATE-LIMITED " : "", + zombie ? "ZOMBIE " : ""); if (print_timestamp) printf("T=%u ", be32_to_cpu(hdr.timestamp)); if (deleted && !print_deleted) { diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index b1e2ffebc34b..b60e77d5a4cd 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -17,8 +17,8 @@ #include #define GOSSIP_STORE_TEMP_FILENAME "gossip_store.tmp" -/* We write it as major version 0, minor version 11 */ -#define GOSSIP_STORE_VER ((0 << 5) | 11) +/* We write it as major version 0, minor version 12 */ +#define GOSSIP_STORE_VER ((0 << 5) | 12) struct gossip_store { /* This is false when we're loading */ @@ -73,7 +73,7 @@ static ssize_t gossip_pwritev(int fd, const struct iovec *iov, int iovcnt, #endif /* !HAVE_PWRITEV */ static bool append_msg(int fd, const u8 *msg, u32 timestamp, - bool push, bool spam, u64 *len) + bool push, bool zombie, bool spam, u64 *len) { struct gossip_hdr hdr; u32 msglen; @@ -88,6 +88,8 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, hdr.len |= CPU_TO_BE32(GOSSIP_STORE_LEN_PUSH_BIT); if (spam) hdr.len |= CPU_TO_BE32(GOSSIP_STORE_LEN_RATELIMIT_BIT); + if (zombie) + hdr.len |= CPU_TO_BE32(GOSSIP_STORE_LEN_ZOMBIE_BIT); hdr.crc = cpu_to_be32(crc32c(timestamp, msg, msglen)); hdr.timestamp = cpu_to_be32(timestamp); @@ -248,7 +250,7 @@ static u32 gossip_store_compact_offline(struct routing_state *rstate) oldlen = lseek(old_fd, SEEK_END, 0); newlen = lseek(new_fd, SEEK_END, 0); append_msg(old_fd, towire_gossip_store_ended(tmpctx, newlen), - 0, true, false, &oldlen); + 0, true, false, false, &oldlen); close(old_fd); status_debug("gossip_store_compact_offline: %zu deleted, %zu copied", deleted, count); @@ -530,7 +532,7 @@ bool gossip_store_compact(struct gossip_store *gs) /* Write end marker now new one is ready */ append_msg(gs->fd, towire_gossip_store_ended(tmpctx, len), - 0, true, false, &gs->len); + 0, true, false, false, &gs->len); gs->count = count; gs->deleted = 0; @@ -550,7 +552,7 @@ bool gossip_store_compact(struct gossip_store *gs) } u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg, - u32 timestamp, bool push, + u32 timestamp, bool push, bool zombie, bool spam, const u8 *addendum) { u64 off = gs->len; @@ -558,12 +560,12 @@ u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg, /* Should never get here during loading! */ assert(gs->writable); - if (!append_msg(gs->fd, gossip_msg, timestamp, push, spam, &gs->len)) { + if (!append_msg(gs->fd, gossip_msg, timestamp, push, zombie, spam, &gs->len)) { status_broken("Failed writing to gossip store: %s", strerror(errno)); return 0; } - if (addendum && !append_msg(gs->fd, addendum, 0, false, false, &gs->len)) { + if (addendum && !append_msg(gs->fd, addendum, 0, false, false, false, &gs->len)) { status_broken("Failed writing addendum to gossip store: %s", strerror(errno)); return 0; @@ -580,7 +582,7 @@ u64 gossip_store_add_private_update(struct gossip_store *gs, const u8 *update) /* A local update for an unannounced channel: not broadcastable, but * otherwise the same as a normal channel_update */ const u8 *pupdate = towire_gossip_store_private_update(tmpctx, update); - return gossip_store_add(gs, pupdate, 0, false, false, NULL); + return gossip_store_add(gs, pupdate, 0, false, false, false, NULL); } /* Returns index of following entry. */ @@ -640,7 +642,7 @@ void gossip_store_mark_channel_deleted(struct gossip_store *gs, const struct short_channel_id *scid) { gossip_store_add(gs, towire_gossip_store_delete_chan(tmpctx, scid), - 0, false, false, NULL); + 0, false, false, false, NULL); } const u8 *gossip_store_get(const tal_t *ctx, diff --git a/gossipd/gossip_store.h b/gossipd/gossip_store.h index 44a0b4d303bc..67ed9dc798cc 100644 --- a/gossipd/gossip_store.h +++ b/gossipd/gossip_store.h @@ -41,11 +41,13 @@ u64 gossip_store_add_private_update(struct gossip_store *gs, const u8 *update); * @timestamp: the timestamp for filtering of this messsage. * @push: true if this should be sent to peers despite any timestamp filters. * @spam: true if this message is rate-limited and squelched to peers. + * @zombie: true if this channel is missing a current channel_update. * @addendum: another message to append immediately after this * (for appending amounts to channel_announcements for internal use). */ u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg, - u32 timestamp, bool push, bool spam, const u8 *addendum); + u32 timestamp, bool push, bool zombie, bool spam, + const u8 *addendum); /** diff --git a/gossipd/routing.c b/gossipd/routing.c index 3df90bdc602b..d8364fef10f4 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1797,7 +1797,7 @@ bool routing_add_node_announcement(struct routing_state *rstate, = gossip_store_add(rstate->gs, msg, timestamp, node_id_eq(&node_id, &rstate->local_id), - spam, NULL); + false, spam, NULL); if (node->bcast.timestamp > rstate->last_timestamp && node->bcast.timestamp < time_now().ts.tv_sec) rstate->last_timestamp = node->bcast.timestamp; @@ -2025,7 +2025,8 @@ bool routing_add_private_channel(struct routing_state *rstate, u8 *msg = towire_gossip_store_private_channel(tmpctx, capacity, chan_ann); - index = gossip_store_add(rstate->gs, msg, 0, false, false, NULL); + index = gossip_store_add(rstate->gs, msg, 0, false, false, + false, NULL); } chan->bcast.index = index; return true; @@ -2170,7 +2171,7 @@ void routing_channel_spent(struct routing_state *rstate, /* Save to gossip_store in case we restart */ msg = towire_gossip_store_chan_dying(tmpctx, &chan->scid, deadline); - index = gossip_store_add(rstate->gs, msg, 0, false, false, NULL); + index = gossip_store_add(rstate->gs, msg, 0, false, false, false, NULL); /* Remember locally so we can kill it in 12 blocks */ status_debug("channel %s closing soon due" diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 0e983618e024..bd3cf97dcf2a 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -61,7 +61,8 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, { fprintf(stderr, "cupdate_different called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, - u32 timestamp UNNEEDED, bool push UNNEEDED, bool spam UNNEEDED, const u8 *addendum UNNEEDED) + u32 timestamp UNNEEDED, bool push UNNEEDED, bool zombie UNNEEDED, bool spam UNNEEDED, + const u8 *addendum UNNEEDED) { fprintf(stderr, "gossip_store_add called!\n"); abort(); } /* Generated stub for gossip_store_add_private_update */ u64 gossip_store_add_private_update(struct gossip_store *gs UNNEEDED, const u8 *update UNNEEDED) diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 598f11ff9d27..5db4ef8f3c0f 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -32,7 +32,8 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, { fprintf(stderr, "cupdate_different called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, - u32 timestamp UNNEEDED, bool push UNNEEDED, bool spam UNNEEDED, const u8 *addendum UNNEEDED) + u32 timestamp UNNEEDED, bool push UNNEEDED, bool zombie UNNEEDED, bool spam UNNEEDED, + const u8 *addendum UNNEEDED) { fprintf(stderr, "gossip_store_add called!\n"); abort(); } /* Generated stub for gossip_store_add_private_update */ u64 gossip_store_add_private_update(struct gossip_store *gs UNNEEDED, const u8 *update UNNEEDED) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 053749e1b9a5..b15cda03dd80 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1159,7 +1159,7 @@ def test_gossip_store_load(node_factory): """Make sure we can read canned gossip store""" l1 = node_factory.get_node(start=False) with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), 'wb') as f: - f.write(bytearray.fromhex("0a" # GOSSIP_STORE_VERSION + f.write(bytearray.fromhex("0c" # GOSSIP_STORE_VERSION "000001b0" # len "fea676e8" # csum "5b8d9b44" # timestamp @@ -1217,7 +1217,7 @@ def test_gossip_store_load_announce_before_update(node_factory): """Make sure we can read canned gossip store with node_announce before update. This happens when a channel_update gets replaced, leaving node_announce before it""" l1 = node_factory.get_node(start=False) with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), 'wb') as f: - f.write(bytearray.fromhex("0a" # GOSSIP_STORE_VERSION + f.write(bytearray.fromhex("0c" # GOSSIP_STORE_VERSION "000001b0" # len "fea676e8" # csum "5b8d9b44" # timestamp @@ -1262,7 +1262,7 @@ def test_gossip_store_load_amount_truncated(node_factory): """Make sure we can read canned gossip store with truncated amount""" l1 = node_factory.get_node(start=False, allow_broken_log=True) with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), 'wb') as f: - f.write(bytearray.fromhex("0a" # GOSSIP_STORE_VERSION + f.write(bytearray.fromhex("0c" # GOSSIP_STORE_VERSION "000001b0" # len "fea676e8" # csum "5b8d9b44" # timestamp @@ -1727,7 +1727,7 @@ def test_gossip_store_load_no_channel_update(node_factory): # A channel announcement with no channel_update. with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), 'wb') as f: - f.write(bytearray.fromhex("0b" # GOSSIP_STORE_VERSION + f.write(bytearray.fromhex("0c" # GOSSIP_STORE_VERSION "000001b0" # len "fea676e8" # csum "5b8d9b44" # timestamp @@ -1754,7 +1754,7 @@ def test_gossip_store_load_no_channel_update(node_factory): l1.rpc.call('dev-compact-gossip-store') with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), "rb") as f: - assert bytearray(f.read()) == bytearray.fromhex("0b") + assert bytearray(f.read()) == bytearray.fromhex("0c") @pytest.mark.developer("gossip without DEVELOPER=1 is slow")