/ circuit / program / src / data / identifier / mod.rs
mod.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the deltavm 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  #[cfg(test)]
 17  use deltavm_circuit_types::environment::assert_scope;
 18  
 19  mod equal;
 20  mod from_bits;
 21  mod from_field;
 22  mod size_in_bits;
 23  mod to_bits;
 24  mod to_field;
 25  
 26  use deltavm_circuit_network::Alpha;
 27  use deltavm_circuit_types::{Boolean, Field, U8, environment::prelude::*};
 28  use deltavm_utilities::ToBits as TB;
 29  
 30  /// An identifier is an **immutable** UTF-8 string,
 31  /// represented as a **constant** field element in the circuit.
 32  ///
 33  /// # Requirements
 34  /// The identifier must not be an empty string.
 35  /// The identifier must not start with a number.
 36  /// The identifier must be alphanumeric, and may include underscores.
 37  /// The identifier must not consist solely of underscores.
 38  /// The identifier must fit within the data capacity of a base field element.
 39  #[derive(Clone)]
 40  pub struct Identifier<A: Alpha>(Field<A>, u8); // Number of bytes in the identifier.
 41  
 42  impl<A: Alpha> Inject for Identifier<A> {
 43      type Primitive = console::Identifier<A::Network>;
 44  
 45      /// Initializes a new identifier from a string.
 46      /// Note: Identifiers are always `Mode::Constant`.
 47      fn new(_: Mode, identifier: Self::Primitive) -> Self {
 48          // Convert the identifier to a string to check its validity.
 49          let identifier = identifier.to_string();
 50  
 51          // Note: The string bytes themselves are **not** little-endian. Rather, they are order-preserving
 52          // for reconstructing the string when recovering the field element back into bytes.
 53          let field = Field::from_bits_le(&Vec::<Boolean<_>>::constant(identifier.to_bits_le()));
 54  
 55          // Return the identifier.
 56          Self(field, identifier.len() as u8)
 57      }
 58  }
 59  
 60  impl<A: Alpha> Eject for Identifier<A> {
 61      type Primitive = console::Identifier<A::Network>;
 62  
 63      /// Ejects the mode of the identifier.
 64      fn eject_mode(&self) -> Mode {
 65          debug_assert!(self.0.eject_mode() == Mode::Constant, "Identifier::eject_mode - Mode must be 'Constant'");
 66          Mode::Constant
 67      }
 68  
 69      /// Ejects the identifier as a string.
 70      fn eject_value(&self) -> Self::Primitive {
 71          match console::FromField::from_field(&self.0.eject_value()) {
 72              Ok(identifier) => identifier,
 73              Err(error) => A::halt(format!("Failed to convert an identifier to a string: {error}")),
 74          }
 75      }
 76  }
 77  
 78  impl<A: Alpha> Parser for Identifier<A> {
 79      /// Parses a UTF-8 string into an identifier.
 80      #[inline]
 81      fn parse(string: &str) -> ParserResult<Self> {
 82          // Parse the identifier from the string.
 83          let (string, identifier) = console::Identifier::parse(string)?;
 84  
 85          Ok((string, Identifier::constant(identifier)))
 86      }
 87  }
 88  
 89  impl<A: Alpha> FromStr for Identifier<A> {
 90      type Err = Error;
 91  
 92      /// Parses a UTF-8 string into an identifier.
 93      #[inline]
 94      fn from_str(string: &str) -> Result<Self> {
 95          match Self::parse(string) {
 96              Ok((remainder, object)) => {
 97                  // Ensure the remainder is empty.
 98                  ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
 99                  // Return the object.
100                  Ok(object)
101              }
102              Err(error) => bail!("Failed to parse string. {error}"),
103          }
104      }
105  }
106  
107  impl<A: Alpha> Debug for Identifier<A> {
108      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
109          Display::fmt(self, f)
110      }
111  }
112  
113  impl<A: Alpha> Display for Identifier<A> {
114      /// Prints the identifier as a string.
115      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
116          write!(f, "{}", self.eject_value())
117      }
118  }
119  
120  impl<A: Alpha> Eq for Identifier<A> {}
121  
122  impl<A: Alpha> PartialEq for Identifier<A> {
123      /// Implements the `Eq` trait for the identifier.
124      fn eq(&self, other: &Self) -> bool {
125          self.0.eject_value() == other.0.eject_value()
126      }
127  }
128  
129  impl<A: Alpha> core::hash::Hash for Identifier<A> {
130      /// Implements the `Hash` trait for the identifier.
131      fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
132          self.0.eject_value().hash(state);
133      }
134  }
135  
136  impl<A: Alpha> From<Identifier<A>> for LinearCombination<A::BaseField> {
137      /// Note: Identifier is always `Mode::Constant`.
138      fn from(identifier: Identifier<A>) -> Self {
139          From::from(&identifier)
140      }
141  }
142  
143  impl<A: Alpha> From<&Identifier<A>> for LinearCombination<A::BaseField> {
144      /// Note: Identifier is always `Mode::Constant`.
145      fn from(identifier: &Identifier<A>) -> Self {
146          LinearCombination::from(&identifier.0)
147      }
148  }
149  
150  #[cfg(test)]
151  pub(crate) mod tests {
152      use super::*;
153      use crate::Circuit;
154      use console::{Rng, TestRng};
155  
156      use anyhow::{Result, bail};
157      use core::str::FromStr;
158      use rand::distributions::Alphanumeric;
159  
160      /// Samples a random identifier.
161      pub(crate) fn sample_console_identifier<A: Alpha>() -> Result<console::Identifier<A::Network>> {
162          // Sample a random fixed-length alphanumeric string, that always starts with an alphabetic character.
163          let string = sample_console_identifier_as_string::<A>()?;
164          // Return the identifier.
165          console::Identifier::from_str(&string)
166      }
167  
168      /// Samples a random identifier as a string.
169      pub(crate) fn sample_console_identifier_as_string<A: Alpha>() -> Result<String> {
170          // Initialize a test RNG.
171          let rng = &mut TestRng::default();
172          // Sample a random fixed-length alphanumeric string, that always starts with an alphabetic character.
173          let string = "a".to_string()
174              + &rng
175                  .sample_iter(&Alphanumeric)
176                  .take(A::BaseField::size_in_data_bits() / (8 * 2))
177                  .map(char::from)
178                  .collect::<String>();
179          // Ensure identifier fits within the data capacity of the base field.
180          let max_bytes = A::BaseField::size_in_data_bits() / 8; // Note: This intentionally rounds down.
181          match string.len() <= max_bytes {
182              // Return the identifier.
183              true => Ok(string),
184              false => bail!("Identifier exceeds the maximum capacity allowed"),
185          }
186      }
187  
188      /// Samples a random identifier as a string.
189      pub(crate) fn sample_lowercase_console_identifier_as_string<A: Alpha>() -> Result<String> {
190          // Sample a random identifier.
191          let string = sample_console_identifier_as_string::<A>()?;
192          // Return the identifier as lowercase.
193          Ok(string.to_lowercase())
194      }
195  
196      #[test]
197      fn test_identifier_parse() -> Result<()> {
198          let candidate = Identifier::<Circuit>::parse("foo_bar").unwrap();
199          assert_eq!("", candidate.0);
200          assert_eq!(Identifier::<Circuit>::constant("foo_bar".try_into()?).eject(), candidate.1.eject());
201          Ok(())
202      }
203  
204      #[test]
205      fn test_identifier_parse_fails() -> Result<()> {
206          // Must be alphanumeric or underscore.
207          let identifier = Identifier::<Circuit>::parse("foo_bar~baz").unwrap();
208          assert_eq!(("~baz", Identifier::<Circuit>::from_str("foo_bar")?.eject()), (identifier.0, identifier.1.eject()));
209          let identifier = Identifier::<Circuit>::parse("foo_bar-baz").unwrap();
210          assert_eq!(("-baz", Identifier::<Circuit>::from_str("foo_bar")?.eject()), (identifier.0, identifier.1.eject()));
211  
212          // Must not be solely underscores.
213          assert!(Identifier::<Circuit>::parse("_").is_err());
214          assert!(Identifier::<Circuit>::parse("__").is_err());
215          assert!(Identifier::<Circuit>::parse("___").is_err());
216          assert!(Identifier::<Circuit>::parse("____").is_err());
217  
218          // Must not start with a number.
219          assert!(Identifier::<Circuit>::parse("1").is_err());
220          assert!(Identifier::<Circuit>::parse("2").is_err());
221          assert!(Identifier::<Circuit>::parse("3").is_err());
222          assert!(Identifier::<Circuit>::parse("1foo").is_err());
223          assert!(Identifier::<Circuit>::parse("12").is_err());
224          assert!(Identifier::<Circuit>::parse("111").is_err());
225  
226          // Must fit within the data capacity of a base field element.
227          let identifier =
228              Identifier::<Circuit>::parse("foo_bar_baz_qux_quux_quuz_corge_grault_garply_waldo_fred_plugh_xyzzy");
229          assert!(identifier.is_err());
230          Ok(())
231      }
232  
233      #[test]
234      fn test_identifier_display() -> Result<()> {
235          let identifier = Identifier::<Circuit>::from_str("foo_bar")?;
236          assert_eq!("foo_bar", format!("{identifier}"));
237          Ok(())
238      }
239  
240      #[test]
241      fn test_identifier_bits() -> Result<()> {
242          let identifier = Identifier::<Circuit>::from_str("foo_bar")?;
243          assert_eq!(
244              identifier.to_bits_le().eject(),
245              Identifier::from_bits_le(&identifier.to_bits_le()).to_bits_le().eject()
246          );
247          assert_eq!(
248              identifier.to_bits_be().eject(),
249              Identifier::from_bits_be(&identifier.to_bits_be()).to_bits_be().eject()
250          );
251          Ok(())
252      }
253  }