1
//! Logic for filtering and selecting channels in order to find suitable channels for a target.
2

            
3
use crate::mgr::AbstractChannel;
4
use crate::mgr::state::{ChannelState, OpenEntry, PendingEntry};
5
use tor_linkspec::{HasChanMethod, HasRelayIds, RelayIds};
6

            
7
/// Returns `true` if the open channel is allowed to be used for a new channel request to the
8
/// target.
9
40
pub(crate) fn open_channel_is_allowed<C: AbstractChannel>(
10
40
    chan: &OpenEntry<C>,
11
40
    target: &(impl HasRelayIds + HasChanMethod),
12
40
) -> bool {
13
40
    Some(chan)
14
        // only usable channels
15
40
        .filter(|entry| entry.channel.is_usable())
16
        // only channels which have *all* the relay ids of `target`
17
40
        .filter(|entry| entry.channel.has_all_relay_ids_from(target))
18
        // TODO: only channels that satisfy the torspec rules at
19
        // https://spec.torproject.org/tor-spec/creating-circuits.html#canonical-connections
20
40
        .filter(|_entry| {
21
            // Any of:
22
            // - the IP matches the requested IP
23
            // - the relay knows that the IP of the connection it's using is canonical because it
24
            //   was listed in the NETINFO cell
25
            // - the IP matches the relay address in the consensus
26
18
            true
27
18
        })
28
40
        .is_some()
29
40
}
30

            
31
/// Returns `true` if the pending channel could possibly be used for a new channel request to the
32
/// target. You still need to verify the final built channel with [`open_channel_is_allowed`] before
33
/// using it.
34
26
pub(crate) fn pending_channel_maybe_allowed(
35
26
    chan: &PendingEntry,
36
26
    target: &(impl HasRelayIds + HasChanMethod),
37
26
) -> bool {
38
    /// An empty [`RelayIds`].
39
    const EMPTY_IDS: RelayIds = RelayIds::empty();
40

            
41
    // TODO RELAY: The comments and behaviour below assume that it's better to create a new channel
42
    // than to wait around for a channel which may or may not end up being usable for `target`. This
43
    // has the benefit that malicious circuit extension requests won't delay legitimate circuit
44
    // extension requests, but also means that we could end up creating more channels than
45
    // necessary. This is different from C-tor, which will wait for a channel even if that channel
46
    // might not end up being usable for `target`. For example in tor's `channel_get_for_extend`,
47
    // tor will wait for an "in progress" channel if all of the following are true:
48
    //
49
    // - The requested ids are a subset of the channel's ids. (Note that in the comments below we
50
    //   require it to be a superset, not a subset.)
51
    // - The requested IPv4 or IPv6 address matches either the channel's IPv4 or IPv6 address. (Note
52
    //   that in the comments below, we require `target`s addresses to exactly match.)
53
    //
54
    // It might be good to re-evaluate what behaviour we want as we implement more channel code.
55
    //
56
    // NOTE (opara): It has been decided that C-tor's approach would be better. See the thread at:
57
    // https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/2544#note_3094696
58

            
59
    // We want to avoid returning pending channels that were initially created from malicious
60
    // channel requests (for example from malicious relay-extend requests) that build channels which
61
    // will never complete successfully. Two cases where this can happen are:
62
    // 1. A malicious channel request asks us to build a channel to a target with a correct relay id
63
    //    and address, but also an additional incorrect relay id. Later when the target sends its
64
    //    CERTS cell, all of the relay ids won't match and the channel will fail to build. We don't
65
    //    want to assign non-malicious channel requests to this pending channel that will eventually
66
    //    fail to build.
67
    // 2. A malicious channel request asks us to build a channel to a target with an incorrect
68
    //    address. This pending channel may stall. We don't want to assign non-malicious channel
69
    //    requests to this pending channel that will stall for potentially a long time.
70
26
    Some(chan)
71
        // Only channels where `target`s relay ids are a superset of `entry`s relay ids.
72
        // - Hopefully the built channel will gain the additional ids that are requested by
73
        //   `target`. This should happen in most cases where none of the channels are made
74
        //   maliciously, since the `target` should return all of its relay ids in its CERTS cell.
75
        // - (Addressing 1. above) By only returning pending channels that have a subset of
76
        //   `target`s relay ids, we ensure that the returned pending channel does not have
77
        //   additional incorrect relay ids that will intentionally cause the pending channel to
78
        //   fail.
79
        // - If the built channel does not gain the remaining ids required by `target, then we won't
80
        //   be able to use this channel for the channel request to `target`. But we won't be able
81
        //   to create a new channel either, since we know that that a new channel also won't have
82
        //   all of the relay ids. So this channel request was doomed from the start.
83
        // - If the built channel gains additional ids that `target` doesn't have, that's fine and
84
        //   we can still use the channel for `target`.
85
26
        .filter(|entry| target.has_all_relay_ids_from(&entry.ids))
86
        // TODO: Only channels which have the exact same address list as `target` (the two sets of
87
        // addresses must match exactly).
88
        // - While an EXTEND2 message usually only contains one IPv4 and IPv6 address, `target`
89
        //   (which is a `HasAddrs`) may have more addresses. According to tor-spec, an EXTEND2
90
        //   message can contain multiple IPv4 and IPv6 addresses:
91
        //   > Nodes MUST ignore unrecognized specifiers, and MUST accept multiple instances of
92
        //   > specifiers other than 'legacy identity' and 'Ed25519 identity'. (Nodes SHOULD reject
93
        //   > link specifier lists that include multiple instances of either one of those
94
        //   > specifiers.)
95
        // - (Addressing 2. above) By only returning pending channels that have exactly the same
96
        //   addresses, we ensure that the returned pending channel does not have any incorrect
97
        //   addresses that will cause the pending channel to stall.
98
        // - If the pending channel had additional addresses compared to `target`, the channel could
99
        //   get built using an address that is not valid for `target` and we wouldn't be able to
100
        //   use the built channel.
101
        // - If the pending channel had fewer addresses compared to `target`, the channel would have
102
        //   a lower possibility of building successfully compared to a newly created channel to
103
        //   `target`, so this would not be a good channel for us to return.
104
26
        .filter(|_entry| true)
105
        // Don't allow a pending channel that has no relay ids. I don't have a good reason for
106
        // excluding this, other than "it seems weird".
107
26
        .filter(|entry| entry.ids != EMPTY_IDS)
108
26
        .is_some()
109
26
}
110

            
111
/// Returns the best channel for `target`.
112
// TODO: remove me when the below TODOs are implemented
113
#[allow(clippy::only_used_in_recursion)]
114
154
pub(crate) fn choose_best_channel<'a, C: AbstractChannel>(
115
154
    channels: impl IntoIterator<Item = &'a ChannelState<C>>,
