1#![deny(missing_docs)]
2
3use std::io::{Read, Write};
12
13use tls_codec::{Deserialize, Serialize, Size};
14
15use crate::tls_map::{TlsMap, TlsMapDelta, TlsMapError, TlsMapMutation};
16
17#[derive(Clone, Copy, PartialEq, Eq, Hash)]
28pub struct TlsKeyHash([u8; 32]);
29
30impl TlsKeyHash {
31 #[inline]
35 pub fn of<K: Serialize + Size>(key: &K) -> Result<Self, tls_codec::Error> {
36 let bytes = key.tls_serialize_detached()?;
37 Ok(Self(xmtp_common::sha256_array(&bytes)))
38 }
39
40 #[inline]
43 pub(crate) const fn from_bytes(bytes: [u8; 32]) -> Self {
44 Self(bytes)
45 }
46
47 #[inline]
49 pub const fn as_bytes(&self) -> &[u8; 32] {
50 &self.0
51 }
52}
53
54impl std::fmt::Debug for TlsKeyHash {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 f.write_str("TlsKeyHash(")?;
57 for byte in &self.0 {
58 write!(f, "{byte:02x}")?;
59 }
60 f.write_str(")")
61 }
62}
63
64#[derive(Debug, thiserror::Error)]
66pub enum TlsSetError {
67 #[error("duplicate hash in set")]
79 DuplicateHash,
80 #[error(transparent)]
82 Map(#[from] TlsMapError),
83 #[error("tls codec error: {0}")]
85 Codec(#[from] tls_codec::Error),
86}
87
88#[derive(Debug, Clone, PartialEq, Eq)]
106pub struct TlsSet<K> {
107 inner: TlsMap<K, ()>,
108}
109
110impl<K> Default for TlsSet<K> {
111 #[inline]
112 fn default() -> Self {
113 Self {
114 inner: TlsMap::default(),
115 }
116 }
117}
118
119impl<K: Ord + Eq> TlsSet<K> {
120 #[inline]
122 pub fn new() -> Self {
123 Self::default()
124 }
125
126 #[inline]
135 pub fn from_keys(iter: impl IntoIterator<Item = K>) -> Self {
136 Self {
137 inner: TlsMap::from_pairs(iter.into_iter().map(|k| (k, ()))),
138 }
139 }
140
141 #[inline]
151 pub fn insert(&mut self, key: K) -> Result<(), TlsSetError> {
152 Ok(self.inner.insert(key, ())?)
153 }
154
155 #[inline]
166 pub fn remove(&mut self, key: &K) -> Result<(), TlsSetError> {
167 self.inner.remove(key).map(|_| ())?;
168 Ok(())
169 }
170
171 #[inline]
173 pub fn contains(&self, key: &K) -> bool {
174 self.inner.contains_key(key)
175 }
176
177 #[inline]
179 pub fn len(&self) -> usize {
180 self.inner.len()
181 }
182
183 #[inline]
185 pub fn is_empty(&self) -> bool {
186 self.inner.is_empty()
187 }
188
189 #[inline]
199 pub fn iter(&self) -> impl Iterator<Item = &K> {
200 self.inner.keys()
201 }
202}
203
204impl<K: Ord + Eq + Clone + Serialize + Size> TlsSet<K> {
205 pub fn apply_delta(&mut self, delta: TlsSetDelta<K>) -> Result<(), TlsSetError> {
238 let has_hash_removal = delta
239 .mutations
240 .iter()
241 .any(|m| matches!(m, TlsSetMutation::RemoveByHash(_)));
242
243 let hash_index: Option<std::collections::HashMap<TlsKeyHash, &K>> = if has_hash_removal {
246 let mut idx = std::collections::HashMap::with_capacity(self.inner.len());
247 for key in self.inner.keys() {
248 if idx.insert(TlsKeyHash::of(key)?, key).is_some() {
249 return Err(TlsSetError::DuplicateHash);
250 }
251 }
252 Some(idx)
253 } else {
254 None
255 };
256
257 let resolved: Vec<TlsMapMutation<K, ()>> = delta
258 .mutations
259 .into_iter()
260 .map(|m| -> Result<TlsMapMutation<K, ()>, TlsSetError> {
261 match m {
262 TlsSetMutation::Insert(key) => Ok(TlsMapMutation::Insert { key, value: () }),
263 TlsSetMutation::Remove(key) => Ok(TlsMapMutation::Delete { key }),
264 TlsSetMutation::RemoveByHash(target_hash) => {
265 let idx = hash_index
266 .as_ref()
267 .expect("hash index must be built for RemoveByHash");
268 let key = idx.get(&target_hash).ok_or(TlsMapError::KeyNotFound)?;
269 Ok(TlsMapMutation::Delete {
270 key: (*key).clone(),
271 })
272 }
273 }
274 })
275 .collect::<Result<Vec<_>, _>>()?;
276 let map_delta = TlsMapDelta {
277 mutations: resolved,
278 };
279 self.inner.apply_delta(map_delta)?;
280 Ok(())
281 }
282}
283
284impl<K: Size> Size for TlsSet<K> {
287 #[inline]
288 fn tls_serialized_len(&self) -> usize {
289 self.inner.tls_serialized_len()
290 }
291}
292
293impl<K: Serialize + Size + std::fmt::Debug> Serialize for TlsSet<K> {
294 #[inline]
295 fn tls_serialize<W: Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
296 self.inner.tls_serialize(writer)
297 }
298}
299
300impl<K> Deserialize for TlsSet<K>
301where
302 K: Deserialize + Size + Ord + Eq,
303{
304 #[inline]
305 fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, tls_codec::Error>
306 where
307 Self: Sized,
308 {
309 let inner = TlsMap::tls_deserialize(bytes)?;
310 Ok(Self { inner })
311 }
312}
313
314impl<K: Ord + Eq> FromIterator<K> for TlsSet<K> {
315 #[inline]
316 fn from_iter<T: IntoIterator<Item = K>>(iter: T) -> Self {
317 Self::from_keys(iter)
318 }
319}
320
321impl<K> IntoIterator for TlsSet<K> {
322 type Item = K;
323 type IntoIter = std::iter::Map<<TlsMap<K, ()> as IntoIterator>::IntoIter, fn((K, ())) -> K>;
324
325 #[inline]
326 fn into_iter(self) -> Self::IntoIter {
327 self.inner.into_iter().map(|(k, _)| k)
328 }
329}
330
331#[derive(Debug, Clone, PartialEq, Eq)]
342pub enum TlsSetMutation<K> {
343 Insert(K),
345 Remove(K),
347 RemoveByHash(TlsKeyHash),
351}
352
353#[derive(Debug, Clone, PartialEq, Eq)]
355pub struct TlsSetDelta<K> {
356 pub mutations: Vec<TlsSetMutation<K>>,
358}
359
360impl<K> TlsSetDelta<K> {
361 #[inline]
363 pub fn new() -> Self {
364 Self {
365 mutations: Vec::new(),
366 }
367 }
368
369 #[inline]
371 pub fn insert(mut self, key: K) -> Self {
372 self.mutations.push(TlsSetMutation::Insert(key));
373 self
374 }
375
376 #[inline]
378 pub fn remove(mut self, key: K) -> Self {
379 self.mutations.push(TlsSetMutation::Remove(key));
380 self
381 }
382
383 #[inline]
385 pub fn remove_by_hash(mut self, hash: TlsKeyHash) -> Self {
386 self.mutations.push(TlsSetMutation::RemoveByHash(hash));
387 self
388 }
389}
390
391impl<K> Default for TlsSetDelta<K> {
392 #[inline]
393 fn default() -> Self {
394 Self::new()
395 }
396}
397
398impl<K: Size> Size for TlsSetMutation<K> {
401 #[inline]
402 fn tls_serialized_len(&self) -> usize {
403 1 + match self {
404 Self::Insert(key) | Self::Remove(key) => key.tls_serialized_len(),
405 Self::RemoveByHash(_) => 32,
406 }
407 }
408}
409
410impl<K: Serialize + Size> Serialize for TlsSetMutation<K> {
411 #[inline]
412 fn tls_serialize<W: Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
413 match self {
414 Self::Insert(key) => {
415 let mut written = 0_u8.tls_serialize(writer)?;
416 written += key.tls_serialize(writer)?;
417 Ok(written)
418 }
419 Self::Remove(key) => {
420 let mut written = 1_u8.tls_serialize(writer)?;
421 written += key.tls_serialize(writer)?;
422 Ok(written)
423 }
424 Self::RemoveByHash(hash) => {
425 let written = 2_u8.tls_serialize(writer)?;
426 let bytes = hash.as_bytes();
427 writer
428 .write_all(bytes)
429 .map_err(|e| tls_codec::Error::EncodingError(e.to_string()))?;
430 Ok(written + bytes.len())
431 }
432 }
433 }
434}
435
436impl<K: Deserialize + Size> Deserialize for TlsSetMutation<K> {
437 #[inline]
438 fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, tls_codec::Error>
439 where
440 Self: Sized,
441 {
442 let tag = u8::tls_deserialize(bytes)?;
443 match tag {
444 0 => Ok(Self::Insert(K::tls_deserialize(bytes)?)),
445 1 => Ok(Self::Remove(K::tls_deserialize(bytes)?)),
446 2 => {
447 let mut buf = [0_u8; 32];
448 bytes
449 .read_exact(&mut buf)
450 .map_err(|e| tls_codec::Error::DecodingError(e.to_string()))?;
451 Ok(Self::RemoveByHash(TlsKeyHash::from_bytes(buf)))
452 }
453 _ => Err(tls_codec::Error::DecodingError(format!(
454 "unknown TlsSetMutation tag: {tag}"
455 ))),
456 }
457 }
458}
459
460impl<K: Size> Size for TlsSetDelta<K> {
461 #[inline]
462 fn tls_serialized_len(&self) -> usize {
463 self.mutations.tls_serialized_len()
464 }
465}
466
467impl<K: Serialize + Size + std::fmt::Debug> Serialize for TlsSetDelta<K> {
468 #[inline]
469 fn tls_serialize<W: Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
470 self.mutations.tls_serialize(writer)
471 }
472}
473
474impl<K: Deserialize + Size> Deserialize for TlsSetDelta<K> {
475 #[inline]
476 fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, tls_codec::Error>
477 where
478 Self: Sized,
479 {
480 let mutations = Vec::<TlsSetMutation<K>>::tls_deserialize(bytes)?;
481 Ok(Self { mutations })
482 }
483}
484
485#[cfg(test)]
486mod tests {
487 use super::*;
488 use tls_codec::{Deserialize, Serialize};
489
490 #[xmtp_common::test]
491 fn test_insert_and_contains() {
492 let mut set = TlsSet::<u16>::new();
493 assert!(set.remove(&42).is_err());
494 set.insert(42).unwrap();
495 assert!(set.contains(&42));
496 assert!(!set.contains(&99));
497 assert!(set.insert(42).is_err());
498 set.remove(&42).unwrap();
499 assert!(!set.contains(&42));
500 assert!(set.is_empty());
501 assert!(set.remove(&42).is_err());
502
503 let set = TlsSet::from_keys([1_u8, 2, 3, 1, 2]);
504 assert_eq!(set.len(), 3);
505 assert!(set.contains(&1));
506 }
507
508 #[xmtp_common::test]
509 fn test_iter_sorted() {
510 let set = TlsSet::from_keys([3_u8, 1, 2]);
511 let keys: Vec<_> = set.iter().copied().collect();
512 assert_eq!(keys, vec![1, 2, 3]);
513 assert_eq!(set.clone().into_iter().collect::<Vec<_>>(), vec![1, 2, 3]);
514 let collected: TlsSet<u8> = [3, 1, 2].into_iter().collect();
515 assert_eq!(collected.len(), 3);
516 assert!(collected.contains(&1));
517 assert_eq!(collected.iter().copied().collect::<Vec<_>>(), keys);
518 }
519
520 #[xmtp_common::test]
521 fn test_tls_round_trip() {
522 let set = TlsSet::from_keys([10_u16, 20, 30]);
523 let bytes = set.tls_serialize_detached().unwrap();
524 let restored = TlsSet::<u16>::tls_deserialize_exact(&bytes).unwrap();
525 assert_eq!(set, restored);
526 let empty = TlsSet::<u8>::new();
527 let bytes = empty.tls_serialize_detached().unwrap();
528 let restored = TlsSet::<u8>::tls_deserialize_exact(&bytes).unwrap();
529 assert_eq!(empty, restored);
530 assert!(restored.is_empty());
531 }
532
533 #[xmtp_common::test]
534 fn test_apply_delta() {
535 let mut set = TlsSet::from_keys([1_u8, 2, 3]);
536 let delta = TlsSetDelta::new().insert(4).remove(2);
537 set.apply_delta(delta).unwrap();
538 assert!(set.contains(&1));
539 assert!(!set.contains(&2));
540 assert!(set.contains(&3));
541 assert!(set.contains(&4));
542 }
543
544 #[xmtp_common::test]
545 fn test_apply_delta_rollback_on_failure() {
547 let mut set = TlsSet::from_keys([1_u8, 2]);
548 let delta = TlsSetDelta::new().insert(3).insert(1);
550 assert!(set.apply_delta(delta).is_err());
551 assert_eq!(set.len(), 2);
553 assert!(!set.contains(&3));
554 }
555
556 #[xmtp_common::test]
557 fn test_remove_by_hash() {
559 let mut set = TlsSet::from_keys([10_u16, 20, 30]);
560 let hash = TlsKeyHash::of(&20_u16).unwrap();
561 let delta = TlsSetDelta::new().remove_by_hash(hash);
562 set.apply_delta(delta).unwrap();
563 assert!(set.contains(&10));
564 assert!(!set.contains(&20));
565 assert!(set.contains(&30));
566 }
567
568 #[xmtp_common::test]
569 fn test_remove_by_hash_not_found() {
570 let mut set = TlsSet::from_keys([10_u16, 20]);
571 let hash = TlsKeyHash::of(&99_u16).unwrap(); let delta = TlsSetDelta::new().remove_by_hash(hash);
573 assert!(set.apply_delta(delta).is_err());
574 }
575
576 #[xmtp_common::test]
577 fn test_remove_by_hash_matches_remove_by_value() {
578 let mut set_a = TlsSet::from_keys([1_u16, 2, 3]);
579 let mut set_b = set_a.clone();
580
581 let delta_a = TlsSetDelta::new().remove(2);
583 set_a.apply_delta(delta_a).unwrap();
584
585 let hash = TlsKeyHash::of(&2_u16).unwrap();
587 let delta_b = TlsSetDelta::new().remove_by_hash(hash);
588 set_b.apply_delta(delta_b).unwrap();
589
590 assert_eq!(set_a, set_b);
591 }
592
593 #[xmtp_common::test]
594 fn test_delta_tls_round_trip() {
595 let delta = TlsSetDelta::<u16>::new().insert(10).remove(20).insert(30);
596 let bytes = delta.tls_serialize_detached().unwrap();
597 let restored = TlsSetDelta::<u16>::tls_deserialize_exact(&bytes).unwrap();
598 assert_eq!(delta, restored);
599 }
600
601 #[xmtp_common::test]
602 fn test_remove_by_hash_mutation_tls_round_trip() {
603 let hash = TlsKeyHash::of(&42_u16).unwrap();
604 let mutation = TlsSetMutation::<u16>::RemoveByHash(hash);
605 let bytes = mutation.tls_serialize_detached().unwrap();
606 let restored = TlsSetMutation::<u16>::tls_deserialize_exact(&bytes).unwrap();
607 assert_eq!(mutation, restored);
608 }
609
610 #[xmtp_common::test]
611 fn test_mutation_tls_round_trip() {
612 let add = TlsSetMutation::Insert(42_u16);
613 let bytes = add.tls_serialize_detached().unwrap();
614 let restored = TlsSetMutation::<u16>::tls_deserialize_exact(&bytes).unwrap();
615 assert_eq!(add, restored);
616
617 let remove = TlsSetMutation::Remove(99_u16);
618 let bytes = remove.tls_serialize_detached().unwrap();
619 let restored = TlsSetMutation::<u16>::tls_deserialize_exact(&bytes).unwrap();
620 assert_eq!(remove, restored);
621 }
622
623 #[xmtp_common::test]
624 fn test_deserialize_unknown_tag() {
625 let bytes = [3_u8, 0, 42];
627 let result = TlsSetMutation::<u16>::tls_deserialize_exact(bytes);
628 assert!(matches!(result, Err(tls_codec::Error::DecodingError(_))));
629 }
630
631 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
636 struct CollidingKey(u16);
637
638 impl Size for CollidingKey {
639 fn tls_serialized_len(&self) -> usize {
640 1
641 }
642 }
643
644 impl Serialize for CollidingKey {
645 fn tls_serialize<W: std::io::Write>(
646 &self,
647 writer: &mut W,
648 ) -> Result<usize, tls_codec::Error> {
649 0_u8.tls_serialize(writer)
651 }
652 }
653
654 impl Deserialize for CollidingKey {
655 fn tls_deserialize<R: std::io::Read>(bytes: &mut R) -> Result<Self, tls_codec::Error>
656 where
657 Self: Sized,
658 {
659 let _ = u8::tls_deserialize(bytes)?;
660 Ok(CollidingKey(0))
661 }
662 }
663
664 #[xmtp_common::test]
665 fn test_apply_delta_duplicate_hash() {
667 let mut set = TlsSet::<CollidingKey>::new();
668 set.insert(CollidingKey(1)).unwrap();
670 set.insert(CollidingKey(2)).unwrap();
671
672 let any_hash = TlsKeyHash::of(&CollidingKey(0)).unwrap();
673 let delta = TlsSetDelta::new().remove_by_hash(any_hash);
674 let result = set.apply_delta(delta);
675 assert!(matches!(result, Err(TlsSetError::DuplicateHash)));
676 }
677
678 #[xmtp_common::test]
679 fn test_apply_delta_remove_by_hash_not_found_in_index() {
681 let mut set = TlsSet::<u16>::new();
685 set.insert(10).unwrap();
686 set.insert(20).unwrap();
687
688 let missing_hash = TlsKeyHash::of(&999_u16).unwrap();
689 let delta = TlsSetDelta::new().remove_by_hash(missing_hash);
690 let result = set.apply_delta(delta);
691 assert!(matches!(
692 result,
693 Err(TlsSetError::Map(TlsMapError::KeyNotFound))
694 ));
695 assert!(set.contains(&10));
697 assert!(set.contains(&20));
698 }
699}