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::{
};
use lightning::routing::gossip::NetworkGraph;
use lightning::util::logger::Logger;
-use lightning::{log_warn, log_trace, log_given_level};
+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(all(feature = "std", not(test)))]
use std::time::{SystemTime, UNIX_EPOCH};
const STALE_RGS_UPDATE_AGE_LIMIT_SECS: u64 = 60 * 60 * 24 * 14;
impl<NG: Deref<Target=NetworkGraph<L>>, L: Deref> RapidGossipSync<NG, L> where L::Target: Logger {
+ #[cfg(feature = "std")]
pub(crate) fn update_network_graph_from_byte_stream<R: io::Read>(
&self,
read_cursor: &mut R,
) -> Result<u32, GraphSyncError> {
- #[allow(unused_mut)]
+ #[allow(unused_mut, unused_assignments)]
let mut current_time_unix = None;
- #[cfg(all(feature = "std", not(test)))]
+ #[cfg(not(test))]
{
// Note that many tests rely on being able to set arbitrarily old timestamps, thus we
// disable this check during tests!
mut read_cursor: &mut R,
current_time_unix: Option<u64>
) -> Result<u32, GraphSyncError> {
+ log_trace!(self.logger, "Processing RGS data...");
let mut prefix = [0u8; 4];
read_cursor.read_exact(&mut prefix)?;
return Err(DecodeError::UnknownVersion.into());
}
- let chain_hash: BlockHash = Readable::read(read_cursor)?;
+ 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 latest_seen_timestamp: u32 = Readable::read(read_cursor)?;
if let Some(time) = current_time_unix {
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,
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);
}
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, .. }) => {},
}
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)
}
}
mod tests {
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::processing::STALE_RGS_UPDATE_AGE_LIMIT_SECS;
- use crate::RapidGossipSync;
+ 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,
const VALID_BINARY_TIMESTAMP: u64 = 1642291930;
#[test]
+ #[cfg(feature = "std")]
fn network_graph_fails_to_update_from_clipped_input() {
let logger = TestLogger::new();
let network_graph = NetworkGraph::new(Network::Bitcoin, &logger);
}
#[test]
+ #[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,
}
#[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,
}
#[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,
}
#[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,
}
#[test]
+ #[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,
}
#[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(), 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 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(), 2);
+ assert_eq!(network_graph.read_only().channels().len(), 0);
}
{
}
#[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,
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)
+ }
+ }
}