1
//! Declare traits to be implemented by types that describe a place
2
//! that Tor can connect to, directly or indirectly.
3

            
4
use derive_deftly::derive_deftly_adhoc;
5
use itertools::Itertools;
6
use safelog::Redactable;
7
use std::{fmt, iter::FusedIterator, net::SocketAddr};
8
use tor_llcrypto::pk;
9

            
10
use crate::{ChannelMethod, RelayIdRef, RelayIdType, RelayIdTypeIter};
11

            
12
#[cfg(feature = "pt-client")]
13
use crate::PtTargetAddr;
14

            
15
/// Legacy implementation helper for HasRelayIds.
16
///
17
/// Previously, we assumed that everything had these two identity types, which
18
/// is not an assumption we want to keep making in the future.
19
pub trait HasRelayIdsLegacy {
20
    /// Return the ed25519 identity for this relay.
21
    fn ed_identity(&self) -> &pk::ed25519::Ed25519Identity;
22
    /// Return the RSA identity for this relay.
23
    fn rsa_identity(&self) -> &pk::rsa::RsaIdentity;
24
}
25

            
26
/// An object containing information about a relay's identity keys.
27
///
28
/// This trait has a fairly large number of methods, most of which you're not
29
/// actually expected to implement.  The only one that you need to provide is
30
/// [`identity`](HasRelayIds::identity).
31
pub trait HasRelayIds {
32
    /// Return the identity of this relay whose type is `key_type`, or None if
33
    /// the relay has no such identity.
34
    ///
35
    /// (Currently all relays have all recognized identity types, but we might
36
    /// implement or deprecate an identity type in the future.)
37
    fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>>;
38

            
39
    /// Return an iterator over all of the identities held by this object.
40
43309193
    fn identities(&self) -> RelayIdIter<'_, Self> {
41
43309193
        RelayIdIter {
42
43309193
            info: self,
43
43309193
            next_key: RelayIdType::all_types(),
44
43309193
        }
45
43309193
    }
46

            
47
    /// Return the ed25519 identity for this relay if it has one.
48
15271226
    fn ed_identity(&self) -> Option<&pk::ed25519::Ed25519Identity> {
49
15271226
        self.identity(RelayIdType::Ed25519)
50
15271226
            .map(RelayIdRef::unwrap_ed25519)
51
15271226
    }
52

            
53
    /// Return the RSA identity for this relay if it has one.
54
15510186
    fn rsa_identity(&self) -> Option<&pk::rsa::RsaIdentity> {
55
15510186
        self.identity(RelayIdType::Rsa).map(RelayIdRef::unwrap_rsa)
56
15510186
    }
57

            
58
    /// Check whether the provided Id is a known identity of this relay.
59
    ///
60
    /// Remember that a given set of identity keys may be incomplete: some
61
    /// objects that represent a relay have only a subset of the relay's
62
    /// identities. Therefore, a "true" answer means that the relay has this
63
    /// identity,  but a "false" answer could mean that the relay has a
64
    /// different identity of this type, or that it has _no_ known identity of
65
    /// this type.
66
1201668
    fn has_identity(&self, id: RelayIdRef<'_>) -> bool {
67
1227267
        self.identity(id.id_type()).map(|my_id| my_id == id) == Some(true)
68
1201668
    }
69

            
70
    /// Return true if this object has any known identity.
71
4
    fn has_any_identity(&self) -> bool {
72
8
        RelayIdType::all_types().any(|id_type| self.identity(id_type).is_some())
73
4
    }
74

            
75
    /// Return true if this object has exactly the same relay IDs as `other`.
76
    //
77
    // TODO: Once we make it so particular identity key types are optional, we
78
    // should add a note saying that this function is usually not what you want
79
    // for many cases, since you might want to know "could this be the same
80
    // relay" vs "is this definitely the same relay."
81
    //
82
    // NOTE: We don't make this an `Eq` method, since we want to make callers
83
    // choose carefully among this method, `has_all_relay_ids_from`, and any
84
    // similar methods we add in the future.
85
    #[allow(clippy::nonminimal_bool)] // rust-clippy/issues/12627