116
154
    target: &(impl HasRelayIds + HasChanMethod),
117
154
) -> Option<&'a ChannelState<C>> {
118
    use ChannelState::*;
119
    use std::cmp::Ordering;
120

            
121
154
    let channels = channels.into_iter();
122

            
123
    /// Compare two channels to determine the better channel for `target`.
124
204
    fn choose_channel<C: AbstractChannel>(
125
204
        a: &&ChannelState<C>,
126
204
        b: &&ChannelState<C>,
127
204
        target: &(impl HasRelayIds + HasChanMethod),
128
204
    ) -> Choice {
129
        // TODO: follow `channel_is_better` in C tor
130
204
        match (a, b) {
131
            // if the open channel is not usable, prefer the pending channel
132
108
            (Open(a), Building(_b)) if !a.channel.is_usable() => Choice::Second,
133
            // otherwise prefer the open channel
134
84
            (Open(_a), Building(_b)) => Choice::First,
135

            
136
            // the logic above, but reversed
137
48
            (Building(_), Open(_)) => choose_channel(b, a, target).reverse(),
138

            
139
            // not much info to help choose when both channels are pending, but this should be rare
140
16
            (Building(_a), Building(_b)) => Choice::Either,
141

            
142
            // both channels are open
143
32
            (Open(a), Open(b)) => {
144
32
                let a_is_usable = a.channel.is_usable();
145
32
                let b_is_usable = b.channel.is_usable();
146

            
147
                // if neither open channel is usable, don't take preference
148
32
                if !a_is_usable && !b_is_usable {
149
                    return Choice::Either;
150
32
                }
151

            
152
                // prefer a channel that is usable
153
32
                if !a_is_usable {
154
6
                    return Choice::Second;
155
26
                }
156
26
                if !b_is_usable {
157
26
                    return Choice::First;
158
                }
159

            
160
                // Prefer a channel that we see as canonical.
161
                let a_is_canonical = a.channel.is_canonical();
162
                let b_is_canonical = b.channel.is_canonical();
163

            
164
                if a_is_canonical && !b_is_canonical {
165
                    return Choice::First;
166
                }
167
                if !a_is_canonical && b_is_canonical {
168
                    return Choice::Second;
169
                }
170

            
171
                // Prefer a channel that the peer sees as canonical.
172
                let a_is_canonical_to_peer = a.channel.is_canonical_to_peer();
173
                let b_is_canonical_to_peer = b.channel.is_canonical_to_peer();
174

            
175
                if a_is_canonical_to_peer && !b_is_canonical_to_peer {
176
                    return Choice::First;
177
                }
178
                if !a_is_canonical_to_peer && b_is_canonical_to_peer {
179
                    return Choice::Second;
180
                }
181

            
182
                // TODO: prefer a channel where the address matches the target
183

            
184
                // TODO: prefer older channels
185

            
186
                // TODO: use number of circuits as tie-breaker?
187

            
188
                Choice::Either
189
            }
190
        }
191
204
    }
