defuse_core/nonce/
versioned.rs1use borsh::{BorshDeserialize, BorshSerialize};
2use hex_literal::hex;
3
4use crate::{
5 Nonce,
6 nonce::{expirable::ExpirableNonce, salted::SaltedNonce},
7};
8
9#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
16pub enum VersionedNonce {
17 V1(SaltedNonce<ExpirableNonce<[u8; 15]>>),
18}
19
20impl VersionedNonce {
22 pub const VERSIONED_MAGIC_PREFIX: [u8; 4] = hex!("5628f6c6");
24
25 pub fn maybe_from(n: Nonce) -> Option<Self> {
26 let mut versioned = n.strip_prefix(&Self::VERSIONED_MAGIC_PREFIX)?;
27 Self::deserialize_reader(&mut versioned).ok()
28 }
29}
30
31impl From<VersionedNonce> for Nonce {
32 fn from(value: VersionedNonce) -> Self {
33 const SIZE: usize = size_of::<Nonce>();
34 let mut result = [0u8; SIZE];
35
36 (VersionedNonce::VERSIONED_MAGIC_PREFIX, value)
37 .serialize(&mut result.as_mut_slice())
38 .unwrap_or_else(|_| unreachable!());
39
40 result
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 use crate::{Timestamp, nonce::salted::Salt};
49 use arbitrary::Unstructured;
50 use defuse_test_utils::random::random_bytes;
51 use rstest::rstest;
52
53 #[rstest]
54 fn maybe_from_test(random_bytes: Vec<u8>) {
55 let mut u = Unstructured::new(&random_bytes);
56 let legacy_nonce: Nonce = u.arbitrary().unwrap();
57
58 let expected = VersionedNonce::maybe_from(legacy_nonce);
59 assert!(expected.is_none());
60
61 let mut u = Unstructured::new(&random_bytes);
62 let nonce_bytes: [u8; 15] = u.arbitrary().unwrap();
63 let now = Timestamp::now();
64 let salt: Salt = u.arbitrary().unwrap();
65
66 let salted = SaltedNonce::new(salt, ExpirableNonce::new(now, nonce_bytes));
67 let nonce: Nonce = VersionedNonce::V1(salted.clone()).into();
68
69 let exp = VersionedNonce::maybe_from(nonce);
70 assert_eq!(exp, Some(VersionedNonce::V1(salted)));
71 }
72}