1
//! Define a type for a set of HasRelayIds objects that can be looked up by any
2
//! of their keys.
3

            
4
use tor_basic_utils::{n_key_list, n_key_set};
5
use tor_llcrypto::pk::ed25519::Ed25519Identity;
6
use tor_llcrypto::pk::rsa::RsaIdentity;
7

            
8
use crate::{HasRelayIds, RelayIdRef};
9

            
10
n_key_list! {
11
    /// A list of objects that can be accessed by relay identity.
12
    ///
13
    /// Multiple objects in the list can have a given relay identity.
14
    ///
15
    /// # Invariants
16
    ///
17
    /// Every object in the list **must** have at least one recognized relay identity; if it does
18
    /// not, it cannot be inserted.
19
    ///
20
    /// This list may panic or give incorrect results if the values can change their keys through
21
    /// interior mutability.
22
    #[derive(Clone, Debug)]
23
    pub struct[H:HasRelayIds] ListByRelayIds[H] for H
24
    {
25
        (Option) rsa: RsaIdentity { rsa_identity() },
26
        (Option) ed25519: Ed25519Identity { ed_identity() },
27
    }
28
}
29

            
30
n_key_set! {
31
    /// A set of objects that can be accessed by relay identity.
32
    ///
33
    /// No more than one object in the set can have any given relay identity.
34
    ///
35
    /// # Invariants
36
    ///
37
    /// Every object in the set MUST have at least one recognized relay
38
    /// identity; if it does not, it cannot be inserted.
39
    ///
40
    /// This set may panic or give incorrect results if the values can change their
41
    /// keys through interior mutability.
42
    ///
43
    #[derive(Clone, Debug)]
44
    pub struct[H:HasRelayIds] ByRelayIds[H] for H
45
    {
46
        (Option) rsa: RsaIdentity { rsa_identity() },
47
        (Option) ed25519: Ed25519Identity { ed_identity() },
48
    }
49
}
50

            
51
impl<H: HasRelayIds> ByRelayIds<H> {
52
    /// Return the value in this set (if any) that has the key `key`.
53
8138804
    pub fn by_id<'a, T>(&self, key: T) -> Option<&H>
54
8138804
    where
55
8138804
        T: Into<RelayIdRef<'a>>,
56
    {
57
8138804
        match key.into() {
58
8138714
            RelayIdRef::Ed25519(ed) => self.by_ed25519(ed),
59
90
            RelayIdRef::Rsa(rsa) => self.by_rsa(rsa),
60
        }
61
8138804
    }
62

            
63
    /// Return the value in this set (if any) that has the key `key`.
64
22
    pub fn remove_by_id<'a, T>(&mut self, key: T) -> Option<H>
65
22
    where
66
22
        T: Into<RelayIdRef<'a>>,
67
    {
68
22
        match key.into() {
69
22
            RelayIdRef::Ed25519(ed) => self.remove_by_ed25519(ed),
70
            RelayIdRef::Rsa(rsa) => self.remove_by_rsa(rsa),
71
        }
72
22
    }
73

            
74
    /// Modify the value in this set (if any) that has the key `key`.
75
    ///
76
    /// Return values are as for [`modify_by_ed25519`](Self::modify_by_ed25519)
77
2088012
    pub fn modify_by_id<'a, T, F>(&mut self, key: T, func: F) -> Vec<H>
78
2088012
    where
79
2088012
        T: Into<RelayIdRef<'a>>,
80
2088012
        F: FnOnce(&mut H),
81
    {
82
2088012
        match key.into() {
83
2088012
            RelayIdRef::Ed25519(ed) => self.modify_by_ed25519(ed, func),
84
            RelayIdRef::Rsa(rsa) => self.modify_by_rsa(rsa, func),
85
        }
86
2088012
    }
87

            
88
    /// Return the value in this set (if any) that has _all_ the relay IDs
89
    /// that `key` does.
90
    ///
91
    /// Return `None` if `key` has no relay IDs.
92
8138606
    pub fn by_all_ids<T>(&self, key: &T) -> Option<&H>
93
8138606
    where
94
8138606
        T: HasRelayIds,
95
    {
96
8138606
        let any_id = key.identities().next()?;
97
8138604
        self.by_id(any_id)
98
8138604
            .filter(|val| val.has_all_relay_ids_from(key))
99
8138606
    }
100

            
101
    /// Modify the value in this set (if any) that has _all_ the relay IDs
102
    /// that `key` does.
103
    ///
104
    /// Return values are as for [`modify_by_ed25519`](Self::modify_by_ed25519)
105
2088012
    pub fn modify_by_all_ids<T, F>(&mut self, key: &T, func: F) -> Vec<H>
106
2088012
    where
107
2088012
        T: HasRelayIds,
108
2088012
        F: FnOnce(&mut H),
109
    {
110
2088012
        let any_id = match key.identities().next() {
111
2088012
            Some(id) => id,
112
            None => return Vec::new(),
113
        };
114
2088012
        self.modify_by_id(any_id, |val| {
115
2088012
            if val.has_all_relay_ids_from(key) {
116
2088012
                func(val);
117
2088012
            }
118
2088012
        })
119
2088012
    }
120

            
121
    /// Remove the single value in this set (if any) that has _exactly the same_
122
    /// relay IDs that `key` does
123
24
    pub fn remove_exact<T>(&mut self, key: &T) -> Option<H>
124
24
    where
125
24
        T: HasRelayIds,
126
    {
127
24
        let any_id = key.identities().next()?;
128
24
        if self
129
24
            .by_id(any_id)
130
24
            .filter(|ent| ent.same_relay_ids(key))
131
24
            .is_some()
132
        {
133
20
            self.remove_by_id(any_id)
134
        } else {
135
4
            None
136
        }
137
24
    }
138

            
139
    /// Remove the single value in this set (if any) that has all the same
140
    /// relay IDs that `key` does. If `key` does not have any relay IDs, no
141
    /// value is returned.
142
4
    pub fn remove_by_all_ids<T>(&mut self, key: &T) -> Option<H>
143
4
    where
144
4
        T: HasRelayIds,
145
    {
146
4
        let any_id = key.identities().next()?;
147
4
        if self
148
4
            .by_id(any_id)
149
4
            .filter(|ent| ent.has_all_relay_ids_from(key))
150
4
            .is_some()
151
        {
152
2
            self.remove_by_id(any_id)
153
        } else {
154
2
            None
155
        }
156
4
    }
157

            
158
    /// Return a reference to every element in this set that shares _any_ ID
159
    /// with `key`.
160
    ///
161
    /// No element is returned more than once.
162
82
    pub fn all_overlapping<T>(&self, key: &T) -> Vec<&H>
163
82
    where
164
82
        T: HasRelayIds,
165
    {
166
        use by_address::ByAddress;
167
        use std::collections::HashSet;
168

            
169
82
        let mut items: HashSet<ByAddress<&H>> = HashSet::new();
170

            
171
160
        for ident in key.identities() {
172
160
            if let Some(found) = self.by_id(ident) {
173
62
                items.insert(ByAddress(found));
174
110
            }
175
        }
176

            
177
82
        items.into_iter().map(|by_addr| by_addr.0).collect()
178
82
    }
179
}
180

            
181
impl<H: HasRelayIds> ListByRelayIds<H> {
182
    /// Return an iterator of the values in this list that have the key `key`.
183
410
    pub fn by_id<'a, T>(&self, key: T) -> ListByRelayIdsIter<H>
184
410
    where
185
410
        T: Into<RelayIdRef<'a>>,
186
    {
187
410
        match key.into() {
188
364
            RelayIdRef::Ed25519(ed) => self.by_ed25519(ed),
189
46
            RelayIdRef::Rsa(rsa) => self.by_rsa(rsa),
190
        }
191
410
    }
192

            
193
    /// Return the values in this list that have *all* the relay IDs that `key` does.
194
    ///
195
    /// Returns an empty iterator if `key` has no relay IDs.
196
130
    pub fn by_all_ids<'a>(&'a self, key: &'a impl HasRelayIds) -> impl Iterator<Item = &'a H> + 'a {
197
130
        key.identities()
198
130
            .next()
199
130
            .map_or_else(Default::default, |id| self.by_id(id))
200
130
            .filter(|val| val.has_all_relay_ids_from(key))
201
130
    }
