mod.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the alphavm library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 mod bytes; 17 mod equal; 18 mod from_bits; 19 mod from_field; 20 mod parse; 21 mod serialize; 22 mod size_in_bits; 23 mod to_bits; 24 mod to_field; 25 26 use alphavm_console_network::Network; 27 use alphavm_console_types::{Field, prelude::*}; 28 29 /// An identifier is an **immutable** UTF-8 string, 30 /// represented as a **constant** field element in the CurrentNetwork. 31 /// 32 /// # Requirements 33 /// The identifier must not be an empty string. 34 /// The identifier must not start with a number. 35 /// The identifier must be alphanumeric, and may include underscores. 36 /// The identifier must not consist solely of underscores. 37 /// The identifier must fit within the data capacity of a base field element. 38 #[derive(Copy, Clone)] 39 pub struct Identifier<N: Network>(Field<N>, u8); // Number of bytes in the identifier. 40 41 impl<N: Network> From<&Identifier<N>> for Identifier<N> { 42 /// Returns a copy of the identifier. 43 fn from(identifier: &Identifier<N>) -> Self { 44 *identifier 45 } 46 } 47 48 impl<N: Network> TryFrom<String> for Identifier<N> { 49 type Error = Error; 50 51 /// Initializes an identifier from a string. 52 fn try_from(identifier: String) -> Result<Self> { 53 Self::from_str(&identifier) 54 } 55 } 56 57 impl<N: Network> TryFrom<&String> for Identifier<N> { 58 type Error = Error; 59 60 /// Initializes an identifier from a string. 61 fn try_from(identifier: &String) -> Result<Self> { 62 Self::from_str(identifier) 63 } 64 } 65 66 impl<N: Network> TryFrom<&str> for Identifier<N> { 67 type Error = Error; 68 69 /// Initializes an identifier from a string. 70 fn try_from(identifier: &str) -> Result<Self> { 71 Self::from_str(identifier) 72 } 73 } 74 75 #[cfg(test)] 76 pub(crate) mod tests { 77 use super::*; 78 use alphavm_console_network::MainnetV0; 79 80 type CurrentNetwork = MainnetV0; 81 82 const ITERATIONS: usize = 100; 83 84 /// Samples a random identifier. 85 pub(crate) fn sample_identifier<N: Network>(rng: &mut TestRng) -> Result<Identifier<N>> { 86 // Sample a random fixed-length alphanumeric string, that always starts with an alphabetic character. 87 let string = sample_identifier_as_string::<N>(rng)?; 88 // Recover the field element from the bits. 89 let field = Field::<N>::from_bits_le(&string.as_bytes().to_bits_le())?; 90 // Return the identifier. 91 Ok(Identifier(field, u8::try_from(string.len()).or_halt_with::<CurrentNetwork>("Invalid identifier length"))) 92 } 93 94 /// Samples a random identifier as a string. 95 pub(crate) fn sample_identifier_as_string<N: Network>(rng: &mut TestRng) -> Result<String> { 96 // Sample a random fixed-length alphanumeric string, that always starts with an alphabetic character. 97 let string = "a".to_string() 98 + &rng 99 .sample_iter(&Alphanumeric) 100 .take(Field::<N>::size_in_data_bits() / (8 * 2)) 101 .map(char::from) 102 .collect::<String>(); 103 // Ensure identifier fits within the data capacity of the base field. 104 let max_bytes = Field::<N>::size_in_data_bits() / 8; // Note: This intentionally rounds down. 105 match string.len() <= max_bytes { 106 // Return the identifier. 107 true => Ok(string), 108 false => bail!("Identifier exceeds the maximum capacity allowed"), 109 } 110 } 111 112 /// Samples a random lowercase identifier as a string. 113 pub(crate) fn sample_lowercase_identifier_as_string<N: Network>(rng: &mut TestRng) -> Result<String> { 114 // Sample a random identifier. 115 let string = sample_identifier_as_string::<N>(rng)?; 116 // Return the identifier as lowercase. 117 Ok(string.to_lowercase()) 118 } 119 120 #[test] 121 fn test_try_from() -> Result<()> { 122 let mut rng = TestRng::default(); 123 124 for _ in 0..ITERATIONS { 125 // Sample a random fixed-length alphanumeric string, that always starts with an alphabetic character. 126 let expected_string = sample_identifier_as_string::<CurrentNetwork>(&mut rng)?; 127 // Recover the field element from the bits. 128 let expected_field = Field::<CurrentNetwork>::from_bits_le(&expected_string.as_bytes().to_bits_le())?; 129 130 // Try to initialize an identifier from the string. 131 let candidate = Identifier::<CurrentNetwork>::try_from(expected_string.as_str())?; 132 assert_eq!(expected_field, candidate.0); 133 assert_eq!(expected_string.len(), candidate.1 as usize); 134 } 135 Ok(()) 136 } 137 138 #[test] 139 fn test_identifier_try_from_illegal() { 140 assert!(Identifier::<CurrentNetwork>::try_from("123").is_err()); 141 assert!(Identifier::<CurrentNetwork>::try_from("abc\x08def").is_err()); 142 assert!(Identifier::<CurrentNetwork>::try_from("abc\u{202a}def").is_err()); 143 } 144 }