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}