From f0db4f45e2c7732fc574928b379b287d5a2ede3d Mon Sep 17 00:00:00 2001 From: Charmaine Ndolo Date: Fri, 12 Jan 2024 17:01:05 +0100 Subject: [PATCH] Add support for network addresses --- network-parser/Cargo.toml | 6 +- network-parser/src/helpers.rs | 186 +++++++++++++++++++++++++++++++--- network-parser/src/lib.rs | 32 ++++-- 3 files changed, 202 insertions(+), 22 deletions(-) diff --git a/network-parser/Cargo.toml b/network-parser/Cargo.toml index 9f85da2..785fc4c 100644 --- a/network-parser/Cargo.toml +++ b/network-parser/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "network-parser" -version = "0.1.0" +version = "0.2.0" #repository = "" -authors = ["Anonymous Author <>"] +authors = ["Charmaine Ndolo "] description = "Read the LN's topology from a JSON file." -#keywords = [] +keywords = ["lightning-network", "graph"] license = "MIT" readme = "./README.md" edition = "2021" diff --git a/network-parser/src/helpers.rs b/network-parser/src/helpers.rs index d0b634d..c0925a2 100644 --- a/network-parser/src/helpers.rs +++ b/network-parser/src/helpers.rs @@ -1,27 +1,41 @@ -use serde::Deserialize; +use serde::{Deserialize, Deserializer}; use serde_aux::prelude::*; use std::hash::{Hash, Hasher}; +use std::str::FromStr; use crate::*; #[derive(Deserialize, Debug, Default)] pub struct RawLnresearchGraph { - pub(crate) nodes: Vec, + pub(crate) nodes: Vec, #[serde(alias = "adjacency")] pub(crate) edges: Vec>, } #[derive(Deserialize, Debug, Default)] pub struct RawLndGraph { - pub(crate) nodes: Vec, + pub(crate) nodes: Vec, #[serde(alias = "adjacency")] pub(crate) edges: Vec, } +serde_aux::StringOrVecToVecParser!(parse_between_commas, |c| { c == ',' }, true); + +#[derive(Deserialize, Debug, Clone, Default, Eq, PartialEq)] +pub struct RawLnresearchNode { + #[serde(alias = "pub_key")] + pub(crate) id: Option, + pub(crate) alias: Option, + #[serde(deserialize_with = "addr_lnr_deserialize")] + #[serde(default)] + pub(crate) addresses: Option>, +} + #[derive(Deserialize, Debug, Clone, Default, Eq, PartialEq)] -pub struct RawNode { +pub struct RawLndNode { #[serde(alias = "pub_key")] pub(crate) id: Option, pub(crate) alias: Option, + pub(crate) addresses: Option>, } #[derive(Deserialize, Debug, Clone, Default)] @@ -69,15 +83,44 @@ pub struct NodePolicy { pub htlc_maximum_msat: Option, #[serde(alias = "time_lock_delta")] pub cltv_expiry_delta: Option, - pub last_update: u32, } impl Node { - pub(crate) fn from_raw(raw_node: RawNode) -> Node { + pub(crate) fn from_raw_lnresearch(raw_node: RawLnresearchNode) -> Node { + let mut addresses = vec![]; + if let Some(raw_addresses) = raw_node.addresses { + for raw_addr in raw_addresses { + let mut addr = Address::default(); + for (i, part) in raw_addr.split("://").enumerate() { + if i == 0 { + // the network part which always seems to be tcp + println!("part {}", part); + match part { + "ipv4" | "ipv6" | "torv2" | "torv3" => addr.network = "tcp".to_owned(), + _ => { + break; + } + } + } else if i == 1 { + println!("part 1 {}", part); + // the addr part + addr.addr = part.to_owned(); + } + } + addresses.push(addr); + } + } + Node { + id: raw_node.id.expect("Error in node ID"), + alias: raw_node.alias.unwrap_or_default(), + addresses, + } + } + pub(crate) fn from_raw_lnd(raw_node: RawLndNode) -> Node { Node { id: raw_node.id.expect("Error in node ID"), alias: raw_node.alias.unwrap_or_default(), - last_update: Default::default(), + addresses: raw_node.addresses.unwrap_or_default(), } } } @@ -275,12 +318,31 @@ impl PartialEq for LndRawEdge { self.channel_id == other.channel_id && self.source == other.source } } + +fn addr_lnr_deserialize<'de, D>(deserializer: D) -> Result>, D::Error> +where + D: Deserializer<'de>, +{ + let str_sequence = String::deserialize(deserializer)?; + println!("str_sequence {}", str_sequence); + if str_sequence.is_empty() { + Ok(None) + } else { + Ok(Some( + str_sequence + .split(',') + .map(|item| item.to_owned()) + .collect(), + )) + } +} + #[cfg(test)] mod tests { use super::*; #[test] - fn node_wo_id_is_ignored() { + fn lnr_node_wo_id_is_ignored() { let json_str = r##"{ "nodes": [ { @@ -310,10 +372,6 @@ mod tests { let actual = graph.nodes.len(); let expected = 1; assert_eq!(actual, expected); - let graph = Graph::from_lnd_json_str(json_str).unwrap(); - let actual = graph.nodes.len(); - let expected = 1; - assert_eq!(actual, expected); } #[test] @@ -526,4 +584,108 @@ mod tests { assert_eq!(e.capacity, expected); } } + + #[test] + fn lnd_addresses_works() { + let json_str = r##"{ + "nodes": [ + { + "id": "021fa5be893f5b3ec37cf5a0f4e984f35a32", + "addresses": [ + { + "network": "tcp", + "addr": "159.69.16.168:9735" + }, + { + "network": "tcp", + "addr": "[2a01:4f8:1c1e:abc1::1]:9735" + } + ] + }, + { + "id": "00e332bc1b7d8db0e705df3f087d285f9c06", + "addresses": [ + ] + }, + { + "id": "026cf8782a7735ac62f0e71da85c93f1d864", + "addresses": [ + { + "network": "tcp", + "addr": "br4uj734xva77u7yt6oevyp2ropqjl7nw2jyzeejwmd7dzlouenkfmid.onion:9735" + } + ] + } + ], + "edges": [] + }"##; + let graph = Graph::from_lnd_json_str(&json_str).unwrap(); + assert_eq!(graph.nodes.len(), 3); + let expected: HashMap> = HashMap::from([ + ( + "021fa5be893f5b3ec37cf5a0f4e984f35a32".to_owned(), + vec![ + Address { + network: "tcp".to_owned(), + addr: "159.69.16.168:9735".to_owned(), + }, + Address { + network: "tcp".to_owned(), + addr: "[2a01:4f8:1c1e:abc1::1]:9735".to_owned(), + }, + ], + ), + ("00e332bc1b7d8db0e705df3f087d285f9c06".to_owned(), vec![]), + ( + "026cf8782a7735ac62f0e71da85c93f1d864".to_owned(), + vec![Address { + network: "tcp".to_owned(), + addr: "br4uj734xva77u7yt6oevyp2ropqjl7nw2jyzeejwmd7dzlouenkfmid.onion:9735" + .to_owned(), + }], + ), + ]); + for node in graph.nodes { + assert_eq!(*expected.get(&node.id).unwrap(), node.addresses); + } + } + + #[test] + fn lnr_addresses_works() { + let path_to_file = Path::new("../test_data/trivial_connected.json"); + let graph = Graph::from_json_file(path_to_file, GraphSource::Lnresearch).unwrap(); + let expected: HashMap> = HashMap::from([ + ( + "034".to_owned(), + vec![Address { + network: "tcp".to_owned(), + addr: "212.108.220.135:9735".to_owned(), + }], + ), + ( + "025".to_owned(), + vec![Address { + network: "tcp".to_owned(), + addr: "104.236.54.112:9735".to_owned(), + }], + ), + ( + "036".to_owned(), + vec![ + Address { + network: "tcp".to_owned(), + addr: "218.250.157.241:9735".to_owned(), + }, + Address { + network: "tcp".to_string(), + addr: "wu5mkpokybtbf6dwdaepnujbzxpm6mqqqm2hwob6ndt5k74iujd2pdyd.onion:9735" + .to_owned(), + }, + ], + ), + ]); + for node in graph.nodes { + assert_eq!(*expected.get(&node.id).unwrap(), node.addresses); + } + } } diff --git a/network-parser/src/lib.rs b/network-parser/src/lib.rs index 165e43e..d30272b 100644 --- a/network-parser/src/lib.rs +++ b/network-parser/src/lib.rs @@ -26,7 +26,13 @@ pub struct Graph { pub struct Node { pub id: ID, pub alias: String, - pub last_update: usize, + pub addresses: Vec
, +} + +#[derive(Deserialize, Clone, Debug, Default, PartialEq, Eq)] +pub struct Address { + pub network: String, + pub addr: String, } #[derive(Deserialize, Clone, Debug, Default)] @@ -79,19 +85,28 @@ impl Graph { Self::from_json_str(&json_str, graph_source) } - fn nodes_from_raw_graph(nodes: &[RawNode]) -> HashSet { + fn nodes_from_raw_lnd_graph(nodes: &[RawLndNode]) -> HashSet { + // discard nodes without ID + nodes + .iter() + .filter(|raw_node| raw_node.id.clone().unwrap_or_default() != ID::default()) + .map(|raw_node| Node::from_raw_lnd(raw_node.clone())) + .collect() + } + + fn nodes_from_raw_lnresearch_graph(nodes: &[RawLnresearchNode]) -> HashSet { // discard nodes without ID nodes .iter() .filter(|raw_node| raw_node.id.clone().unwrap_or_default() != ID::default()) - .map(|raw_node| Node::from_raw(raw_node.clone())) + .map(|raw_node| Node::from_raw_lnresearch(raw_node.clone())) .collect() } pub fn from_lnresearch_json_str(json_str: &str) -> Result { let raw_graph: RawLnresearchGraph = serde_json::from_str(json_str).expect("Error deserialising JSON str!"); - let nodes = Self::nodes_from_raw_graph(&raw_graph.nodes); + let nodes = Self::nodes_from_raw_lnresearch_graph(&raw_graph.nodes); let mut edges: HashMap> = HashMap::with_capacity(raw_graph.edges.len()); // discard edges with unknown IDs let edges_vec: Vec> = raw_graph @@ -132,7 +147,7 @@ impl Graph { pub fn from_lnd_json_str(json_str: &str) -> Result { let raw_graph: RawLndGraph = serde_json::from_str(json_str).expect("Error deserialising JSON str!"); - let nodes = Self::nodes_from_raw_graph(&raw_graph.nodes); + let nodes = Self::nodes_from_raw_lnd_graph(&raw_graph.nodes); let mut edges: HashMap> = HashMap::with_capacity(raw_graph.edges.len()); // discard edges with unknown IDs let mut edges_vec = vec![]; @@ -279,7 +294,10 @@ mod tests { let expected = Node { id: "021f0f2a5b46871b23f690a5be893f5b3ec37cf5a0fd8b89872234e984df35ea32".to_string(), alias: "MilliBit".to_string(), - last_update: 54321, + addresses: vec![Address { + network: "tcp".to_string(), + addr: "80.115.186.52:9735".to_string(), + }], }; assert_eq!(*actual, expected); } @@ -395,7 +413,7 @@ mod tests { let expected = Node { id: "021f0f2a5b46871b23f690a5be893f5b3ec37cf5a0fd8b89872234e984df35ea32".to_string(), alias: String::default(), - last_update: 54321, + addresses: vec![], }; assert_eq!(*actual, expected); }