86
51649979
    fn same_relay_ids<T: HasRelayIds + ?Sized>(&self, other: &T) -> bool {
87
        // We use derive-deftly to iterate over the id types, rather than strum
88
        //
89
        // Empirically, with rustc 1.77.0-beta.5, this arranges that
90
        //     <tor_netdir::Relay as HasRelayIds>::same_relay_ids
91
        // compiles to the same asm (on amd64) as the open-coded inherent
92
        //     tor_netdir::Relay::has_same_relay_ids
93
        //
94
        // The problem with the strum approach seems to be that the compiler doesn't inline
95
        //     <RelayIdTypeIter as Iterator>::next
96
        // and unroll the loop.
97
        // Adding `#[inline]` and even `#[inline(always)]` to the strum output didn't help.
98
        //
99
        // When `next()` isn't inlined and the loop unrolled,
100
        // the compiler can't inline the matching on the id type,
101
        // and generate the obvious simple function.
102
        //
103
        // Empirically, the same results with non-inlined next() and non-unrolled loop,
104
        // were obtained with:
105
        //   - a simpler hand-coded Iterator struct
106
        //   - that hand-coded Iterator struct locally present in tor-netdir,
107
        //   - using `<[RelayIdType; ] as IntoIterator>`
108
        //
109
        // I experimented to see if this was a general problem with `strum`'s iterator.
110
        // In a smaller test program the compiler *does* unroll and inline.
111
        // I suspect that the compiler is having trouble with the complexities
112
        // of disentangling `HasLegacyRelayIds` and/or comparing `Option<RelayIdRef>`.
113
        //
114
        // TODO: do we want to replace RelayIdType::all_types with derive-deftly
115
        // in RelayIdIter, has_all_relay_ids_from, has_any_relay_id_from, etc.?
116
        // If so, search this crate for all_types.
117
51649979
        derive_deftly_adhoc! {
118
            RelayIdType:
119
            $(
120
1499525
                self.identity($vtype) == other.identity($vtype) &&
121
            )
122
1499507
                true
123
        }
124
51649979
    }
125

            
126
    /// Return true if this object has every relay ID that `other` does.
127
    ///
128
    /// (It still returns true if there are some IDs in this object that are not
129
    /// present in `other`.)
130
10559618
    fn has_all_relay_ids_from<T: HasRelayIds + ?Sized>(&self, other: &T) -> bool {
131
21344523
        RelayIdType::all_types().all(|key_type| {
132
21100682
            match (self.identity(key_type), other.identity(key_type)) {
133
                // If we both have the same key for this type, great.
134
21100522
                (Some(mine), Some(theirs)) if mine == theirs => true,
135
                // Uh oh. They do have a key for his type, but it's not ours.
136
18586
                (_, Some(_theirs)) => false,
137
                // If they don't care what we have for this type, great.
138
132
                (_, None) => true,
139
            }
140
21100682
        })
141
10559618
    }
142

            
143
    /// Return true if this object has any relay ID that `other` has.
144
    ///
145
    /// This is symmetrical:
146
    /// it returns true if the two objects have any overlap in their identities.
147
76102
    fn has_any_relay_id_from<T: HasRelayIds + ?Sized>(&self, other: &T) -> bool {
148
76102
        RelayIdType::all_types()
149
190235
            .filter_map(|key_type| Some((self.identity(key_type)?, other.identity(key_type)?)))
150
190193
            .any(|(self_id, other_id)| self_id == other_id)
151
76102
    }
152

            
153
    /// Compare this object to another HasRelayIds.
154
    ///
155
    /// Objects are sorted by Ed25519 identities, with ties decided by RSA
156
    /// identities. An absent identity of a given type is sorted before a
157
    /// present identity of that type.
158
    ///
159
    /// If additional identities are added in the future, they may taken into
160
    /// consideration before _or_ after the current identity types.
161
901176
    fn cmp_by_relay_ids<T: HasRelayIds + ?Sized>(&self, other: &T) -> std::cmp::Ordering {
162
919592
        for key_type in RelayIdType::all_types() {
163
919592
            let ordering = Ord::cmp(&self.identity(key_type), &other.identity(key_type));
164
919592
            if ordering.is_ne() {
165
882788
                return ordering;
166
36804
            }
167
        }
168
18388
        std::cmp::Ordering::Equal
169
901176
    }
170

            
171
    /// Return a reference to this object suitable for formatting its
172
    /// [`HasRelayIds`] members.
173
1877
    fn display_relay_ids(&self) -> DisplayRelayIds<'_, Self> {
174
1877
        DisplayRelayIds { inner: self }
175
1877
    }
