X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning-rapid-gossip-sync%2Fsrc%2Fprocessing.rs;h=b3fae0ffd8bea8e19822c5a2cf9bc19d3fe33939;hb=3a9fe209e104e048921ad50be77a80b18a98b45c;hp=3a60b7c986c85354ee5789f8dadeaf3f3e6d519f;hpb=559ed20f92cb86b02094421fa51ad243dbf800b6;p=rust-lightning diff --git a/lightning-rapid-gossip-sync/src/processing.rs b/lightning-rapid-gossip-sync/src/processing.rs index 3a60b7c9..b3fae0ff 100644 --- a/lightning-rapid-gossip-sync/src/processing.rs +++ b/lightning-rapid-gossip-sync/src/processing.rs @@ -2,7 +2,7 @@ use core::cmp::max; use core::ops::Deref; use core::sync::atomic::Ordering; -use bitcoin::BlockHash; +use bitcoin::blockdata::constants::ChainHash; use bitcoin::secp256k1::PublicKey; use lightning::ln::msgs::{ @@ -10,13 +10,16 @@ use lightning::ln::msgs::{ }; use lightning::routing::gossip::NetworkGraph; use lightning::util::logger::Logger; +use lightning::{log_debug, log_warn, log_trace, log_given_level, log_gossip}; use lightning::util::ser::{BigSize, Readable}; use lightning::io; -use crate::error::GraphSyncError; -use crate::RapidGossipSync; +use crate::{GraphSyncError, RapidGossipSync}; -#[cfg(not(feature = "std"))] +#[cfg(all(feature = "std", not(test)))] +use std::time::{SystemTime, UNIX_EPOCH}; + +#[cfg(all(not(feature = "std"), not(test)))] use alloc::{vec::Vec, borrow::ToOwned}; /// The purpose of this prefix is to identify the serialization format, should other rapid gossip @@ -29,23 +32,59 @@ const GOSSIP_PREFIX: [u8; 4] = [76, 68, 75, 1]; /// avoid malicious updates being able to trigger excessive memory allocation. const MAX_INITIAL_NODE_ID_VECTOR_CAPACITY: u32 = 50_000; +/// We disallow gossip data that's more than two weeks old, per BOLT 7's +/// suggestion. +const STALE_RGS_UPDATE_AGE_LIMIT_SECS: u64 = 60 * 60 * 24 * 14; + impl>, L: Deref> RapidGossipSync where L::Target: Logger { + #[cfg(feature = "std")] pub(crate) fn update_network_graph_from_byte_stream( + &self, + read_cursor: &mut R, + ) -> Result { + #[allow(unused_mut, unused_assignments)] + let mut current_time_unix = None; + #[cfg(not(test))] + { + // Note that many tests rely on being able to set arbitrarily old timestamps, thus we + // disable this check during tests! + current_time_unix = Some(SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs()); + } + self.update_network_graph_from_byte_stream_no_std(read_cursor, current_time_unix) + } + + pub(crate) fn update_network_graph_from_byte_stream_no_std( &self, mut read_cursor: &mut R, + current_time_unix: Option ) -> Result { + log_trace!(self.logger, "Processing RGS data..."); let mut prefix = [0u8; 4]; read_cursor.read_exact(&mut prefix)?; - match prefix { - GOSSIP_PREFIX => {} - _ => { - return Err(DecodeError::UnknownVersion.into()); - } - }; + if prefix != GOSSIP_PREFIX { + return Err(DecodeError::UnknownVersion.into()); + } + + let chain_hash: ChainHash = Readable::read(read_cursor)?; + let ng_chain_hash = self.network_graph.get_chain_hash(); + if chain_hash != ng_chain_hash { + return Err( + LightningError { + err: "Rapid Gossip Sync data's chain hash does not match the network graph's".to_owned(), + action: ErrorAction::IgnoreError, + }.into() + ); + } - let chain_hash: BlockHash = Readable::read(read_cursor)?; let latest_seen_timestamp: u32 = Readable::read(read_cursor)?; + + if let Some(time) = current_time_unix { + if (latest_seen_timestamp as u64) < time.saturating_sub(STALE_RGS_UPDATE_AGE_LIMIT_SECS) { + return Err(LightningError{err: "Rapid Gossip Sync data is more than two weeks old".to_owned(), action: ErrorAction::IgnoreError}.into()); + } + } + // backdate the applied timestamp by a week let backdated_timestamp = latest_seen_timestamp.saturating_sub(24 * 3600 * 7); @@ -75,12 +114,16 @@ impl>, L: Deref> RapidGossipSync where L let node_id_1_index: BigSize = Readable::read(read_cursor)?; let node_id_2_index: BigSize = Readable::read(read_cursor)?; + if max(node_id_1_index.0, node_id_2_index.0) >= node_id_count as u64 { return Err(DecodeError::InvalidValue.into()); }; let node_id_1 = node_ids[node_id_1_index.0 as usize]; let node_id_2 = node_ids[node_id_2_index.0 as usize]; + log_gossip!(self.logger, "Adding channel {} from RGS announcement at {}", + short_channel_id, latest_seen_timestamp); + let announcement_result = network_graph.add_channel_from_partial_announcement( short_channel_id, backdated_timestamp as u64, @@ -92,6 +135,7 @@ impl>, L: Deref> RapidGossipSync where L if let ErrorAction::IgnoreDuplicateGossip = lightning_error.action { // everything is fine, just a duplicate channel announcement } else { + log_warn!(self.logger, "Failed to process channel announcement: {:?}", lightning_error); return Err(lightning_error.into()); } } @@ -100,6 +144,8 @@ impl>, L: Deref> RapidGossipSync where L previous_scid = 0; // updates start at a new scid let update_count: u32 = Readable::read(read_cursor)?; + log_debug!(self.logger, "Processing RGS update from {} with {} nodes, {} channel announcements and {} channel updates.", + latest_seen_timestamp, node_id_count, announcement_count, update_count); if update_count == 0 { return Ok(latest_seen_timestamp); } @@ -123,49 +169,38 @@ impl>, L: Deref> RapidGossipSync where L // flags are always sent in full, and hence always need updating let standard_channel_flags = channel_flags & 0b_0000_0011; - let mut synthetic_update = if channel_flags & 0b_1000_0000 == 0 { - // full update, field flags will indicate deviations from the default - UnsignedChannelUpdate { - chain_hash, - short_channel_id, - timestamp: backdated_timestamp, - flags: standard_channel_flags, - cltv_expiry_delta: default_cltv_expiry_delta, - htlc_minimum_msat: default_htlc_minimum_msat, - htlc_maximum_msat: default_htlc_maximum_msat, - fee_base_msat: default_fee_base_msat, - fee_proportional_millionths: default_fee_proportional_millionths, - excess_data: Vec::new(), - } - } else { + let mut synthetic_update = UnsignedChannelUpdate { + chain_hash, + short_channel_id, + timestamp: backdated_timestamp, + flags: standard_channel_flags, + cltv_expiry_delta: default_cltv_expiry_delta, + htlc_minimum_msat: default_htlc_minimum_msat, + htlc_maximum_msat: default_htlc_maximum_msat, + fee_base_msat: default_fee_base_msat, + fee_proportional_millionths: default_fee_proportional_millionths, + excess_data: Vec::new(), + }; + + let mut skip_update_for_unknown_channel = false; + + if (channel_flags & 0b_1000_0000) != 0 { // incremental update, field flags will indicate mutated values let read_only_network_graph = network_graph.read_only(); - let channel = read_only_network_graph - .channels() - .get(&short_channel_id) - .ok_or(LightningError { - err: "Couldn't find channel for update".to_owned(), - action: ErrorAction::IgnoreError, - })?; - - let directional_info = channel - .get_directional_info(channel_flags) - .ok_or(LightningError { - err: "Couldn't find previous directional data for update".to_owned(), - action: ErrorAction::IgnoreError, - })?; - - UnsignedChannelUpdate { - chain_hash, - short_channel_id, - timestamp: backdated_timestamp, - flags: standard_channel_flags, - cltv_expiry_delta: directional_info.cltv_expiry_delta, - htlc_minimum_msat: directional_info.htlc_minimum_msat, - htlc_maximum_msat: directional_info.htlc_maximum_msat, - fee_base_msat: directional_info.fees.base_msat, - fee_proportional_millionths: directional_info.fees.proportional_millionths, - excess_data: Vec::new(), + if let Some(directional_info) = + read_only_network_graph.channels().get(&short_channel_id) + .and_then(|channel| channel.get_directional_info(channel_flags)) + { + synthetic_update.cltv_expiry_delta = directional_info.cltv_expiry_delta; + synthetic_update.htlc_minimum_msat = directional_info.htlc_minimum_msat; + synthetic_update.htlc_maximum_msat = directional_info.htlc_maximum_msat; + synthetic_update.fee_base_msat = directional_info.fees.base_msat; + synthetic_update.fee_proportional_millionths = directional_info.fees.proportional_millionths; + } else { + log_trace!(self.logger, + "Skipping application of channel update for chan {} with flags {} as original data is missing.", + short_channel_id, channel_flags); + skip_update_for_unknown_channel = true; } }; @@ -194,32 +229,72 @@ impl>, L: Deref> RapidGossipSync where L synthetic_update.htlc_maximum_msat = htlc_maximum_msat; } - network_graph.update_channel_unsigned(&synthetic_update)?; + if skip_update_for_unknown_channel { + continue; + } + + log_gossip!(self.logger, "Updating channel {} with flags {} from RGS announcement at {}", + short_channel_id, channel_flags, latest_seen_timestamp); + match network_graph.update_channel_unsigned(&synthetic_update) { + Ok(_) => {}, + Err(LightningError { action: ErrorAction::IgnoreDuplicateGossip, .. }) => {}, + Err(LightningError { action: ErrorAction::IgnoreAndLog(level), err }) => { + log_given_level!(self.logger, level, "Failed to apply channel update: {:?}", err); + }, + Err(LightningError { action: ErrorAction::IgnoreError, .. }) => {}, + Err(e) => return Err(e.into()), + } } self.network_graph.set_last_rapid_gossip_sync_timestamp(latest_seen_timestamp); + + if let Some(time) = current_time_unix { + self.network_graph.remove_stale_channels_and_tracking_with_time(time) + } + self.is_initial_sync_complete.store(true, Ordering::Release); + log_trace!(self.logger, "Done processing RGS data from {}", latest_seen_timestamp); Ok(latest_seen_timestamp) } } #[cfg(test)] mod tests { - use bitcoin::blockdata::constants::genesis_block; use bitcoin::Network; + #[cfg(feature = "std")] use lightning::ln::msgs::DecodeError; + use lightning::routing::gossip::NetworkGraph; use lightning::util::test_utils::TestLogger; - use crate::error::GraphSyncError; - use crate::RapidGossipSync; + use crate::processing::STALE_RGS_UPDATE_AGE_LIMIT_SECS; + use crate::{GraphSyncError, RapidGossipSync}; + + const VALID_RGS_BINARY: [u8; 300] = [ + 76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, + 79, 147, 30, 131, 101, 225, 90, 8, 156, 104, 214, 25, 0, 0, 0, 0, 0, 97, 227, 98, 218, + 0, 0, 0, 4, 2, 22, 7, 207, 206, 25, 164, 197, 231, 230, 231, 56, 102, 61, 250, 251, + 187, 172, 38, 46, 79, 247, 108, 44, 155, 48, 219, 238, 252, 53, 192, 6, 67, 2, 36, 125, + 157, 176, 223, 175, 234, 116, 94, 248, 201, 225, 97, 235, 50, 47, 115, 172, 63, 136, + 88, 216, 115, 11, 111, 217, 114, 84, 116, 124, 231, 107, 2, 158, 1, 242, 121, 152, 106, + 204, 131, 186, 35, 93, 70, 216, 10, 237, 224, 183, 89, 95, 65, 3, 83, 185, 58, 138, + 181, 64, 187, 103, 127, 68, 50, 2, 201, 19, 17, 138, 136, 149, 185, 226, 156, 137, 175, + 110, 32, 237, 0, 217, 90, 31, 100, 228, 149, 46, 219, 175, 168, 77, 4, 143, 38, 128, + 76, 97, 0, 0, 0, 2, 0, 0, 255, 8, 153, 192, 0, 2, 27, 0, 0, 0, 1, 0, 0, 255, 2, 68, + 226, 0, 6, 11, 0, 1, 2, 3, 0, 0, 0, 4, 0, 40, 0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 3, 232, + 0, 0, 0, 1, 0, 0, 0, 0, 29, 129, 25, 192, 255, 8, 153, 192, 0, 2, 27, 0, 0, 60, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 100, 0, 0, 2, 224, 0, 0, 0, 0, 58, 85, 116, 216, 0, 29, 0, + 0, 0, 1, 0, 0, 0, 125, 0, 0, 0, 0, 58, 85, 116, 216, 255, 2, 68, 226, 0, 6, 11, 0, 1, + 0, 0, 1, + ]; + const VALID_BINARY_TIMESTAMP: u64 = 1642291930; #[test] + #[cfg(feature = "std")] fn network_graph_fails_to_update_from_clipped_input() { - let block_hash = genesis_block(Network::Bitcoin).block_hash(); let logger = TestLogger::new(); - let network_graph = NetworkGraph::new(block_hash, &logger); + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); let example_input = vec![ 76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, @@ -237,7 +312,7 @@ mod tests { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 58, 85, 116, 216, 255, 2, 68, 226, 0, 6, 11, 0, 1, 24, 0, 0, 3, 232, 0, 0, 0, ]; - let rapid_sync = RapidGossipSync::new(&network_graph); + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); let update_result = rapid_sync.update_network_graph(&example_input[..]); assert!(update_result.is_err()); if let Err(GraphSyncError::DecodeError(DecodeError::ShortRead)) = update_result { @@ -248,7 +323,8 @@ mod tests { } #[test] - fn incremental_only_update_fails_without_prior_announcements() { + #[cfg(feature = "std")] + fn incremental_only_update_ignores_missing_channel() { let incremental_update_input = vec![ 76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, 79, 147, 30, 131, 101, 225, 90, 8, 156, 104, 214, 25, 0, 0, 0, 0, 0, 97, 229, 183, 167, @@ -257,23 +333,18 @@ mod tests { 68, 226, 0, 6, 11, 0, 1, 128, ]; - let block_hash = genesis_block(Network::Bitcoin).block_hash(); let logger = TestLogger::new(); - let network_graph = NetworkGraph::new(block_hash, &logger); + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); assert_eq!(network_graph.read_only().channels().len(), 0); - let rapid_sync = RapidGossipSync::new(&network_graph); + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); let update_result = rapid_sync.update_network_graph(&incremental_update_input[..]); - assert!(update_result.is_err()); - if let Err(GraphSyncError::LightningError(lightning_error)) = update_result { - assert_eq!(lightning_error.err, "Couldn't find channel for update"); - } else { - panic!("Unexpected update result: {:?}", update_result) - } + assert!(update_result.is_ok()); } #[test] + #[cfg(feature = "std")] fn incremental_only_update_fails_without_prior_updates() { let announced_update_input = vec![ 76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, @@ -291,26 +362,17 @@ mod tests { 2, 68, 226, 0, 6, 11, 0, 1, 128, ]; - let block_hash = genesis_block(Network::Bitcoin).block_hash(); let logger = TestLogger::new(); - let network_graph = NetworkGraph::new(block_hash, &logger); + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); assert_eq!(network_graph.read_only().channels().len(), 0); - let rapid_sync = RapidGossipSync::new(&network_graph); - let update_result = rapid_sync.update_network_graph(&announced_update_input[..]); - assert!(update_result.is_err()); - if let Err(GraphSyncError::LightningError(lightning_error)) = update_result { - assert_eq!( - lightning_error.err, - "Couldn't find previous directional data for update" - ); - } else { - panic!("Unexpected update result: {:?}", update_result) - } + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); + rapid_sync.update_network_graph(&announced_update_input[..]).unwrap(); } #[test] + #[cfg(feature = "std")] fn incremental_only_update_fails_without_prior_same_direction_updates() { let initialization_input = vec![ 76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, @@ -328,13 +390,12 @@ mod tests { 0, 1, 0, 0, 0, 125, 255, 2, 68, 226, 0, 6, 11, 0, 1, 5, 0, 0, 0, 0, 29, 129, 25, 192, ]; - let block_hash = genesis_block(Network::Bitcoin).block_hash(); let logger = TestLogger::new(); - let network_graph = NetworkGraph::new(block_hash, &logger); + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); assert_eq!(network_graph.read_only().channels().len(), 0); - let rapid_sync = RapidGossipSync::new(&network_graph); + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); let initialization_result = rapid_sync.update_network_graph(&initialization_input[..]); if initialization_result.is_err() { panic!( @@ -363,19 +424,11 @@ mod tests { 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 8, 153, 192, 0, 2, 27, 0, 0, 136, 0, 0, 0, 221, 255, 2, 68, 226, 0, 6, 11, 0, 1, 128, ]; - let update_result = rapid_sync.update_network_graph(&opposite_direction_incremental_update_input[..]); - assert!(update_result.is_err()); - if let Err(GraphSyncError::LightningError(lightning_error)) = update_result { - assert_eq!( - lightning_error.err, - "Couldn't find previous directional data for update" - ); - } else { - panic!("Unexpected update result: {:?}", update_result) - } + rapid_sync.update_network_graph(&opposite_direction_incremental_update_input[..]).unwrap(); } #[test] + #[cfg(feature = "std")] fn incremental_update_succeeds_with_prior_announcements_and_full_updates() { let initialization_input = vec![ 76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, @@ -395,13 +448,12 @@ mod tests { 25, 192, ]; - let block_hash = genesis_block(Network::Bitcoin).block_hash(); let logger = TestLogger::new(); - let network_graph = NetworkGraph::new(block_hash, &logger); + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); assert_eq!(network_graph.read_only().channels().len(), 0); - let rapid_sync = RapidGossipSync::new(&network_graph); + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); let initialization_result = rapid_sync.update_network_graph(&initialization_input[..]); assert!(initialization_result.is_ok()); @@ -436,8 +488,9 @@ mod tests { } #[test] - fn full_update_succeeds() { - let valid_input = vec![ + #[cfg(feature = "std")] + fn update_succeeds_when_duplicate_gossip_is_applied() { + let initialization_input = vec![ 76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, 79, 147, 30, 131, 101, 225, 90, 8, 156, 104, 214, 25, 0, 0, 0, 0, 0, 97, 227, 98, 218, 0, 0, 0, 4, 2, 22, 7, 207, 206, 25, 164, 197, 231, 230, 231, 56, 102, 61, 250, 251, @@ -449,20 +502,45 @@ mod tests { 110, 32, 237, 0, 217, 90, 31, 100, 228, 149, 46, 219, 175, 168, 77, 4, 143, 38, 128, 76, 97, 0, 0, 0, 2, 0, 0, 255, 8, 153, 192, 0, 2, 27, 0, 0, 0, 1, 0, 0, 255, 2, 68, 226, 0, 6, 11, 0, 1, 2, 3, 0, 0, 0, 4, 0, 40, 0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 3, 232, - 0, 0, 0, 1, 0, 0, 0, 0, 29, 129, 25, 192, 255, 8, 153, 192, 0, 2, 27, 0, 0, 60, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 100, 0, 0, 2, 224, 0, 0, 0, 0, 58, 85, 116, 216, 0, 29, 0, - 0, 0, 1, 0, 0, 0, 125, 0, 0, 0, 0, 58, 85, 116, 216, 255, 2, 68, 226, 0, 6, 11, 0, 1, - 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 58, 85, 116, 216, 255, 8, 153, 192, 0, 2, 27, 0, 0, 56, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 100, 0, 0, 2, 224, 0, 25, 0, 0, 0, 1, 0, 0, 0, 125, 255, 2, + 68, 226, 0, 6, 11, 0, 1, 4, 0, 0, 0, 0, 29, 129, 25, 192, 0, 5, 0, 0, 0, 0, 29, 129, + 25, 192, ]; - let block_hash = genesis_block(Network::Bitcoin).block_hash(); let logger = TestLogger::new(); - let network_graph = NetworkGraph::new(block_hash, &logger); + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); assert_eq!(network_graph.read_only().channels().len(), 0); - let rapid_sync = RapidGossipSync::new(&network_graph); - let update_result = rapid_sync.update_network_graph(&valid_input[..]); + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); + let initialization_result = rapid_sync.update_network_graph(&initialization_input[..]); + assert!(initialization_result.is_ok()); + + let single_direction_incremental_update_input = vec![ + 76, 68, 75, 1, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, + 79, 147, 30, 131, 101, 225, 90, 8, 156, 104, 214, 25, 0, 0, 0, 0, 0, 97, 229, 183, 167, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 8, 153, 192, 0, 2, 27, 0, 0, 136, 0, 0, 0, 221, 255, 2, + 68, 226, 0, 6, 11, 0, 1, 128, + ]; + let update_result_1 = rapid_sync.update_network_graph(&single_direction_incremental_update_input[..]); + // Apply duplicate update + let update_result_2 = rapid_sync.update_network_graph(&single_direction_incremental_update_input[..]); + assert!(update_result_1.is_ok()); + assert!(update_result_2.is_ok()); + } + + #[test] + #[cfg(feature = "std")] + fn full_update_succeeds() { + let logger = TestLogger::new(); + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); + + assert_eq!(network_graph.read_only().channels().len(), 0); + + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); + let update_result = rapid_sync.update_network_graph(&VALID_RGS_BINARY); if update_result.is_err() { panic!("Unexpected update result: {:?}", update_result) } @@ -484,4 +562,135 @@ mod tests { assert!(after.contains("619737530008010752")); assert!(after.contains("783241506229452801")); } + + #[test] + fn full_update_succeeds_at_the_beginning_of_the_unix_era() { + let logger = TestLogger::new(); + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); + + assert_eq!(network_graph.read_only().channels().len(), 0); + + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); + // this is mostly for checking uint underflow issues before the fuzzer does + let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(0)); + assert!(update_result.is_ok()); + assert_eq!(network_graph.read_only().channels().len(), 2); + } + + #[test] + fn prunes_after_update() { + // this is the timestamp encoded in the binary data of valid_input below + let logger = TestLogger::new(); + + let latest_nonpruning_time = VALID_BINARY_TIMESTAMP + 60 * 60 * 24 * 7; + + { + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); + assert_eq!(network_graph.read_only().channels().len(), 0); + + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); + let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(latest_nonpruning_time)); + assert!(update_result.is_ok()); + assert_eq!(network_graph.read_only().channels().len(), 2); + } + + { + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); + assert_eq!(network_graph.read_only().channels().len(), 0); + + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); + let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(latest_nonpruning_time + 1)); + assert!(update_result.is_ok()); + assert_eq!(network_graph.read_only().channels().len(), 0); + } + } + + #[test] + fn timestamp_edge_cases_are_handled_correctly() { + // this is the timestamp encoded in the binary data of valid_input below + let logger = TestLogger::new(); + + let latest_succeeding_time = VALID_BINARY_TIMESTAMP + STALE_RGS_UPDATE_AGE_LIMIT_SECS; + let earliest_failing_time = latest_succeeding_time + 1; + + { + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); + assert_eq!(network_graph.read_only().channels().len(), 0); + + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); + let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(latest_succeeding_time)); + assert!(update_result.is_ok()); + assert_eq!(network_graph.read_only().channels().len(), 0); + } + + { + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); + assert_eq!(network_graph.read_only().channels().len(), 0); + + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); + let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(earliest_failing_time)); + assert!(update_result.is_err()); + if let Err(GraphSyncError::LightningError(lightning_error)) = update_result { + assert_eq!( + lightning_error.err, + "Rapid Gossip Sync data is more than two weeks old" + ); + } else { + panic!("Unexpected update result: {:?}", update_result) + } + } + } + + #[test] + #[cfg(feature = "std")] + pub fn update_fails_with_unknown_version() { + let unknown_version_input = vec![ + 76, 68, 75, 2, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, + 79, 147, 30, 131, 101, 225, 90, 8, 156, 104, 214, 25, 0, 0, 0, 0, 0, 97, 227, 98, 218, + 0, 0, 0, 4, 2, 22, 7, 207, 206, 25, 164, 197, 231, 230, 231, 56, 102, 61, 250, 251, + 187, 172, 38, 46, 79, 247, 108, 44, 155, 48, 219, 238, 252, 53, 192, 6, 67, 2, 36, 125, + 157, 176, 223, 175, 234, 116, 94, 248, 201, 225, 97, 235, 50, 47, 115, 172, 63, 136, + 88, 216, 115, 11, 111, 217, 114, 84, 116, 124, 231, 107, 2, 158, 1, 242, 121, 152, 106, + 204, 131, 186, 35, 93, 70, 216, 10, 237, 224, 183, 89, 95, 65, 3, 83, 185, 58, 138, + 181, 64, 187, 103, 127, 68, 50, 2, 201, 19, 17, 138, 136, 149, 185, 226, 156, 137, 175, + 110, 32, 237, 0, 217, 90, 31, 100, 228, 149, 46, 219, 175, 168, 77, 4, 143, 38, 128, + 76, 97, 0, 0, 0, 2, 0, 0, 255, 8, 153, 192, 0, 2, 27, 0, 0, 0, 1, 0, 0, 255, 2, 68, + 226, 0, 6, 11, 0, 1, 2, 3, 0, 0, 0, 4, 0, 40, 0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 3, 232, + 0, 0, 0, 1, 0, 0, 0, 0, 29, 129, 25, 192, 255, 8, 153, 192, 0, 2, 27, 0, 0, 60, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 100, 0, 0, 2, 224, 0, 0, 0, 0, 58, 85, 116, 216, 0, 29, 0, + 0, 0, 1, 0, 0, 0, 125, 0, 0, 0, 0, 58, 85, 116, 216, 255, 2, 68, 226, 0, 6, 11, 0, 1, + 0, 0, 1, + ]; + + let logger = TestLogger::new(); + let network_graph = NetworkGraph::new(Network::Bitcoin, &logger); + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); + let update_result = rapid_sync.update_network_graph(&unknown_version_input[..]); + + assert!(update_result.is_err()); + + if let Err(GraphSyncError::DecodeError(DecodeError::UnknownVersion)) = update_result { + // this is the expected error type + } else { + panic!("Unexpected update result: {:?}", update_result) + } + } + + #[test] + fn fails_early_on_chain_hash_mismatch() { + let logger = TestLogger::new(); + // Set to testnet so that the VALID_RGS_BINARY chain hash of mainnet does not match. + let network_graph = NetworkGraph::new(Network::Testnet, &logger); + + assert_eq!(network_graph.read_only().channels().len(), 0); + + let rapid_sync = RapidGossipSync::new(&network_graph, &logger); + let update_result = rapid_sync.update_network_graph_no_std(&VALID_RGS_BINARY, Some(0)); + assert!(update_result.is_err()); + if let Err(GraphSyncError::LightningError(err)) = update_result { + assert_eq!(err.err, "Rapid Gossip Sync data's chain hash does not match the network graph's"); + } else { + panic!("Unexpected update result: {:?}", update_result) + } + } }