Skip to main content

rialo_types/
nonce.rs

1// Copyright (c) Subzero Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! # Nonce
5//!
6//! This module provides a nonce implementation for use as an identifier in the Rialo system.
7//!
8//! ## Overview
9//!
10//! In the context of Rialo, a nonce is a unique value that serves as an identifier,
11//! particularly for generating Program Derived Addresses (PDAs). PDAs are deterministic
12//! addresses derived from a program ID and one or more seeds (including nonces).
13//!
14//! This implementation is designed to be:
15//!
16//! - **Consistent**: Same input always produces the same nonce
17//! - **Flexible**: Can be created from various data types (strings, byte arrays, integers)
18//! - **Fixed-size**: Always `NONCE_LEN` bytes regardless of input size, making it ideal for use in
19//!   Rialo Transactions and Instructions where fixed-length identifiers are important
20//! - **Serializable**: Can be converted to/from string representations
21//! - **Secure**: Uses Blake3 hashing for inputs exceeding `NONCE_LEN` bytes
22//!
23//! ## Behavior
24//!
25//! The module supports creating nonces from different data formats and handles
26//! inputs of various sizes by either:
27//!
28//! - **Padding**: If input is ≤ `NONCE_LEN` bytes, it's padded with zeros to reach `NONCE_LEN` bytes
29//! - **Hashing**: If input is > `NONCE_LEN` bytes, it's hashed using Blake3 to produce a `NONCE_LEN`-byte value
30//!
31//! ## Usage
32//!
33//! Nonces can be created from various data types, and will always result in a `NONCE_LEN`-byte value:
34//!
35//! ```
36//! # use rialo_types::Nonce;
37//! // From a string
38//! let nonce_from_str = Nonce::from("my_identifier");
39//!
40//! // From a byte string literal
41//! let nonce_from_byte_str = Nonce::from(b"my_identifier");
42//!
43//! // From a byte array of any size (small example)
44//! let small_bytes = [1, 2, 3, 4];
45//! let nonce_from_small_bytes = Nonce::from(&small_bytes);
46//! assert_eq!(nonce_from_small_bytes.as_bytes().len(), 32); // Always 32 bytes
47//!
48//! // From a byte array of any size (large example)
49//! let large_bytes = [1u8; 64];
50//! let nonce_from_large_bytes = Nonce::from(&large_bytes);
51//! assert_eq!(nonce_from_large_bytes.as_bytes().len(), 32); // Always 32 bytes
52//!
53//! // From a dynamic slice
54//! let dynamic_slice: &[u8] = &[5, 6, 7, 8, 9];
55//! let nonce_from_slice = Nonce::from(dynamic_slice);
56//!
57//! // From a u64
58//! let nonce_from_int = Nonce::from(12345u64);
59//!
60//! // Direct creation (if you already have a 32-byte array)
61//! let array = [0u8; 32];
62//! let nonce_direct = Nonce::new(array);
63//! ```
64//!
65//! ## Use in Rialo Transactions and Instructions
66//!
67//! The fixed-size nature of `Nonce` (always `NONCE_LEN` bytes) makes it particularly useful in contexts
68//! where knowing the exact size of an identifier is important, such as in Rialo Transactions
69//! and Instructions. This allows for more efficient serialization and deserialization, as well
70//! as predictable memory layouts.
71//!
72//! ## Security Considerations
73//!
74//! When using nonces for security-critical operations:
75//!
76//! - Prefer using cryptographically strong random values when uniqueness is critical
77//! - Be aware that predictable nonces may lead to address collisions or vulnerabilities
78//! - For deterministic address generation, ensure all inputs are properly validated
79
80use std::fmt;
81
82use blake3::Hasher;
83use borsh::{BorshDeserialize, BorshSerialize};
84use serde::{Deserialize, Serialize};
85
86/// Length of a nonce in bytes.
87const NONCE_LEN: usize = 32;
88
89#[derive(
90    Debug,
91    Default,
92    Clone,
93    Copy,
94    PartialEq,
95    Eq,
96    Hash,
97    PartialOrd,
98    Ord,
99    Serialize,
100    Deserialize,
101    BorshSerialize,
102    BorshDeserialize,
103)]
104pub struct Nonce([u8; NONCE_LEN]);
105
106impl Nonce {
107    /// Creates a new Nonce directly from a `NONCE_LEN`-byte array.
108    ///
109    /// This is the most direct way to create a Nonce when you already have
110    /// a properly sized `NONCE_LEN`-byte array. No padding or hashing is performed.
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// # use rialo_types::Nonce;
116    /// let bytes = [1u8; 32];
117    /// let nonce = Nonce::new(bytes);
118    /// assert_eq!(nonce.as_bytes(), &bytes);
119    /// ```
120    pub const fn new(nonce: [u8; NONCE_LEN]) -> Self {
121        Self(nonce)
122    }
123
124    /// Creates a Nonce from a string slice at compile time.
125    ///
126    /// If the string is shorter than `NONCE_LEN` bytes, it is padded with zeros.
127    /// If the string is longer than `NONCE_LEN` bytes, it is truncated to `NONCE_LEN` bytes.
128    ///
129    /// Note: This behavior matches `From<&str>` for strings up to `NONCE_LEN` bytes.
130    /// For longer strings, `From<&str>` would perform a Blake3 hash instead of truncation.
131    ///
132    /// # Examples
133    ///
134    /// ```
135    /// # use rialo_types::Nonce;
136    /// const CONNECT: Nonce = Nonce::from_short_str("connect");
137    /// ```
138    pub const fn from_short_str(s: &str) -> Self {
139        let bytes = s.as_bytes();
140        let mut data = [0u8; NONCE_LEN];
141        let mut i = 0;
142        while i < bytes.len() && i < NONCE_LEN {
143            data[i] = bytes[i];
144            i += 1;
145        }
146        Self(data)
147    }
148
149    /// Returns a reference to the inner `NONCE_LEN`-byte array.
150    ///
151    /// This method is useful when you need to access the raw bytes
152    /// of the nonce, for example when using it as a seed for PDA derivation.
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// # use rialo_types::Nonce;
158    /// let nonce = Nonce::from("example");
159    /// let bytes = nonce.as_bytes();
160    /// // Use bytes for PDA derivation or other operations
161    /// ```
162    pub fn as_bytes(&self) -> &[u8; NONCE_LEN] {
163        &self.0
164    }
165}
166
167impl<const N: usize> From<[u8; N]> for Nonce {
168    /// Creates a Nonce from an owned fixed-size byte array.
169    ///
170    /// This implementation delegates to the reference implementation to avoid code duplication.
171    /// The same padding or hashing rules apply as in the reference implementation.
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// # use rialo_types::Nonce;
177    /// let array = [5u8; 16];
178    /// let nonce = Nonce::from(array);
179    /// ```
180    fn from(nonce: [u8; N]) -> Self {
181        Self::from(&nonce)
182    }
183}
184
185impl<const N: usize> From<&[u8; N]> for Nonce {
186    /// Creates a Nonce from a reference to a fixed-size byte array of any length.
187    ///
188    /// # Behavior
189    ///
190    /// - If `N > NONCE_LEN`: The input is hashed using Blake3 to produce a `NONCE_LEN`-byte nonce
191    /// - If `N <= NONCE_LEN`: The input is padded with zeros to reach `NONCE_LEN` bytes
192    ///
193    /// The padding is applied to the right side, preserving the original bytes at the beginning.
194    ///
195    /// # Examples
196    ///
197    /// ```
198    /// # use rialo_types::Nonce;
199    /// // Small array (padded)
200    /// let small = [1, 2, 3, 4];
201    /// let nonce_small = Nonce::from(&small);
202    ///
203    /// // Large array (hashed)
204    /// let large = [1u8; 64];
205    /// let nonce_large = Nonce::from(&large);
206    /// ```
207    fn from(nonce: &[u8; N]) -> Self {
208        // If the slice is longer than `NONCE_LEN` bytes, it will be hashed
209        if N > NONCE_LEN {
210            let mut hasher = Hasher::new();
211            hasher.update(nonce);
212            let result = hasher.finalize();
213            Self(result.into())
214        } else {
215            // If the slice is equal to or shorter than `NONCE_LEN` bytes, it will be padded with zeros
216            let mut padded = [0u8; NONCE_LEN];
217            padded[..N].copy_from_slice(nonce);
218            Self(padded)
219        }
220    }
221}
222
223impl From<&[u8]> for Nonce {
224    /// Creates a Nonce from a byte slice of any length.
225    ///
226    /// This implementation handles dynamic-length byte slices, applying the same
227    /// logic as the fixed-size array implementation:
228    ///
229    /// # Behavior
230    ///
231    /// - If `nonce.len() > NONCE_LEN`: The input is hashed using Blake3
232    /// - If `nonce.len() <= NONCE_LEN`: The input is padded with zeros
233    ///
234    /// # Edge Cases
235    ///
236    /// - Empty slice: Results in a nonce of all zeros
237    /// - Exactly `NONCE_LEN` bytes: No transformation is applied, but the bytes are copied
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// # use rialo_types::Nonce;
243    /// // Empty slice
244    /// let empty: &[u8] = &[];
245    /// let nonce_empty = Nonce::from(empty);
246    /// assert_eq!(*nonce_empty.as_bytes(), [0u8; 32]);
247    ///
248    /// // Dynamic slice
249    /// let data = b"dynamic length data".to_vec();
250    /// let nonce_dynamic = Nonce::from(data.as_slice());
251    /// ```
252    fn from(nonce: &[u8]) -> Self {
253        // If the slice is longer than `NONCE_LEN` bytes, it will be hashed
254        if nonce.len() > NONCE_LEN {
255            let mut hasher = Hasher::new();
256            hasher.update(nonce);
257            let result = hasher.finalize();
258            Self(result.into())
259        } else {
260            // If the slice is equal to or shorter than `NONCE_LEN` bytes, it will be padded with zeros
261            let mut padded = [0u8; NONCE_LEN];
262            padded[..nonce.len()].copy_from_slice(nonce);
263            Self(padded)
264        }
265    }
266}
267
268impl From<Vec<u8>> for Nonce {
269    /// Creates a Nonce from an owned vector of bytes.
270    ///
271    /// This implementation converts the vector to a slice and applies the same
272    /// padding/hashing rules as the slice implementation.
273    ///
274    /// # Examples
275    ///
276    /// ```
277    /// # use rialo_types::Nonce;
278    /// let vec = vec![1, 2, 3, 4];
279    /// let nonce = Nonce::from(vec);
280    /// ```
281    fn from(nonce: Vec<u8>) -> Self {
282        Self::from(nonce.as_slice())
283    }
284}
285
286impl From<&Vec<u8>> for Nonce {
287    /// Creates a Nonce from a reference to a vector of bytes.
288    ///
289    /// This implementation converts the vector to a slice and applies the same
290    /// padding/hashing rules as the slice implementation.
291    ///
292    /// # Examples
293    ///
294    /// ```
295    /// # use rialo_types::Nonce;
296    /// let vec = vec![1, 2, 3, 4];
297    /// let nonce = Nonce::from(vec.as_slice());
298    /// ```
299    fn from(nonce: &Vec<u8>) -> Self {
300        Self::from(nonce.as_slice())
301    }
302}
303
304impl From<&str> for Nonce {
305    /// Creates a Nonce from a string slice.
306    ///
307    /// This implementation converts the string to bytes and then applies
308    /// the standard padding/hashing rules. This is particularly useful for
309    /// creating deterministic nonces from human-readable identifiers.
310    ///
311    /// # Examples
312    ///
313    /// ```
314    /// # use rialo_types::Nonce;
315    /// let nonce = Nonce::from("account:user123");
316    /// ```
317    ///
318    /// # Note
319    ///
320    /// String-based nonces are convenient but be aware that:
321    /// - Different string encodings could produce different nonces
322    /// - Unicode strings may have unexpected byte representations
323    fn from(nonce: &str) -> Self {
324        Self::from(nonce.as_bytes())
325    }
326}
327
328impl From<String> for Nonce {
329    /// Creates a Nonce from an owned String.
330    ///
331    /// This implementation converts the String to bytes and applies the
332    /// same padding/hashing rules as the string slice implementation.
333    ///
334    /// # Examples
335    ///
336    /// ```
337    /// # use rialo_types::Nonce;
338    /// let nonce = Nonce::from(String::from("account:user123"));
339    /// ```
340    fn from(nonce: String) -> Self {
341        Self::from(nonce.as_str())
342    }
343}
344
345impl From<&String> for Nonce {
346    /// Creates a Nonce from a reference to a String.
347    ///
348    /// This implementation is similar to the owned String implementation,
349    /// converting the string to bytes and applying the standard rules.
350    ///
351    /// # Examples
352    ///
353    /// ```
354    /// # use rialo_types::Nonce;
355    /// let nonce = Nonce::from(&String::from("account:user123"));
356    /// ```
357    fn from(nonce: &String) -> Self {
358        Self::from(nonce.as_str())
359    }
360}
361
362impl From<u64> for Nonce {
363    /// Creates a Nonce from a u64 integer.
364    ///
365    /// The integer is converted to bytes in little-endian format before being
366    /// padded to `NONCE_LEN` bytes. This is useful for creating sequential or indexed nonces.
367    ///
368    /// # Examples
369    ///
370    /// ```
371    /// # use rialo_types::Nonce;
372    /// // Create sequential nonces
373    /// let nonce1 = Nonce::from(1u64);
374    /// let nonce2 = Nonce::from(2u64);
375    /// ```
376    ///
377    /// # Note
378    ///
379    /// Since u64 is 8 bytes, the resulting nonce will always be padded rather than hashed.
380    /// The first 8 bytes will contain the little-endian representation of the integer,
381    /// and the remaining 24 bytes will be zeros.
382    fn from(nonce: u64) -> Self {
383        Self::from(nonce.to_le_bytes())
384    }
385}
386
387impl fmt::Display for Nonce {
388    /// Formats the Nonce as a hexadecimal string.
389    ///
390    /// This implementation converts the `NONCE_LEN`-byte nonce to a 64-character
391    /// hexadecimal string representation, which is useful for logging,
392    /// debugging, and serialization to human-readable formats.
393    ///
394    /// # Examples
395    ///
396    /// ```
397    /// # use rialo_types::Nonce;
398    /// let nonce = Nonce::from([1u8; 32]);
399    /// let hex_string = nonce.to_string();
400    /// println!("Nonce: {}", nonce); // Prints the hex representation
401    /// ```
402    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403        write!(f, "{}", hex::encode(self.0))
404    }
405}