Skip to content

Commit bfb24fd

Browse files
committed
simln-lib/bugfix: return all nodes from ln_node_from_graph
39257da introduced bug where we would not return all `SimNode`s from `ln_node_from_graph` because `SimGraph` did not have the excluded nodes. Here we store all the nodes but exclude them from the capacity based on the exclude field in the `SimulatedChannel`.
1 parent f8a7359 commit bfb24fd

2 files changed

Lines changed: 62 additions & 34 deletions

File tree

sim-cli/src/parsing.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ pub async fn create_simulation_with_network(
301301
));
302302

303303
// Copy all simulated channels into a read-only routing graph, allowing to pathfind for
304-
// individual payments without locking th simulation graph (this is a duplication of the channels,
304+
// individual payments without locking the simulation graph (this is a duplication of the channels,
305305
// but the performance tradeoff is worthwhile for concurrent pathfinding).
306306
let routing_graph = Arc::new(
307307
populate_network_graph(channels, clock.clone())
@@ -312,7 +312,7 @@ pub async fn create_simulation_with_network(
312312
// custom actions on the simulated network. For the nodes we'll pass our simulation, cast them
313313
// to a dyn trait and exclude any nodes that shouldn't be included in random activity
314314
// generation.
315-
let nodes = ln_node_from_graph(simulation_graph.clone(), routing_graph, clock.clone()).await?;
315+
let nodes = ln_node_from_graph(simulation_graph, routing_graph, clock.clone()).await?;
316316
let mut nodes_dyn: HashMap<_, Arc<Mutex<dyn LightningNode>>> = nodes
317317
.iter()
318318
.map(|(pk, node)| (*pk, Arc::clone(node) as Arc<Mutex<dyn LightningNode>>))

simln-lib/src/sim_node.rs

Lines changed: 60 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ impl SimulatedChannel {
477477

478478
/// SimNetwork represents a high level network coordinator that is responsible for the task of actually propagating
479479
/// payments through the simulated network.
480+
#[async_trait]
480481
pub trait SimNetwork: Send + Sync {
481482
/// Sends payments over the route provided through the network, reporting the final payment outcome to the sender
482483
/// channel provided.
@@ -490,7 +491,7 @@ pub trait SimNetwork: Send + Sync {
490491
);
491492

492493
/// Looks up a node in the simulated network and a list of its channel capacities.
493-
fn lookup_node(&self, node: &PublicKey) -> Result<(NodeInfo, Vec<u64>), LightningError>;
494+
async fn lookup_node(&self, node: &PublicKey) -> Result<(NodeInfo, Vec<u64>), LightningError>;
494495
/// Lists all nodes in the simulated network.
495496
fn list_nodes(&self) -> Vec<NodeInfo>;
496497
}
@@ -794,11 +795,16 @@ impl<T: SimNetwork, C: Clock> LightningNode for SimNode<T, C> {
794795
}
795796

796797
async fn get_node_info(&self, node_id: &PublicKey) -> Result<NodeInfo, LightningError> {
797-
Ok(self.network.lock().await.lookup_node(node_id)?.0)
798+
Ok(self.network.lock().await.lookup_node(node_id).await?.0)
798799
}
799800

800801
async fn channel_capacities(&self) -> Result<u64, LightningError> {
801-
let channels = self.network.lock().await.lookup_node(&self.info.pubkey)?;
802+
let channels = self
803+
.network
804+
.lock()
805+
.await
806+
.lookup_node(&self.info.pubkey)
807+
.await?;
802808
Ok(channels.1.iter().sum())
803809
}
804810

@@ -1018,9 +1024,9 @@ async fn handle_intercepted_htlc(
10181024

10191025
/// Graph is the top level struct that is used to coordinate simulation of lightning nodes.
10201026
pub struct SimGraph {
1021-
/// nodes caches the list of nodes in the network with a vector of their channel capacities, only used for quick
1027+
/// nodes caches the list of nodes in the network with a vector of their channel ids, only used for quick
10221028
/// lookup.
1023-
nodes: HashMap<PublicKey, (NodeInfo, Vec<u64>)>,
1029+
nodes: HashMap<PublicKey, (NodeInfo, Vec<ShortChannelID>)>,
10241030

10251031
/// channels maps the scid of a channel to its current simulation state.
10261032
channels: Arc<Mutex<HashMap<ShortChannelID, SimulatedChannel>>>,
@@ -1052,7 +1058,7 @@ impl SimGraph {
10521058
default_custom_records: CustomRecords,
10531059
shutdown_signal: (Trigger, Listener),
10541060
) -> Result<Self, SimulationError> {
1055-
let mut nodes: HashMap<PublicKey, (NodeInfo, Vec<u64>)> = HashMap::new();
1061+
let mut nodes: HashMap<PublicKey, (NodeInfo, Vec<ShortChannelID>)> = HashMap::new();
10561062
let mut channels = HashMap::new();
10571063

10581064
for channel in graph_channels.iter() {
@@ -1069,18 +1075,16 @@ impl SimGraph {
10691075
Entry::Vacant(v) => v.insert(channel.clone()),
10701076
};
10711077

1072-
if !channel.exclude_capacity {
1073-
// It's okay to have duplicate pubkeys because one node can have many channels.
1074-
for info in [&channel.node_1.policy, &channel.node_2.policy] {
1075-
match nodes.entry(info.pubkey) {
1076-
Entry::Occupied(o) => o.into_mut().1.push(channel.capacity_msat),
1077-
Entry::Vacant(v) => {
1078-
v.insert((
1079-
node_info(info.pubkey, info.alias.clone()),
1080-
vec![channel.capacity_msat],
1081-
));
1082-
},
1083-
}
1078+
// It's okay to have duplicate pubkeys because one node can have many channels.
1079+
for info in [&channel.node_1.policy, &channel.node_2.policy] {
1080+
match nodes.entry(info.pubkey) {
1081+
Entry::Occupied(o) => o.into_mut().1.push(channel.short_channel_id),
1082+
Entry::Vacant(v) => {
1083+
v.insert((
1084+
node_info(info.pubkey, info.alias.clone()),
1085+
vec![channel.short_channel_id],
1086+
));
1087+
},
10841088
}
10851089
}
10861090
}
@@ -1102,9 +1106,11 @@ pub async fn ln_node_from_graph<C: Clock>(
11021106
routing_graph: Arc<LdkNetworkGraph>,
11031107
clock: Arc<C>,
11041108
) -> Result<HashMap<PublicKey, Arc<Mutex<SimNode<SimGraph, C>>>>, LightningError> {
1105-
let mut nodes: HashMap<PublicKey, Arc<Mutex<SimNode<SimGraph, C>>>> = HashMap::new();
1109+
let sim_graph = graph.lock().await;
1110+
let mut nodes: HashMap<PublicKey, Arc<Mutex<SimNode<SimGraph, C>>>> =
1111+
HashMap::with_capacity(sim_graph.nodes.len());
11061112

1107-
for node in graph.lock().await.nodes.iter() {
1113+
for node in sim_graph.nodes.iter() {
11081114
nodes.insert(
11091115
*node.0,
11101116
Arc::new(Mutex::new(SimNode::new(
@@ -1183,6 +1189,7 @@ pub fn populate_network_graph<C: Clock>(
11831189
Ok(graph)
11841190
}
11851191

1192+
#[async_trait]
11861193
impl SimNetwork for SimGraph {
11871194
/// dispatch_payment asynchronously propagates a payment through the simulated network, returning a tracking
11881195
/// channel that can be used to obtain the result of the payment. At present, MPP payments are not supported.
@@ -1232,13 +1239,27 @@ impl SimNetwork for SimGraph {
12321239
}
12331240

12341241
/// lookup_node fetches a node's information and channel capacities.
1235-
fn lookup_node(&self, node: &PublicKey) -> Result<(NodeInfo, Vec<u64>), LightningError> {
1236-
match self.nodes.get(node) {
1237-
Some(node) => Ok(node.clone()),
1238-
None => Err(LightningError::GetNodeInfoError(
1239-
"Node not found".to_string(),
1240-
)),
1241-
}
1242+
async fn lookup_node(&self, node: &PublicKey) -> Result<(NodeInfo, Vec<u64>), LightningError> {
1243+
let node_info = match self.nodes.get(node) {
1244+
Some(node) => node.clone(),
1245+
None => {
1246+
return Err(LightningError::GetNodeInfoError(format!(
1247+
"Node {} not found",
1248+
node
1249+
)))
1250+
},
1251+
};
1252+
1253+
let channels = self.channels.lock().await;
1254+
let capacities: Vec<u64> = node_info
1255+
.1
1256+
.iter()
1257+
.filter_map(|scid| channels.get(scid))
1258+
.filter(|channel| !channel.exclude_capacity)
1259+
.map(|channel| channel.capacity_msat)
1260+
.collect();
1261+
1262+
Ok((node_info.0, capacities))
12421263
}
12431264

12441265
fn list_nodes(&self) -> Vec<NodeInfo> {
@@ -1966,19 +1987,25 @@ mod tests {
19661987
.await
19671988
.unwrap();
19681989

1990+
assert!(nodes.len() == 3);
1991+
19691992
let node_1 = nodes.get(&pk1).unwrap().lock().await;
19701993
let node_1_capacity = node_1.channel_capacities().await.unwrap();
19711994

1972-
// Node 1 has 2 channels but one was excluded so here we should only have the one that was
1973-
// not excluded.
1995+
// Node 1 has 2 channels but one was excluded so here we should only have the capacity of
1996+
// the channel that was not excluded.
19741997
assert!(node_1_capacity == capacity_1);
19751998

19761999
let node_2 = nodes.get(&pk2).unwrap().lock().await;
19772000
let node_2_capacity = node_2.channel_capacities().await.unwrap();
19782001
assert!(node_2_capacity == capacity_1);
19792002

1980-
// Node 3's only channel was excluded so it won't be present here.
1981-
assert!(!nodes.contains_key(&pk3));
2003+
// Node 3 should be returned from ln_node_from_graph but it won't have any channel capacity
2004+
// present because its only channel was excluded.
2005+
let node_3 = nodes.get(&pk3);
2006+
assert!(node_3.is_some());
2007+
let node_3 = node_3.unwrap().lock().await;
2008+
assert!(node_3.channel_capacities().await.unwrap() == 0);
19822009
}
19832010

19842011
/// Tests basic functionality of a `SimulatedChannel` but does no endeavor to test the underlying
@@ -2048,6 +2075,7 @@ mod tests {
20482075
mock! {
20492076
Network{}
20502077

2078+
#[async_trait]
20512079
impl SimNetwork for Network{
20522080
fn dispatch_payment(
20532081
&mut self,
@@ -2058,7 +2086,7 @@ mod tests {
20582086
sender: Sender<Result<PaymentResult, LightningError>>,
20592087
);
20602088

2061-
fn lookup_node(&self, node: &PublicKey) -> Result<(NodeInfo, Vec<u64>), LightningError>;
2089+
async fn lookup_node(&self, node: &PublicKey) -> Result<(NodeInfo, Vec<u64>), LightningError>;
20622090
fn list_nodes(&self) -> Vec<NodeInfo>;
20632091
}
20642092
}

0 commit comments

Comments
 (0)