snarkvm_circuit_program/data/identifier/
mod.rs#[cfg(test)]
use snarkvm_circuit_types::environment::assert_scope;
mod equal;
mod from_bits;
mod from_field;
mod size_in_bits;
mod to_bits;
mod to_field;
use snarkvm_circuit_network::Aleo;
use snarkvm_circuit_types::{Boolean, Field, U8, environment::prelude::*};
use snarkvm_utilities::ToBits as TB;
#[derive(Clone)]
pub struct Identifier<A: Aleo>(Field<A>, u8); #[cfg(feature = "console")]
impl<A: Aleo> Inject for Identifier<A> {
type Primitive = console::Identifier<A::Network>;
fn new(_: Mode, identifier: Self::Primitive) -> Self {
let identifier = identifier.to_string();
let field = Field::from_bits_le(&Vec::<Boolean<_>>::constant(identifier.to_bits_le()));
Self(field, identifier.len() as u8)
}
}
#[cfg(feature = "console")]
impl<A: Aleo> Eject for Identifier<A> {
type Primitive = console::Identifier<A::Network>;
fn eject_mode(&self) -> Mode {
debug_assert!(self.0.eject_mode() == Mode::Constant, "Identifier::eject_mode - Mode must be 'Constant'");
Mode::Constant
}
fn eject_value(&self) -> Self::Primitive {
match console::FromField::from_field(&self.0.eject_value()) {
Ok(identifier) => identifier,
Err(error) => A::halt(format!("Failed to convert an identifier to a string: {error}")),
}
}
}
#[cfg(feature = "console")]
impl<A: Aleo> Parser for Identifier<A> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
let (string, identifier) = console::Identifier::parse(string)?;
Ok((string, Identifier::constant(identifier)))
}
}
#[cfg(feature = "console")]
impl<A: Aleo> FromStr for Identifier<A> {
type Err = Error;
#[inline]
fn from_str(string: &str) -> Result<Self> {
match Self::parse(string) {
Ok((remainder, object)) => {
ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
Ok(object)
}
Err(error) => bail!("Failed to parse string. {error}"),
}
}
}
#[cfg(feature = "console")]
impl<A: Aleo> Debug for Identifier<A> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
#[cfg(feature = "console")]
impl<A: Aleo> Display for Identifier<A> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.eject_value())
}
}
impl<A: Aleo> Eq for Identifier<A> {}
impl<A: Aleo> PartialEq for Identifier<A> {
fn eq(&self, other: &Self) -> bool {
self.0.eject_value() == other.0.eject_value()
}
}
impl<A: Aleo> core::hash::Hash for Identifier<A> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.0.eject_value().hash(state);
}
}
impl<A: Aleo> From<Identifier<A>> for LinearCombination<A::BaseField> {
fn from(identifier: Identifier<A>) -> Self {
From::from(&identifier)
}
}
impl<A: Aleo> From<&Identifier<A>> for LinearCombination<A::BaseField> {
fn from(identifier: &Identifier<A>) -> Self {
LinearCombination::from(&identifier.0)
}
}
#[cfg(all(test, feature = "console"))]
pub(crate) mod tests {
use super::*;
use crate::Circuit;
use console::{Rng, TestRng};
use anyhow::{Result, bail};
use core::str::FromStr;
use rand::distributions::Alphanumeric;
pub(crate) fn sample_console_identifier<A: Aleo>() -> Result<console::Identifier<A::Network>> {
let string = sample_console_identifier_as_string::<A>()?;
console::Identifier::from_str(&string)
}
pub(crate) fn sample_console_identifier_as_string<A: Aleo>() -> Result<String> {
let rng = &mut TestRng::default();
let string = "a".to_string()
+ &rng
.sample_iter(&Alphanumeric)
.take(A::BaseField::size_in_data_bits() / (8 * 2))
.map(char::from)
.collect::<String>();
let max_bytes = A::BaseField::size_in_data_bits() / 8; match string.len() <= max_bytes {
true => Ok(string),
false => bail!("Identifier exceeds the maximum capacity allowed"),
}
}
pub(crate) fn sample_lowercase_console_identifier_as_string<A: Aleo>() -> Result<String> {
let string = sample_console_identifier_as_string::<A>()?;
Ok(string.to_lowercase())
}
#[test]
fn test_identifier_parse() -> Result<()> {
let candidate = Identifier::<Circuit>::parse("foo_bar").unwrap();
assert_eq!("", candidate.0);
assert_eq!(Identifier::<Circuit>::constant("foo_bar".try_into()?).eject(), candidate.1.eject());
Ok(())
}
#[test]
fn test_identifier_parse_fails() -> Result<()> {
let identifier = Identifier::<Circuit>::parse("foo_bar~baz").unwrap();
assert_eq!(("~baz", Identifier::<Circuit>::from_str("foo_bar")?.eject()), (identifier.0, identifier.1.eject()));
let identifier = Identifier::<Circuit>::parse("foo_bar-baz").unwrap();
assert_eq!(("-baz", Identifier::<Circuit>::from_str("foo_bar")?.eject()), (identifier.0, identifier.1.eject()));
assert!(Identifier::<Circuit>::parse("_").is_err());
assert!(Identifier::<Circuit>::parse("__").is_err());
assert!(Identifier::<Circuit>::parse("___").is_err());
assert!(Identifier::<Circuit>::parse("____").is_err());
assert!(Identifier::<Circuit>::parse("1").is_err());
assert!(Identifier::<Circuit>::parse("2").is_err());
assert!(Identifier::<Circuit>::parse("3").is_err());
assert!(Identifier::<Circuit>::parse("1foo").is_err());
assert!(Identifier::<Circuit>::parse("12").is_err());
assert!(Identifier::<Circuit>::parse("111").is_err());
let identifier =
Identifier::<Circuit>::parse("foo_bar_baz_qux_quux_quuz_corge_grault_garply_waldo_fred_plugh_xyzzy");
assert!(identifier.is_err());
Ok(())
}
#[test]
fn test_identifier_display() -> Result<()> {
let identifier = Identifier::<Circuit>::from_str("foo_bar")?;
assert_eq!("foo_bar", format!("{identifier}"));
Ok(())
}
#[test]
fn test_identifier_bits() -> Result<()> {
let identifier = Identifier::<Circuit>::from_str("foo_bar")?;
assert_eq!(
identifier.to_bits_le().eject(),
Identifier::from_bits_le(&identifier.to_bits_le()).to_bits_le().eject()
);
assert_eq!(
identifier.to_bits_be().eject(),
Identifier::from_bits_be(&identifier.to_bits_be()).to_bits_be().eject()
);
Ok(())
}
}