192

            
193
    // preferred channels will be ordered higher, and we choose the max
194
156
    channels.max_by(|a, b| match choose_channel(a, b, target) {
195
90
        Choice::First => Ordering::Greater,
196
50
        Choice::Second => Ordering::Less,
197
16
        Choice::Either => Ordering::Equal,
198
156
    })
199
154
}
200

            
201
/// Similar to [`Ordering`](std::cmp::Ordering), but is easier to reason about when comparing two
202
/// objects that don't have a numeric sense of ordering (ex: returning `Greater` is confusing if the
203
/// ordering isn't numeric).
204
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
205
enum Choice {
206
    /// Choose the first.
207
    First,
208
    /// Choose the second.
209
    Second,
210
    /// Choose either.
211
    Either,
212
}
213

            
214
impl Choice {
215
    /// Reverses the `Choice`.
216
    ///
217
    /// - `First` becomes `Second`.
218
    /// - `Second` becomes `First`.
219
    /// - `Either` becomes `Either`.
220
48
    fn reverse(self) -> Self {
221
48
        match self {
222
34
            Self::First => Self::Second,
223
14
            Self::Second => Self::First,
224
            Self::Either => Self::Either,
225
        }
226
48
    }
227
}
228

            
229
#[cfg(test)]
230
mod test {
231
    // @@ begin test lint list maintained by maint/add_warning @@
232
    #![allow(clippy::bool_assert_comparison)]
233
    #![allow(clippy::clone_on_copy)]
234
    #![allow(clippy::dbg_macro)]
235
    #![allow(clippy::mixed_attributes_style)]
236
    #![allow(clippy::print_stderr)]
237
    #![allow(clippy::print_stdout)]
238
    #![allow(clippy::single_char_pattern)]
239
    #![allow(clippy::unwrap_used)]
240
    #![allow(clippy::unchecked_time_subtraction)]
241
    #![allow(clippy::useless_vec)]
242
    #![allow(clippy::needless_pass_by_value)]
243
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
244

            
245
    use super::*;
246

            
247
    use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
248
    use std::sync::Arc;
249
    use std::time::Duration;
250

            
251
    use tor_linkspec::ChannelMethod;
252
    use tor_llcrypto::pk::ed25519::Ed25519Identity;
253
    use tor_llcrypto::pk::rsa::RsaIdentity;
254
    use tor_proto::channel::ChannelPaddingInstructionsUpdates;
255
    use tor_proto::channel::kist::KistParams;
256

            
257
    // Address we can use in tests.
258
    const ADDR_A: SocketAddr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 443));
259

            
260
    #[derive(Debug)]
261
    struct FakeChannel {
262
        usable: bool,
263
        ids: RelayIds,
264
    }
265

            
266
    impl AbstractChannel for FakeChannel {
267
        fn is_canonical(&self) -> bool {
268
            unimplemented!()
269
        }
270
        fn is_canonical_to_peer(&self) -> bool {
271
            unimplemented!()
272
        }
273
        fn is_usable(&self) -> bool {
274
            self.usable
275
        }
276
        fn duration_unused(&self) -> Option<Duration> {
277
            None
278
        }
279
        fn reparameterize(
280
            &self,
281
            _updates: Arc<ChannelPaddingInstructionsUpdates>,
282
        ) -> tor_proto::Result<()> {
283
            Ok(())
284
        }
285
        fn reparameterize_kist(&self, _kist_params: KistParams) -> tor_proto::Result<()> {
286
            Ok(())
287
        }
288
        fn engage_padding_activities(&self) {}
289
    }
