#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![deny(missing_docs)]
#![deny(unsafe_code)]
#![deny(non_upper_case_globals)]
#![deny(non_camel_case_types)]
#![deny(non_snake_case)]
#![deny(unused_mut)]
#![deny(unused_variables)]
#![deny(unused_imports)]
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#[cfg(ldk_bench)] extern crate criterion;
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(feature = "std")]
use std::fs::File;
use core::ops::Deref;
use core::sync::atomic::{AtomicBool, Ordering};
use lightning::io;
use lightning::routing::gossip::NetworkGraph;
use lightning::util::logger::Logger;
pub use crate::error::GraphSyncError;
mod error;
mod processing;
pub struct RapidGossipSync<NG: Deref<Target=NetworkGraph<L>>, L: Deref>
where L::Target: Logger {
network_graph: NG,
logger: L,
is_initial_sync_complete: AtomicBool
}
impl<NG: Deref<Target=NetworkGraph<L>>, L: Deref> RapidGossipSync<NG, L> where L::Target: Logger {
pub fn new(network_graph: NG, logger: L) -> Self {
Self {
network_graph,
logger,
is_initial_sync_complete: AtomicBool::new(false)
}
}
#[cfg(feature = "std")]
pub fn sync_network_graph_with_file_path(
&self,
sync_path: &str,
) -> Result<u32, GraphSyncError> {
let mut file = File::open(sync_path)?;
self.update_network_graph_from_byte_stream(&mut file)
}
#[cfg(feature = "std")]
pub fn update_network_graph(&self, update_data: &[u8]) -> Result<u32, GraphSyncError> {
let mut read_cursor = io::Cursor::new(update_data);
self.update_network_graph_from_byte_stream(&mut read_cursor)
}
pub fn update_network_graph_no_std(&self, update_data: &[u8], current_time_unix: Option<u64>) -> Result<u32, GraphSyncError> {
let mut read_cursor = io::Cursor::new(update_data);
self.update_network_graph_from_byte_stream_no_std(&mut read_cursor, current_time_unix)
}
pub fn network_graph(&self) -> &NG {
&self.network_graph
}
pub fn is_initial_sync_complete(&self) -> bool {
self.is_initial_sync_complete.load(Ordering::Acquire)
}
}
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
use std::fs;
use bitcoin::Network;
use lightning::ln::msgs::DecodeError;
use lightning::routing::gossip::NetworkGraph;
use lightning::util::test_utils::TestLogger;
use crate::RapidGossipSync;
#[test]
fn test_sync_from_file() {
struct FileSyncTest {
directory: String,
}
impl FileSyncTest {
fn new(tmp_directory: &str, valid_response: &[u8]) -> FileSyncTest {
let test = FileSyncTest { directory: tmp_directory.to_owned() };
let graph_sync_test_directory = test.get_test_directory();
fs::create_dir_all(graph_sync_test_directory).unwrap();
let graph_sync_test_file = test.get_test_file_path();
fs::write(graph_sync_test_file, valid_response).unwrap();
test
}
fn get_test_directory(&self) -> String {
self.directory.clone() + "/graph-sync-tests"
}
fn get_test_file_path(&self) -> String {
self.get_test_directory() + "/test_data.lngossip"
}
}
impl Drop for FileSyncTest {
fn drop(&mut self) {
fs::remove_dir_all(self.directory.clone()).unwrap();
}
}
let valid_response = 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,
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, 2, 0, 40, 0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 3, 232,
0, 0, 0, 1, 0, 0, 0, 0, 58, 85, 116, 216, 255, 8, 153, 192, 0, 2, 27, 0, 0, 25, 0, 0,
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 tmp_directory = "./rapid-gossip-sync-tests-tmp";
let sync_test = FileSyncTest::new(tmp_directory, &valid_response);
let graph_sync_test_file = sync_test.get_test_file_path();
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 sync_result = rapid_sync.sync_network_graph_with_file_path(&graph_sync_test_file);
if sync_result.is_err() {
panic!("Unexpected sync result: {:?}", sync_result)
}
assert_eq!(network_graph.read_only().channels().len(), 2);
let after = network_graph.to_string();
assert!(
after.contains("021607cfce19a4c5e7e6e738663dfafbbbac262e4ff76c2c9b30dbeefc35c00643")
);
assert!(
after.contains("02247d9db0dfafea745ef8c9e161eb322f73ac3f8858d8730b6fd97254747ce76b")
);
assert!(
after.contains("029e01f279986acc83ba235d46d80aede0b7595f410353b93a8ab540bb677f4432")
);
assert!(
after.contains("02c913118a8895b9e29c89af6e20ed00d95a1f64e4952edbafa84d048f26804c61")
);
assert!(after.contains("619737530008010752"));
assert!(after.contains("783241506229452801"));
}
#[test]
fn measure_native_read_from_file() {
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 start = std::time::Instant::now();
let sync_result = rapid_sync
.sync_network_graph_with_file_path("./res/full_graph.lngossip");
if let Err(crate::error::GraphSyncError::DecodeError(DecodeError::Io(io_error))) = &sync_result {
let error_string = format!("Input file lightning-rapid-gossip-sync/res/full_graph.lngossip is missing! Download it from https://bitcoin.ninja/ldk-compressed_graph-285cb27df79-2022-07-21.bin\n\n{:?}", io_error);
#[cfg(not(require_route_graph_test))]
{
println!("{}", error_string);
return;
}
#[cfg(require_route_graph_test)]
panic!("{}", error_string);
}
let elapsed = start.elapsed();
println!("initialization duration: {:?}", elapsed);
if sync_result.is_err() {
panic!("Unexpected sync result: {:?}", sync_result)
}
}
}
#[cfg(ldk_bench)]
pub mod bench {
use bitcoin::Network;
use criterion::Criterion;
use std::fs;
use lightning::routing::gossip::NetworkGraph;
use lightning::util::test_utils::TestLogger;
use crate::RapidGossipSync;
pub fn bench_reading_full_graph_from_file(b: &mut Criterion) {
let logger = TestLogger::new();
b.bench_function("read_full_graph_from_rgs", |b| b.iter(|| {
let network_graph = NetworkGraph::new(Network::Bitcoin, &logger);
let rapid_sync = RapidGossipSync::new(&network_graph, &logger);
let mut file = match fs::read("../lightning-rapid-gossip-sync/res/full_graph.lngossip") {
Ok(f) => f,
Err(io_error) => {
let error_string = format!(
"Input file lightning-rapid-gossip-sync/res/full_graph.lngossip is missing! Download it from https://bitcoin.ninja/ldk-compressed_graph-bc08df7542-2022-05-05.bin\n\n{:?}",
io_error);
#[cfg(not(require_route_graph_test))]
{
println!("{}", error_string);
return;
}
#[cfg(require_route_graph_test)]
panic!("{}", error_string);
},
};
rapid_sync.update_network_graph_no_std(&mut file, None).unwrap();
}));
}
}