176
}
177

            
178
impl<T: HasRelayIdsLegacy> HasRelayIds for T {
179
194907265
    fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
180
194907265
        match key_type {
181
44849763
            RelayIdType::Rsa => Some(self.rsa_identity().into()),
182
150057502
            RelayIdType::Ed25519 => Some(self.ed_identity().into()),
183
        }
184
194907265
    }
185
}
186

            
187
/// A helper type used to format the [`RelayId`](crate::RelayId)s in a
188
/// [`HasRelayIds`].
189
#[derive(Clone)]
190
pub struct DisplayRelayIds<'a, T: HasRelayIds + ?Sized> {
191
    /// The HasRelayIds that we're displaying.
192
    inner: &'a T,
193
}
194
// Redactable must implement Debug.
195
impl<'a, T: HasRelayIds + ?Sized> fmt::Debug for DisplayRelayIds<'a, T> {
196
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197
        f.debug_struct("DisplayRelayIds").finish_non_exhaustive()
198
    }
199
}
200

            
201
impl<'a, T: HasRelayIds + ?Sized> DisplayRelayIds<'a, T> {
202
    /// Helper: output `self` in a possibly redacted way.
203
1877
    fn fmt_impl(&self, f: &mut fmt::Formatter<'_>, redact: bool) -> fmt::Result {
204
1877
        let mut iter = self.inner.identities();
205
1877
        if let Some(ident) = iter.next() {
206
1818
            write!(f, "{}", ident.maybe_redacted(redact))?;
207
59
        }
208
1877
        if redact {
209
2
            return Ok(());
210
1875
        }
211
3689
        for ident in iter {
212
1814
            write!(f, " {}", ident.maybe_redacted(redact))?;
213
        }
214
1875
        Ok(())
215
1877
    }
216
}
217
impl<'a, T: HasRelayIds + ?Sized> fmt::Display for DisplayRelayIds<'a, T> {
218
1871
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219
1871
        self.fmt_impl(f, false)
220
1871
    }
221
}
222
impl<'a, T: HasRelayIds + ?Sized> Redactable for DisplayRelayIds<'a, T> {
223
2
    fn display_redacted(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224
2
        self.fmt_impl(f, true)
225
2
    }
226
}
227

            
228
/// An iterator over all of the relay identities held by a [`HasRelayIds`]
229
#[derive(Clone)]
230
pub struct RelayIdIter<'a, T: HasRelayIds + ?Sized> {
231
    /// The object holding the keys
232
    info: &'a T,
233
    /// The next key type to yield
234
    next_key: RelayIdTypeIter,
235
}
236

            
237
impl<'a, T: HasRelayIds + ?Sized> Iterator for RelayIdIter<'a, T> {
238
    type Item = RelayIdRef<'a>;
239

            
240
109468599
    fn next(&mut self) -> Option<Self::Item> {
241
109473091
        for key_type in &mut self.next_key {
242
76396360
            if let Some(key) = self.info.identity(key_type) {
243
76391868
                return Some(key);
244
4492
            }
245
        }
246
33076731
        None
247
109468599
    }
248
}
249
// RelayIdIter is fused since next_key is fused.
250
impl<'a, T: HasRelayIds + ?Sized> FusedIterator for RelayIdIter<'a, T> {}
251

            
252
/// An object that represents a host on the network which may have known IP addresses.
253
pub trait HasAddrs {
254
    /// Return the addresses listed for this server.
255
    ///
256
    /// NOTE that these addresses are not necessarily ones that we should
257
    /// connect to directly!  They can be useful for telling where a server is
258
    /// located, or whether it is "close" to another server, but without knowing
259
    /// the associated protocols you cannot use these to launch a connection.
260
    ///
261
    /// Also, for some servers, we may not actually have any relevant addresses;
262
    /// in that case, the returned slice is empty.
263
    ///
264
    /// To see how to _connect_ to a relay, use [`HasChanMethod::chan_method`]
265
    //
266
    // TODO: This is a questionable API. I'd rather return an iterator
267
    // of addresses or references to addresses, but both of those options
268
    // make defining the right associated types rather tricky.
269
    fn addrs(&self) -> impl Iterator<Item = SocketAddr>;
270
}
271

            
272
impl<T: HasAddrs> HasAddrs for &T {
273
    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
274
        // Be explicit about the type here so that we don't end up in an infinite loop by accident.
275
        <T as HasAddrs>::addrs(self)
276
    }