290

            
291
    impl HasRelayIds for FakeChannel {
292
        fn identity(
293
            &self,
294
            key_type: tor_linkspec::RelayIdType,
295
        ) -> Option<tor_linkspec::RelayIdRef<'_>> {
296
            self.ids.identity(key_type)
297
        }
298
    }
299

            
300
    #[derive(Clone, Debug)]
301
    struct FakeBuildSpec {
302
        ids: RelayIds,
303
        addrs: Vec<SocketAddr>,
304
    }
305

            
306
    impl FakeBuildSpec {
307
        fn new(ids: RelayIds, addrs: Vec<SocketAddr>) -> Self {
308
            Self { ids, addrs }
309
        }
310
    }
311

            
312
    impl HasRelayIds for FakeBuildSpec {
313
        fn identity(
314
            &self,
315
            key_type: tor_linkspec::RelayIdType,
316
        ) -> Option<tor_linkspec::RelayIdRef<'_>> {
317
            self.ids.identity(key_type)
318
        }
319
    }
320

            
321
    impl HasChanMethod for FakeBuildSpec {
322
        fn chan_method(&self) -> ChannelMethod {
323
            ChannelMethod::Direct(self.addrs.clone())
324
        }
325
    }
326

            
327
    /// Assert that two `Option<&T>` point to the same data.
328
    macro_rules! assert_opt_ptr_eq {
329
        ($a:expr, $b:expr) => {
330
            assert_opt_ptr_eq!($a, $b,);
331
        };
332
        ($a:expr, $b:expr, $($x:tt)*) => {
333
            assert_eq!($a.map(std::ptr::from_ref), $b.map(std::ptr::from_ref), $($x)*);
334
        };
335
    }
336

            
337
    /// Calls `f` with every permutation of `list`. Don't use with large lists :)
338
    fn with_permutations<T>(list: &[T], mut f: impl FnMut(Vec<&T>)) {
339
        use itertools::Itertools;
340
        for new_list in list.iter().permutations(list.len()) {
341
            f(new_list);
342
        }
343
    }
344

            
345
    /// Helper to make a fake Ed identity from some bytes.
346
    fn ed(a: &[u8]) -> Ed25519Identity {
347
        let mut bytes = [0; 32];
348
        bytes[0..a.len()].copy_from_slice(a);
349
        bytes.into()
350
    }
351

            
352
    /// Helper to make a fake rsa identity from some bytes.
353
    fn rsa(a: &[u8]) -> RsaIdentity {
354
        let mut bytes = [0; 20];
355
        bytes[0..a.len()].copy_from_slice(a);
356
        bytes.into()
357
    }
358

            
359
    /// Helper to build a `RelayIds` to make tests shorter.
360
    fn ids(
361
        rsa: impl Into<Option<RsaIdentity>>,
362
        ed: impl Into<Option<Ed25519Identity>>,
363
    ) -> RelayIds {
364
        let mut ids = tor_linkspec::RelayIdsBuilder::default();
365
        if let Some(rsa) = rsa.into() {
366
            ids.rsa_identity(rsa);
367
        }
368
        if let Some(ed) = ed.into() {
369
            ids.ed_identity(ed);
370
        }
371
        ids.build().unwrap()
372
    }
373

            
374
    /// Create an open channel entry.
375
    fn open_channel<C>(chan: C) -> OpenEntry<C> {
376
        OpenEntry {
377
            channel: Arc::new(chan),
378
            max_unused_duration: Duration::from_secs(0),
379
        }
380
    }
381

            
382
    /// Create a pending channel entry with the given IDs.
383
    fn pending_channel(ids: RelayIds) -> PendingEntry {
384
        use crate::mgr::state::UniqPendingChanId;
385
        use futures::FutureExt;
386
        use oneshot_fused_workaround as oneshot;
387

            
388
        PendingEntry {
389
            ids,
390
            pending: oneshot::channel().1.shared(),
391
            unique_id: UniqPendingChanId::new(),
392
        }
393
    }
394

            
395
    #[test]
396
    fn best_channel_usable_unusable() {
397
        // two channels where only the first is usable
398
        let channels = [
399
            ChannelState::Open(open_channel(FakeChannel {
400
                usable: true,
401
                ids: ids(None, ed(b"A")),
402
            })),
403
            ChannelState::Open(open_channel(FakeChannel {
404
                usable: false,
405
                ids: ids(None, ed(b"A")),
406
            })),
407
        ];
408

            
409
        // should return the usable channel
410
        let target = FakeBuildSpec::new(ids(None, ed(b"A")), vec![ADDR_A]);
411
        with_permutations(&channels, |x| {
412
            assert_opt_ptr_eq!(choose_best_channel(x, &target), Some(&channels[0]));
413
        });
414
    }
