From 1d7522ee155e3e20540a523229d089a3a97477b5 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Thu, 17 Apr 2025 13:02:21 +0100 Subject: [PATCH 01/17] sim-ln/refactor: Improve activity dest node lookup via Graph Switches from an async closure to direct NetworkGraph usage for activity destination node lookup. This improves efficiency and maintainability, and enables destination aliases by validating against the graph and erroring on duplicates. --- sim-cli/src/parsing.rs | 104 +++++++++++++++++++++++++----------- simln-lib/src/cln.rs | 37 ++++++++++++- simln-lib/src/eclair.rs | 39 +++++++++++++- simln-lib/src/lib.rs | 46 +++++++++++++++- simln-lib/src/lnd.rs | 39 +++++++++++++- simln-lib/src/sim_node.rs | 41 +++++++++++++- simln-lib/src/test_utils.rs | 3 +- 7 files changed, 270 insertions(+), 39 deletions(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index 84696ba7..1b3dd360 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -5,12 +5,11 @@ use log::LevelFilter; use serde::{Deserialize, Serialize}; use simln_lib::{ cln, cln::ClnNode, eclair, eclair::EclairNode, lnd, lnd::LndNode, serializers, - ActivityDefinition, Amount, Interval, LightningError, LightningNode, NodeId, NodeInfo, + ActivityDefinition, Amount, Graph, Interval, LightningError, LightningNode, NodeId, NodeInfo, Simulation, SimulationCfg, WriteResults, }; use std::collections::HashMap; use std::fs; -use std::ops::AsyncFn; use std::path::PathBuf; use std::sync::Arc; use tokio::sync::Mutex; @@ -159,20 +158,15 @@ pub async fn create_simulation(cli: &Cli) -> Result { let (clients, clients_info) = get_clients(nodes).await?; // We need to be able to look up destination nodes in the graph, because we allow defined activities to send to // nodes that we do not control. To do this, we can just grab the first node in our map and perform the lookup. - let get_node = async |pk: &PublicKey| -> Result { - if let Some(c) = clients.values().next() { - return c.lock().await.get_node_info(pk).await; - } - - Err(LightningError::GetNodeInfoError( - "no nodes for query".to_string(), - )) + let graph = match clients.values().next() { + Some(client) => client.lock().await.get_graph().await?, + None => panic!("Graph is empty"), }; let (pk_node_map, alias_node_map) = add_node_to_maps(&clients_info).await?; let validated_activities = - validate_activities(activity, pk_node_map, alias_node_map, get_node).await?; + validate_activities(activity, pk_node_map, alias_node_map, graph).await?; let tasks = TaskTracker::new(); Ok(Simulation::new(cfg, clients, validated_activities, tasks)) @@ -215,9 +209,9 @@ async fn get_clients( /// validation. async fn add_node_to_maps( nodes: &HashMap, -) -> Result<(HashMap, HashMap), LightningError> { +) -> Result<(HashMap, HashMap>), LightningError> { let mut pk_node_map = HashMap::new(); - let mut alias_node_map = HashMap::new(); + let mut alias_node_map: HashMap> = HashMap::new(); for node_info in nodes.values() { log::info!( @@ -241,7 +235,7 @@ async fn add_node_to_maps( ))); } - alias_node_map.insert(node_info.alias.clone(), node_info.clone()); + alias_node_map.insert(node_info.alias.clone(), vec![node_info.clone()]); } pk_node_map.insert(node_info.pubkey, node_info.clone()); @@ -255,31 +249,72 @@ async fn add_node_to_maps( async fn validate_activities( activity: Vec, pk_node_map: HashMap, - alias_node_map: HashMap, - get_node_info: impl AsyncFn(&PublicKey) -> Result, + alias_node_map: HashMap>, + mut graph: Graph, ) -> Result, LightningError> { let mut validated_activities = vec![]; + // Store graph nodes' information keyed by their alias. + // An alias can be mapped to multiple nodes because it is not a unique identifier. + let mut graph_nodes_by_alias: HashMap> = HashMap::new(); + + // sort graph nodes by alias for easy lookups. + for node in graph.get_nodes()? { + if graph_nodes_by_alias.contains_key(&node.1.alias) { + if let Some(node_infos) = graph_nodes_by_alias.get(&node.1.alias) { + let mut updated_node_infos = node_infos.clone(); + updated_node_infos.extend(vec![node.1.clone()]); + graph_nodes_by_alias.insert(node.1.alias.clone(), vec![node.1]); + } + } else { + graph_nodes_by_alias.insert(node.1.alias.clone(), vec![node.1]); + } + } + // Make all the activities identifiable by PK internally for act in activity.into_iter() { - // We can only map aliases to nodes we control, so if either the source or destination alias + // We can only map source aliases to nodes we control, so if the source alias // is not in alias_node_map, we fail - let source = if let Some(source) = match &act.source { - NodeId::PublicKey(pk) => pk_node_map.get(pk), - NodeId::Alias(a) => alias_node_map.get(a), - } { - source.clone() - } else { - return Err(LightningError::ValidationError(format!( - "activity source {} not found in nodes.", - act.source - ))); + let source = match &act.source { + NodeId::PublicKey(pk) => { + if let Some(node_info) = pk_node_map.get(pk) { + node_info.clone() + } else { + return Err(LightningError::ValidationError(format!( + "activity source {} not found in simulation nodes.", + act.source + ))); + } + }, + NodeId::Alias(a) => { + if let Some(node_infos) = alias_node_map.get(a) { + node_infos[0].clone() + } else { + return Err(LightningError::ValidationError(format!( + "activity source {} not found in simulation nodes.", + act.source + ))); + } + }, }; let destination = match &act.destination { NodeId::Alias(a) => { - if let Some(info) = alias_node_map.get(a) { - info.clone() + if let Some(node_infos) = alias_node_map.get(a) { + node_infos[0].clone() + } else if let Some(node_infos) = graph_nodes_by_alias.get(a) { + if node_infos.len() > 1 { + let pks: Vec = node_infos + .iter() + .map(|node_info| node_info.pubkey) + .collect(); + return Err(LightningError::ValidationError(format!( + "Multiple nodes in the graph have the same destination alias - {}. + Use one of these public keys as the destination instead - {:?}", + a, pks + ))); + } + node_infos[0].clone() } else { return Err(LightningError::ValidationError(format!( "unknown activity destination: {}.", @@ -288,10 +323,15 @@ async fn validate_activities( } }, NodeId::PublicKey(pk) => { - if let Some(info) = pk_node_map.get(pk) { - info.clone() + if let Some(node_info) = pk_node_map.get(pk) { + node_info.clone() + } else if let Some(node_info) = graph.get_node_by_pk(*pk)? { + node_info.clone() } else { - get_node_info(pk).await? + return Err(LightningError::ValidationError(format!( + "unknown activity destination: {}.", + act.destination + ))); } }, }; diff --git a/simln-lib/src/cln.rs b/simln-lib/src/cln.rs index 1aa722ae..d99c71c2 100644 --- a/simln-lib/src/cln.rs +++ b/simln-lib/src/cln.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use async_trait::async_trait; use bitcoin::secp256k1::PublicKey; use bitcoin::Network; @@ -17,7 +19,8 @@ use tonic::transport::{Certificate, Channel, ClientTlsConfig, Identity}; use triggered::Listener; use crate::{ - serializers, LightningError, LightningNode, NodeId, NodeInfo, PaymentOutcome, PaymentResult, + serializers, Graph, LightningError, LightningNode, NodeId, NodeInfo, PaymentOutcome, + PaymentResult, }; #[derive(Serialize, Deserialize, Debug, Clone)] @@ -263,6 +266,38 @@ impl LightningNode for ClnNode { node_channels.extend(self.node_channels(false).await?); Ok(node_channels) } + + async fn get_graph(&mut self) -> Result { + let nodes: Vec = self + .client + .list_nodes(ListnodesRequest { id: None }) + .await + .map_err(|err| LightningError::GetNodeInfoError(err.to_string()))? + .into_inner() + .nodes; + + let mut nodes_by_pk: HashMap = HashMap::new(); + let channels = HashMap::new(); + + for node in nodes { + nodes_by_pk.insert( + PublicKey::from_slice(&node.nodeid).expect("Public Key not valid"), + NodeInfo { + pubkey: PublicKey::from_slice(&node.nodeid).expect("Public Key not valid"), + alias: node.clone().alias.unwrap_or(String::new()), + features: node + .features + .clone() + .map_or(NodeFeatures::empty(), NodeFeatures::from_be_bytes), + }, + ); + } + + Ok(Graph { + nodes_by_pk, + channels, + }) + } } async fn reader(filename: &str) -> Result, Error> { diff --git a/simln-lib/src/eclair.rs b/simln-lib/src/eclair.rs index c13a1b2c..b4d3e98c 100644 --- a/simln-lib/src/eclair.rs +++ b/simln-lib/src/eclair.rs @@ -1,5 +1,6 @@ use crate::{ - serializers, LightningError, LightningNode, NodeId, NodeInfo, PaymentOutcome, PaymentResult, + serializers, Graph, LightningError, LightningNode, NodeId, NodeInfo, PaymentOutcome, + PaymentResult, }; use async_trait::async_trait; use bitcoin::secp256k1::PublicKey; @@ -243,6 +244,33 @@ impl LightningNode for EclairNode { Ok(capacities_msat) } + + async fn get_graph(&mut self) -> Result { + let nodes: NodesResponse = self + .client + .request("nodes", None) + .await + .map_err(|err| LightningError::GetNodeInfoError(err.to_string()))?; + + let mut nodes_by_pk: HashMap = HashMap::new(); + let channels = HashMap::new(); + + for node in nodes { + nodes_by_pk.insert( + PublicKey::from_str(&node.node_id).expect("Public Key not valid"), + NodeInfo { + pubkey: PublicKey::from_str(&node.node_id).expect("Public Key not valid"), + alias: node.alias.clone(), + features: parse_json_to_node_features(&node.features), + }, + ); + } + + Ok(Graph { + nodes_by_pk, + channels, + }) + } } #[derive(Debug, Deserialize)] @@ -288,7 +316,16 @@ struct NodeResponse { announcement: Announcement, } +#[derive(Debug, Deserialize)] +struct NodeInGraph { + #[serde(rename = "nodeId")] + node_id: String, + alias: String, + features: Value, +} + type ChannelsResponse = Vec; +type NodesResponse = Vec; #[derive(Debug, Deserialize)] struct Channel { diff --git a/simln-lib/src/lib.rs b/simln-lib/src/lib.rs index 47dba7b0..58e16c3d 100755 --- a/simln-lib/src/lib.rs +++ b/simln-lib/src/lib.rs @@ -284,9 +284,51 @@ impl Display for NodeInfo { } } +#[derive(Debug, Clone)] +pub struct ChannelInfo { + pub channel_id: ShortChannelID, + pub capacity_msat: u64, +} + +#[derive(Debug, Clone)] +/// Graph represents the network graph of the simulated network and is useful for efficient lookups. +pub struct Graph { + // Store nodes' information keyed by their public key. + nodes_by_pk: HashMap, + // Represent channels as a mapping from ShortChannelID to ChannelInfo. + #[allow(dead_code)] + channels: HashMap, +} + +impl Graph { + pub fn new() -> Self { + Graph { + nodes_by_pk: HashMap::new(), + channels: HashMap::new(), + } + } + + pub fn get_nodes(&mut self) -> Result, LightningError> { + Ok(self.nodes_by_pk.clone()) + } + + pub fn get_node_by_pk( + &mut self, + public_key: PublicKey, + ) -> Result, LightningError> { + Ok(self.nodes_by_pk.get(&public_key)) + } +} + +impl Default for Graph { + fn default() -> Self { + Self::new() + } +} + /// LightningNode represents the functionality that is required to execute events on a lightning node. #[async_trait] -pub trait LightningNode: Send { +pub trait LightningNode: Send + Sync { /// Get information about the node. fn get_info(&self) -> &NodeInfo; /// Get the network this node is running at. @@ -308,6 +350,8 @@ pub trait LightningNode: Send { /// Lists all channels, at present only returns a vector of channel capacities in msat because no further /// information is required. async fn list_channels(&mut self) -> Result, LightningError>; + /// Get the network graph from the point of view of a given node. + async fn get_graph(&mut self) -> Result; } /// Represents an error that occurs when generating a destination for a payment. diff --git a/simln-lib/src/lnd.rs b/simln-lib/src/lnd.rs index 88a6b485..4d9aac1d 100644 --- a/simln-lib/src/lnd.rs +++ b/simln-lib/src/lnd.rs @@ -2,7 +2,8 @@ use std::collections::HashSet; use std::{collections::HashMap, str::FromStr}; use crate::{ - serializers, LightningError, LightningNode, NodeId, NodeInfo, PaymentOutcome, PaymentResult, + serializers, Graph, LightningError, LightningNode, NodeId, NodeInfo, PaymentOutcome, + PaymentResult, }; use async_trait::async_trait; use bitcoin::hashes::{sha256, Hash}; @@ -12,7 +13,9 @@ use lightning::ln::features::NodeFeatures; use lightning::ln::{PaymentHash, PaymentPreimage}; use serde::{Deserialize, Serialize}; use tonic_lnd::lnrpc::{payment::PaymentStatus, GetInfoRequest, GetInfoResponse}; -use tonic_lnd::lnrpc::{ListChannelsRequest, NodeInfoRequest, PaymentFailureReason}; +use tonic_lnd::lnrpc::{ + ChannelGraphRequest, ListChannelsRequest, NodeInfoRequest, PaymentFailureReason, +}; use tonic_lnd::routerrpc::TrackPaymentRequest; use tonic_lnd::tonic::Code::Unavailable; use tonic_lnd::tonic::Status; @@ -275,6 +278,38 @@ impl LightningNode for LndNode { .map(|channel| 1000 * channel.capacity as u64) .collect()) } + + async fn get_graph(&mut self) -> Result { + let nodes = self + .client + .lightning() + .describe_graph(ChannelGraphRequest { + include_unannounced: false, + }) + .await + .map_err(|err| LightningError::GetNodeInfoError(err.to_string()))? + .into_inner() + .nodes; + + let mut nodes_by_pk: HashMap = HashMap::new(); + let channels = HashMap::new(); + + for node in nodes { + nodes_by_pk.insert( + PublicKey::from_str(&node.pub_key).expect("Public Key not valid"), + NodeInfo { + pubkey: PublicKey::from_str(&node.pub_key).expect("Public Key not valid"), + alias: node.alias.clone(), + features: parse_node_features(node.features.keys().cloned().collect()), + }, + ); + } + + Ok(Graph { + nodes_by_pk, + channels, + }) + } } fn string_to_payment_hash(hash: &str) -> Result { diff --git a/simln-lib/src/sim_node.rs b/simln-lib/src/sim_node.rs index dde16835..f609914e 100755 --- a/simln-lib/src/sim_node.rs +++ b/simln-lib/src/sim_node.rs @@ -1,5 +1,5 @@ use crate::{ - LightningError, LightningNode, NodeInfo, PaymentOutcome, PaymentResult, SimulationError, + Graph, LightningError, LightningNode, NodeInfo, PaymentOutcome, PaymentResult, SimulationError, }; use async_trait::async_trait; use bitcoin::constants::ChainHash; @@ -7,6 +7,7 @@ use bitcoin::secp256k1::PublicKey; use bitcoin::{Network, ScriptBuf, TxOut}; use lightning::ln::chan_utils::make_funding_redeemscript; use std::collections::{hash_map::Entry, HashMap}; +use std::str::FromStr; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; use tokio_util::task::TaskTracker; @@ -441,6 +442,8 @@ trait SimNetwork: Send + Sync { /// Looks up a node in the simulated network and a list of its channel capacities. async fn lookup_node(&self, node: &PublicKey) -> Result<(NodeInfo, Vec), LightningError>; + /// fetches all nodes in the simulated network + async fn fetch_nodes(&self) -> Result, LightningError>; } /// A wrapper struct used to implement the LightningNode trait (can be thought of as "the" lightning node). Passes @@ -641,6 +644,30 @@ impl LightningNode for SimNode<'_, T> { .await? .1) } + + async fn get_graph(&mut self) -> Result { + let nodes = self.network.lock().await.fetch_nodes().await?; + + let mut nodes_by_pk = HashMap::new(); + let channels = HashMap::new(); + + for node in nodes { + nodes_by_pk.insert( + PublicKey::from_str(&node.pubkey.to_string()).expect("Public Key not valid"), + NodeInfo { + pubkey: PublicKey::from_str(&node.pubkey.to_string()) + .expect("Public Key not valid"), + alias: node.alias.clone(), + features: node.features, + }, + ); + } + + Ok(Graph { + nodes_by_pk, + channels, + }) + } } /// Graph is the top level struct that is used to coordinate simulation of lightning nodes. @@ -837,6 +864,17 @@ impl SimNetwork for SimGraph { "Node not found".to_string(), )) } + + /// fetch_nodes fetches all nodes in the network + async fn fetch_nodes(&self) -> Result, LightningError> { + let mut nodes = vec![]; + + for node in &self.nodes { + nodes.push(node_info(*node.0)); + } + + Ok(nodes) + } } /// Adds htlcs to the simulation state along the path provided. Returning the index in the path from which to fail @@ -1469,6 +1507,7 @@ mod tests { ); async fn lookup_node(&self, node: &PublicKey) -> Result<(NodeInfo, Vec), LightningError>; + async fn fetch_nodes(&self) -> Result, LightningError>; } } diff --git a/simln-lib/src/test_utils.rs b/simln-lib/src/test_utils.rs index 6a754747..7512ac96 100644 --- a/simln-lib/src/test_utils.rs +++ b/simln-lib/src/test_utils.rs @@ -11,7 +11,7 @@ use tokio::sync::Mutex; use tokio_util::task::TaskTracker; use crate::{ - ActivityDefinition, LightningError, LightningNode, NodeInfo, PaymentGenerationError, + ActivityDefinition, Graph, LightningError, LightningNode, NodeInfo, PaymentGenerationError, PaymentGenerator, Simulation, SimulationCfg, ValueOrRange, }; @@ -88,6 +88,7 @@ mock! { ) -> Result; async fn get_node_info(&mut self, node_id: &PublicKey) -> Result; async fn list_channels(&mut self) -> Result, LightningError>; + async fn get_graph(&mut self) -> Result; } } From 933c5ae2c2d5509ab2e7043ec75ca4a8ed7f6efe Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Sat, 26 Apr 2025 13:48:26 +0100 Subject: [PATCH 02/17] sim-ln/docs: Update README for activity alias usage --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index bca2f1e3..c528c315 100644 --- a/README.md +++ b/README.md @@ -248,10 +248,7 @@ The example simulation file below sets up the following simulation: ``` -Nodes can be identified by their public key or an id string (as -described above). Activity sources and destinations may reference the -`id` defined in `nodes`, but destinations that are not listed in `nodes` -*must* provide a valid public key. +Nodes can be identified by their public key or an id string (as described above). Activity sources and destinations may reference the `id` defined in `nodes`. However, if multiple destination nodes have the same id string (alias), valid public keys *must* be used to identify the nodes. ### Simulation Output From 4bb4f6089103b8e8a523e17dbcdac435693a310a Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Fri, 2 May 2025 17:17:06 +0100 Subject: [PATCH 03/17] fixup! sim-ln/refactor: Improve activity dest node lookup via Graph --- sim-cli/src/parsing.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index 1b3dd360..b5548786 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -209,9 +209,9 @@ async fn get_clients( /// validation. async fn add_node_to_maps( nodes: &HashMap, -) -> Result<(HashMap, HashMap>), LightningError> { +) -> Result<(HashMap, HashMap), LightningError> { let mut pk_node_map = HashMap::new(); - let mut alias_node_map: HashMap> = HashMap::new(); + let mut alias_node_map: HashMap = HashMap::new(); for node_info in nodes.values() { log::info!( @@ -235,7 +235,7 @@ async fn add_node_to_maps( ))); } - alias_node_map.insert(node_info.alias.clone(), vec![node_info.clone()]); + alias_node_map.insert(node_info.alias.clone(), node_info.clone()); } pk_node_map.insert(node_info.pubkey, node_info.clone()); @@ -249,7 +249,7 @@ async fn add_node_to_maps( async fn validate_activities( activity: Vec, pk_node_map: HashMap, - alias_node_map: HashMap>, + alias_node_map: HashMap, mut graph: Graph, ) -> Result, LightningError> { let mut validated_activities = vec![]; @@ -287,8 +287,8 @@ async fn validate_activities( } }, NodeId::Alias(a) => { - if let Some(node_infos) = alias_node_map.get(a) { - node_infos[0].clone() + if let Some(node_info) = alias_node_map.get(a) { + node_info.clone() } else { return Err(LightningError::ValidationError(format!( "activity source {} not found in simulation nodes.", @@ -300,8 +300,8 @@ async fn validate_activities( let destination = match &act.destination { NodeId::Alias(a) => { - if let Some(node_infos) = alias_node_map.get(a) { - node_infos[0].clone() + if let Some(node_info) = alias_node_map.get(a) { + node_info.clone() } else if let Some(node_infos) = graph_nodes_by_alias.get(a) { if node_infos.len() > 1 { let pks: Vec = node_infos From be7b1e44352012398d0920be3e44efa9a6f6cfea Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Fri, 2 May 2025 17:18:34 +0100 Subject: [PATCH 04/17] fixup! sim-ln/refactor: Improve activity dest node lookup via Graph --- sim-cli/src/parsing.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index b5548786..06580ede 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -258,7 +258,6 @@ async fn validate_activities( // An alias can be mapped to multiple nodes because it is not a unique identifier. let mut graph_nodes_by_alias: HashMap> = HashMap::new(); - // sort graph nodes by alias for easy lookups. for node in graph.get_nodes()? { if graph_nodes_by_alias.contains_key(&node.1.alias) { if let Some(node_infos) = graph_nodes_by_alias.get(&node.1.alias) { From 6e7c818d2e907214066b6ed58af1cc12cc8d2506 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Fri, 2 May 2025 17:48:10 +0100 Subject: [PATCH 05/17] fixup! sim-ln/refactor: Improve activity dest node lookup via Graph --- sim-cli/src/parsing.rs | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index 06580ede..ece49149 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -274,27 +274,16 @@ async fn validate_activities( for act in activity.into_iter() { // We can only map source aliases to nodes we control, so if the source alias // is not in alias_node_map, we fail - let source = match &act.source { - NodeId::PublicKey(pk) => { - if let Some(node_info) = pk_node_map.get(pk) { - node_info.clone() - } else { - return Err(LightningError::ValidationError(format!( - "activity source {} not found in simulation nodes.", - act.source - ))); - } - }, - NodeId::Alias(a) => { - if let Some(node_info) = alias_node_map.get(a) { - node_info.clone() - } else { - return Err(LightningError::ValidationError(format!( - "activity source {} not found in simulation nodes.", - act.source - ))); - } - }, + let source = if let Some(source) = match &act.source { + NodeId::PublicKey(pk) => pk_node_map.get(pk), + NodeId::Alias(a) => alias_node_map.get(a), + } { + source.clone() + } else { + return Err(LightningError::ValidationError(format!( + "activity source {} not found in nodes.", + act.source + ))); }; let destination = match &act.destination { From 9db39ef8a27c89c4d6286746a5046b26574fd1db Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Fri, 2 May 2025 17:57:52 +0100 Subject: [PATCH 06/17] fixup! sim-ln/refactor: Improve activity dest node lookup via Graph --- simln-lib/src/cln.rs | 2 -- simln-lib/src/eclair.rs | 2 -- simln-lib/src/lib.rs | 4 ---- simln-lib/src/lnd.rs | 2 -- simln-lib/src/sim_node.rs | 2 -- 5 files changed, 12 deletions(-) diff --git a/simln-lib/src/cln.rs b/simln-lib/src/cln.rs index d99c71c2..bfa9dae2 100644 --- a/simln-lib/src/cln.rs +++ b/simln-lib/src/cln.rs @@ -277,7 +277,6 @@ impl LightningNode for ClnNode { .nodes; let mut nodes_by_pk: HashMap = HashMap::new(); - let channels = HashMap::new(); for node in nodes { nodes_by_pk.insert( @@ -295,7 +294,6 @@ impl LightningNode for ClnNode { Ok(Graph { nodes_by_pk, - channels, }) } } diff --git a/simln-lib/src/eclair.rs b/simln-lib/src/eclair.rs index b4d3e98c..0615a002 100644 --- a/simln-lib/src/eclair.rs +++ b/simln-lib/src/eclair.rs @@ -253,7 +253,6 @@ impl LightningNode for EclairNode { .map_err(|err| LightningError::GetNodeInfoError(err.to_string()))?; let mut nodes_by_pk: HashMap = HashMap::new(); - let channels = HashMap::new(); for node in nodes { nodes_by_pk.insert( @@ -268,7 +267,6 @@ impl LightningNode for EclairNode { Ok(Graph { nodes_by_pk, - channels, }) } } diff --git a/simln-lib/src/lib.rs b/simln-lib/src/lib.rs index 58e16c3d..74869f13 100755 --- a/simln-lib/src/lib.rs +++ b/simln-lib/src/lib.rs @@ -295,16 +295,12 @@ pub struct ChannelInfo { pub struct Graph { // Store nodes' information keyed by their public key. nodes_by_pk: HashMap, - // Represent channels as a mapping from ShortChannelID to ChannelInfo. - #[allow(dead_code)] - channels: HashMap, } impl Graph { pub fn new() -> Self { Graph { nodes_by_pk: HashMap::new(), - channels: HashMap::new(), } } diff --git a/simln-lib/src/lnd.rs b/simln-lib/src/lnd.rs index 4d9aac1d..dfe1fdff 100644 --- a/simln-lib/src/lnd.rs +++ b/simln-lib/src/lnd.rs @@ -292,7 +292,6 @@ impl LightningNode for LndNode { .nodes; let mut nodes_by_pk: HashMap = HashMap::new(); - let channels = HashMap::new(); for node in nodes { nodes_by_pk.insert( @@ -307,7 +306,6 @@ impl LightningNode for LndNode { Ok(Graph { nodes_by_pk, - channels, }) } } diff --git a/simln-lib/src/sim_node.rs b/simln-lib/src/sim_node.rs index f609914e..49f6d442 100755 --- a/simln-lib/src/sim_node.rs +++ b/simln-lib/src/sim_node.rs @@ -649,7 +649,6 @@ impl LightningNode for SimNode<'_, T> { let nodes = self.network.lock().await.fetch_nodes().await?; let mut nodes_by_pk = HashMap::new(); - let channels = HashMap::new(); for node in nodes { nodes_by_pk.insert( @@ -665,7 +664,6 @@ impl LightningNode for SimNode<'_, T> { Ok(Graph { nodes_by_pk, - channels, }) } } From 315d0b48026d1f500db2675980831f076bb66b88 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Sat, 3 May 2025 09:53:01 +0100 Subject: [PATCH 07/17] fixup! sim-ln/refactor: Improve activity dest node lookup via Graph --- simln-lib/src/sim_node.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simln-lib/src/sim_node.rs b/simln-lib/src/sim_node.rs index 49f6d442..d5f6eec0 100755 --- a/simln-lib/src/sim_node.rs +++ b/simln-lib/src/sim_node.rs @@ -442,7 +442,7 @@ trait SimNetwork: Send + Sync { /// Looks up a node in the simulated network and a list of its channel capacities. async fn lookup_node(&self, node: &PublicKey) -> Result<(NodeInfo, Vec), LightningError>; - /// fetches all nodes in the simulated network + /// Fetches all nodes in the simulated network. async fn fetch_nodes(&self) -> Result, LightningError>; } From a0739aa9ebeba7e67164e43a109d5d96b6709c62 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Mon, 5 May 2025 16:23:39 +0100 Subject: [PATCH 08/17] fixup! sim-ln/refactor: Improve activity dest node lookup via Graph --- sim-cli/src/parsing.rs | 7 ++++--- simln-lib/src/lib.rs | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index ece49149..3088c07b 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -159,9 +159,10 @@ pub async fn create_simulation(cli: &Cli) -> Result { // We need to be able to look up destination nodes in the graph, because we allow defined activities to send to // nodes that we do not control. To do this, we can just grab the first node in our map and perform the lookup. let graph = match clients.values().next() { - Some(client) => client.lock().await.get_graph().await?, - None => panic!("Graph is empty"), - }; + Some(client) => client.lock().await.get_graph().await + .map_err(|e| LightningError::GetGraphError(format!("Error getting graph {:?}", e))), + None => Err(LightningError::GetGraphError("Graph is empty".to_string())), + }?; let (pk_node_map, alias_node_map) = add_node_to_maps(&clients_info).await?; diff --git a/simln-lib/src/lib.rs b/simln-lib/src/lib.rs index 74869f13..bba39a94 100755 --- a/simln-lib/src/lib.rs +++ b/simln-lib/src/lib.rs @@ -256,6 +256,9 @@ pub enum LightningError { /// Error that occurred while listing channels. #[error("List channels error: {0}")] ListChannelsError(String), + /// Error that occurred while getting graph. + #[error("Get graph error: {0}")] + GetGraphError(String), } /// Information about a Lightning Network node. From 3cb00e74ee0d715db9c7d84961e1866ff1ad78c2 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Mon, 5 May 2025 17:31:37 +0100 Subject: [PATCH 09/17] fixup! sim-ln/refactor: Improve activity dest node lookup via Graph --- sim-cli/src/parsing.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index 3088c07b..655486ba 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -260,15 +260,10 @@ async fn validate_activities( let mut graph_nodes_by_alias: HashMap> = HashMap::new(); for node in graph.get_nodes()? { - if graph_nodes_by_alias.contains_key(&node.1.alias) { - if let Some(node_infos) = graph_nodes_by_alias.get(&node.1.alias) { - let mut updated_node_infos = node_infos.clone(); - updated_node_infos.extend(vec![node.1.clone()]); - graph_nodes_by_alias.insert(node.1.alias.clone(), vec![node.1]); - } - } else { - graph_nodes_by_alias.insert(node.1.alias.clone(), vec![node.1]); - } + graph_nodes_by_alias + .entry(node.1.alias.clone()) + .or_insert_with(Vec::new) + .push(node.1); } // Make all the activities identifiable by PK internally From fb161755a888c6b3d2e89e3f3763d04e3fe91843 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Mon, 5 May 2025 18:19:05 +0100 Subject: [PATCH 10/17] fixup! sim-ln/refactor: Improve activity dest node lookup via Graph --- sim-cli/src/parsing.rs | 53 +++++++++++++++++++++++++++++------------- simln-lib/src/lib.rs | 7 ------ 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index 655486ba..494f7128 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -5,7 +5,7 @@ use log::LevelFilter; use serde::{Deserialize, Serialize}; use simln_lib::{ cln, cln::ClnNode, eclair, eclair::EclairNode, lnd, lnd::LndNode, serializers, - ActivityDefinition, Amount, Graph, Interval, LightningError, LightningNode, NodeId, NodeInfo, + ActivityDefinition, Amount, Interval, LightningError, LightningNode, NodeId, NodeInfo, Simulation, SimulationCfg, WriteResults, }; use std::collections::HashMap; @@ -118,6 +118,13 @@ struct ActivityParser { pub amount_msat: Amount, } +struct ActivityValidationParams { + pk_node_map: HashMap, + alias_node_map: HashMap, + graph_nodes_by_pk: HashMap, + graph_nodes_by_alias: HashMap>, +} + impl TryFrom<&Cli> for SimulationCfg { type Error = anyhow::Error; @@ -158,7 +165,7 @@ pub async fn create_simulation(cli: &Cli) -> Result { let (clients, clients_info) = get_clients(nodes).await?; // We need to be able to look up destination nodes in the graph, because we allow defined activities to send to // nodes that we do not control. To do this, we can just grab the first node in our map and perform the lookup. - let graph = match clients.values().next() { + let mut graph = match clients.values().next() { Some(client) => client.lock().await.get_graph().await .map_err(|e| LightningError::GetGraphError(format!("Error getting graph {:?}", e))), None => Err(LightningError::GetGraphError("Graph is empty".to_string())), @@ -166,8 +173,28 @@ pub async fn create_simulation(cli: &Cli) -> Result { let (pk_node_map, alias_node_map) = add_node_to_maps(&clients_info).await?; + let graph_nodes_by_pk = graph.get_nodes()?; + + // Store graph nodes' information keyed by their alias. + // An alias can be mapped to multiple nodes because it is not a unique identifier. + let mut graph_nodes_by_alias: HashMap> = HashMap::new(); + + for node in graph.get_nodes()? { + graph_nodes_by_alias + .entry(node.1.alias.clone()) + .or_insert_with(Vec::new) + .push(node.1); + } + + let activity_validation_params = ActivityValidationParams { + pk_node_map, + alias_node_map, + graph_nodes_by_pk, + graph_nodes_by_alias + }; + let validated_activities = - validate_activities(activity, pk_node_map, alias_node_map, graph).await?; + validate_activities(activity, activity_validation_params).await?; let tasks = TaskTracker::new(); Ok(Simulation::new(cfg, clients, validated_activities, tasks)) @@ -249,22 +276,16 @@ async fn add_node_to_maps( /// have been configured. async fn validate_activities( activity: Vec, - pk_node_map: HashMap, - alias_node_map: HashMap, - mut graph: Graph, + activity_validation_params: ActivityValidationParams, ) -> Result, LightningError> { let mut validated_activities = vec![]; - // Store graph nodes' information keyed by their alias. - // An alias can be mapped to multiple nodes because it is not a unique identifier. - let mut graph_nodes_by_alias: HashMap> = HashMap::new(); - - for node in graph.get_nodes()? { + let ActivityValidationParams { + pk_node_map, + alias_node_map, + graph_nodes_by_pk, graph_nodes_by_alias - .entry(node.1.alias.clone()) - .or_insert_with(Vec::new) - .push(node.1); - } + } = activity_validation_params; // Make all the activities identifiable by PK internally for act in activity.into_iter() { @@ -309,7 +330,7 @@ async fn validate_activities( NodeId::PublicKey(pk) => { if let Some(node_info) = pk_node_map.get(pk) { node_info.clone() - } else if let Some(node_info) = graph.get_node_by_pk(*pk)? { + } else if let Some(node_info) = graph_nodes_by_pk.get(pk){ node_info.clone() } else { return Err(LightningError::ValidationError(format!( diff --git a/simln-lib/src/lib.rs b/simln-lib/src/lib.rs index bba39a94..20e9ae7c 100755 --- a/simln-lib/src/lib.rs +++ b/simln-lib/src/lib.rs @@ -310,13 +310,6 @@ impl Graph { pub fn get_nodes(&mut self) -> Result, LightningError> { Ok(self.nodes_by_pk.clone()) } - - pub fn get_node_by_pk( - &mut self, - public_key: PublicKey, - ) -> Result, LightningError> { - Ok(self.nodes_by_pk.get(&public_key)) - } } impl Default for Graph { From e96b53090769f5b47d0d901791c9923717dd4a96 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Mon, 5 May 2025 18:32:41 +0100 Subject: [PATCH 11/17] fixup! sim-ln/refactor: Improve activity dest node lookup via Graph --- sim-cli/src/parsing.rs | 8 ++++---- simln-lib/src/lib.rs | 6 +----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index 494f7128..83c9817c 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -165,7 +165,7 @@ pub async fn create_simulation(cli: &Cli) -> Result { let (clients, clients_info) = get_clients(nodes).await?; // We need to be able to look up destination nodes in the graph, because we allow defined activities to send to // nodes that we do not control. To do this, we can just grab the first node in our map and perform the lookup. - let mut graph = match clients.values().next() { + let graph = match clients.values().next() { Some(client) => client.lock().await.get_graph().await .map_err(|e| LightningError::GetGraphError(format!("Error getting graph {:?}", e))), None => Err(LightningError::GetGraphError("Graph is empty".to_string())), @@ -173,17 +173,17 @@ pub async fn create_simulation(cli: &Cli) -> Result { let (pk_node_map, alias_node_map) = add_node_to_maps(&clients_info).await?; - let graph_nodes_by_pk = graph.get_nodes()?; + let graph_nodes_by_pk = graph.nodes_by_pk; // Store graph nodes' information keyed by their alias. // An alias can be mapped to multiple nodes because it is not a unique identifier. let mut graph_nodes_by_alias: HashMap> = HashMap::new(); - for node in graph.get_nodes()? { + for node in &graph_nodes_by_pk { graph_nodes_by_alias .entry(node.1.alias.clone()) .or_insert_with(Vec::new) - .push(node.1); + .push(node.1.clone()); } let activity_validation_params = ActivityValidationParams { diff --git a/simln-lib/src/lib.rs b/simln-lib/src/lib.rs index 20e9ae7c..6b67260f 100755 --- a/simln-lib/src/lib.rs +++ b/simln-lib/src/lib.rs @@ -297,7 +297,7 @@ pub struct ChannelInfo { /// Graph represents the network graph of the simulated network and is useful for efficient lookups. pub struct Graph { // Store nodes' information keyed by their public key. - nodes_by_pk: HashMap, + pub nodes_by_pk: HashMap, } impl Graph { @@ -306,10 +306,6 @@ impl Graph { nodes_by_pk: HashMap::new(), } } - - pub fn get_nodes(&mut self) -> Result, LightningError> { - Ok(self.nodes_by_pk.clone()) - } } impl Default for Graph { From 735979e08b453bf784f0e581ddae2554a71c0494 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Mon, 5 May 2025 18:38:08 +0100 Subject: [PATCH 12/17] fixup! sim-ln/docs: Update README for activity alias usage --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c528c315..a7848111 100644 --- a/README.md +++ b/README.md @@ -248,7 +248,11 @@ The example simulation file below sets up the following simulation: ``` -Nodes can be identified by their public key or an id string (as described above). Activity sources and destinations may reference the `id` defined in `nodes`. However, if multiple destination nodes have the same id string (alias), valid public keys *must* be used to identify the nodes. +Activity sources must reference an `id` defined in `nodes`, because the simulator can +only send payments from nodes that it controls. Destinations may reference either an +`id` defined in `nodes` or provide a pubkey or alias of a node in the public network. +If the alias provided is not unique in the public network, a pubkey must be used +to identify the node. ### Simulation Output From bf4bc6f2405ad5eaee3406f7fdbcfc247dd7c8dc Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Mon, 5 May 2025 18:51:14 +0100 Subject: [PATCH 13/17] sim-ln/style: Fix formatting issues --- sim-cli/src/parsing.rs | 21 ++++++++++++--------- simln-lib/src/cln.rs | 4 +--- simln-lib/src/eclair.rs | 4 +--- simln-lib/src/lnd.rs | 4 +--- simln-lib/src/sim_node.rs | 4 +--- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index 83c9817c..32943e34 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -166,15 +166,19 @@ pub async fn create_simulation(cli: &Cli) -> Result { // We need to be able to look up destination nodes in the graph, because we allow defined activities to send to // nodes that we do not control. To do this, we can just grab the first node in our map and perform the lookup. let graph = match clients.values().next() { - Some(client) => client.lock().await.get_graph().await + Some(client) => client + .lock() + .await + .get_graph() + .await .map_err(|e| LightningError::GetGraphError(format!("Error getting graph {:?}", e))), None => Err(LightningError::GetGraphError("Graph is empty".to_string())), }?; let (pk_node_map, alias_node_map) = add_node_to_maps(&clients_info).await?; - let graph_nodes_by_pk = graph.nodes_by_pk; - + let graph_nodes_by_pk = graph.nodes_by_pk; + // Store graph nodes' information keyed by their alias. // An alias can be mapped to multiple nodes because it is not a unique identifier. let mut graph_nodes_by_alias: HashMap> = HashMap::new(); @@ -182,7 +186,7 @@ pub async fn create_simulation(cli: &Cli) -> Result { for node in &graph_nodes_by_pk { graph_nodes_by_alias .entry(node.1.alias.clone()) - .or_insert_with(Vec::new) + .or_default() .push(node.1.clone()); } @@ -190,11 +194,10 @@ pub async fn create_simulation(cli: &Cli) -> Result { pk_node_map, alias_node_map, graph_nodes_by_pk, - graph_nodes_by_alias + graph_nodes_by_alias, }; - let validated_activities = - validate_activities(activity, activity_validation_params).await?; + let validated_activities = validate_activities(activity, activity_validation_params).await?; let tasks = TaskTracker::new(); Ok(Simulation::new(cfg, clients, validated_activities, tasks)) @@ -284,7 +287,7 @@ async fn validate_activities( pk_node_map, alias_node_map, graph_nodes_by_pk, - graph_nodes_by_alias + graph_nodes_by_alias, } = activity_validation_params; // Make all the activities identifiable by PK internally @@ -330,7 +333,7 @@ async fn validate_activities( NodeId::PublicKey(pk) => { if let Some(node_info) = pk_node_map.get(pk) { node_info.clone() - } else if let Some(node_info) = graph_nodes_by_pk.get(pk){ + } else if let Some(node_info) = graph_nodes_by_pk.get(pk) { node_info.clone() } else { return Err(LightningError::ValidationError(format!( diff --git a/simln-lib/src/cln.rs b/simln-lib/src/cln.rs index bfa9dae2..9144a34c 100644 --- a/simln-lib/src/cln.rs +++ b/simln-lib/src/cln.rs @@ -292,9 +292,7 @@ impl LightningNode for ClnNode { ); } - Ok(Graph { - nodes_by_pk, - }) + Ok(Graph { nodes_by_pk }) } } diff --git a/simln-lib/src/eclair.rs b/simln-lib/src/eclair.rs index 0615a002..7f019b4c 100644 --- a/simln-lib/src/eclair.rs +++ b/simln-lib/src/eclair.rs @@ -265,9 +265,7 @@ impl LightningNode for EclairNode { ); } - Ok(Graph { - nodes_by_pk, - }) + Ok(Graph { nodes_by_pk }) } } diff --git a/simln-lib/src/lnd.rs b/simln-lib/src/lnd.rs index dfe1fdff..841e98dc 100644 --- a/simln-lib/src/lnd.rs +++ b/simln-lib/src/lnd.rs @@ -304,9 +304,7 @@ impl LightningNode for LndNode { ); } - Ok(Graph { - nodes_by_pk, - }) + Ok(Graph { nodes_by_pk }) } } diff --git a/simln-lib/src/sim_node.rs b/simln-lib/src/sim_node.rs index d5f6eec0..06dc3199 100755 --- a/simln-lib/src/sim_node.rs +++ b/simln-lib/src/sim_node.rs @@ -662,9 +662,7 @@ impl LightningNode for SimNode<'_, T> { ); } - Ok(Graph { - nodes_by_pk, - }) + Ok(Graph { nodes_by_pk }) } } From f420f595e1989d0b763e19e6ec225894b3a20e43 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Tue, 6 May 2025 16:09:25 +0100 Subject: [PATCH 14/17] fixup! sim-ln/refactor: Improve activity dest node lookup via Graph --- sim-cli/src/parsing.rs | 12 +++++------- simln-lib/src/lib.rs | 2 +- simln-lib/src/sim_node.rs | 17 +++++++---------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index 32943e34..7c7878ff 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -122,6 +122,8 @@ struct ActivityValidationParams { pk_node_map: HashMap, alias_node_map: HashMap, graph_nodes_by_pk: HashMap, + // Store graph nodes' information keyed by their alias. + // An alias can be mapped to multiple nodes because it is not a unique identifier. graph_nodes_by_alias: HashMap>, } @@ -177,13 +179,9 @@ pub async fn create_simulation(cli: &Cli) -> Result { let (pk_node_map, alias_node_map) = add_node_to_maps(&clients_info).await?; - let graph_nodes_by_pk = graph.nodes_by_pk; - - // Store graph nodes' information keyed by their alias. - // An alias can be mapped to multiple nodes because it is not a unique identifier. let mut graph_nodes_by_alias: HashMap> = HashMap::new(); - for node in &graph_nodes_by_pk { + for node in &graph.nodes_by_pk { graph_nodes_by_alias .entry(node.1.alias.clone()) .or_default() @@ -193,7 +191,7 @@ pub async fn create_simulation(cli: &Cli) -> Result { let activity_validation_params = ActivityValidationParams { pk_node_map, alias_node_map, - graph_nodes_by_pk, + graph_nodes_by_pk: graph.nodes_by_pk, graph_nodes_by_alias, }; @@ -242,7 +240,7 @@ async fn add_node_to_maps( nodes: &HashMap, ) -> Result<(HashMap, HashMap), LightningError> { let mut pk_node_map = HashMap::new(); - let mut alias_node_map: HashMap = HashMap::new(); + let mut alias_node_map = HashMap::new(); for node_info in nodes.values() { log::info!( diff --git a/simln-lib/src/lib.rs b/simln-lib/src/lib.rs index 6b67260f..ba438570 100755 --- a/simln-lib/src/lib.rs +++ b/simln-lib/src/lib.rs @@ -316,7 +316,7 @@ impl Default for Graph { /// LightningNode represents the functionality that is required to execute events on a lightning node. #[async_trait] -pub trait LightningNode: Send + Sync { +pub trait LightningNode: Send { /// Get information about the node. fn get_info(&self) -> &NodeInfo; /// Get the network this node is running at. diff --git a/simln-lib/src/sim_node.rs b/simln-lib/src/sim_node.rs index 06dc3199..fc11be31 100755 --- a/simln-lib/src/sim_node.rs +++ b/simln-lib/src/sim_node.rs @@ -7,7 +7,6 @@ use bitcoin::secp256k1::PublicKey; use bitcoin::{Network, ScriptBuf, TxOut}; use lightning::ln::chan_utils::make_funding_redeemscript; use std::collections::{hash_map::Entry, HashMap}; -use std::str::FromStr; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; use tokio_util::task::TaskTracker; @@ -442,8 +441,8 @@ trait SimNetwork: Send + Sync { /// Looks up a node in the simulated network and a list of its channel capacities. async fn lookup_node(&self, node: &PublicKey) -> Result<(NodeInfo, Vec), LightningError>; - /// Fetches all nodes in the simulated network. - async fn fetch_nodes(&self) -> Result, LightningError>; + /// Lists all nodes in the simulated network. + async fn list_nodes(&self) -> Result, LightningError>; } /// A wrapper struct used to implement the LightningNode trait (can be thought of as "the" lightning node). Passes @@ -646,16 +645,15 @@ impl LightningNode for SimNode<'_, T> { } async fn get_graph(&mut self) -> Result { - let nodes = self.network.lock().await.fetch_nodes().await?; + let nodes = self.network.lock().await.list_nodes().await?; let mut nodes_by_pk = HashMap::new(); for node in nodes { nodes_by_pk.insert( - PublicKey::from_str(&node.pubkey.to_string()).expect("Public Key not valid"), + node.pubkey, NodeInfo { - pubkey: PublicKey::from_str(&node.pubkey.to_string()) - .expect("Public Key not valid"), + pubkey: node.pubkey, alias: node.alias.clone(), features: node.features, }, @@ -861,8 +859,7 @@ impl SimNetwork for SimGraph { )) } - /// fetch_nodes fetches all nodes in the network - async fn fetch_nodes(&self) -> Result, LightningError> { + async fn list_nodes(&self) -> Result, LightningError> { let mut nodes = vec![]; for node in &self.nodes { @@ -1503,7 +1500,7 @@ mod tests { ); async fn lookup_node(&self, node: &PublicKey) -> Result<(NodeInfo, Vec), LightningError>; - async fn fetch_nodes(&self) -> Result, LightningError>; + async fn list_nodes(&self) -> Result, LightningError>; } } From 072c2a6cd23a4a72f076a863a0b1a6b578af00c4 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Tue, 6 May 2025 16:28:39 +0100 Subject: [PATCH 15/17] fixup! sim-ln/refactor: Improve activity dest node lookup via Graph --- sim-cli/src/parsing.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index 7c7878ff..d68c796a 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -180,7 +180,6 @@ pub async fn create_simulation(cli: &Cli) -> Result { let (pk_node_map, alias_node_map) = add_node_to_maps(&clients_info).await?; let mut graph_nodes_by_alias: HashMap> = HashMap::new(); - for node in &graph.nodes_by_pk { graph_nodes_by_alias .entry(node.1.alias.clone()) From ddb0a95114f118bf5261366db8a47e9489e8c773 Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Tue, 6 May 2025 16:33:44 +0100 Subject: [PATCH 16/17] fixup! sim-ln/style: Fix formatting issues --- sim-cli/src/parsing.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index d68c796a..93a768eb 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -180,6 +180,7 @@ pub async fn create_simulation(cli: &Cli) -> Result { let (pk_node_map, alias_node_map) = add_node_to_maps(&clients_info).await?; let mut graph_nodes_by_alias: HashMap> = HashMap::new(); + for node in &graph.nodes_by_pk { graph_nodes_by_alias .entry(node.1.alias.clone()) From 852a0ee4af3fe68479b84cb5b92ba72577d4825a Mon Sep 17 00:00:00 2001 From: Chuks Agbakuru Date: Tue, 6 May 2025 16:34:27 +0100 Subject: [PATCH 17/17] fixup! sim-ln/style: Fix formatting issues --- sim-cli/src/parsing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim-cli/src/parsing.rs b/sim-cli/src/parsing.rs index 93a768eb..7c7878ff 100755 --- a/sim-cli/src/parsing.rs +++ b/sim-cli/src/parsing.rs @@ -180,7 +180,7 @@ pub async fn create_simulation(cli: &Cli) -> Result { let (pk_node_map, alias_node_map) = add_node_to_maps(&clients_info).await?; let mut graph_nodes_by_alias: HashMap> = HashMap::new(); - + for node in &graph.nodes_by_pk { graph_nodes_by_alias .entry(node.1.alias.clone())