277
}
278

            
279
/// An object that can be connected to via [`ChannelMethod`]s.
280
pub trait HasChanMethod {
281
    /// Return the known ways to contact this
282
    // TODO: See notes on HasAddrs above.
283
    // TODO: I don't like having this return a new ChannelMethod, but I
284
    // don't see a great alternative. Let's revisit that.-nickm.
285
    fn chan_method(&self) -> ChannelMethod;
286
}
287

            
288
/// Implement `HasChanMethods` for an object with `HasAddr` whose addresses
289
/// _all_ represent a host we can connect to by a direct Tor connection at its
290
/// IP addresses.
291
pub trait DirectChanMethodsHelper: HasAddrs {}
292

            
293
impl<D: DirectChanMethodsHelper> HasChanMethod for D {
294
1765192
    fn chan_method(&self) -> ChannelMethod {
295
1765192
        ChannelMethod::Direct(self.addrs().collect_vec())
296
1765192
    }
297
}
298

            
299
/// Information about a Tor relay used to connect to it.
300
///
301
/// Anything that implements 'ChanTarget' can be used as the
302
/// identity of a relay for the purposes of launching a new
303
/// channel.
304
pub trait ChanTarget: HasRelayIds + HasAddrs + HasChanMethod {
305
    /// Return a reference to this object suitable for formatting its
306
    /// [`ChanTarget`]-specific members.
307
    ///
308
    /// The display format is not exhaustive, but tries to give enough
309
    /// information to identify which channel target we're talking about.
310
4
    fn display_chan_target(&self) -> DisplayChanTarget<'_, Self>
311
4
    where
312
4
        Self: Sized,
313
    {
314
4
        DisplayChanTarget { inner: self }
315
4
    }
316
}
317

            
318
/// Information about a Tor relay used to extend a circuit to it.
319
///
320
/// Anything that implements 'CircTarget' can be used as the
321
/// identity of a relay for the purposes of extending a circuit.
322
pub trait CircTarget: ChanTarget {
323
    /// Return a new vector of encoded link specifiers for this relay.
324
    ///
325
    /// Note that, outside of this method, nothing in Arti should be re-ordering
326
    /// the link specifiers returned by this method.  It is this method's
327
    /// responsibility to return them in the correct order.
328
    ///
329
    /// The default implementation for this method builds a list of link
330
    /// specifiers from this object's identities and IP addresses, and sorts
331
    /// them into the order specified in tor-spec to avoid implementation
332
    /// fingerprinting attacks.
333
    //
334
    // TODO: This is a questionable API. I'd rather return an iterator
335
    // of link specifiers, but that's not so easy to do, since it seems
336
    // doing so correctly would require default associated types.
337
76
    fn linkspecs(&self) -> tor_bytes::EncodeResult<Vec<crate::EncodedLinkSpec>> {
338
190
        let mut result: Vec<_> = self.identities().map(|id| id.to_owned().into()).collect();
339
        #[allow(irrefutable_let_patterns)]
340
76
        if let ChannelMethod::Direct(addrs) = self.chan_method() {
341
76
            result.extend(addrs.into_iter().map(crate::LinkSpec::from));
342
76
        }
343
76
        crate::LinkSpec::sort_by_type(&mut result[..]);
344
196
        result.into_iter().map(|ls| ls.encode()).collect()
345
76
    }
346
    /// Return the ntor onion key for this relay
347
    fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey;
348
    /// Return the subprotocols implemented by this relay.
349
    fn protovers(&self) -> &tor_protover::Protocols;
350
}
351

            
352
/// A reference to a ChanTarget that implements Display using a hopefully useful
353
/// format.
354
#[derive(Debug, Clone)]
355
pub struct DisplayChanTarget<'a, T> {
356
    /// The ChanTarget that we're formatting.
357
    inner: &'a T,
