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
7104949
    pub fn by_id<'a, T>(&self, key: T) -> Option<&H>
54
7104949
    where
55
7104949
        T: Into<RelayIdRef<'a>>,
56
7104949
    {
57
7104949
        match key.into() {
58
7104883
            RelayIdRef::Ed25519(ed) => self.by_ed25519(ed),
59
66
            RelayIdRef::Rsa(rsa) => self.by_rsa(rsa),
60
        }
61
7104949
    }
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
22
    {
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
1736354
    pub fn modify_by_id<'a, T, F>(&mut self, key: T, func: F) -> Vec<H>
78
1736354
    where
79
1736354
        T: Into<RelayIdRef<'a>>,
80
1736354
        F: FnOnce(&mut H),
81
1736354
    {
82
1736354
        match key.into() {
83
1736354
            RelayIdRef::Ed25519(ed) => self.modify_by_ed25519(ed, func),
84
            RelayIdRef::Rsa(rsa) => self.modify_by_rsa(rsa, func),
85
        }
86
1736354
    }
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
7104799
    pub fn by_all_ids<T>(&self, key: &T) -> Option<&H>
93
7104799
    where
94
7104799
        T: HasRelayIds,
95
7104799
    {
96
7104799
        let any_id = key.identities().next()?;
97
7104797
        self.by_id(any_id)
98
7104797
            .filter(|val| val.has_all_relay_ids_from(key))
99
7104799
    }
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
1736354
    pub fn modify_by_all_ids<T, F>(&mut self, key: &T, func: F) -> Vec<H>
106
1736354
    where
107
1736354
        T: HasRelayIds,
108
1736354
        F: FnOnce(&mut H),
109
1736354
    {
110
1736354
        let any_id = match key.identities().next() {
111
1736354
            Some(id) => id,
112
            None => return Vec::new(),
113
        };
114
1736354
        self.modify_by_id(any_id, |val| {
115
1736354
            if val.has_all_relay_ids_from(key) {
116
1736354
                func(val);
117
1736354
            }
118
1736354
        })
119
1736354
    }
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
24
    {
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
4
    {
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
58
    pub fn all_overlapping<T>(&self, key: &T) -> Vec<&H>
163
58
    where
164
58
        T: HasRelayIds,
165
58
    {
166
        use by_address::ByAddress;
167
        use std::collections::HashSet;
168

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

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

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

            
181
impl<H: HasRelayIds> ListByRelayIds<H> {
182
    /// Return an iterator of the values in this list that have the key `key`.
183
226
    pub fn by_id<'a, T>(&self, key: T) -> ListByRelayIdsIter<H>
184
226
    where
185
226
        T: Into<RelayIdRef<'a>>,
186
226
    {
187
226
        match key.into() {
188
180
            RelayIdRef::Ed25519(ed) => self.by_ed25519(ed),
189
46
            RelayIdRef::Rsa(rsa) => self.by_rsa(rsa),
190
        }
191
226
    }
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
66
    pub fn by_all_ids<'a>(&'a self, key: &'a impl HasRelayIds) -> impl Iterator<Item = &'a H> + 'a {
197
66
        key.identities()
198
66
            .next()
199
66
            .map_or_else(Default::default, |id| self.by_id(id))
200
66
            .filter(|val| val.has_all_relay_ids_from(key))
201
66
    }
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
48
    pub fn all_overlapping<T>(&self, key: &T) -> Vec<&H>
208
48
    where
209
48
        T: HasRelayIds,
210
48
    {
211
        use by_address::ByAddress;
212
        use std::collections::HashSet;
213

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

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

            
222
62
        items.into_iter().map(|by_addr| by_addr.0).collect()
223
48
    }
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
60
    pub fn all_subset<T>(&self, key: &T) -> Vec<&H>
231
60
    where
232
60
        T: HasRelayIds,
233
60
    {
234
        use by_address::ByAddress;
235
        use std::collections::HashSet;
236

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

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

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

            
251
    /// Return the values in this list that have the key `key` and where `filter` returns `true`.
252
44
    pub fn remove_by_id<'a, T>(&mut self, key: T, filter: impl FnMut(&H) -> bool) -> Vec<H>
253
44
    where
254
44
        T: Into<RelayIdRef<'a>>,
255
44
    {
256
44
        match key.into() {
257
40
            RelayIdRef::Ed25519(ed) => self.remove_by_ed25519(ed, filter),
258
4
            RelayIdRef::Rsa(rsa) => self.remove_by_rsa(rsa, filter),
259
        }
260
44
    }
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
8
    {
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
6
    {
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_duration_subtraction)]
305
    #![allow(clippy::useless_vec)]
306
    #![allow(clippy::needless_pass_by_value)]
307
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
308

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

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

            
318
    #[test]
319
    #[allow(clippy::cognitive_complexity)]
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
    #[allow(clippy::cognitive_complexity)]
430
    fn remove_exact() {
431
        let rsa1: RsaIdentity = (*b"12345678901234567890").into();
432
        let rsa2: RsaIdentity = (*b"abcefghijklmnopqrstu").into();
433
        let ed1: Ed25519Identity = (*b"12345678901234567890123456789012").into();
434
        let ed2: Ed25519Identity = (*b"abcefghijklmnopqrstuvwxyzABCDEFG").into();
435

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
554
    #[test]
555
    #[allow(clippy::cognitive_complexity)]
556
    fn list_by_relay_ids() {
557
        #[derive(Clone, Debug)]
558
        struct ErsatzChannel<T> {
559
            val: T,
560
            ids: RelayIds,
561
        }
562

            
563
        impl<T> ErsatzChannel<T> {
564
            fn new(val: T, ids: RelayIds) -> Self {
565
                Self { val, ids }
566
            }
567
        }
568

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

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

            
590
        // ids for relay A
591
        let rsa_a: RsaIdentity = (*b"12345678901234567890").into();
592
        let ed_a: Ed25519Identity = (*b"12345678901234567890123456789012").into();
593

            
594
        // ids for relay B
595
        let ed_b: Ed25519Identity = (*b"abcefghijklmnopqrstuvwxyzABCDEFG").into();
596
        let rsa_b: RsaIdentity = (*b"abcefghijklmnopqrstu").into();
597

            
598
        // channel to A with all ids
599
        let channel_a_all = ErsatzChannel::new("channel-a-all", ids(rsa_a, ed_a));
600

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

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

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

            
611
        // channel to B with all ids
612
        let channel_b_all = ErsatzChannel::new("channel-b-all", ids(rsa_b, ed_b));
613

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

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

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

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

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

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

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

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

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

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

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

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

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

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