hopr_path/
path.rs

1use std::collections::{hash_map::RandomState, HashSet};
2use std::fmt::{Display, Formatter};
3use std::hash::Hash;
4
5use futures::future::FutureExt;
6use futures::stream::FuturesOrdered;
7use futures::TryStreamExt;
8use libp2p_identity::PeerId;
9
10use hopr_crypto_types::types::OffchainPublicKey;
11use hopr_db_api::resolver::HoprDbResolverOperations;
12use hopr_internal_types::channels::ChannelStatus;
13use hopr_primitive_types::{primitives::Address, traits::ToHex};
14
15use crate::channel_graph::ChannelGraph;
16use crate::errors::PathError;
17use crate::errors::PathError::{ChannelNotOpened, InvalidPeer, LoopsNotAllowed, MissingChannel, PathNotValid};
18use crate::errors::Result;
19
20/// Base implementation of an abstract path.
21/// Must contain always at least a single entry.
22pub trait Path<N>: Display + Clone + Eq + PartialEq
23where
24    N: Copy + Clone + Eq + PartialEq + Hash,
25{
26    /// Individual hops in the path.
27    /// There must be always at least one hop.
28    fn hops(&self) -> &[N];
29
30    /// Shorthand for number of hops.
31    fn length(&self) -> usize {
32        self.hops().len()
33    }
34
35    /// Gets the last hop
36    fn last_hop(&self) -> &N {
37        // Path must contain at least one hop
38        self.hops().last().expect("path is invalid")
39    }
40
41    /// Checks if all the hops in this path are to distinct addresses.
42    /// Returns `true` if there are duplicate Addresses on this path.
43    /// Note that the duplicate Addresses can never be adjacent.
44    fn contains_cycle(&self) -> bool {
45        let set = HashSet::<&N, RandomState>::from_iter(self.hops().iter());
46        set.len() != self.hops().len()
47    }
48}
49
50/// Represents an on-chain path in the [ChannelGraph].
51///
52/// This path is never allowed to be empty and is always constructed from
53/// [Addresses](Address) that must be known to have open channels between them (at the time of construction).
54/// This path is not useful for transport, because it *does never contain the last hop*
55/// to the destination (which does not require and open channel).
56/// To make it useful for transport, it must be converted to a [TransportPath] via [`ChannelPath::into_path<R>`].
57/// The `ChannelPath` does not allow simple loops (same adjacent hops)
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct ChannelPath {
60    pub(crate) hops: Vec<Address>,
61}
62
63impl ChannelPath {
64    /// Creates a new path by validating the list of addresses using the channel graph.
65    ///
66    /// The given list of `hops` *must not* contain the sender node as the first entry,
67    /// since this node is always assumed to be the sender.
68    /// The list of `hops` also *must not* contain the destination, because an open
69    /// channel is not required for the last hop.
70    pub fn new(hops: Vec<Address>, graph: &ChannelGraph) -> Result<Self> {
71        if hops.is_empty() || hops[0] == graph.my_address() {
72            return Err(PathNotValid);
73        }
74
75        let mut ticket_receiver;
76        let mut ticket_issuer = graph.my_address();
77
78        for hop in hops.iter() {
79            ticket_receiver = *hop;
80
81            // Check for loops
82            if ticket_issuer == ticket_receiver {
83                return Err(LoopsNotAllowed(ticket_receiver.to_hex()));
84            }
85
86            // Check if the channel is opened
87            let channel = graph
88                .get_channel(&ticket_issuer, &ticket_receiver)
89                .ok_or(MissingChannel(ticket_issuer.to_hex(), ticket_receiver.to_hex()))?;
90
91            if channel.status != ChannelStatus::Open {
92                return Err(ChannelNotOpened(ticket_issuer.to_hex(), ticket_receiver.to_hex()));
93            }
94
95            ticket_issuer = ticket_receiver;
96        }
97
98        Ok(Self { hops })
99    }
100
101    /// For internal use and testing only
102    pub(crate) fn new_valid(hops: Vec<Address>) -> Self {
103        assert!(!hops.is_empty(), "must not be empty");
104        Self { hops }
105    }
106
107    /// Resolves this on-chain `ChannelPath` into the off-chain [TransportPath] and adds the final hop
108    /// to the given `destination` (which does not require an open channel).
109    /// The given [resolver](HoprDbResolverOperations) is used for the mapping between `PeerId`s and `Address`es.
110    pub async fn into_path<R: HoprDbResolverOperations>(
111        self,
112        resolver: &R,
113        destination: Address,
114    ) -> Result<TransportPath> {
115        let mut hops = self
116            .hops
117            .iter()
118            .map(|addr| {
119                resolver
120                    .resolve_packet_key(addr)
121                    .map(move |opt| opt?.map(PeerId::from).ok_or(InvalidPeer(addr.to_string())))
122            })
123            .collect::<FuturesOrdered<_>>()
124            .try_collect::<Vec<PeerId>>()
125            .await?;
126
127        let last_hop = resolver
128            .resolve_packet_key(&destination)
129            .await?
130            .ok_or(InvalidPeer(destination.to_string()))?;
131
132        hops.push(last_hop.into());
133        Ok(TransportPath::new_valid(hops))
134    }
135}
136
137impl Path<Address> for ChannelPath {
138    fn hops(&self) -> &[Address] {
139        &self.hops
140    }
141}
142
143impl Display for ChannelPath {
144    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
145        write!(
146            f,
147            "[ {} ] ({} hops)",
148            self.hops.iter().map(|p| p.to_string()).collect::<Vec<_>>().join("->"),
149            self.hops.len()
150        )
151    }
152}
153
154/// Represents an off-chain path of [PeerIds](PeerId).
155///
156/// The path is never allowed to be empty and *always contains the destination*.
157/// In the case of the direct path, this path contains only the destination.
158/// In the case of multiple hops, it also must represent a valid [ChannelPath], therefore,
159/// open channels must exist (at the time of construction) except for the last hop.
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct TransportPath {
162    hops: Vec<PeerId>,
163}
164
165impl TransportPath {
166    /// Resolves vector of [PeerIds](PeerId) into the corresponding [TransportPath] and optionally an associated [ChannelPath].
167    ///
168    /// - If `peers` contains only a single entry (destination), the resulting `TransportPath` contains just this entry.
169    ///   Since in this case the [ChannelPath] would be empty (0-hop), it is `None`. This case is equivalent to construction with `direct()`.
170    /// - If `peers` contains more than a single entry, it first resolves `PeerId`s into `Address`es and then validates and returns
171    ///   also the multi-hop `ChannelPath`.
172    ///
173    /// To do an inverse resolution, from [Addresses](Address) to `PeerId`s, construct the [ChannelPath] and use its `to_path()` method to resolve the
174    /// on-chain path.
175    /// The given [resolver](HoprDbResolverOperations) is used for the mapping between `PeerId`s and `Address`es.
176    pub async fn resolve<R: HoprDbResolverOperations>(
177        peers: Vec<PeerId>,
178        resolver: &R,
179        graph: &ChannelGraph,
180    ) -> Result<(Self, Option<ChannelPath>)> {
181        if peers.is_empty() {
182            Err(PathNotValid)
183        } else if peers.len() == 1 {
184            Ok((Self { hops: peers }, None))
185        } else {
186            let (mut addrs, hops): (Vec<Address>, Vec<PeerId>) = peers
187                .into_iter()
188                .map(|peer| async move {
189                    let key = OffchainPublicKey::try_from(peer)?;
190                    resolver
191                        .resolve_chain_key(&key)
192                        .await?
193                        .map(|addr| (addr, peer))
194                        .ok_or(InvalidPeer(peer.to_string()))
195                })
196                .collect::<FuturesOrdered<_>>()
197                .try_collect::<Vec<_>>()
198                .await?
199                .into_iter()
200                .unzip();
201
202            addrs.pop(); // remove the last hop
203            Ok((Self { hops }, Some(ChannelPath::new(addrs, graph)?)))
204        }
205    }
206
207    /// Constructs a direct `TransportPath` (= 0-hop [ChannelPath])
208    pub fn direct(destination: PeerId) -> Self {
209        Self {
210            hops: vec![destination],
211        }
212    }
213
214    /// Used for testing only.
215    pub(crate) fn new_valid(hops: Vec<PeerId>) -> Self {
216        assert!(!hops.is_empty(), "must not be empty");
217        Self { hops }
218    }
219}
220
221impl Path<PeerId> for TransportPath {
222    /// The `TransportPath` always returns one extra hop to the destination.
223    /// So it contains one more hop than a [ChannelPath] (the final hop does not require a channel).
224    fn hops(&self) -> &[PeerId] {
225        &self.hops
226    }
227}
228
229impl<T> TryFrom<&TransportPath> for Vec<T>
230where
231    T: TryFrom<PeerId>,
232{
233    type Error = PathError;
234
235    fn try_from(value: &TransportPath) -> std::result::Result<Self, Self::Error> {
236        value
237            .hops()
238            .iter()
239            .map(|p| T::try_from(*p).map_err(|_| InvalidPeer(p.to_string())))
240            .collect()
241    }
242}
243
244impl Display for TransportPath {
245    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
246        write!(
247            f,
248            "[ {} ] ({} hops)",
249            self.hops.iter().map(|p| p.to_string()).collect::<Vec<_>>().join("->"),
250            self.length() - 1
251        )
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258    use anyhow::Context;
259    use async_trait::async_trait;
260    use hex_literal::hex;
261    use hopr_crypto_types::prelude::*;
262    use hopr_internal_types::channels::ChannelEntry;
263    use hopr_primitive_types::prelude::*;
264    use std::ops::Add;
265    use std::time::{Duration, SystemTime};
266
267    const PEERS_PRIVS: [[u8; 32]; 5] = [
268        hex!("492057cf93e99b31d2a85bc5e98a9c3aa0021feec52c227cc8170e8f7d047775"),
269        hex!("5bf21ea8cccd69aa784346b07bf79c84dac606e00eecaa68bf8c31aff397b1ca"),
270        hex!("3477d7de923ba3a7d5d72a7d6c43fd78395453532d03b2a1e2b9a7cc9b61bafa"),
271        hex!("db7e3e8fcac4c817aa4cecee1d6e2b4d53da51f9881592c0e1cc303d8a012b92"),
272        hex!("0726a9704d56a013980a9077d195520a61b5aed28f92d89c50bca6e0e0c48cfc"),
273    ];
274
275    fn dummy_channel(src: Address, dst: Address, status: ChannelStatus) -> ChannelEntry {
276        ChannelEntry::new(
277            src,
278            dst,
279            Balance::new_from_str("1", BalanceType::HOPR),
280            1u32.into(),
281            status,
282            1u32.into(),
283        )
284    }
285
286    fn create_graph_and_resolver_entries(me: Address) -> (ChannelGraph, Vec<(OffchainPublicKey, Address)>) {
287        let mut cg = ChannelGraph::new(me, Default::default());
288        let addrs = PEERS_PRIVS
289            .iter()
290            .map(|p| {
291                (
292                    OffchainPublicKey::from_privkey(p).unwrap(),
293                    PublicKey::from_privkey(p).unwrap().to_address(),
294                )
295            })
296            .collect::<Vec<_>>();
297
298        let ts = SystemTime::now().add(Duration::from_secs(10));
299
300        // Channels: 0 -> 1 -> 2 -> 3 -> 4, 4 /> 0, 3 -> 1
301        cg.update_channel(dummy_channel(addrs[0].1, addrs[1].1, ChannelStatus::Open));
302        cg.update_channel(dummy_channel(addrs[1].1, addrs[2].1, ChannelStatus::Open));
303        cg.update_channel(dummy_channel(addrs[2].1, addrs[3].1, ChannelStatus::Open));
304        cg.update_channel(dummy_channel(addrs[3].1, addrs[4].1, ChannelStatus::Open));
305        cg.update_channel(dummy_channel(addrs[3].1, addrs[1].1, ChannelStatus::Open));
306        cg.update_channel(dummy_channel(addrs[4].1, addrs[0].1, ChannelStatus::PendingToClose(ts)));
307
308        (cg, addrs)
309    }
310
311    #[test]
312    fn test_channel_path_zero_hop_should_fail() -> anyhow::Result<()> {
313        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
314        let (cg, _) = create_graph_and_resolver_entries(me);
315
316        ChannelPath::new(vec![], &cg).expect_err("path must not be constructible");
317
318        Ok(())
319    }
320
321    #[test]
322    fn test_channel_path_one_hop() -> anyhow::Result<()> {
323        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
324        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
325        let (_, addrs): (Vec<OffchainPublicKey>, Vec<Address>) = peer_addrs.into_iter().unzip();
326
327        // path: 0 -> 1
328        let cp = ChannelPath::new(vec![addrs[1]], &cg)?;
329        assert_eq!(1, cp.length(), "must be one hop");
330        assert!(!cp.contains_cycle(), "must not be cyclic");
331        Ok(())
332    }
333
334    #[test]
335    fn test_channel_path_two_hop() -> anyhow::Result<()> {
336        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
337        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
338        let (_, addrs): (Vec<OffchainPublicKey>, Vec<Address>) = peer_addrs.into_iter().unzip();
339
340        // path: 0 -> 1 -> 2
341        let cp = ChannelPath::new(vec![addrs[1], addrs[2]], &cg)?;
342        assert_eq!(2, cp.length(), "must be two hop");
343        assert!(!cp.contains_cycle(), "must not be cyclic");
344        Ok(())
345    }
346
347    #[test]
348    fn test_channel_path_three_hop() -> anyhow::Result<()> {
349        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
350        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
351        let (_, addrs): (Vec<OffchainPublicKey>, Vec<Address>) = peer_addrs.into_iter().unzip();
352
353        // path: 0 -> 1 -> 2 -> 3
354        let cp = ChannelPath::new(vec![addrs[1], addrs[2], addrs[3]], &cg)?;
355        assert_eq!(3, cp.length(), "must be three hop");
356        assert!(!cp.contains_cycle(), "must not be cyclic");
357        Ok(())
358    }
359
360    #[test]
361    fn test_channel_path_must_allow_cyclic() -> anyhow::Result<()> {
362        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
363        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
364        let (_, addrs): (Vec<OffchainPublicKey>, Vec<Address>) = peer_addrs.into_iter().unzip();
365
366        // path: 0 -> 1 -> 2 -> 3 -> 1
367        let cp = ChannelPath::new(vec![addrs[1], addrs[2], addrs[3], addrs[1]], &cg)?;
368        assert_eq!(4, cp.length(), "must be four hop");
369        assert!(cp.contains_cycle(), "must not be cyclic");
370        Ok(())
371    }
372
373    #[test]
374    fn test_channel_path_should_fail_for_non_open_channel() -> anyhow::Result<()> {
375        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
376        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
377        let (_, addrs): (Vec<OffchainPublicKey>, Vec<Address>) = peer_addrs.into_iter().unzip();
378
379        // path: 0 -> 4 -> 0 (channel 4 -> 0 is PendingToClose)
380        ChannelPath::new(vec![addrs[4], addrs[0]], &cg).expect_err("path must not be constructible");
381        Ok(())
382    }
383
384    #[test]
385    fn test_channel_path_should_fail_for_non_open_channel_from_self() -> anyhow::Result<()> {
386        let me = PublicKey::from_privkey(&PEERS_PRIVS[4]).unwrap().to_address();
387        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
388        let (_, addrs): (Vec<OffchainPublicKey>, Vec<Address>) = peer_addrs.into_iter().unzip();
389
390        // path: 4 -> 0
391        ChannelPath::new(vec![addrs[4], addrs[0]], &cg).expect_err("path must not be constructible");
392        Ok(())
393    }
394
395    #[test]
396    fn test_channel_path_should_fail_for_non_existing_channel() -> anyhow::Result<()> {
397        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
398        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
399        let (_, addrs): (Vec<OffchainPublicKey>, Vec<Address>) = peer_addrs.into_iter().unzip();
400
401        // path: 0 -> 1 -> 3 (channel 1 -> 3 does not exist)
402        ChannelPath::new(vec![addrs[1], addrs[3]], &cg).expect_err("path 1 must not be constructible");
403
404        // path: 0 -> 1 -> 2 -> 4 (channel 2 -> 4 does not exist)
405        ChannelPath::new(vec![addrs[1], addrs[2], addrs[4]], &cg).expect_err("path 2 must not be constructible");
406        Ok(())
407    }
408
409    #[test]
410    fn test_channel_path_should_not_allow_loops() -> anyhow::Result<()> {
411        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
412        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
413        let (_, addrs): (Vec<OffchainPublicKey>, Vec<Address>) = peer_addrs.into_iter().unzip();
414
415        // path: 0 -> 1 -> 1 -> 2
416        ChannelPath::new(vec![addrs[1], addrs[1], addrs[0]], &cg).expect_err("path must not be constructible");
417        Ok(())
418    }
419
420    struct TestResolver(Vec<(OffchainPublicKey, Address)>);
421
422    #[async_trait]
423    impl HoprDbResolverOperations for TestResolver {
424        async fn resolve_packet_key(
425            &self,
426            onchain_key: &Address,
427        ) -> hopr_db_sql::api::errors::Result<Option<OffchainPublicKey>> {
428            Ok(self.0.iter().find(|(_, addr)| addr.eq(onchain_key)).map(|(pk, _)| *pk))
429        }
430
431        async fn resolve_chain_key(
432            &self,
433            offchain_key: &OffchainPublicKey,
434        ) -> hopr_db_sql::api::errors::Result<Option<Address>> {
435            Ok(self.0.iter().find(|(pk, _)| pk.eq(offchain_key)).map(|(_, addr)| *addr))
436        }
437    }
438
439    #[async_std::test]
440    async fn test_transport_path_empty_should_fail() -> anyhow::Result<()> {
441        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
442        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
443        let resolver = TestResolver(peer_addrs.clone());
444
445        TransportPath::resolve(vec![], &resolver, &cg)
446            .await
447            .expect_err("should not resolve path");
448        Ok(())
449    }
450
451    fn make_address_pairs(peer_addrs: Vec<(OffchainPublicKey, Address)>) -> (Vec<PeerId>, Vec<Address>) {
452        peer_addrs.into_iter().map(|(p, a)| (PeerId::from(p), a)).unzip()
453    }
454
455    #[async_std::test]
456    async fn test_transport_path_resolve_direct() -> anyhow::Result<()> {
457        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
458        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
459        let resolver = TestResolver(peer_addrs.clone());
460        let (peers, _) = make_address_pairs(peer_addrs);
461
462        // path 0 -> 1
463        let (p, cp) = TransportPath::resolve(vec![peers[1]], &resolver, &cg).await?;
464        assert_eq!(1, p.length(), "must contain destination");
465        assert!(cp.is_none(), "no channel path for direct message");
466
467        Ok(())
468    }
469
470    #[async_std::test]
471    async fn test_transport_path_resolve_direct_to_self() -> anyhow::Result<()> {
472        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
473        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
474        let resolver = TestResolver(peer_addrs.clone());
475        let (peers, _) = make_address_pairs(peer_addrs);
476
477        // path 0 -> 0
478        let (p, cp) = TransportPath::resolve(vec![peers[0]], &resolver, &cg).await?;
479        assert_eq!(1, p.length(), "must contain destination");
480        assert!(cp.is_none(), "no channel path for direct message");
481
482        Ok(())
483    }
484
485    #[async_std::test]
486    async fn test_transport_path_resolve_direct_is_allowed_without_channel_to_destination() -> anyhow::Result<()> {
487        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
488        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
489        let resolver = TestResolver(peer_addrs.clone());
490        let (peers, _) = make_address_pairs(peer_addrs);
491
492        // path 0 -> 3
493        let (p, cp) = TransportPath::resolve(vec![peers[3]], &resolver, &cg).await?;
494        assert_eq!(1, p.length(), "must contain destination");
495        assert!(cp.is_none(), "no channel path for direct message");
496        Ok(())
497    }
498
499    #[async_std::test]
500    async fn test_transport_path_resolve_direct_is_allowed_with_closed_channel_to_destination() -> anyhow::Result<()> {
501        let me = PublicKey::from_privkey(&PEERS_PRIVS[4]).unwrap().to_address();
502        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
503        let resolver = TestResolver(peer_addrs.clone());
504        let (peers, _) = make_address_pairs(peer_addrs);
505
506        // path 4 -> 0
507        let (p, cp) = TransportPath::resolve(vec![peers[0]], &resolver, &cg).await?;
508        assert_eq!(1, p.length(), "must contain destination");
509        assert!(cp.is_none(), "no channel path for direct message");
510
511        Ok(())
512    }
513
514    #[async_std::test]
515    async fn test_transport_path_resolve_one_hop() -> anyhow::Result<()> {
516        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
517        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
518        let resolver = TestResolver(peer_addrs.clone());
519        let (peers, addrs) = make_address_pairs(peer_addrs);
520
521        // path 0 -> 1 -> 2
522        let (p, cp) = TransportPath::resolve(vec![peers[1], peers[2]], &resolver, &cg).await?;
523        assert_eq!(2, p.length(), "must be two hop");
524        assert!(!p.contains_cycle(), "transport path must not contain a cycle");
525
526        let cp = cp.context("must have channel path")?;
527        assert_eq!(1, cp.length(), "channel path must be one hop");
528        assert_eq!(vec![addrs[1]], cp.hops(), "channel path address must match");
529        assert!(!cp.contains_cycle(), "channel path must not contain a cycle");
530        Ok(())
531    }
532
533    #[async_std::test]
534    async fn test_transport_path_resolve_one_hop_without_channel_to_destination() -> anyhow::Result<()> {
535        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
536        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
537        let resolver = TestResolver(peer_addrs.clone());
538        let (peers, addrs) = make_address_pairs(peer_addrs);
539
540        // path 0 -> 1 -> 4
541        let (p, cp) = TransportPath::resolve(vec![peers[1], peers[4]], &resolver, &cg).await?;
542        assert_eq!(2, p.length(), "must be two hop");
543        assert!(!p.contains_cycle(), "transport path must not contain a cycle");
544
545        let cp = cp.context("must have channel path")?;
546        assert_eq!(1, cp.length(), "channel path must be one hop");
547        assert_eq!(vec![addrs[1]], cp.hops(), "channel path address must match");
548        assert!(!cp.contains_cycle(), "channel path must not contain a cycle");
549        Ok(())
550    }
551
552    #[async_std::test]
553    async fn test_transport_path_resolve_two_hop() -> anyhow::Result<()> {
554        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
555        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
556        let resolver = TestResolver(peer_addrs.clone());
557        let (peers, addrs) = make_address_pairs(peer_addrs);
558
559        // path 0 -> 1 -> 2 -> 3
560        let (p, cp) = TransportPath::resolve(vec![peers[1], peers[2], peers[3]], &resolver, &cg).await?;
561        assert_eq!(3, p.length(), "must be three hop");
562        assert!(!p.contains_cycle(), "transport path must not contain a cycle");
563
564        let cp = cp.context("must have channel path")?;
565        assert_eq!(2, cp.length(), "channel path must be two hop");
566        assert_eq!(vec![addrs[1], addrs[2]], cp.hops(), "channel path address must match");
567        assert!(!cp.contains_cycle(), "channel path must not contain a cycle");
568        Ok(())
569    }
570
571    #[async_std::test]
572    async fn test_transport_path_resolve_two_hop_without_channel_to_destination() -> anyhow::Result<()> {
573        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
574        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
575        let resolver = TestResolver(peer_addrs.clone());
576        let (peers, addrs) = make_address_pairs(peer_addrs);
577
578        // path 0 -> 1 -> 2 -> 4
579        let (p, cp) = TransportPath::resolve(vec![peers[1], peers[2], peers[4]], &resolver, &cg).await?;
580        assert_eq!(3, p.length(), "must be three hop");
581        assert!(!p.contains_cycle(), "transport path must not contain a cycle");
582
583        let cp = cp.context("must have channel path")?;
584        assert_eq!(2, cp.length(), "channel path must be two hop");
585        assert_eq!(vec![addrs[1], addrs[2]], cp.hops(), "channel path address must match");
586        assert!(!cp.contains_cycle(), "channel path must not contain a cycle");
587        Ok(())
588    }
589
590    #[async_std::test]
591    async fn test_transport_path_resolve_three_hop() -> anyhow::Result<()> {
592        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
593        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
594        let resolver = TestResolver(peer_addrs.clone());
595        let (peers, addrs) = make_address_pairs(peer_addrs);
596
597        // path 0 -> 1 -> 2 -> 3 -> 4
598        let (p, cp) = TransportPath::resolve(vec![peers[1], peers[2], peers[3], peers[4]], &resolver, &cg).await?;
599        assert_eq!(4, p.length(), "must have 4 entries");
600        assert!(!p.contains_cycle(), "transport path must not contain a cycle");
601
602        let cp = cp.context("must have channel path")?;
603        assert_eq!(3, cp.length(), "channel path must be two hop");
604        assert_eq!(
605            vec![addrs[1], addrs[2], addrs[3]],
606            cp.hops(),
607            "channel path address must match"
608        );
609        assert!(!cp.contains_cycle(), "channel path must not contain a cycle");
610        Ok(())
611    }
612
613    #[async_std::test]
614    async fn test_transport_path_resolve_with_cycle() -> anyhow::Result<()> {
615        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
616        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
617        let resolver = TestResolver(peer_addrs.clone());
618        let (peers, addrs) = make_address_pairs(peer_addrs);
619
620        // path 0 -> 1 -> 2 -> 3 -> 1
621        let (p, cp) = TransportPath::resolve(vec![peers[1], peers[2], peers[3], peers[1]], &resolver, &cg).await?;
622        assert_eq!(4, p.length(), "must have 4 entries");
623        assert!(p.contains_cycle(), "transport path must contain a cycle");
624
625        let cp = cp.context("must have channel path")?;
626        assert_eq!(3, cp.length(), "channel path must be 3 hop");
627        assert_eq!(
628            vec![addrs[1], addrs[2], addrs[3]],
629            cp.hops(),
630            "channel path address must match"
631        );
632        assert!(!cp.contains_cycle(), "channel path must not contain a cycle");
633        Ok(())
634    }
635
636    #[async_std::test]
637    async fn test_transport_path_resolve_with_channel_cycle() -> anyhow::Result<()> {
638        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
639        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
640        let resolver = TestResolver(peer_addrs.clone());
641        let (peers, addrs) = make_address_pairs(peer_addrs);
642
643        // path 0 -> 1 -> 2 -> 3 -> 1 -> 2
644        let (p, cp) =
645            TransportPath::resolve(vec![peers[1], peers[2], peers[3], peers[1], peers[2]], &resolver, &cg).await?;
646        assert_eq!(5, p.length(), "must be 5 hop");
647        assert!(p.contains_cycle(), "transport path must contain a cycle");
648
649        let cp = cp.context("must have channel path")?;
650        assert_eq!(4, cp.length(), "channel path must be 4 hop");
651        assert_eq!(
652            vec![addrs[1], addrs[2], addrs[3], addrs[1]],
653            cp.hops(),
654            "channel path address must match"
655        );
656        assert!(cp.contains_cycle(), "channel path must not contain a cycle");
657        Ok(())
658    }
659
660    #[async_std::test]
661    async fn test_transport_path_should_not_resolve_for_non_existing_intermediate_channel() -> anyhow::Result<()> {
662        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
663        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
664        let resolver = TestResolver(peer_addrs.clone());
665        let (peers, _) = make_address_pairs(peer_addrs);
666
667        // path 0 -> 2 -> 3
668        TransportPath::resolve(vec![peers[2], peers[3]], &resolver, &cg)
669            .await
670            .expect_err("should not resolve path 1");
671
672        // path 0 -> 1 -> 3 -> 1
673        TransportPath::resolve(vec![peers[1], peers[3], peers[1]], &resolver, &cg)
674            .await
675            .expect_err("should not resolve path 2");
676        Ok(())
677    }
678
679    #[async_std::test]
680    async fn test_transport_path_should_not_resolve_for_non_open_intermediate_channel() -> anyhow::Result<()> {
681        let me = PublicKey::from_privkey(&PEERS_PRIVS[2]).unwrap().to_address();
682        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
683        let resolver = TestResolver(peer_addrs.clone());
684        let (peers, _) = make_address_pairs(peer_addrs);
685
686        // path 2 -> 3 -> 4 -> 0 -> 1
687        TransportPath::resolve(vec![peers[3], peers[4], peers[0], peers[1]], &resolver, &cg)
688            .await
689            .expect_err("should not resolve path");
690        Ok(())
691    }
692
693    #[async_std::test]
694    async fn test_channel_path_to_transport_path() -> anyhow::Result<()> {
695        let me = PublicKey::from_privkey(&PEERS_PRIVS[0]).unwrap().to_address();
696        let (cg, peer_addrs) = create_graph_and_resolver_entries(me);
697        let resolver = TestResolver(peer_addrs.clone());
698        let (peers, addrs) = make_address_pairs(peer_addrs);
699
700        // path: 0 -> 1 -> 2 -> 3
701        let cp = ChannelPath::new(vec![addrs[1], addrs[2], addrs[3]], &cg)?;
702
703        // path: 0 -> 1 -> 2 -> 3 -> 4
704        let tp = cp.clone().into_path(&resolver, addrs[4]).await?;
705        assert_eq!(
706            tp.length(),
707            cp.length() + 1,
708            "transport path must have extra hop to destination"
709        );
710        assert_eq!(
711            vec![peers[1], peers[2], peers[3], peers[4]],
712            tp.hops(),
713            "must contain all peer ids"
714        );
715        Ok(())
716    }
717}