1use std::sync::Arc;
2
3use bimap::BiHashMap;
4use hopr_api::OffchainPublicKey;
5use parking_lot::RwLock;
6use petgraph::graph::{DiGraph, NodeIndex};
7
8use crate::{Observations, errors::ChannelGraphError};
9
10#[derive(Debug, Clone, Default)]
12pub(crate) struct InnerGraph {
13 pub(crate) graph: DiGraph<OffchainPublicKey, Observations>,
14 pub(crate) indices: BiHashMap<OffchainPublicKey, NodeIndex>,
15}
16
17#[derive(Debug, Clone)]
29pub struct ChannelGraph {
30 pub(crate) me: OffchainPublicKey,
31 pub(crate) edge_penalty: f64,
32 pub(crate) min_ack_rate: f64,
33 pub(crate) inner: Arc<RwLock<InnerGraph>>,
34}
35
36impl ChannelGraph {
37 pub fn new(me: OffchainPublicKey) -> Self {
46 Self::with_edge_params(me, 0.5, 0.1)
47 }
48
49 pub fn with_edge_params(me: OffchainPublicKey, edge_penalty: f64, min_ack_rate: f64) -> Self {
55 let mut graph = DiGraph::new();
56 let mut indices = BiHashMap::new();
57
58 let idx = graph.add_node(me);
59 indices.insert(me, idx);
60
61 Self {
62 me,
63 edge_penalty,
64 min_ack_rate,
65 inner: Arc::new(RwLock::new(InnerGraph { graph, indices })),
66 }
67 }
68
69 pub fn me(&self) -> &OffchainPublicKey {
71 &self.me
72 }
73}
74
75impl hopr_api::graph::NetworkGraphView for ChannelGraph {
76 type NodeId = OffchainPublicKey;
77 type Observed = Observations;
78
79 fn node_count(&self) -> usize {
80 self.inner.read().graph.node_count()
81 }
82
83 fn contains_node(&self, key: &OffchainPublicKey) -> bool {
84 self.inner.read().indices.contains_left(key)
85 }
86
87 fn nodes(&self) -> futures::stream::BoxStream<'static, Self::NodeId> {
88 let keys: Vec<OffchainPublicKey> = {
89 let inner = self.inner.read();
90 inner.indices.left_values().copied().collect()
91 };
92
93 Box::pin(futures::stream::iter(keys))
94 }
95
96 fn has_edge(&self, src: &OffchainPublicKey, dest: &OffchainPublicKey) -> bool {
97 let inner = self.inner.read();
98 let (Some(src_idx), Some(dest_idx)) = (inner.indices.get_by_left(src), inner.indices.get_by_left(dest)) else {
99 return false;
100 };
101 inner.graph.contains_edge(*src_idx, *dest_idx)
102 }
103
104 fn edge(&self, src: &Self::NodeId, dest: &Self::NodeId) -> Option<Self::Observed> {
105 let inner = self.inner.read();
106 let src_idx = inner.indices.get_by_left(src)?;
107 let dest_idx = inner.indices.get_by_left(dest)?;
108 let edge_idx = inner.graph.find_edge(*src_idx, *dest_idx)?;
109 inner.graph.edge_weight(edge_idx).copied()
110 }
111
112 fn identity(&self) -> &OffchainPublicKey {
113 &self.me
114 }
115}
116
117impl hopr_api::graph::NetworkGraphWrite for ChannelGraph {
118 type Error = ChannelGraphError;
119 type NodeId = OffchainPublicKey;
120 type Observed = Observations;
121
122 fn add_node(&self, key: OffchainPublicKey) {
123 let mut inner = self.inner.write();
124 if !inner.indices.contains_left(&key) {
125 let idx = inner.graph.add_node(key);
126 inner.indices.insert(key, idx);
127 }
128 }
129
130 fn remove_node(&self, key: &OffchainPublicKey) {
131 let mut inner = self.inner.write();
132 if let Some((_, idx)) = inner.indices.remove_by_left(key) {
133 inner.graph.remove_node(idx);
134
135 if let Some(swapped_key) = inner.graph.node_weight(idx) {
138 let swapped_key = *swapped_key;
139 inner.indices.insert(swapped_key, idx);
140 }
141 }
142 }
143
144 fn add_edge(&self, src: &OffchainPublicKey, dest: &OffchainPublicKey) -> Result<(), ChannelGraphError> {
145 let mut inner = self.inner.write();
146 let src_idx = inner
147 .indices
148 .get_by_left(src)
149 .copied()
150 .ok_or(ChannelGraphError::PublicKeyNodeNotFound(*src))?;
151 let dest_idx = inner
152 .indices
153 .get_by_left(dest)
154 .copied()
155 .ok_or(ChannelGraphError::PublicKeyNodeNotFound(*dest))?;
156
157 if inner.graph.find_edge(src_idx, dest_idx).is_none() {
158 inner.graph.add_edge(src_idx, dest_idx, Observations::default());
159 }
160
161 Ok(())
162 }
163
164 fn remove_edge(&self, src: &OffchainPublicKey, dest: &OffchainPublicKey) {
165 let mut inner = self.inner.write();
166 if let (Some(src_idx), Some(dest_idx)) = (
167 inner.indices.get_by_left(src).copied(),
168 inner.indices.get_by_left(dest).copied(),
169 ) && let Some(edge_idx) = inner.graph.find_edge(src_idx, dest_idx)
170 {
171 inner.graph.remove_edge(edge_idx);
172 }
173 }
174
175 #[tracing::instrument(level = "debug", skip(self, f))]
181 fn upsert_edge<F>(&self, src: &OffchainPublicKey, dest: &OffchainPublicKey, f: F)
182 where
183 F: FnOnce(&mut Observations),
184 {
185 let mut inner = self.inner.write();
186
187 let src_idx = if let Some(src_idx) = inner.indices.get_by_left(src) {
188 *src_idx
189 } else {
190 let idx = inner.graph.add_node(*src);
192 inner.indices.insert(*src, idx);
193 idx
194 };
195
196 let dest_idx = if let Some(dest_idx) = inner.indices.get_by_left(dest) {
197 *dest_idx
198 } else {
199 let idx = inner.graph.add_node(*dest);
201 inner.indices.insert(*dest, idx);
202 idx
203 };
204
205 let edge_idx = inner
206 .graph
207 .find_edge(src_idx, dest_idx)
208 .unwrap_or_else(|| inner.graph.add_edge(src_idx, dest_idx, Observations::default()));
209
210 if let Some(weight) = inner.graph.edge_weight_mut(edge_idx) {
211 f(weight);
212 tracing::debug!(%src, %dest, ?weight, "updated edge weight with an observation");
213 }
214 }
215}
216
217impl hopr_api::graph::NetworkGraphConnectivity for ChannelGraph {
218 type NodeId = OffchainPublicKey;
219 type Observed = Observations;
220
221 fn connected_edges(&self) -> Vec<(OffchainPublicKey, OffchainPublicKey, Observations)> {
222 let inner = self.inner.read();
223 inner
224 .graph
225 .edge_indices()
226 .filter_map(|ei| {
227 let (src_idx, dst_idx) = inner.graph.edge_endpoints(ei)?;
228 let src = inner.graph.node_weight(src_idx)?;
229 let dst = inner.graph.node_weight(dst_idx)?;
230 let obs = inner.graph.edge_weight(ei)?;
231 Some((*src, *dst, *obs))
232 })
233 .collect()
234 }
235
236 fn reachable_edges(&self) -> Vec<(OffchainPublicKey, OffchainPublicKey, Observations)> {
237 let inner = self.inner.read();
238 let Some(&me_idx) = inner.indices.get_by_left(&self.me) else {
239 return vec![];
240 };
241
242 let mut reachable = std::collections::HashSet::new();
243 let mut bfs = petgraph::visit::Bfs::new(&inner.graph, me_idx);
244 while let Some(node_idx) = bfs.next(&inner.graph) {
245 reachable.insert(node_idx);
246 }
247
248 inner
249 .graph
250 .edge_indices()
251 .filter_map(|ei| {
252 let (src_idx, dst_idx) = inner.graph.edge_endpoints(ei)?;
253 if !reachable.contains(&src_idx) || !reachable.contains(&dst_idx) {
254 return None;
255 }
256 let src = inner.graph.node_weight(src_idx)?;
257 let dst = inner.graph.node_weight(dst_idx)?;
258 let obs = inner.graph.edge_weight(ei)?;
259 Some((*src, *dst, *obs))
260 })
261 .collect()
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use hex_literal::hex;
268 use hopr_api::{
269 graph::{
270 EdgeLinkObservable, NetworkGraphConnectivity, NetworkGraphView, NetworkGraphWrite,
271 traits::{EdgeObservableRead, EdgeObservableWrite, EdgeWeightType},
272 },
273 types::crypto::prelude::{Keypair, OffchainKeypair},
274 };
275
276 use super::*;
277
278 const SECRET_0: [u8; 32] = hex!("60741b83b99e36aa0c1331578156e16b8e21166d01834abb6c64b103f885734d");
280 const SECRET_1: [u8; 32] = hex!("71bf1f42ebbfcd89c3e197a3fd7cda79b92499e509b6fefa0fe44d02821d146a");
281 const SECRET_2: [u8; 32] = hex!("c24bd833704dd2abdae3933fcc9962c2ac404f84132224c474147382d4db2299");
282 const SECRET_3: [u8; 32] = hex!("e0bf93e9c916104da00b1850adc4608bd7e9087bbd3f805451f4556aa6b3fd6e");
283 const SECRET_4: [u8; 32] = hex!("cfc66f718ec66fb822391775d749d7a0d66b690927673634816b63339bc12a3c");
284 const SECRET_5: [u8; 32] = hex!("203ca4d3c5f98dd2066bb204b5930c10b15c095585c224c826b4e11f08bfa85d");
285 const SECRET_7: [u8; 32] = hex!("4ab03f6f75f845ca1bf8b7104804ea5bda18bda29d1ec5fc5d4267feca5fb8e1");
286
287 fn pubkey_from(secret: &[u8; 32]) -> OffchainPublicKey {
289 *OffchainKeypair::from_secret(secret).expect("valid secret key").public()
290 }
291
292 #[test]
293 fn new_graph_contains_self_node() -> anyhow::Result<()> {
294 let me = pubkey_from(&SECRET_0);
295 let graph = ChannelGraph::new(me);
296 assert!(graph.contains_node(&me));
297 assert_eq!(graph.node_count(), 1);
298 Ok(())
299 }
300
301 #[test]
302 fn adding_a_node_increases_count() -> anyhow::Result<()> {
303 let me = pubkey_from(&SECRET_0);
304 let graph = ChannelGraph::new(me);
305 let peer = pubkey_from(&SECRET_1);
306 graph.add_node(peer);
307 assert!(graph.contains_node(&peer));
308 assert_eq!(graph.node_count(), 2);
309 Ok(())
310 }
311
312 #[test]
313 fn adding_duplicate_node_is_idempotent() -> anyhow::Result<()> {
314 let me = pubkey_from(&SECRET_0);
315 let graph = ChannelGraph::new(me);
316 let peer = pubkey_from(&SECRET_1);
317 graph.add_node(peer);
318 graph.add_node(peer);
319 assert_eq!(graph.node_count(), 2);
320 Ok(())
321 }
322
323 #[test]
324 fn removing_a_node_decreases_count() -> anyhow::Result<()> {
325 let me = pubkey_from(&SECRET_0);
326 let graph = ChannelGraph::new(me);
327 let peer = pubkey_from(&SECRET_1);
328 graph.add_node(peer);
329 assert_eq!(graph.node_count(), 2);
330 graph.remove_node(&peer);
331 assert!(!graph.contains_node(&peer));
332 assert_eq!(graph.node_count(), 1);
333 Ok(())
334 }
335
336 #[test]
337 fn removing_nonexistent_node_is_noop() -> anyhow::Result<()> {
338 let me = pubkey_from(&SECRET_0);
339 let graph = ChannelGraph::new(me);
340 graph.remove_node(&pubkey_from(&SECRET_7));
341 assert_eq!(graph.node_count(), 1);
342 Ok(())
343 }
344
345 #[test]
346 fn adding_an_edge_between_nodes() -> anyhow::Result<()> {
347 let me = pubkey_from(&SECRET_0);
348 let graph = ChannelGraph::new(me);
349 let peer = pubkey_from(&SECRET_1);
350 graph.add_node(peer);
351 graph.add_edge(&me, &peer)?;
352 assert!(graph.has_edge(&me, &peer));
353 assert!(!graph.has_edge(&peer, &me));
354 Ok(())
355 }
356
357 #[test]
358 fn adding_edge_to_missing_node_errors() -> anyhow::Result<()> {
359 let me = pubkey_from(&SECRET_0);
360 let graph = ChannelGraph::new(me);
361 assert!(graph.add_edge(&me, &pubkey_from(&SECRET_7)).is_err());
362 Ok(())
363 }
364
365 #[test]
366 fn removing_a_node_also_removes_its_edges() -> anyhow::Result<()> {
367 let me = pubkey_from(&SECRET_0);
368 let graph = ChannelGraph::new(me);
369 let peer = pubkey_from(&SECRET_1);
370 graph.add_node(peer);
371 graph.add_edge(&me, &peer)?;
372 assert!(graph.has_edge(&me, &peer));
373 graph.remove_node(&peer);
374 assert!(!graph.has_edge(&me, &peer));
375 Ok(())
376 }
377
378 #[tokio::test]
379 async fn view_nodes_returns_all_graph_nodes() -> anyhow::Result<()> {
380 use futures::StreamExt;
381
382 let me = pubkey_from(&SECRET_0);
383 let graph = ChannelGraph::new(me);
384 let peers: Vec<_> = [SECRET_1, SECRET_2, SECRET_3, SECRET_4, SECRET_5]
385 .iter()
386 .map(pubkey_from)
387 .collect();
388 for &peer in &peers {
389 graph.add_node(peer);
390 }
391 let nodes: Vec<_> = graph.nodes().collect().await;
392 assert_eq!(nodes.len(), 6);
393 assert!(nodes.contains(&me));
394 for peer in &peers {
395 assert!(nodes.contains(peer));
396 }
397 Ok(())
398 }
399
400 #[test]
401 fn view_edge_returns_observations_for_existing_edge() -> anyhow::Result<()> {
402 let me = pubkey_from(&SECRET_0);
403 let graph = ChannelGraph::new(me);
404 let peer = pubkey_from(&SECRET_1);
405 graph.add_node(peer);
406 graph.add_edge(&me, &peer)?;
407 assert!(graph.edge(&me, &peer).is_some());
408 Ok(())
409 }
410
411 #[test]
412 fn view_edge_returns_none_for_missing_edge() -> anyhow::Result<()> {
413 let me = pubkey_from(&SECRET_0);
414 let graph = ChannelGraph::new(me);
415 let peer = pubkey_from(&SECRET_1);
416 assert!(graph.edge(&me, &peer).is_none());
417 Ok(())
418 }
419
420 #[test]
421 fn me_returns_self_identity() {
422 let me = pubkey_from(&SECRET_0);
423 let graph = ChannelGraph::new(me);
424 assert_eq!(*graph.me(), me);
425 }
426
427 #[test]
428 fn removing_an_edge_disconnects_nodes() -> anyhow::Result<()> {
429 let me = pubkey_from(&SECRET_0);
430 let peer = pubkey_from(&SECRET_1);
431 let graph = ChannelGraph::new(me);
432 graph.add_node(peer);
433 graph.add_edge(&me, &peer)?;
434 assert!(graph.has_edge(&me, &peer));
435
436 graph.remove_edge(&me, &peer);
437 assert!(!graph.has_edge(&me, &peer));
438 assert!(graph.contains_node(&me));
440 assert!(graph.contains_node(&peer));
441 Ok(())
442 }
443
444 #[test]
445 fn removing_nonexistent_edge_is_noop() {
446 let me = pubkey_from(&SECRET_0);
447 let peer = pubkey_from(&SECRET_1);
448 let graph = ChannelGraph::new(me);
449 graph.add_node(peer);
450 graph.remove_edge(&me, &peer);
452 assert!(!graph.has_edge(&me, &peer));
453 }
454
455 #[test]
456 fn removing_edge_for_unknown_nodes_is_noop() {
457 let me = pubkey_from(&SECRET_0);
458 let graph = ChannelGraph::new(me);
459 let unknown = pubkey_from(&SECRET_7);
460 graph.remove_edge(&me, &unknown);
462 }
463
464 #[test]
465 fn edge_should_not_be_present_when_nodes_not_in_graph() {
466 let me = pubkey_from(&SECRET_0);
467 let graph = ChannelGraph::new(me);
468 let unknown = pubkey_from(&SECRET_7);
469 assert!(!graph.has_edge(&me, &unknown));
470 assert!(!graph.has_edge(&unknown, &me));
471 }
472
473 #[test]
474 fn edge_returns_none_when_nodes_not_in_graph() {
475 let me = pubkey_from(&SECRET_0);
476 let graph = ChannelGraph::new(me);
477 let unknown = pubkey_from(&SECRET_7);
478 assert!(graph.edge(&me, &unknown).is_none());
479 assert!(graph.edge(&unknown, &me).is_none());
480 }
481
482 #[test]
483 fn upsert_edge_creates_edge_when_absent() {
484 let me = pubkey_from(&SECRET_0);
485 let peer = pubkey_from(&SECRET_1);
486 let graph = ChannelGraph::new(me);
487 graph.add_node(peer);
488
489 assert!(!graph.has_edge(&me, &peer));
490 graph.upsert_edge(&me, &peer, |obs| {
491 obs.record(EdgeWeightType::Immediate(Ok(std::time::Duration::from_millis(50))));
492 });
493 assert!(graph.has_edge(&me, &peer));
494
495 let obs = graph.edge(&me, &peer).expect("edge should exist after upsert");
496 assert!(obs.immediate_qos().is_some());
497 }
498
499 #[test]
500 fn upsert_edge_updates_existing_edge() -> anyhow::Result<()> {
501 let me = pubkey_from(&SECRET_0);
502 let peer = pubkey_from(&SECRET_1);
503 let graph = ChannelGraph::new(me);
504 graph.add_node(peer);
505 graph.add_edge(&me, &peer)?;
506
507 graph.upsert_edge(&me, &peer, |obs| {
508 obs.record(EdgeWeightType::Immediate(Ok(std::time::Duration::from_millis(100))));
509 });
510 graph.upsert_edge(&me, &peer, |obs| {
511 obs.record(EdgeWeightType::Immediate(Ok(std::time::Duration::from_millis(200))));
512 });
513
514 let obs = graph.edge(&me, &peer).expect("edge should exist");
515 let latency = obs
516 .immediate_qos()
517 .expect("should have immediate QoS")
518 .average_latency()
519 .expect("should have latency");
520 assert!(latency > std::time::Duration::from_millis(100));
522 assert!(latency < std::time::Duration::from_millis(200));
523 Ok(())
524 }
525
526 #[test]
527 fn upsert_edge_adds_missing_dest_node_and_creates_edge() {
528 let me = pubkey_from(&SECRET_0);
529 let unknown = pubkey_from(&SECRET_7);
530 let graph = ChannelGraph::new(me);
531
532 assert!(!graph.contains_node(&unknown));
533 graph.upsert_edge(&me, &unknown, |obs| {
534 obs.record(EdgeWeightType::Immediate(Ok(std::time::Duration::from_millis(50))));
535 });
536 assert!(graph.contains_node(&unknown), "dest node should be auto-added");
537 assert!(graph.has_edge(&me, &unknown), "edge should be created");
538 assert!(graph.edge(&me, &unknown).unwrap().immediate_qos().is_some());
539 }
540
541 #[test]
542 fn upsert_edge_adds_missing_src_node_and_creates_edge() {
543 let me = pubkey_from(&SECRET_0);
544 let unknown = pubkey_from(&SECRET_7);
545 let graph = ChannelGraph::new(me);
546
547 assert!(!graph.contains_node(&unknown));
548 graph.upsert_edge(&unknown, &me, |obs| {
549 obs.record(EdgeWeightType::Immediate(Ok(std::time::Duration::from_millis(50))));
550 });
551 assert!(graph.contains_node(&unknown), "src node should be auto-added");
552 assert!(graph.has_edge(&unknown, &me), "edge should be created");
553 assert!(graph.edge(&unknown, &me).unwrap().immediate_qos().is_some());
554 }
555
556 #[test]
557 fn upsert_edge_adds_both_missing_nodes_and_creates_edge() {
558 let me = pubkey_from(&SECRET_0);
559 let a = pubkey_from(&SECRET_1);
560 let b = pubkey_from(&SECRET_2);
561 let graph = ChannelGraph::new(me);
562
563 assert!(!graph.contains_node(&a));
564 assert!(!graph.contains_node(&b));
565 graph.upsert_edge(&a, &b, |obs| {
566 obs.record(EdgeWeightType::Immediate(Ok(std::time::Duration::from_millis(50))));
567 });
568 assert!(graph.contains_node(&a), "src node should be auto-added");
569 assert!(graph.contains_node(&b), "dest node should be auto-added");
570 assert!(graph.has_edge(&a, &b), "edge should be created");
571 assert!(graph.edge(&a, &b).unwrap().immediate_qos().is_some());
572 }
573
574 #[test]
575 fn removing_non_last_node_preserves_other_nodes() -> anyhow::Result<()> {
576 let me = pubkey_from(&SECRET_0);
577 let a = pubkey_from(&SECRET_1);
578 let b = pubkey_from(&SECRET_2);
579 let c = pubkey_from(&SECRET_3);
580
581 let graph = ChannelGraph::new(me);
582 graph.add_node(a);
583 graph.add_node(b);
584 graph.add_node(c);
585 assert_eq!(graph.node_count(), 4);
586
587 graph.remove_node(&a);
589 assert_eq!(graph.node_count(), 3);
590 assert!(!graph.contains_node(&a));
591 assert!(graph.contains_node(&me));
592 assert!(graph.contains_node(&b));
593 assert!(graph.contains_node(&c));
594
595 graph.add_edge(&me, &b)?;
597 graph.add_edge(&me, &c)?;
598 assert!(graph.has_edge(&me, &b));
599 assert!(graph.has_edge(&me, &c));
600 Ok(())
601 }
602
603 #[test]
604 fn removing_multiple_nodes_preserves_consistency() -> anyhow::Result<()> {
605 let me = pubkey_from(&SECRET_0);
606 let a = pubkey_from(&SECRET_1);
607 let b = pubkey_from(&SECRET_2);
608 let c = pubkey_from(&SECRET_3);
609 let d = pubkey_from(&SECRET_4);
610
611 let graph = ChannelGraph::new(me);
612 graph.add_node(a);
613 graph.add_node(b);
614 graph.add_node(c);
615 graph.add_node(d);
616
617 graph.add_edge(&me, &a)?;
618 graph.add_edge(&a, &b)?;
619 graph.add_edge(&b, &c)?;
620 graph.add_edge(&c, &d)?;
621
622 graph.remove_node(&b);
624 graph.remove_node(&c);
625
626 assert_eq!(graph.node_count(), 3);
627 assert!(graph.contains_node(&me));
628 assert!(graph.contains_node(&a));
629 assert!(graph.contains_node(&d));
630
631 assert!(!graph.has_edge(&a, &b));
633 assert!(!graph.has_edge(&b, &c));
634 assert!(!graph.has_edge(&c, &d));
635
636 assert!(graph.has_edge(&me, &a));
638 Ok(())
639 }
640
641 #[test]
642 fn edges_are_directed() -> anyhow::Result<()> {
643 let me = pubkey_from(&SECRET_0);
644 let peer = pubkey_from(&SECRET_1);
645 let graph = ChannelGraph::new(me);
646 graph.add_node(peer);
647 graph.add_edge(&me, &peer)?;
648
649 assert!(graph.has_edge(&me, &peer));
650 assert!(!graph.has_edge(&peer, &me));
651
652 assert!(graph.edge(&me, &peer).is_some());
653 assert!(graph.edge(&peer, &me).is_none());
654 Ok(())
655 }
656
657 #[test]
658 fn connected_edges_should_exclude_isolated_nodes() {
659 let me = pubkey_from(&SECRET_0);
660 let a = pubkey_from(&SECRET_1);
661 let isolated = pubkey_from(&SECRET_2);
662 let graph = ChannelGraph::new(me);
663 graph.add_node(a);
664 graph.add_node(isolated); graph.add_edge(&me, &a).unwrap();
666
667 let edges = graph.connected_edges();
668 assert_eq!(edges.len(), 1);
669 assert_eq!(edges[0].0, me);
670 assert_eq!(edges[0].1, a);
671
672 let all_keys: std::collections::HashSet<_> = edges.iter().flat_map(|(s, d, _)| [*s, *d]).collect();
674 assert!(!all_keys.contains(&isolated));
675 }
676
677 #[test]
678 fn connected_edges_should_preserve_observations() {
679 let me = pubkey_from(&SECRET_0);
680 let peer = pubkey_from(&SECRET_1);
681 let graph = ChannelGraph::new(me);
682 graph.add_node(peer);
683 graph.upsert_edge(&me, &peer, |obs| {
684 obs.record(EdgeWeightType::Connected(true));
685 obs.record(EdgeWeightType::Immediate(Ok(std::time::Duration::from_millis(42))));
686 });
687
688 let edges = graph.connected_edges();
689 assert_eq!(edges.len(), 1);
690 let obs = &edges[0].2;
691 assert!(obs.immediate_qos().is_some());
692 }
693
694 #[test]
695 fn connected_edges_should_be_empty_when_no_edges() {
696 let me = pubkey_from(&SECRET_0);
697 let graph = ChannelGraph::new(me);
698 graph.add_node(pubkey_from(&SECRET_1));
699 assert!(graph.connected_edges().is_empty());
700 }
701
702 #[test]
703 fn connected_edges_should_return_all_edges_in_diamond_topology() -> anyhow::Result<()> {
704 let me = pubkey_from(&SECRET_0);
705 let a = pubkey_from(&SECRET_1);
706 let b = pubkey_from(&SECRET_2);
707 let dest = pubkey_from(&SECRET_3);
708 let graph = ChannelGraph::new(me);
709 for n in [a, b, dest] {
710 graph.add_node(n);
711 }
712 graph.add_edge(&me, &a)?;
713 graph.add_edge(&me, &b)?;
714 graph.add_edge(&a, &dest)?;
715 graph.add_edge(&b, &dest)?;
716
717 let edges = graph.connected_edges();
718 assert_eq!(edges.len(), 4);
719 Ok(())
720 }
721}