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 safelog::Redactable;
6
use std::{fmt, iter::FusedIterator, net::SocketAddr};
7
use tor_llcrypto::pk;
8

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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