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