358
}
359

            
360
impl<'a, T: ChanTarget> DisplayChanTarget<'a, T> {
361
    /// helper: output `self` in a possibly redacted way.
362
4
    fn fmt_impl(&self, f: &mut fmt::Formatter<'_>, redact: bool) -> fmt::Result {
363
4
        write!(f, "[")?;
364
        // We look at the chan_method() (where we would connect to) rather than
365
        // the addrs() (where the relay is, nebulously, "located").  This lets us
366
        // give a less surprising description.
367
4
        match self.inner.chan_method() {
368
2
            ChannelMethod::Direct(v) if v.is_empty() => write!(f, "?")?,
369
2
            ChannelMethod::Direct(v) if v.len() == 1 => {
370
                write!(f, "{}", v[0].maybe_redacted(redact))?;
371
            }
372
2
            ChannelMethod::Direct(v) => write!(f, "{}+", v[0].maybe_redacted(redact))?,
373
            #[cfg(feature = "pt-client")]
374
2
            ChannelMethod::Pluggable(target) => {
375
2
                match target.addr() {
376
                    PtTargetAddr::None => {}
377
2
                    other => write!(f, "{} ", other.maybe_redacted(redact))?,
378
                }
379
2
                write!(f, "via {}", target.transport())?;
380
                // This deliberately doesn't include the PtTargetSettings, since
381
                // they can be large, and they're typically unnecessary.
382
            }
383
        }
384

            
385
4
        write!(f, " ")?;
386
4
        self.inner.display_relay_ids().fmt_impl(f, redact)?;
387

            
388
4
        write!(f, "]")
389
4
    }
390
}
391

            
392
impl<'a, T: ChanTarget> fmt::Display for DisplayChanTarget<'a, T> {
393
4
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394
4
        self.fmt_impl(f, false)
395
4
    }
396
}
397

            
398
impl<'a, T: ChanTarget + fmt::Debug> safelog::Redactable for DisplayChanTarget<'a, T> {
399
    fn display_redacted(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400
        self.fmt_impl(f, true)
401
    }
402
    fn debug_redacted(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403
        write!(f, "ChanTarget({:?})", self.redacted().to_string())
404
    }
405
}
406

            
407
#[cfg(test)]
408
mod test {
409
    // @@ begin test lint list maintained by maint/add_warning @@
410
    #![allow(clippy::bool_assert_comparison)]
411
    #![allow(clippy::clone_on_copy)]
412
    #![allow(clippy::dbg_macro)]
413
    #![allow(clippy::mixed_attributes_style)]
414
    #![allow(clippy::print_stderr)]
415
    #![allow(clippy::print_stdout)]
416
    #![allow(clippy::single_char_pattern)]
417
    #![allow(clippy::unwrap_used)]
418
    #![allow(clippy::unchecked_time_subtraction)]
419
    #![allow(clippy::useless_vec)]
420
    #![allow(clippy::needless_pass_by_value)]
421
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
422
    use super::*;
423
    use crate::RelayIds;
424
    use hex_literal::hex;
425
    use std::net::IpAddr;
426
    use tor_llcrypto::pk::{self, ed25519::Ed25519Identity, rsa::RsaIdentity};
427

            
428
    struct Example {
429
        addrs: Vec<SocketAddr>,
430
        ed_id: pk::ed25519::Ed25519Identity,
431
        rsa_id: pk::rsa::RsaIdentity,
432
        ntor: pk::curve25519::PublicKey,
433
        pv: tor_protover::Protocols,
434
    }
435
    impl HasAddrs for Example {
436
        fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
437
            self.addrs.iter().copied()
438
        }
439
    }
440
    impl DirectChanMethodsHelper for Example {}
441
    impl HasRelayIdsLegacy for Example {
442
        fn ed_identity(&self) -> &pk::ed25519::Ed25519Identity {
443
            &self.ed_id
444
        }
445
        fn rsa_identity(&self) -> &pk::rsa::RsaIdentity {
446
            &self.rsa_id
447
        }
448
    }
449
    impl ChanTarget for Example {}
450
    impl CircTarget for Example {
451
        fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey {
452
            &self.ntor
453
        }
454
        fn protovers(&self) -> &tor_protover::Protocols {
455
            &self.pv
456
        }
457
    }
458

            
459
    /// Return an `Example` object, for use in tests below.
460
    fn example() -> Example {
461
        Example {
462
            addrs: vec![
463
                "127.0.0.1:99".parse::<SocketAddr>().unwrap(),
464
                "[::1]:909".parse::<SocketAddr>().unwrap(),
465
            ],
466
            ed_id: pk::ed25519::PublicKey::from_bytes(&hex!(
467
                "fc51cd8e6218a1a38da47ed00230f058
468
                 0816ed13ba3303ac5deb911548908025"
469
            ))
470
            .unwrap()
471
            .into(),
472
            rsa_id: pk::rsa::RsaIdentity::from_bytes(&hex!(
473
                "1234567890abcdef12341234567890abcdef1234"
474
            ))
475
            .unwrap(),
476
            ntor: pk::curve25519::PublicKey::from(hex!(
477
                "e6db6867583030db3594c1a424b15f7c
478
                 726624ec26b3353b10a903a6d0ab1c4c"
479
            )),
480
            pv: tor_protover::Protocols::default(),
481
        }
482
    }
