Skip to content

Commit 1110d4d

Browse files
committed
Add support for reading graph updates from a file, and example binary reading test.
1 parent dca5d39 commit 1110d4d

File tree

2 files changed

+123
-35
lines changed

2 files changed

+123
-35
lines changed

lightning-graph-sync/src/lib.rs

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use core::ops::Deref;
2+
use std::fs::File;
23

34
use lightning::chain;
45
use lightning::routing::network_graph;
@@ -9,19 +10,22 @@ use crate::error::GraphSyncError;
910
pub mod error;
1011
pub mod processing;
1112

12-
pub async fn sync_network_graph<CS: Deref>(
13+
pub async fn sync_network_graph_with_url<CS: Deref>(
1314
network_graph: &network_graph::NetworkGraph,
1415
sync_host: &str,
1516
sync_port: u16,
1617
sync_path: &str,
1718
chain_source: &Option<CS>,
1819
) -> Result<(), GraphSyncError>
19-
where
20-
CS::Target: chain::Access,
20+
where
21+
CS::Target: chain::Access,
2122
{
2223
// make sure there is precisely one leading slash
2324
let canonical_path = format!("/{}", sync_path.trim_start_matches("/"));
24-
println!("Attempting sync from: http://{}:{}{}", sync_host, sync_port, canonical_path);
25+
println!(
26+
"Attempting sync from: http://{}:{}{}",
27+
sync_host, sync_port, canonical_path
28+
);
2529
let http_endpoint = HttpEndpoint::for_host(sync_host.into()).with_port(sync_port);
2630
let http_client_result = HttpClient::connect(&http_endpoint);
2731
let mut http_client = http_client_result?;
@@ -36,20 +40,29 @@ pub async fn sync_network_graph<CS: Deref>(
3640
processing::update_network_graph(&network_graph, &response_bytes[..], &chain_source)
3741
}
3842

43+
pub fn sync_network_graph_with_file_path<CS: Deref>(
44+
network_graph: &network_graph::NetworkGraph,
45+
sync_path: &str,
46+
chain_source: &Option<CS>,
47+
) -> Result<(), GraphSyncError>
48+
where
49+
CS::Target: chain::Access,
50+
{
51+
let mut file = File::open(sync_path)?;
52+
processing::read_network_graph(&network_graph, &mut file, &chain_source)
53+
}
54+
3955
#[cfg(test)]
4056
mod tests {
4157
use bitcoin::blockdata::constants::genesis_block;
4258
use bitcoin::Network;
4359

4460
use lightning::routing::network_graph::NetworkGraph;
61+
4562
use crate::error::GraphSyncError;
63+
use crate::sync_network_graph_with_url;
4664

47-
use crate::sync_network_graph;
4865

49-
#[test]
50-
fn it_works() {
51-
assert_eq!(2 + 2, 4);
52-
}
5366

5467
#[tokio::test]
5568
async fn test_http_request() {
@@ -58,28 +71,21 @@ mod tests {
5871

5972
let chain_source: Option<Box<dyn lightning::chain::Access>> = None;
6073

61-
let sync_result = sync_network_graph(
74+
let sync_result = sync_network_graph_with_url(
6275
&network_graph,
6376
"localhost",
6477
3000,
6578
"delta/500",
6679
&chain_source,
67-
).await;
80+
)
81+
.await;
6882

6983
if !sync_result.is_ok() {
7084
let error_string = match sync_result.as_ref().unwrap_err() {
71-
GraphSyncError::IOError(error) => {
72-
error.to_string()
73-
}
74-
GraphSyncError::DecodeError(error) => {
75-
error.to_string()
76-
}
77-
GraphSyncError::LightningError(error) => {
78-
(&error.err).to_string()
79-
}
80-
GraphSyncError::ProcessingError(error) => {
81-
error.to_string()
82-
}
85+
GraphSyncError::IOError(error) => error.to_string(),
86+
GraphSyncError::DecodeError(error) => error.to_string(),
87+
GraphSyncError::LightningError(error) => (&error.err).to_string(),
88+
GraphSyncError::ProcessingError(error) => error.to_string(),
8389
};
8490
println!("Error: {}", error_string);
8591
}

lightning-graph-sync/src/processing.rs

Lines changed: 94 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,66 @@
11
use core::ops::Deref;
22
use std::io;
33
use std::io::Read;
4-
use lightning::chain;
54

5+
use lightning::chain;
66
use lightning::ln::msgs;
77
use lightning::ln::wire::encode::Encode;
88
use lightning::routing::network_graph;
9-
use lightning::util::ser::{BigSize, FixedLengthReader, Readable};
9+
use lightning::util::ser::{BigSize, Readable};
1010

1111
use crate::error::GraphSyncError;
1212

13-
pub fn update_network_graph<CS: Deref>(network_graph: &network_graph::NetworkGraph, update_data: &[u8], chain_source: &Option<CS>) -> Result<(), GraphSyncError> where CS::Target: chain::Access {
14-
let total_input_length = update_data.len() as u64;
13+
pub fn update_network_graph<CS: Deref>(
14+
network_graph: &network_graph::NetworkGraph,
15+
update_data: &[u8],
16+
chain_source: &Option<CS>,
17+
) -> Result<(), GraphSyncError>
18+
where
19+
CS::Target: chain::Access,
20+
{
1521
let mut read_cursor = io::Cursor::new(update_data);
22+
read_network_graph(&network_graph, &mut read_cursor, &chain_source)
23+
}
1624

25+
pub(crate) fn read_network_graph<R: Read, CS: Deref>(
26+
network_graph: &network_graph::NetworkGraph,
27+
read_cursor: &mut R,
28+
chain_source: &Option<CS>,
29+
) -> Result<(), GraphSyncError>
30+
where
31+
CS::Target: chain::Access,
32+
{
1733
let mut prefix = Vec::with_capacity(4);
1834
prefix.resize(4, 0);
1935
read_cursor.read_exact(&mut prefix)?;
2036

2137
let _deserialize_unsigned = match &prefix[..] {
2238
&[71, 83, 80, 1] => {
23-
return Err(GraphSyncError::ProcessingError("Signed deserialization not supported".to_string()));
39+
return Err(GraphSyncError::ProcessingError(
40+
"Signed deserialization not supported".to_string(),
41+
));
2442
}
2543
&[76, 68, 75, 2] => true,
2644
_ => {
27-
return Err(GraphSyncError::ProcessingError("Prefix must equal 'LDK' and the byte 2".to_string()));
45+
return Err(GraphSyncError::ProcessingError(
46+
"Prefix must equal 'LDK' and the byte 2".to_string(),
47+
));
2848
}
2949
};
3050

51+
// shadow variable for ownership release after length-restricted reads
52+
let mut read_cursor = read_cursor;
3153
loop {
32-
let message_length: BigSize = Readable::read(&mut read_cursor)?;
33-
let mut restricted_cursor = FixedLengthReader::new(&mut read_cursor, message_length.0);
54+
let message_length_result = Readable::read(read_cursor);
55+
let message_length: BigSize = if let Ok(message_length) = message_length_result {
56+
message_length
57+
} else if let Err(msgs::DecodeError::ShortRead) = message_length_result {
58+
break;
59+
} else {
60+
return Err(message_length_result.err().unwrap().into());
61+
};
62+
// let mut restricted_cursor = FixedLengthReader::new(&read_cursor, message_length.0);
63+
let mut restricted_cursor = read_cursor.take(message_length.0);
3464

3565
let message_type: u16 = Readable::read(&mut restricted_cursor)?;
3666

@@ -60,10 +90,62 @@ pub fn update_network_graph<CS: Deref>(network_graph: &network_graph::NetworkGra
6090
// propagate the error
6191
let _update = update_result?;
6292

63-
if read_cursor.position() == total_input_length {
64-
break;
65-
}
93+
read_cursor = restricted_cursor.into_inner();
6694
}
6795

6896
Ok(())
69-
}
97+
}
98+
99+
#[cfg(test)]
100+
mod tests {
101+
use bitcoin::blockdata::constants::genesis_block;
102+
use bitcoin::Network;
103+
104+
use lightning::routing::network_graph::NetworkGraph;
105+
106+
use crate::processing::update_network_graph;
107+
108+
#[test]
109+
fn network_graph_updates_from_example_binary_input() {
110+
let block_hash = genesis_block(Network::Bitcoin).block_hash();
111+
let network_graph = NetworkGraph::new(block_hash);
112+
113+
let before = network_graph.to_string();
114+
assert_eq!(before.len(), 31);
115+
116+
let chain_source: Option<Box<dyn lightning::chain::Access>> = None;
117+
118+
let example_input = vec![
119+
76, 68, 75, 2, 176, 1, 0, 0, 0, 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162,
120+
70, 174, 99, 247, 79, 147, 30, 131, 101, 225, 90, 8, 156, 104, 214, 25, 0, 0, 0, 0, 0,
121+
10, 209, 73, 0, 2, 144, 0, 0, 2, 96, 250, 182, 51, 6, 110, 215, 177, 217, 185, 184,
122+
160, 250, 200, 126, 21, 121, 209, 112, 158, 135, 77, 40, 160, 209, 113, 161, 245, 196,
123+
59, 184, 119, 2, 180, 101, 113, 124, 204, 150, 157, 103, 192, 42, 110, 145, 224, 176,
124+
14, 26, 197, 136, 6, 143, 13, 46, 3, 200, 220, 34, 228, 191, 104, 238, 134, 80, 3, 167,
125+
152, 93, 150, 15, 179, 70, 61, 160, 42, 198, 253, 53, 249, 37, 29, 21, 176, 28, 136,
126+
240, 237, 138, 165, 218, 254, 14, 245, 123, 228, 145, 150, 2, 25, 120, 160, 148, 15,
127+
56, 179, 42, 198, 99, 96, 81, 85, 53, 227, 51, 62, 134, 4, 175, 0, 151, 217, 104, 180,
128+
201, 249, 117, 196, 87, 94, 150, 74, 1, 2, 111, 226, 140, 10, 182, 241, 179, 114, 193,
129+
166, 162, 70, 174, 99, 247, 79, 147, 30, 131, 101, 225, 90, 8, 156, 104, 214, 25, 0, 0,
130+
0, 0, 0, 10, 209, 73, 0, 2, 144, 0, 0, 97, 139, 55, 91, 1, 2, 0, 40, 0, 0, 0, 0, 0, 0,
131+
3, 232, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 123, 235, 5, 192, 74, 1, 2, 111, 226,
132+
140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, 79, 147, 30, 131, 101,
133+
225, 90, 8, 156, 104, 214, 25, 0, 0, 0, 0, 0, 10, 209, 73, 0, 2, 144, 0, 0, 97, 138,
134+
144, 189, 1, 1, 0, 40, 0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 0, 0, 0, 0, 5, 220, 0, 0, 0, 0,
135+
123, 235, 5, 192,
136+
];
137+
let update_result = update_network_graph(&network_graph, &example_input[..], &chain_source);
138+
assert!(update_result.is_ok());
139+
140+
let after = network_graph.to_string();
141+
assert_eq!(after.len(), 1132);
142+
assert!(after.contains("779484474903625728: features: 0000"));
143+
assert!(after.contains(
144+
"node_one: 0260fab633066ed7b1d9b9b8a0fac87e1579d1709e874d28a0d171a1f5c43bb877"
145+
));
146+
assert!(after.contains(
147+
"node_two: 02b465717ccc969d67c02a6e91e0b00e1ac588068f0d2e03c8dc22e4bf68ee8650"
148+
));
149+
assert!(after.contains("channels: [779484474903625728]"));
150+
}
151+
}

0 commit comments

Comments
 (0)