415

            
416
    #[test]
417
    fn best_channel_open_pending() {
418
        // a usable open channel and a pending channel
419
        let channels = [
420
            ChannelState::Open(open_channel(FakeChannel {
421
                usable: true,
422
                ids: ids(None, ed(b"A")),
423
            })),
424
            ChannelState::Building(pending_channel(ids(None, ed(b"A")))),
425
        ];
426

            
427
        // should return the open channel
428
        let target = FakeBuildSpec::new(ids(None, ed(b"A")), vec![ADDR_A]);
429
        with_permutations(&channels, |x| {
430
            assert_opt_ptr_eq!(choose_best_channel(x, &target), Some(&channels[0]));
431
        });
432

            
433
        // an unusable open channel and a pending channel
434
        let channels = [
435
            ChannelState::Open(open_channel(FakeChannel {
436
                usable: false,
437
                ids: ids(None, ed(b"A")),
438
            })),
439
            ChannelState::Building(pending_channel(ids(None, ed(b"A")))),
440
        ];
441

            
442
        // should return the pending channel
443
        let target = FakeBuildSpec::new(ids(None, ed(b"A")), vec![ADDR_A]);
444
        with_permutations(&channels, |x| {
445
            assert_opt_ptr_eq!(choose_best_channel(x, &target), Some(&channels[1]));
446
        });
447
    }
448

            
449
    #[test]
450
    fn best_channel_many() {
451
        // some misc channels (as we make `choose_best_channel` more complex, hopeful we can add
452
        // more channels here)
453
        let channels = [
454
            ChannelState::Open(open_channel(FakeChannel {
455
                usable: false,
456
                ids: ids(None, ed(b"A")),
457
            })),
458
            ChannelState::Open(open_channel(FakeChannel {
459
                usable: true,
460
                ids: ids(None, ed(b"A")),
461
            })),
462
            ChannelState::Building(pending_channel(ids(None, ed(b"A")))),
463
            ChannelState::Building(pending_channel(ids(None, None))),
464
        ];
465

            
466
        // should return the open+usable channel
467
        let target = FakeBuildSpec::new(ids(None, ed(b"A")), vec![ADDR_A]);
468
        with_permutations(&channels, |x| {
469
            assert_opt_ptr_eq!(choose_best_channel(x, &target), Some(&channels[1]));
470
        });
471
    }
472

            
473
    #[test]