202

            
203
    /// Return a reference to every element in this set that shares *any* ID with `key`.
204
    ///
205
    /// No element is returned more than once. Equality is compared using
206
    /// [`ByAddress`](by_address::ByAddress).
207
100
    pub fn all_overlapping<T>(&self, key: &T) -> Vec<&H>
208
100
    where
209
100
        T: HasRelayIds,
210
    {
211
        use by_address::ByAddress;
212
        use std::collections::HashSet;
213

            
214
100
        let mut items: HashSet<ByAddress<&H>> = HashSet::new();
215

            
216
106
        for ident in key.identities() {
217
120
            for found in self.by_id(ident) {
218
40
                items.insert(ByAddress(found));
219
40
            }
220
        }
221

            
222
100
        items.into_iter().map(|by_addr| by_addr.0).collect()
223
100
    }
224

            
225
    /// Return a reference to every element in this list whose relay IDs are a subset of the relay
226
    /// IDs that `key` has.
227
    ///
228
    /// No element is returned more than once. Equality is compared using
229
    /// [`ByAddress`](by_address::ByAddress).
230
124
    pub fn all_subset<T>(&self, key: &T) -> Vec<&H>
231
124
    where
232
124
        T: HasRelayIds,
233
    {
234
        use by_address::ByAddress;
235
        use std::collections::HashSet;
236

            
237
124
        let mut items: HashSet<ByAddress<&H>> = HashSet::new();
238

            
239
128
        for ident in key.identities() {
240
136
            for found in self.by_id(ident) {
241
                // if 'key's relay ids are a superset of 'found's relay ids
242
56
                if key.has_all_relay_ids_from(found) {
243
48
                    items.insert(ByAddress(found));
244
48
                }
245
            }
246
        }
247

            
248
124
        items.into_iter().map(|by_addr| by_addr.0).collect()
249
124
    }