483

            
484
    #[test]
485
    fn test_linkspecs() {
486
        let ex = example();
487
        let specs = ex
488
            .linkspecs()
489
            .unwrap()
490
            .into_iter()
491
            .map(|ls| ls.parse())
492
            .collect::<Result<Vec<_>, _>>()
493
            .unwrap();
494
        assert_eq!(4, specs.len());
495

            
496
        use crate::ls::LinkSpec;
497
        assert_eq!(
498
            specs[0],
499
            LinkSpec::OrPort("127.0.0.1".parse::<IpAddr>().unwrap(), 99)
500
        );
501
        assert_eq!(
502
            specs[1],
503
            LinkSpec::RsaId(
504
                pk::rsa::RsaIdentity::from_bytes(&hex!("1234567890abcdef12341234567890abcdef1234"))
505
                    .unwrap()
506
            )
507
        );
508
        assert_eq!(
509
            specs[2],
510
            LinkSpec::Ed25519Id(
511
                pk::ed25519::PublicKey::from_bytes(&hex!(
512
                    "fc51cd8e6218a1a38da47ed00230f058
513
                     0816ed13ba3303ac5deb911548908025"
514
                ))
515
                .unwrap()
516
                .into()
517
            )
518
        );
519
        assert_eq!(
520
            specs[3],
521
            LinkSpec::OrPort("::1".parse::<IpAddr>().unwrap(), 909)
522
        );
523
    }
524

            
525
    #[test]
526
    fn cmp_by_ids() {
527
        use crate::RelayIds;
528
        use std::cmp::Ordering;
529
        fn b(ed: Option<Ed25519Identity>, rsa: Option<RsaIdentity>) -> RelayIds {
530
            let mut b = RelayIds::builder();
531
            if let Some(ed) = ed {
532
                b.ed_identity(ed);
533
            }
534
            if let Some(rsa) = rsa {
535
                b.rsa_identity(rsa);
536
            }
537
            b.build().unwrap()
538
        }
539
        // Assert that v is strictly ascending.
540
        fn assert_sorted(v: &[RelayIds]) {
541
            for slice in v.windows(2) {
542
                assert_eq!(slice[0].cmp_by_relay_ids(&slice[1]), Ordering::Less);
543
                assert_eq!(slice[1].cmp_by_relay_ids(&slice[0]), Ordering::Greater);
544
                assert_eq!(slice[0].cmp_by_relay_ids(&slice[0]), Ordering::Equal);
545
            }
546
        }
547

            
548
        let ed1 = hex!("0a54686973206973207468652043656e7472616c205363727574696e697a6572").into();
549
        let ed2 = hex!("6962696c69747920746f20656e666f72636520616c6c20746865206c6177730a").into();
550
        let ed3 = hex!("73736564207965740a497420697320616c736f206d7920726573706f6e736962").into();
551
        let rsa1 = hex!("2e2e2e0a4974206973206d7920726573706f6e73").into();
552
        let rsa2 = hex!("5468617420686176656e2774206265656e207061").into();
553
        let rsa3 = hex!("696c69747920746f20616c65727420656163680a").into();
554

            
555
        assert_sorted(&[
556
            b(Some(ed1), None),
557
            b(Some(ed2), None),
558
            b(Some(ed3), None),
559
            b(Some(ed3), Some(rsa1)),
560
        ]);
561
        assert_sorted(&[
562
            b(Some(ed1), Some(rsa3)),
563
            b(Some(ed2), Some(rsa2)),
564
            b(Some(ed3), Some(rsa1)),
565
            b(Some(ed3), Some(rsa2)),
566
        ]);
567
        assert_sorted(&[
568
            b(Some(ed1), Some(rsa1)),
569
            b(Some(ed1), Some(rsa2)),
570
            b(Some(ed1), Some(rsa3)),
571
        ]);
572
        assert_sorted(&[
573
            b(None, Some(rsa1)),
574
            b(None, Some(rsa2)),
575
            b(None, Some(rsa3)),
576
        ]);
577
        assert_sorted(&[
578
            b(None, Some(rsa1)),
579
            b(Some(ed1), None),
580
            b(Some(ed1), Some(rsa1)),
581
        ]);
582
    }
