defuse_core/nonce/
expirable.rs

1use defuse_borsh_utils::adapters::{As, TimestampNanoSeconds};
2use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
3
4use crate::Deadline;
5
6/// Expirable nonces contain deadline which is 8 bytes of timestamp in nanoseconds
7#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
8#[borsh(crate = "::near_sdk::borsh")]
9pub struct ExpirableNonce<T>
10where
11    T: BorshSerialize + BorshDeserialize,
12{
13    #[borsh(
14        serialize_with = "As::<TimestampNanoSeconds>::serialize",
15        deserialize_with = "As::<TimestampNanoSeconds>::deserialize"
16    )]
17    pub deadline: Deadline,
18    pub nonce: T,
19}
20
21impl<T> ExpirableNonce<T>
22where
23    T: BorshSerialize + BorshDeserialize,
24{
25    pub const fn new(deadline: Deadline, nonce: T) -> Self {
26        Self { deadline, nonce }
27    }
28
29    #[inline]
30    pub fn has_expired(&self) -> bool {
31        self.deadline.has_expired()
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    use chrono::{Days, Utc};
40    use defuse_test_utils::random::random_bytes;
41    use rstest::rstest;
42
43    #[rstest]
44    fn expirable_test(random_bytes: Vec<u8>) {
45        let current_timestamp = Utc::now();
46        let mut u = arbitrary::Unstructured::new(&random_bytes);
47        let nonce: [u8; 24] = u.arbitrary().unwrap();
48
49        let expired = ExpirableNonce::new(
50            Deadline::new(current_timestamp.checked_sub_days(Days::new(1)).unwrap()),
51            nonce,
52        );
53        assert!(expired.has_expired());
54
55        let not_expired = ExpirableNonce::new(
56            Deadline::new(current_timestamp.checked_add_days(Days::new(1)).unwrap()),
57            nonce,
58        );
59        assert!(!not_expired.has_expired());
60    }
61}