250

            
251
    /// Return the values in this list that have the key `key` and where `filter` returns `true`.
252
96
    pub fn remove_by_id<'a, T>(&mut self, key: T, filter: impl FnMut(&H) -> bool) -> Vec<H>
253
96
    where
254
96
        T: Into<RelayIdRef<'a>>,
255
    {
256
96
        match key.into() {
257
92
            RelayIdRef::Ed25519(ed) => self.remove_by_ed25519(ed, filter),
258
4
            RelayIdRef::Rsa(rsa) => self.remove_by_rsa(rsa, filter),
259
        }
260
96
    }
261

            
262
    /// Remove and return the values in this list that have *exactly the same* relay IDs that `key`
263
    /// does.
264
8
    pub fn remove_exact<T>(&mut self, key: &T) -> Vec<H>
265
8
    where
266
8
        T: HasRelayIds,
267
    {
268
8
        let Some(id) = key.identities().next() else {
269
            return Vec::new();
270
        };
271

            
272
16
        self.remove_by_id(id, |val| val.same_relay_ids(key))
273
8
    }
274

            
275
    /// Remove and return the values in this list that have all the same relay IDs that `key` does.
276
    ///
277
    /// If `key` has no relay IDs, then no values are removed.
278
6
    pub fn remove_by_all_ids<T>(&mut self, key: &T) -> Vec<H>
279
6
    where
280
6
        T: HasRelayIds,
281
    {
282
6
        let Some(id) = key.identities().next() else {
283
            return Vec::new();
284
        };
285

            
286
12
        self.remove_by_id(id, |val| val.has_all_relay_ids_from(key))
287
6
    }