583

            
584
    #[test]
585
    fn compare_id_sets() {
586
        // TODO somehow nicely unify these repeated predefined examples
587
        let ed1 = hex!("0a54686973206973207468652043656e7472616c205363727574696e697a6572").into();
588
        let rsa1 = hex!("2e2e2e0a4974206973206d7920726573706f6e73").into();
589
        let rsa2 = RsaIdentity::from(hex!("5468617420686176656e2774206265656e207061"));
590

            
591
        let both1 = RelayIds::builder()
592
            .ed_identity(ed1)
593
            .rsa_identity(rsa1)
594
            .build()
595
            .unwrap();
596
        let mixed = RelayIds::builder()
597
            .ed_identity(ed1)
598
            .rsa_identity(rsa2)
599
            .build()
600
            .unwrap();
601
        let ed1 = RelayIds::builder().ed_identity(ed1).build().unwrap();
602
        let rsa1 = RelayIds::builder().rsa_identity(rsa1).build().unwrap();
603
        let rsa2 = RelayIds::builder().rsa_identity(rsa2).build().unwrap();
604

            
605
        fn chk_equal(v: &impl HasRelayIds) {
606
            assert!(v.same_relay_ids(v));
607
            assert!(v.has_all_relay_ids_from(v));
608
            assert!(v.has_any_relay_id_from(v));
609
        }
610
        fn chk_strict_subset(bigger: &impl HasRelayIds, smaller: &impl HasRelayIds) {
611
            assert!(!bigger.same_relay_ids(smaller));
612
            assert!(bigger.has_all_relay_ids_from(smaller));
613
            assert!(bigger.has_any_relay_id_from(smaller));
614
            assert!(!smaller.same_relay_ids(bigger));
615
            assert!(!smaller.has_all_relay_ids_from(bigger));
616
            assert!(smaller.has_any_relay_id_from(bigger));
617
        }
618
        fn chk_nontrivially_overlapping_one_way(a: &impl HasRelayIds, b: &impl HasRelayIds) {
619
            assert!(!a.same_relay_ids(b));
620
            assert!(!a.has_all_relay_ids_from(b));
621
            assert!(a.has_any_relay_id_from(b));
622
        }
623
        fn chk_nontrivially_overlapping(a: &impl HasRelayIds, b: &impl HasRelayIds) {
624
            chk_nontrivially_overlapping_one_way(a, b);
625
            chk_nontrivially_overlapping_one_way(b, a);
626
        }
627

            
628
        chk_equal(&ed1);
629
        chk_equal(&rsa1);
630
        chk_equal(&both1);
631

            
632
        chk_strict_subset(&both1, &ed1);
633
        chk_strict_subset(&both1, &rsa1);
634
        chk_strict_subset(&mixed, &ed1);
635
        chk_strict_subset(&mixed, &rsa2);
636

            
637
        chk_nontrivially_overlapping(&both1, &mixed);
638
    }
639

            
640
    #[test]
641
    fn display() {
642
        let e1 = example();
643
        assert_eq!(
644
            e1.display_chan_target().to_string(),
645
            "[127.0.0.1:99+ ed25519:/FHNjmIYoaONpH7QAjDwWAgW7RO6MwOsXeuRFUiQgCU \
646
              $1234567890abcdef12341234567890abcdef1234]"
647
        );
648

            
649
        #[cfg(feature = "pt-client")]
650
        {
651
            use crate::PtTarget;
652

            
653
            let rsa = hex!("234461644a6f6b6523436f726e794f6e4d61696e").into();
654
            let mut b = crate::OwnedChanTarget::builder();
655
            b.ids().rsa_identity(rsa);
656
            let e2 = b
657
                .method(ChannelMethod::Pluggable(PtTarget::new(
658
                    "obfs4".parse().unwrap(),
659
                    "127.0.0.1:99".parse().unwrap(),
660
                )))
661
                .build()
662
                .unwrap();
663
            assert_eq!(
664
                e2.to_string(),
665
                "[127.0.0.1:99 via obfs4 $234461644a6f6b6523436f726e794f6e4d61696e]"
666
            );
667
        }
668
    }
669

            
670
    #[test]
671
    fn has_id() {
672
        use crate::RelayIds;
673
        assert!(example().has_any_identity());
674
        assert!(!RelayIds::empty().has_any_identity());
675
    }
676
}