From 5e9bf584934f6396fb1a7f8ef7b02603e8a3fe9e Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Tue, 6 Oct 2026 16:04:54 -0700 Subject: [PATCH] Publish the v2 reference-timestamp symlink v2 clients persist the snapshot header and request that timestamp next. The empty delta was written for both versions, but its public symlink was created only in the v1 directory, so the v2 follow-up was HTTP 404. Fixes lightningdevkit/rapid-gossip-sync-server#108 --- src/snapshot.rs | 14 ++++++++++---- src/tests/mod.rs | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/snapshot.rs b/src/snapshot.rs index 7f88fd0..fabb01c 100644 --- a/src/snapshot.rs +++ b/src/snapshot.rs @@ -144,10 +144,16 @@ impl Snapshotter where L::Target: Logger { fs::write(&dummy_snapshot_path_v1, dummy_snapshot_v1).unwrap(); fs::write(&dummy_snapshot_path_v2, dummy_snapshot_v2).unwrap(); - let dummy_symlink_path = format!("{}/{}.bin", pending_symlink_directory, reference_timestamp); - let relative_dummy_snapshot_path = format!("{}/{}", relative_symlink_to_snapshot_path, dummy_filename); - log_info!(self.logger, "Symlinking dummy: {} -> {}", dummy_symlink_path, relative_dummy_snapshot_path); - symlink(&relative_dummy_snapshot_path, &dummy_symlink_path).unwrap(); + // The timestamp written into every snapshot header must exist for + // both protocol versions. Real deltas already do this in the loop + // below. This symlink used to be v1-only, so a v2 client that + // stored `reference_timestamp` got HTTP 404. + for (suffix, path_to_root) in suffixes { + let dummy_symlink_path = format!("{}{}/{}.bin", pending_symlink_directory, suffix, reference_timestamp); + let relative_dummy_snapshot_path = format!("{}{}{}/{}", path_to_root, relative_symlink_to_snapshot_path, suffix, dummy_filename); + log_info!(self.logger, "Symlinking dummy: {} -> {}", dummy_symlink_path, relative_dummy_snapshot_path); + symlink(&relative_dummy_snapshot_path, &dummy_symlink_path).unwrap(); + } } // Number of intervals since Jan 1, 2022, a few months before RGS server was released. diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 0379aaa..1ed7973 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1213,6 +1213,26 @@ async fn test_full_snapshot_persistence() { // the update result must be a multiple of our snapshot granularity assert_eq!(update_result % config::snapshot_generation_interval(), 0); + // v2 clients request this generation's reference timestamp next. + // That symlink used to be v1-only, so the follow-up sync was HTTP 404. + let v2_dir = std::env::current_dir().unwrap().join(&cache_path).join("symlinks/v2"); + let reference_symlink = fs::read_dir(&v2_dir) + .unwrap() + .flatten() + .filter_map(|e| { + let ts = e.path().file_stem().and_then(|s| s.to_str()).and_then(|s| s.parse::().ok())?; + (ts != 0).then_some((ts, e.path())) + }) + .max_by_key(|(ts, _)| *ts) + .unwrap() + .1; + let v2_empty = fs::read(&reference_symlink).unwrap(); + let v2_graph = NetworkGraph::new(Network::Bitcoin, logger.clone()); + let v2_rgs = RapidGossipSync::new(&v2_graph, logger.clone()); + let applied = v2_rgs.update_network_graph(&v2_empty).unwrap(); + assert_eq!(applied % config::snapshot_generation_interval(), 0); + assert!(v2_graph.read_only().channels().is_empty()); + let readonly_graph = client_graph.read_only(); let channels = readonly_graph.channels(); let client_channel_count = channels.len();