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();