xmtp_api_d14n/protocol/sort/
timestamp.rs

1use crate::protocol::{Envelope, EnvelopeError, Sort};
2
3pub struct TimestampSort<'a, E> {
4    envelopes: &'a mut [E],
5}
6
7impl<'b, 'a: 'b, E> Sort<()> for TimestampSort<'b, E>
8where
9    E: Envelope<'a>,
10{
11    fn sort(mut self) -> Result<Option<()>, EnvelopeError> {
12        let envelopes = &mut self.envelopes;
13        // we can only sort envelopes which have a timestamp
14        envelopes.sort_unstable_by_key(|e| e.timestamp());
15        // timestamp sort can never have missing dependencies
16        Ok(None)
17    }
18}
19
20/// Sorts Envelopes by server-side Timestamp in ascending order
21/// * for d14n this will sort envelopes by
22///   [`originator_ns`](xmtp_proto::xmtp::xmtpv4::envelopes::UnsignedOriginatorEnvelope::originator_ns)
23/// * for v3 this will sort by created_ns on GroupMessage, WelcomeMessage, or WelcomePointer
24///   overall, sorts according to the timestamp extracted by
25///   [`TimestampExtractor`](crate::protocol::TimestampExtractor)
26///
27/// If a timestamp does not have a cursor (extractor return [`Option::None`]) it is
28/// sorted according to [`Ord`], [impl](https://doc.rust-lang.org/src/core/option.rs.html#2341)
29/// This sort will never return any missing envelopes.
30pub fn timestamp<'b, 'a: 'b, E: Envelope<'a>>(envelopes: &'b mut [E]) -> impl Sort<()> {
31    TimestampSort { envelopes }
32}
33
34#[cfg(test)]
35mod tests {
36    use crate::protocol::sort;
37    use chrono::Utc;
38    use proptest::prelude::*;
39    use xmtp_common::Generate;
40    use xmtp_proto::xmtp::xmtpv4::envelopes::ClientEnvelope;
41    use xmtp_proto::xmtp::xmtpv4::envelopes::client_envelope::Payload;
42
43    use super::*;
44
45    #[derive(Debug)]
46    struct TestEnvelope {
47        time: Option<chrono::DateTime<Utc>>,
48    }
49
50    impl TestEnvelope {
51        fn new(time: i64) -> Self {
52            Self {
53                time: Some(chrono::DateTime::from_timestamp_nanos(time)),
54            }
55        }
56    }
57
58    impl Generate for TestEnvelope {
59        fn generate() -> Self {
60            TestEnvelope {
61                time: Some(chrono::DateTime::from_timestamp_nanos(
62                    xmtp_common::rand_i64(),
63                )),
64            }
65        }
66    }
67
68    impl Envelope<'_> for TestEnvelope {
69        fn topic(&self) -> Result<xmtp_proto::types::Topic, crate::protocol::EnvelopeError> {
70            unreachable!()
71        }
72
73        fn payload(&self) -> Result<Payload, crate::protocol::EnvelopeError> {
74            unreachable!()
75        }
76
77        fn timestamp(&self) -> Option<chrono::DateTime<Utc>> {
78            self.time
79        }
80
81        fn client_envelope(&self) -> Result<ClientEnvelope, crate::protocol::EnvelopeError> {
82            unreachable!()
83        }
84
85        fn group_message(
86            &self,
87        ) -> Result<Option<xmtp_proto::types::GroupMessage>, crate::protocol::EnvelopeError>
88        {
89            unreachable!()
90        }
91
92        fn welcome_message(
93            &self,
94        ) -> Result<Option<xmtp_proto::types::WelcomeMessage>, crate::protocol::EnvelopeError>
95        {
96            unreachable!()
97        }
98
99        fn cursor(&self) -> Result<xmtp_proto::types::Cursor, EnvelopeError> {
100            unreachable!()
101        }
102
103        fn depends_on(&self) -> Result<Option<xmtp_proto::types::GlobalCursor>, EnvelopeError> {
104            unreachable!()
105        }
106
107        fn sha256_hash(&self) -> Result<Vec<u8>, EnvelopeError> {
108            unreachable!()
109        }
110
111        fn bytes(&self) -> Result<Vec<u8>, EnvelopeError> {
112            unreachable!()
113        }
114
115        fn orphan(&self) -> Result<xmtp_proto::types::OrphanedEnvelope, EnvelopeError> {
116            todo!()
117        }
118    }
119
120    prop_compose! {
121        fn envelope_all_some()(id in 1..u32::MAX) -> TestEnvelope {
122            TestEnvelope::new(id as i64)
123        }
124    }
125
126    fn is_sorted(sorted: &[TestEnvelope]) -> bool {
127        sorted.is_sorted_by_key(|e| e.time)
128    }
129
130    #[xmtp_common::test]
131    fn sorts_by_timestamp() {
132        proptest!(|(mut envelopes in prop::collection::vec(envelope_all_some(), 0 .. 100))| {
133            sort::timestamp(&mut envelopes).sort().unwrap();
134            assert!(is_sorted(&envelopes), "envelopes were not sorted")
135        });
136    }
137}