gossip_store: add UUID entry at front of the store.
What changed, and why it matters
This commit changes how Core Lightning stores and tracks its internal gossip data file. It adds a unique identifier (UUID) to the front of the gossip store and includes that UUID in a 'store ended' marker so the node can tell when the file has been rewritten. The change is a normal data-format update, not a fix for an active security bug. There is one placeholder note in the code saying 'FIXME: real uuid!' which means the UUID is currently all zeros, but that is a completeness issue rather than an exploitable flaw.
Treat as a routine protocol/data-format evolution. If deploying from this commit, note that the zero-UUID placeholder is incomplete and should be replaced with a real random UUID before relying on the new disambiguation feature. No emergency security action is indicated by the diff itself.
Security signals we found
New on-disk wire format version bump (15 -> 16)
New gossip_store_uuid message type added to wire protocol
Existing gossip_store_ended message extended with 32-byte uuid field
Hard-coded zero UUID placeholder with FIXME comment in gossip_store.c
Downgrade tooling metadata updated for v25.12
Evidence from the diff
The patch bumps the gossip_store on-disk format from v15 to v16. It introduces a new WIRE_GOSSIP_STORE_UUID (type 4107) record carrying a 32-byte UUID written at the start of the store, and extends WIRE_GOSSIP_STORE_ENDED (type 4105) to also carry that UUID. connectd now reads the UUID when it receives gossip_store_ended, and devtools/dump-gossipstore.c learns to parse both the old and new ended formats. The upgrade path now accepts stores back to v9 through v15. A hard-coded zero UUID is inserted in gossip_store.c with a ‘FIXME: real uuid!’ comment. tools/lightning-downgrade.c also flips the v25.12 downgrade entry from ‘needs downgrade’ (true) to ‘no downgrade needed’ (false).
Changed components
gossipd/gossip_store.cconnectd/gossip_store.ccommon/gossip_store.hgossipd/gossip_store_wire.csvdevtools/dump-gossipstore.ctools/lightning-downgrade.ctests/test_gossip.pyInspect captured patch +41 / −9
diff --git a/common/gossip_store.h b/common/gossip_store.h
index 013f8003..1545e868 100644
--- a/common/gossip_store.h
+++ b/common/gossip_store.h
@@ -15,7 +15,7 @@ struct gossip_rcvd_filter;
/* First byte of file is the version.
*
* Top three bits mean incompatible change.
- * As of this writing, major == 0, minor == 15.
+ * As of this writing, major == 0, minor == 16.
*/
#define GOSSIP_STORE_MAJOR_VERSION_MASK 0xE0
#define GOSSIP_STORE_MINOR_VERSION_MASK 0x1F
diff --git a/connectd/gossip_store.c b/connectd/gossip_store.c
index 60da90f3..92298458 100644
--- a/connectd/gossip_store.c
+++ b/connectd/gossip_store.c
@@ -28,8 +28,9 @@ static size_t reopen_gossip_store(int *gossip_store_fd, const u8 *msg)
{
u64 equivalent_offset;
int newfd;
+ u8 uuid[32];
- if (!fromwire_gossip_store_ended(msg, &equivalent_offset))
+ if (!fromwire_gossip_store_ended(msg, &equivalent_offset, uuid))
status_failed(STATUS_FAIL_GOSSIP_IO,
"Bad gossipd GOSSIP_STORE_ENDED msg: %s",
tal_hex(tmpctx, msg));
diff --git a/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c
index 5280da86..c748d76c 100644
--- a/devtools/dump-gossipstore.c
+++ b/devtools/dump-gossipstore.c
@@ -7,13 +7,27 @@
#include <common/utils.h>
#include <fcntl.h>
#include <gossipd/gossip_store_wiregen.h>
+#include <inttypes.h>
#include <stdio.h>
#include <unistd.h>
#include <wire/peer_wire.h>
/* Current versions we support */
#define GSTORE_MAJOR 0
-#define GSTORE_MINOR 15
+#define GSTORE_MINOR 16
+
+/* Ended marker for <= 15 */
+static bool fromwire_gossip_store_ended_obs(const void *p, u64 *equivalent_offset)
+{
+ const u8 *cursor = p;
+ size_t plen = tal_count(p);
+
+ if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIP_STORE_ENDED)
+ return false;
+ *equivalent_offset = fromwire_u64(&cursor, &plen);
+ return cursor != NULL;
+}
+
static bool is_channel_announce(const u8 *msg, struct short_channel_id **scid)
{
@@ -128,6 +142,8 @@ int main(int argc, char *argv[])
u8 *msg, *inner;
bool deleted, dying, complete;
u32 blockheight;
+ u64 offset;
+ u8 uuid[32];
deleted = (flags & GOSSIP_STORE_DELETED_BIT);
dying = (flags & GOSSIP_STORE_DYING_BIT);
@@ -184,6 +200,14 @@ int main(int argc, char *argv[])
printf("dying channel: %s (deadline %u)\n",
fmt_short_channel_id(tmpctx, scid),
blockheight);
+ } else if (fromwire_gossip_store_ended(msg, &offset, uuid)) {
+ printf("gossip store ended: offset %"PRIu64" in uuid %s\n",
+ offset, tal_hexstr(tmpctx, uuid, sizeof(uuid)));
+ } else if (fromwire_gossip_store_ended_obs(msg, &offset)) {
+ printf("gossip store ended (v <= 15): offset %"PRIu64"\n",
+ offset);
+ } else if (fromwire_gossip_store_uuid(msg, uuid)) {
+ printf("uuid %s\n", tal_hexstr(tmpctx, uuid, sizeof(uuid)));
} else {
printf("Unknown message %u: %s\n",
fromwire_peektype(msg), tal_hex(msg, msg));
diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c
index 075cb196..32c2ae7a 100644
--- a/gossipd/gossip_store.c
+++ b/gossipd/gossip_store.c
@@ -20,8 +20,8 @@
#define GOSSIP_STORE_ZOMBIE_BIT_V13 0x1000U
#define GOSSIP_STORE_TEMP_FILENAME "gossip_store.tmp"
-/* We write it as major version 0, minor version 14 */
-#define GOSSIP_STORE_VER ((0 << 5) | 15)
+/* We write it as major version 0, minor version 16 */
+#define GOSSIP_STORE_VER ((0 << 5) | 16)
struct gossip_store {
/* Back pointer. */
@@ -85,10 +85,11 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, u64 *len,
* v13 removed private gossip entries
* v14 removed zombie and spam flags
* v15 added the complete flag
+ * v16 add uuid field, ended field uuid extension
*/
static bool can_upgrade(u8 oldversion)
{
- return oldversion >= 9 && oldversion <= 14;
+ return oldversion >= 9 && oldversion <= 15;
}
/* On upgrade, do best effort on private channels: hand them to
@@ -355,7 +356,9 @@ rename_new:
/* Create end marker now new file exists. */
if (old_fd != -1) {
- append_msg(old_fd, towire_gossip_store_ended(tmpctx, *total_len),
+ /* FIXME: real uuid! */
+ u8 uuid[32] = {0};
+ append_msg(old_fd, towire_gossip_store_ended(tmpctx, *total_len, uuid),
0, &old_len, NULL);
close(old_fd);
}
diff --git a/gossipd/gossip_store_wire.csv b/gossipd/gossip_store_wire.csv
index b0f81c37..d371f9e8 100644
--- a/gossipd/gossip_store_wire.csv
+++ b/gossipd/gossip_store_wire.csv
@@ -23,7 +23,11 @@ msgdata,gossip_store_delete_chan,scid,short_channel_id,
msgtype,gossip_store_ended,4105
msgdata,gossip_store_ended,equivalent_offset,u64,
+msgdata,gossip_store_ended,uuid,u8,32
msgtype,gossip_store_chan_dying,4106
msgdata,gossip_store_chan_dying,scid,short_channel_id,
msgdata,gossip_store_chan_dying,blockheight,u32,
+
+msgtype,gossip_store_uuid,4107
+msgdata,gossip_store_uuid,uuid,u8,32
diff --git a/tests/test_gossip.py b/tests/test_gossip.py
index d6b03dd0..b29f3b97 100644
--- a/tests/test_gossip.py
+++ b/tests/test_gossip.py
@@ -1207,7 +1207,7 @@ def test_gossip_store_load(node_factory):
l1.start()
# May preceed the Started msg waited for in 'start'.
- wait_for(lambda: l1.daemon.is_in_log('Read 1/1/1/0 cannounce/cupdate/nannounce/delete from store in 800 bytes, now 778 bytes'))
+ wait_for(lambda: l1.daemon.is_in_log('Read 1/1/1/0 cannounce/cupdate/nannounce/delete from store in 832 bytes, now 778 bytes'))
assert not l1.daemon.is_in_log('gossip_store.*truncating')
diff --git a/tools/lightning-downgrade.c b/tools/lightning-downgrade.c
index 2c76bf73..069ece08 100644
--- a/tools/lightning-downgrade.c
+++ b/tools/lightning-downgrade.c
@@ -168,7 +168,7 @@ static const char *downgrade_askrene_layers(const tal_t *ctx, struct db *db)
static const struct db_version db_versions[] = {
{ "v25.09", 276, downgrade_askrene_layers, false },
- { "v25.12", 280, NULL, true },
+ { "v25.12", 280, NULL, false },
};
static const struct db_version *version_db(const char *version)
Why this scored 17/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.