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
32
pub(crate) fn pending_channel_maybe_allowed(
35
32
    chan: &PendingEntry,
36
32
    target: &(impl HasRelayIds + HasChanMethod),
37
32
) -> 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
32
    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
32
        .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
32
        .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
32
        .filter(|entry| entry.ids != EMPTY_IDS)
108
32
        .is_some()
109
32
}
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
168
pub(crate) fn choose_best_channel<'a, C: AbstractChannel>(
115
168
    channels: impl IntoIterator<Item = &'a ChannelState<C>>,
116
168
    target: &(impl HasRelayIds + HasChanMethod),
117
168
) -> Option<&'a ChannelState<C>> {
118
    use ChannelState::*;
119
    use std::cmp::Ordering;
120

            
121
168
    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
168
    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
168
}
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
    #![allow(clippy::string_slice)] // See arti#2571
244
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
245

            
246
    use super::*;
247

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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