474
    fn test_open_channel_is_allowed() {
475
        // target with an ed relay id
476
        let target = FakeBuildSpec::new(ids(None, ed(b"A")), vec![ADDR_A]);
477

            
478
        // not allowed: unusable channel
479
        assert!(!open_channel_is_allowed(
480
            &open_channel(FakeChannel {
481
                usable: false,
482
                ids: ids(None, ed(b"A")),
483
            }),
484
            &target,
485
        ));
486

            
487
        // allowed: usable channel with correct relay id
488
        assert!(open_channel_is_allowed(
489
            &open_channel(FakeChannel {
490
                usable: true,
491
                ids: ids(None, ed(b"A")),
492
            }),
493
            &target,
494
        ));
495

            
496
        // not allowed: usable channel with incorrect relay id
497
        assert!(!open_channel_is_allowed(
498
            &open_channel(FakeChannel {
499
                usable: true,
500
                ids: ids(None, ed(b"B")),
501
            }),
502
            &target,
503
        ));
504

            
505
        // not allowed: usable channel with no relay ids
506
        assert!(!open_channel_is_allowed(
507
            &open_channel(FakeChannel {
508
                usable: true,
509
                ids: ids(None, None),
510
            }),
511
            &target,
512
        ));
513

            
514
        // allowed: usable channel with additional relay id
515
        assert!(open_channel_is_allowed(
516
            &open_channel(FakeChannel {
517
                usable: true,
518
                ids: ids(rsa(b"X"), ed(b"A")),
519
            }),
520
            &target,
521
        ));
522

            
523
        // not allowed: usable channel with missing ed relay id
524
        assert!(!open_channel_is_allowed(
525
            &open_channel(FakeChannel {
526
                usable: true,
527
                ids: ids(rsa(b"X"), None),
528
            }),
529
            &target,
530
        ));
531

            
532
        // target with no relay id
533
        let target = FakeBuildSpec::new(ids(None, None), vec![ADDR_A]);
534

            
535
        // not allowed: unusable channel
536
        assert!(!open_channel_is_allowed(
537
            &open_channel(FakeChannel {
538
                usable: false,
539
                ids: ids(None, None),
540
            }),
541
            &target,
542
        ));
543

            
544
        // allowed: usable channel with no relay ids
545
        assert!(open_channel_is_allowed(
546
            &open_channel(FakeChannel {
547
                usable: true,
548
                ids: ids(None, None),
549
            }),
550
            &target,
551
        ));
552

            
553
        // target with multiple relay ids
554
        let target = FakeBuildSpec::new(ids(rsa(b"X"), ed(b"A")), vec![ADDR_A]);
555

            
556
        // not allowed: unusable channel
557
        assert!(!open_channel_is_allowed(
558
            &open_channel(FakeChannel {
559
                usable: false,
560
                ids: ids(rsa(b"X"), ed(b"A")),
561
            }),
562
            &target,
563
        ));
564

            
565
        // allowed: usable channel with correct relay ids
566
        assert!(open_channel_is_allowed(
567
            &open_channel(FakeChannel {
568
                usable: true,
569
                ids: ids(rsa(b"X"), ed(b"A")),
570
            }),
571
            &target,
572
        ));
573

            
574
        // not allowed: usable channel with partial relay ids
575
        assert!(!open_channel_is_allowed(
576
            &open_channel(FakeChannel {
577
                usable: true,
578
                ids: ids(None, ed(b"A")),
579
            }),
580
            &target,
581
        ));
582
        assert!(!open_channel_is_allowed(
583
            &open_channel(FakeChannel {
584
                usable: true,
585
                ids: ids(rsa(b"X"), None),
586
            }),
587
            &target,
588
        ));
589

            
590
        // not allowed: usable channel with one incorrect relay id
591
        assert!(!open_channel_is_allowed(
592
            &open_channel(FakeChannel {
593
                usable: true,
594
                ids: ids(rsa(b"X"), ed(b"B")),
595
            }),
596
            &target,
597
        ));
598
        assert!(!open_channel_is_allowed(
599
            &open_channel(FakeChannel {
600
                usable: true,
601
                ids: ids(rsa(b"Y"), ed(b"A")),
602
            }),
603
            &target,
604
        ));
605
    }
606

            
607
    #[test]
608
    fn test_pending_channel_maybe_allowed() {
609
        // target with an ed relay id
610
        let target = FakeBuildSpec::new(ids(None, ed(b"A")), vec![ADDR_A]);
611

            
612
        // allowed: channel with same relay id
613
        assert!(pending_channel_maybe_allowed(
614
            &pending_channel(ids(None, ed(b"A"))),
615
            &target,
616
        ));
617

            
618
        // not allowed: channel with additional relay id
619
        assert!(!pending_channel_maybe_allowed(
620
            &pending_channel(ids(rsa(b"X"), ed(b"A"))),
621
            &target,
622
        ));
623

            
624
        // target with multiple relay ids
625
        let target = FakeBuildSpec::new(ids(rsa(b"X"), ed(b"A")), vec![ADDR_A]);
626

            
627
        // allowed: channel with same relay ids
628
        assert!(pending_channel_maybe_allowed(
629
            &pending_channel(ids(rsa(b"X"), ed(b"A"))),
630
            &target,
631
        ));
632

            
633
        // allowed: channel with fewer relay ids
634
        assert!(pending_channel_maybe_allowed(
635
            &pending_channel(ids(None, ed(b"A"))),
636
            &target,
637
        ));
638
        assert!(pending_channel_maybe_allowed(
639
            &pending_channel(ids(rsa(b"X"), None)),
640
            &target,
641
        ));
642

            
643
        // not allowed: channel with no relay ids
644
        assert!(!pending_channel_maybe_allowed(
645
            &pending_channel(ids(None, None)),
646
            &target,
647
        ));
648

            
649
        // target with no relay ids
650
        let target = FakeBuildSpec::new(ids(None, None), vec![ADDR_A]);
651

            
652
        // not allowed: channel with a relay id
653
        assert!(!pending_channel_maybe_allowed(
654
            &pending_channel(ids(None, ed(b"A"))),
655
            &target,
656
        ));
657

            
658
        // not allowed: channel with no relay ids
659
        assert!(!pending_channel_maybe_allowed(
660
            &pending_channel(ids(None, None)),
661
            &target,
662
        ));
663
    }
664
}