288
}
289

            
290
pub use tor_basic_utils::n_key_list::Error as ListByRelayIdsError;
291
pub use tor_basic_utils::n_key_set::Error as ByRelayIdsError;
292

            
293
#[cfg(test)]
294
mod test {
295
    // @@ begin test lint list maintained by maint/add_warning @@
296
    #![allow(clippy::bool_assert_comparison)]
297
    #![allow(clippy::clone_on_copy)]
298
    #![allow(clippy::dbg_macro)]
299
    #![allow(clippy::mixed_attributes_style)]
300
    #![allow(clippy::print_stderr)]
301
    #![allow(clippy::print_stdout)]
302
    #![allow(clippy::single_char_pattern)]
303
    #![allow(clippy::unwrap_used)]
304
    #![allow(clippy::unchecked_time_subtraction)]
305
    #![allow(clippy::useless_vec)]
306
    #![allow(clippy::needless_pass_by_value)]
307
    #![allow(clippy::string_slice)] // See arti#2571
308
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
309

            
310
    use super::*;
311
    use crate::{RelayIds, RelayIdsBuilder};
312

            
313
    fn sort<T: std::cmp::Ord>(i: impl Iterator<Item = T>) -> Vec<T> {
314
        let mut v: Vec<_> = i.collect();
315
        v.sort();
316
        v
317
    }
318

            
319
    #[test]
320
    fn lookup() {
321
        let rsa1: RsaIdentity = (*b"12345678901234567890").into();
322
        let rsa2: RsaIdentity = (*b"abcefghijklmnopqrstu").into();
323
        let rsa3: RsaIdentity = (*b"abcefghijklmnopQRSTU").into();
324
        let ed1: Ed25519Identity = (*b"12345678901234567890123456789012").into();
325
        let ed2: Ed25519Identity = (*b"abcefghijklmnopqrstuvwxyzABCDEFG").into();
326
        let ed3: Ed25519Identity = (*b"abcefghijklmnopqrstuvwxyz1234567").into();
327

            
328
        let keys1 = RelayIdsBuilder::default()
329
            .rsa_identity(rsa1)
330
            .ed_identity(ed1)
331
            .build()
332
            .unwrap();
333

            
334
        let keys2 = RelayIdsBuilder::default()
335
            .rsa_identity(rsa2)
336
            .ed_identity(ed2)
337
            .build()
338
            .unwrap();
339

            
340
        // `ByRelayIds` and `ListByRelayIds` work similarly in the case where we only add a single
341
        // value per key, so we can test them both here with the same test cases.
342

            
343
        let mut set = ByRelayIds::new();
344
        set.insert(keys1.clone());
345
        set.insert(keys2.clone());
346

            
347
        let mut list = ListByRelayIds::new();
348
        list.insert(keys1.clone());
349
        list.insert(keys2.clone());
350

            
351
        // Try by_id
352
        assert_eq!(set.by_id(&rsa1), Some(&keys1));
353
        assert_eq!(set.by_id(&ed1), Some(&keys1));
354
        assert_eq!(set.by_id(&rsa2), Some(&keys2));
355
        assert_eq!(set.by_id(&ed2), Some(&keys2));
356
        assert_eq!(set.by_id(&rsa3), None);
357
        assert_eq!(set.by_id(&ed3), None);
358
        assert_eq!(sort(list.by_id(&rsa1)), [&keys1]);
359
        assert_eq!(sort(list.by_id(&ed1)), [&keys1]);
360
        assert_eq!(sort(list.by_id(&rsa2)), [&keys2]);
361
        assert_eq!(sort(list.by_id(&ed2)), [&keys2]);
362
        assert_eq!(list.by_id(&rsa3).len(), 0);
363
        assert_eq!(list.by_id(&ed3).len(), 0);
364

            
365
        // Try exact lookup
366
        assert_eq!(set.by_all_ids(&keys1), Some(&keys1));
367
        assert_eq!(set.by_all_ids(&keys2), Some(&keys2));
368
        assert_eq!(set.by_all_ids(&RelayIds::empty()), None);
369
        assert_eq!(sort(list.by_all_ids(&keys1)), [&keys1]);
370
        assert_eq!(sort(list.by_all_ids(&keys2)), [&keys2]);
371
        assert!(sort(list.by_all_ids(&RelayIds::empty())).is_empty());
372
        {
373
            let search = RelayIdsBuilder::default()
374
                .rsa_identity(rsa1)
375
                .build()
376
                .unwrap();
377
            assert_eq!(set.by_all_ids(&search), Some(&keys1));
378
            assert_eq!(sort(list.by_all_ids(&search)), [&keys1]);
379
        }
380
        {
381
            let search = RelayIdsBuilder::default()
382
                .rsa_identity(rsa1)
383
                .ed_identity(ed2)
384
                .build()
385
                .unwrap();
386
            assert_eq!(set.by_all_ids(&search), None);
387
            assert!(sort(list.by_all_ids(&search)).is_empty());
388
        }
389

            
390
        // Try looking for overlap
391
        assert_eq!(set.all_overlapping(&keys1), vec![&keys1]);
392
        assert_eq!(set.all_overlapping(&keys2), vec![&keys2]);
393
        assert_eq!(list.all_overlapping(&keys1), vec![&keys1]);
394
        assert_eq!(list.all_overlapping(&keys2), vec![&keys2]);
395
        {
396
            let search = RelayIdsBuilder::default()
397
                .rsa_identity(rsa1)
398
                .ed_identity(ed2)
399
                .build()
400
                .unwrap();
401
            let answer = set.all_overlapping(&search);
402
            assert_eq!(answer.len(), 2);
403
            assert!(answer.contains(&&keys1));
404
            assert!(answer.contains(&&keys2));
405
            let answer = list.all_overlapping(&search);
406
            assert_eq!(answer.len(), 2);
407
            assert!(answer.contains(&&keys1));
408
            assert!(answer.contains(&&keys2));
409
        }
410
        {
411
            let search = RelayIdsBuilder::default()
412
                .rsa_identity(rsa2)
413
                .build()
414
                .unwrap();
415
            assert_eq!(set.all_overlapping(&search), vec![&keys2]);
416
            assert_eq!(list.all_overlapping(&search), vec![&keys2]);
417
        }
418
        {
419
            let search = RelayIdsBuilder::default()
420
                .rsa_identity(rsa3)
421
                .build()
422
                .unwrap();
423
            assert!(set.all_overlapping(&search).is_empty());
424
            assert!(list.all_overlapping(&search).is_empty());
425
        }
426
    }
427

            
428
    #[test]
429
    fn remove_exact() {
430
        let rsa1: RsaIdentity = (*b"12345678901234567890").into();
431
        let rsa2: RsaIdentity = (*b"abcefghijklmnopqrstu").into();
432
        let ed1: Ed25519Identity = (*b"12345678901234567890123456789012").into();
433
        let ed2: Ed25519Identity = (*b"abcefghijklmnopqrstuvwxyzABCDEFG").into();
434

            
435
        let keys1 = RelayIdsBuilder::default()
436
            .rsa_identity(rsa1)
437
            .ed_identity(ed1)
438
            .build()
439
            .unwrap();
440

            
441
        let keys2 = RelayIdsBuilder::default()
442
            .rsa_identity(rsa2)
443
            .ed_identity(ed2)
444
            .build()
445
            .unwrap();
446

            
447
        // `ByRelayIds` and `ListByRelayIds` work similarly in the case where we only add a single
448
        // value per key, so we can test them both here with the same test cases.
449

            
450
        let mut set = ByRelayIds::new();
451
        set.insert(keys1.clone());
452
        set.insert(keys2.clone());
453
        assert_eq!(set.len(), 2);
454

            
455
        let mut list = ListByRelayIds::new();
456
        list.insert(keys1.clone());
457
        list.insert(keys2.clone());
458
        assert_eq!(list.len(), 2);
459

            
460
        assert_eq!(set.remove_exact(&keys1), Some(keys1.clone()));
461
        assert_eq!(set.len(), 1);
462
        assert_eq!(list.remove_exact(&keys1), vec![keys1.clone()]);
463
        assert_eq!(list.len(), 1);
464

            
465
        {
466
            let search = RelayIdsBuilder::default().ed_identity(ed2).build().unwrap();
467

            
468
            // We're calling remove_exact, but we did not list _all_ the keys in keys2.
469
            assert_eq!(set.remove_exact(&search), None);
470
            assert_eq!(set.len(), 1);
471
            assert_eq!(list.remove_exact(&search), vec![]);
472
            assert_eq!(list.len(), 1);
473

            
474
            // If we were to use `remove_by_all_ids` with a search that didn't
475
            // match, it wouldn't work.
476
            let no_match = RelayIdsBuilder::default()
477
                .ed_identity(ed2)
478
                .rsa_identity(rsa1)
479
                .build()
480
                .unwrap();
481
            assert_eq!(set.remove_by_all_ids(&no_match), None);
482
            assert_eq!(set.len(), 1);
483
            assert_eq!(list.remove_by_all_ids(&no_match), vec![]);
484
            assert_eq!(list.len(), 1);
485

            
486
            // If we use `remove_by_all_ids` with the original search, though,
487
            // it will remove the element.
488
            assert_eq!(set.remove_by_all_ids(&search), Some(keys2.clone()));
489
            assert!(set.is_empty());
490
            assert_eq!(list.remove_by_all_ids(&search), vec![keys2.clone()]);
491
            assert!(list.is_empty());
492
        }
493
    }
494

            
495
    #[test]
496
    fn all_subset() {
497
        let rsa1: RsaIdentity = (*b"12345678901234567890").into();
498
        let rsa2: RsaIdentity = (*b"abcefghijklmnopqrstu").into();
499
        let ed1: Ed25519Identity = (*b"12345678901234567890123456789012").into();
500

            
501
        // one rsa id and one ed id
502
        let keys1 = RelayIdsBuilder::default()
503
            .rsa_identity(rsa1)
504
            .ed_identity(ed1)
505
            .build()
506
            .unwrap();
507

            
508
        // one rsa id
509
        let keys2 = RelayIdsBuilder::default()
510
            .rsa_identity(rsa2)
511
            .build()
512
            .unwrap();
513

            
514
        let mut list = ListByRelayIds::new();
515
        list.insert(keys1.clone());
516
        list.insert(keys2.clone());
517

            
518
        assert_eq!(list.all_subset(&keys1), vec![&keys1]);
519
        assert_eq!(list.all_subset(&keys2), vec![&keys2]);
520

            
521
        {
522
            let search = RelayIdsBuilder::default()
523
                .rsa_identity(rsa1)
524
                .build()
525
                .unwrap();
526
            assert!(list.all_subset(&search).is_empty());
527
        }
528

            
529
        {
530
            let search = RelayIdsBuilder::default().ed_identity(ed1).build().unwrap();
531
            assert!(list.all_subset(&search).is_empty());
532
        }
533

            
534
        {
535
            let search = RelayIdsBuilder::default()
536
                .rsa_identity(rsa2)
537
                .build()
538
                .unwrap();
539
            assert_eq!(list.all_subset(&search), vec![&keys2]);
540
        }
541

            
542
        {
543
            let search = RelayIdsBuilder::default()
544
                .ed_identity(ed1)
545
                .rsa_identity(rsa2)
546
                .build()
547
                .unwrap();
548
            assert_eq!(list.all_subset(&search), vec![&keys2]);
549
        }
550
    }
551

            
552
    #[test]
553
    fn list_by_relay_ids() {
554
        #[derive(Clone, Debug)]
555
        struct ErsatzChannel<T> {
556
            val: T,
557
            ids: RelayIds,
558
        }
559

            
560
        impl<T> ErsatzChannel<T> {
561
            fn new(val: T, ids: RelayIds) -> Self {
562
                Self { val, ids }
563
            }
564
        }
565

            
566
        impl<T> HasRelayIds for ErsatzChannel<T> {
567
            fn identity(&self, key_type: crate::RelayIdType) -> Option<RelayIdRef<'_>> {
568
                self.ids.identity(key_type)
569
            }
570
        }
571

            
572
        // helper to build a `RelayIds` to make tests shorter
573
        fn ids(
574
            rsa: impl Into<Option<RsaIdentity>>,
575
            ed: impl Into<Option<Ed25519Identity>>,
576
        ) -> RelayIds {
577
            let mut ids = RelayIdsBuilder::default();
578
            if let Some(rsa) = rsa.into() {
579
                ids.rsa_identity(rsa);
580
            }
581
            if let Some(ed) = ed.into() {
582
                ids.ed_identity(ed);
583
            }
584
            ids.build().unwrap()
585
        }
586

            
587
        // ids for relay A
588
        let rsa_a: RsaIdentity = (*b"12345678901234567890").into();
589
        let ed_a: Ed25519Identity = (*b"12345678901234567890123456789012").into();
590

            
591
        // ids for relay B
592
        let ed_b: Ed25519Identity = (*b"abcefghijklmnopqrstuvwxyzABCDEFG").into();
593
        let rsa_b: RsaIdentity = (*b"abcefghijklmnopqrstu").into();
594

            
595
        // channel to A with all ids
596
        let channel_a_all = ErsatzChannel::new("channel-a-all", ids(rsa_a, ed_a));
597

            
598
        // channel to A with only the rsa id
599
        let channel_a_rsa_only_1 = ErsatzChannel::new("channel-a-rsa-only-1", ids(rsa_a, None));
600

            
601
        // channel to A with only the rsa id; this could for example represent a channel with the
602
        // same relay id as above but at a different ip address
603
        let channel_a_rsa_only_2 = ErsatzChannel::new("channel-a-rsa-only-2", ids(rsa_a, None));
604

            
605
        // channel to A with only the ed id
606
        let channel_a_ed_only = ErsatzChannel::new("channel-a-ed-only", ids(None, ed_a));
607

            
608
        // channel to B with all ids
609
        let channel_b_all = ErsatzChannel::new("channel-b-all", ids(rsa_b, ed_b));
610

            
611
        // an "invalid" channel with A's rsa id and B's ed id; this could for example represent an
612
        // in-progress pending channel that hasn't been verified yet
613
        let channel_invalid = ErsatzChannel::new("channel-invalid", ids(rsa_a, ed_b));
614

            
615
        let mut list = ListByRelayIds::new();
616
        list.insert(channel_a_all.clone());
617
        list.insert(channel_a_rsa_only_1.clone());
618
        list.insert(channel_a_rsa_only_2.clone());
619
        list.insert(channel_a_ed_only.clone());
620
        list.insert(channel_b_all.clone());
621
        list.insert(channel_invalid.clone());
622

            
623
        // look up by A's rsa id
624
        assert_eq!(
625
            sort(list.by_id(&rsa_a).map(|x| x.val)),
626
            [
627
                "channel-a-all",
628
                "channel-a-rsa-only-1",
629
                "channel-a-rsa-only-2",
630
                "channel-invalid",
631
            ],
632
        );
633

            
634
        // look up by A's ed id
635
        assert_eq!(
636
            sort(list.by_id(&ed_a).map(|x| x.val)),
637
            ["channel-a-all", "channel-a-ed-only"],
638
        );
639

            
640
        // look up by B's rsa id
641
        assert_eq!(sort(list.by_id(&rsa_b).map(|x| x.val)), ["channel-b-all"]);
642

            
643
        // look up by B's ed id
644
        assert_eq!(
645
            sort(list.by_id(&ed_b).map(|x| x.val)),
646
            ["channel-b-all", "channel-invalid"],
647
        );
648

            
649
        // look up by both A's rsa id and ed id
650
        assert_eq!(
651
            sort(list.by_all_ids(&ids(rsa_a, ed_a)).map(|x| x.val)),
652
            ["channel-a-all"],
653
        );
654

            
655
        // look up by both B's rsa id and ed id
656
        assert_eq!(
657
            sort(list.by_all_ids(&ids(rsa_b, ed_b)).map(|x| x.val)),
658
            ["channel-b-all"],
659
        );
660

            
661
        // look up by either A's rsa id or ed id
662
        assert_eq!(
663
            sort(
664
                list.all_overlapping(&ids(rsa_a, ed_a))
665
                    .into_iter()
666
                    .map(|x| x.val)
667
            ),
668
            [
669
                "channel-a-all",
670
                "channel-a-ed-only",
671
                "channel-a-rsa-only-1",
672
                "channel-a-rsa-only-2",
673
                "channel-invalid",
674
            ],
675
        );
676

            
677
        // look up where channel's ids are a subset of A's ids
678
        assert_eq!(
679
            sort(
680
                list.all_subset(&ids(rsa_a, ed_a))
681
                    .into_iter()
682
                    .map(|x| x.val)
683
            ),
684
            [
685
                "channel-a-all",
686
                "channel-a-ed-only",
687
                "channel-a-rsa-only-1",
688
                "channel-a-rsa-only-2",
689
            ],
690
        );
691

            
692
        // some sanity checks
693
        assert_eq!(list.by_all_ids(&ids(None, None)).count(), 0);
694
        assert!(list.all_overlapping(&ids(None, None)).is_empty());
695
        assert!(list.all_subset(&ids(None, None)).is_empty());
696
        assert_eq!(
697
            sort(
698
                list.all_overlapping(&ids(rsa_a, None))
699
                    .into_iter()
700
                    .map(|x| x.val)
701
            ),
702
            sort(list.by_id(&rsa_a).map(|x| x.val)),
703
        );
704
        assert_eq!(
705
            sort(
706
                list.all_overlapping(&ids(None, ed_b))
707
                    .into_iter()
708
                    .map(|x| x.val)
709
            ),
710
            sort(list.by_id(&ed_b).map(|x| x.val)),
711
        );
712
        assert_eq!(
713
            sort(list.by_id(&rsa_a).map(|x| x.val)),
714
            sort(list.by_rsa(&rsa_a).map(|x| x.val)),
715
        );
716
        assert_eq!(
717
            sort(list.by_id(&ed_a).map(|x| x.val)),
718
            sort(list.by_ed25519(&ed_a).map(|x| x.val)),
719
        );
720

            
721
        // remove channels with exactly A's rsa id and ed id
722
        {
723
            let mut list = list.clone();
724
            assert_eq!(
725
                sort(
726
                    list.remove_exact(&ids(rsa_a, ed_a))
727
                        .into_iter()
728
                        .map(|x| x.val)
729
                ),
730
                ["channel-a-all"],
731
            );
732
            assert_eq!(list.by_all_ids(&ids(rsa_a, ed_a)).count(), 0);
733
        }
734

            
735
        // remove channels with exactly A's rsa id and no ed id
736
        {
737
            let mut list = list.clone();
738
            assert_eq!(
739
                sort(
740
                    list.remove_exact(&ids(rsa_a, None))
741
                        .into_iter()
742
                        .map(|x| x.val)
743
                ),
744
                ["channel-a-rsa-only-1", "channel-a-rsa-only-2"],
745
            );
746
            assert_eq!(
747
                sort(list.by_all_ids(&ids(rsa_a, None)).map(|x| x.val)),
748
                ["channel-a-all", "channel-invalid"],
749
            );
750
        }
751

            
752
        // remove channels with at least A's rsa id
753
        {
754
            let mut list = list.clone();
755
            assert_eq!(
756
                sort(
757
                    list.remove_by_all_ids(&ids(rsa_a, None))
758
                        .into_iter()
759
                        .map(|x| x.val)
760
                ),
761
                [
762
                    "channel-a-all",
763
                    "channel-a-rsa-only-1",
764
                    "channel-a-rsa-only-2",
765
                    "channel-invalid",
766
                ],
767
            );
768
            assert_eq!(list.by_all_ids(&ids(rsa_a, None)).count(), 0);
769
        }
770
    }
771
}