mod equal;
mod helpers;
use snarkvm_circuit_environment::prelude::*;
use snarkvm_circuit_types_boolean::Boolean;
use snarkvm_circuit_types_field::Field;
use snarkvm_circuit_types_integers::U8;
#[cfg(test)]
use snarkvm_circuit_environment::{assert_scope, assert_scope_fails};
use console::{SIZE_IN_BITS, SIZE_IN_BYTES};
#[derive(Clone)]
pub struct IdentifierLiteral<E: Environment> {
bytes: [U8<E>; SIZE_IN_BYTES],
}
impl<E: Environment> IdentifierLiteral<E> {
pub const fn size_in_bits() -> usize {
SIZE_IN_BITS
}
fn from_bytes(bytes: [U8<E>; SIZE_IN_BYTES]) -> Self {
validate_identifier_bytes::<E>(&bytes);
Self { bytes }
}
}
impl<E: Environment> Inject for IdentifierLiteral<E> {
type Primitive = console::IdentifierLiteral<E::Network>;
fn new(mode: Mode, value: Self::Primitive) -> Self {
let raw_bytes = value.bytes();
let bytes: [U8<E>; SIZE_IN_BYTES] = std::array::from_fn(|i| U8::new(mode, console::Integer::new(raw_bytes[i])));
Self::from_bytes(bytes)
}
}
impl<E: Environment> Eject for IdentifierLiteral<E> {
type Primitive = console::IdentifierLiteral<E::Network>;
fn eject_mode(&self) -> Mode {
self.bytes.eject_mode()
}
fn eject_value(&self) -> Self::Primitive {
let mut raw_bytes = [0u8; SIZE_IN_BYTES];
for (i, byte) in self.bytes.iter().enumerate() {
raw_bytes[i] = *byte.eject_value();
}
console::IdentifierLiteral::from_bytes_array(raw_bytes).expect("Failed to eject identifier literal")
}
}
impl<E: Environment> Parser for IdentifierLiteral<E> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
let (string, content) = console::IdentifierLiteral::parse(string)?;
let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?;
match mode {
Some((_, mode)) => Ok((string, IdentifierLiteral::new(mode, content))),
None => Ok((string, IdentifierLiteral::new(Mode::Constant, content))),
}
}
}
impl<E: Environment> FromStr for IdentifierLiteral<E> {
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}"),
}
}
}
impl<E: Environment> TypeName for IdentifierLiteral<E> {
#[inline]
fn type_name() -> &'static str {
console::IdentifierLiteral::<E::Network>::type_name()
}
}
impl<E: Environment> Debug for IdentifierLiteral<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<E: Environment> Display for IdentifierLiteral<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}.{}", self.eject_value(), self.eject_mode())
}
}
fn validate_identifier_bytes<E: Environment>(bytes: &[U8<E>; SIZE_IN_BYTES]) {
let mut bits = Vec::with_capacity(SIZE_IN_BITS);
for byte in bytes.iter() {
byte.write_bits_le(&mut bits);
}
validate_identifier_bits::<E>(&bits);
}
fn validate_identifier_bits<E: Environment>(bits: &[Boolean<E>]) {
assert_eq!(
bits.len(),
SIZE_IN_BITS,
"validate_identifier_bits requires exactly {SIZE_IN_BITS} bits, got {}",
bits.len()
);
let mut null_flags: Vec<Boolean<E>> = Vec::with_capacity(SIZE_IN_BYTES);
for byte_idx in 0..SIZE_IN_BYTES {
let offset = byte_idx * 8;
let byte_bits = <&[Boolean<E>; 8]>::try_from(&bits[offset..offset + 8]).expect("slice length is exactly 8");
let null_flag = validate_byte::<E>(byte_bits, byte_idx == 0);
null_flags.push(null_flag);
}
validate_trailing_nulls::<E>(&null_flags);
}
struct ByteValidationData<E: Environment> {
b4b3: Boolean<E>,
b1b0: Boolean<E>,
all_zero_5: Boolean<E>,
}
impl<E: Environment> ByteValidationData<E> {
fn new(b0: &Boolean<E>, b1: &Boolean<E>, b2: &Boolean<E>, b3: &Boolean<E>, b4: &Boolean<E>) -> Self {
let b4b3 = b4 & b3;
let b1b0 = b1 & b0;
let sum = Field::from_boolean(b0)
+ Field::from_boolean(b1)
+ Field::from_boolean(b2)
+ Field::from_boolean(b3)
+ Field::from_boolean(b4);
let all_zero_5 = sum.is_equal(&Field::zero());
Self { b4b3, b1b0, all_zero_5 }
}
}
fn is_invalid_digit_offset<E: Environment>(b1: &Boolean<E>, b2: &Boolean<E>, b3: &Boolean<E>) -> Boolean<E> {
let b2_or_b1 = b2 | b1;
b3 & &b2_or_b1
}
fn is_invalid_uppercase_offset<E: Environment>(data: &ByteValidationData<E>, b2: &Boolean<E>) -> Boolean<E> {
let xor_b2_b1b0 = b2 ^ &data.b1b0;
&data.b4b3 & &xor_b2_b1b0
}
fn is_invalid_lowercase_offset<E: Environment>(data: &ByteValidationData<E>, b2: &Boolean<E>) -> Boolean<E> {
let b2_or_b1b0 = b2 | &data.b1b0;
&data.b4b3 & &b2_or_b1b0
}
fn validate_byte<E: Environment>(bits: &[Boolean<E>; 8], is_first_byte: bool) -> Boolean<E> {
let [b0, b1, b2, b3, b4, b5, b6, b7] = bits;
E::assert_eq(b7, Boolean::<E>::constant(false)).expect("Identifier literal high bit must be zero");
let data = ByteValidationData::new(b0, b1, b2, b3, b4);
let null = &b6.not() & &b5.not();
let lower = b6 & b5;
let upper_lc = E::zero() + &**b6 - &*lower; let digit_lc = E::zero() + &**b5 - &*lower;
let any_low5 = data.all_zero_5.clone().not();
E::enforce(|| (&null, &any_low5, E::zero())).expect("Identifier literal null byte violation");
let invalid_digit = is_invalid_digit_offset::<E>(b1, b2, b3);
E::enforce(|| (digit_lc, E::one() - &**b4 + &*invalid_digit, E::zero()))
.expect("Identifier literal digit validation");
let bad_upper = is_invalid_uppercase_offset::<E>(&data, b2);
E::enforce(|| (upper_lc, &*data.all_zero_5 + &*bad_upper, E::zero()))
.expect("Identifier literal uppercase validation");
let bad_lower = is_invalid_lowercase_offset::<E>(&data, b2);
E::enforce(|| (&lower, &*data.all_zero_5 + &*bad_lower, E::zero()))
.expect("Identifier literal lowercase validation");
if is_first_byte {
let partial = &data.b4b3 & &data.b1b0;
let is_underscore_offset = &partial & b2;
let is_letter = b6 & &is_underscore_offset.not();
E::assert(is_letter).expect("Identifier literal must start with a letter");
}
null
}
fn validate_trailing_nulls<E: Environment>(null_flags: &[Boolean<E>]) {
for i in 1..null_flags.len() {
let not_null = null_flags[i].clone().not();
E::enforce(|| (&null_flags[i - 1], ¬_null, E::zero())).expect("Identifier literal trailing null violation");
}
}
#[cfg(test)]
mod tests {
use super::*;
use snarkvm_circuit_environment::Circuit;
use snarkvm_utilities::{TestRng, Uniform};
type CurrentEnvironment = Circuit;
const ITERATIONS: usize = 10;
fn check_new(
mode: Mode,
num_constants: u64,
num_public: u64,
num_private: u64,
num_constraints: u64,
) -> Result<()> {
let mut rng = TestRng::default();
for _ in 0..ITERATIONS {
let expected = console::IdentifierLiteral::<<CurrentEnvironment as Environment>::Network>::rand(&mut rng);
Circuit::scope(format!("new {mode}"), || {
let candidate = IdentifierLiteral::<CurrentEnvironment>::new(mode, expected);
assert_eq!(expected, candidate.eject_value());
assert_eq!(mode, candidate.eject_mode());
assert_scope!(num_constants, num_public, num_private, num_constraints);
});
Circuit::reset();
}
Ok(())
}
#[test]
fn test_new_constant() -> Result<()> {
check_new(Mode::Constant, 279, 0, 0, 0)
}
#[test]
fn test_new_public() -> Result<()> {
check_new(Mode::Public, 0, 248, 375, 809)
}
#[test]
fn test_new_private() -> Result<()> {
check_new(Mode::Private, 0, 0, 623, 809)
}
#[test]
fn test_new_max_length_identifier() -> Result<()> {
let max_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde";
assert_eq!(max_str.len(), SIZE_IN_BYTES);
let expected =
console::IdentifierLiteral::<<CurrentEnvironment as Environment>::Network>::new(max_str).unwrap();
Circuit::scope("new max length", || {
let candidate = IdentifierLiteral::<CurrentEnvironment>::new(Mode::Private, expected);
assert_eq!(expected, candidate.eject_value());
assert_scope!(0, 0, 623, 809);
});
Circuit::reset();
Ok(())
}
#[test]
fn test_non_trailing_null_unsatisfied() {
use console::FromBytes;
let mut bad_bytes = vec![0u8; 32];
bad_bytes[0] = b'a';
bad_bytes[2] = b'b';
let field_value = console::Field::<<CurrentEnvironment as Environment>::Network>::from_bytes_le(&bad_bytes)
.expect("Failed to construct field from bytes");
Circuit::scope("test_non_trailing_null", || {
let field = Field::<CurrentEnvironment>::new(Mode::Private, field_value);
let _candidate = IdentifierLiteral::<CurrentEnvironment>::from_field(field);
assert_scope_fails!(0, 0, 881, 1069);
});
assert!(!Circuit::is_satisfied());
Circuit::reset();
}
#[test]
fn test_first_char_must_be_letter() {
use console::FromBytes;
let mut digit_start_bytes = vec![0u8; 32];
digit_start_bytes[0] = b'1';
let field_value =
console::Field::<<CurrentEnvironment as Environment>::Network>::from_bytes_le(&digit_start_bytes)
.expect("Failed to construct field from bytes");
Circuit::scope("test_first_char_digit", || {
let field = Field::<CurrentEnvironment>::new(Mode::Private, field_value);
let _candidate = IdentifierLiteral::<CurrentEnvironment>::from_field(field);
assert_scope_fails!(0, 0, 881, 1069);
});
assert!(!Circuit::is_satisfied());
Circuit::reset();
let mut underscore_start_bytes = vec![0u8; 32];
underscore_start_bytes[0] = b'_';
let field_value =
console::Field::<<CurrentEnvironment as Environment>::Network>::from_bytes_le(&underscore_start_bytes)
.expect("Failed to construct field from bytes");
Circuit::scope("test_first_char_underscore", || {
let field = Field::<CurrentEnvironment>::new(Mode::Private, field_value);
let _candidate = IdentifierLiteral::<CurrentEnvironment>::from_field(field);
assert_scope_fails!(0, 0, 881, 1069);
});
assert!(!Circuit::is_satisfied());
Circuit::reset();
}
#[test]
fn test_size_constants() {
assert_eq!(SIZE_IN_BITS, 248);
assert_eq!(SIZE_IN_BYTES, 31);
assert_eq!(SIZE_IN_BITS, SIZE_IN_BYTES * 8);
assert_eq!(IdentifierLiteral::<CurrentEnvironment>::size_in_bits(), SIZE_IN_BITS);
assert_eq!(SIZE_IN_BYTES, console::SIZE_IN_BYTES);
assert_eq!(SIZE_IN_BITS, console::SIZE_IN_BITS);
assert!(SIZE_IN_BITS < <CurrentEnvironment as Environment>::BaseField::size_in_bits());
}
#[test]
fn test_ascii_character_validation() {
use console::FromBytes;
let mut first_valid = 0u32;
let mut first_invalid = 0u32;
for byte in 0u8..=255 {
let mut raw_bytes = vec![0u8; 32];
raw_bytes[0] = byte;
let field_value = console::Field::<<CurrentEnvironment as Environment>::Network>::from_bytes_le(&raw_bytes)
.expect("Failed to construct field");
let expected_valid = byte.is_ascii_alphabetic();
if expected_valid {
first_valid += 1
} else {
first_invalid += 1
}
Circuit::scope(format!("first_byte_{byte}"), || {
let field = Field::<CurrentEnvironment>::new(Mode::Private, field_value);
let _candidate = IdentifierLiteral::<CurrentEnvironment>::from_field(field);
if expected_valid { assert_scope!(0, 0, 881, 1069) } else { assert_scope_fails!(0, 0, 881, 1069) }
});
Circuit::reset();
}
assert_eq!(first_valid, 52, "Expected 52 valid first-byte values (a-z, A-Z)");
assert_eq!(first_invalid, 204, "Expected 204 invalid first-byte values");
let mut second_valid = 0u32;
let mut second_invalid = 0u32;
for byte in 0u8..=255 {
let mut raw_bytes = vec![0u8; 32];
raw_bytes[0] = b'a'; raw_bytes[1] = byte;
let field_value = console::Field::<<CurrentEnvironment as Environment>::Network>::from_bytes_le(&raw_bytes)
.expect("Failed to construct field");
let expected_valid = byte.is_ascii_alphanumeric() || byte == b'_' || byte == 0;
if expected_valid {
second_valid += 1
} else {
second_invalid += 1
}
Circuit::scope(format!("second_byte_{byte}"), || {
let field = Field::<CurrentEnvironment>::new(Mode::Private, field_value);
let _candidate = IdentifierLiteral::<CurrentEnvironment>::from_field(field);
if expected_valid { assert_scope!(0, 0, 881, 1069) } else { assert_scope_fails!(0, 0, 881, 1069) }
});
Circuit::reset();
}
assert_eq!(second_valid, 64, "Expected 64 valid second-byte values (a-z, A-Z, 0-9, _, null)");
assert_eq!(second_invalid, 192, "Expected 192 invalid second-byte values");
}
fn byte_to_bits(byte: u8, mode: Mode) -> [Boolean<CurrentEnvironment>; 8] {
std::array::from_fn(|i| Boolean::new(mode, (byte >> i) & 1 == 1))
}
fn validate_padding_bits(bits: &[Boolean<CurrentEnvironment>], data_bits: usize) {
for bit in bits.iter().skip(data_bits) {
CurrentEnvironment::assert_eq(bit, Boolean::<CurrentEnvironment>::constant(false))
.expect("Identifier literal padding bit must be zero");
}
}
fn check_validate_byte(byte: u8, is_first_byte: bool) -> bool {
Circuit::scope(format!("validate_byte_{byte}"), || {
let bits = byte_to_bits(byte, Mode::Private);
let _null_flag = validate_byte::<CurrentEnvironment>(&bits, is_first_byte);
});
let satisfied = Circuit::is_satisfied();
Circuit::reset();
satisfied
}
#[test]
fn test_validate_byte_constraint_counts() {
Circuit::scope("validate_byte_first", || {
let bits = byte_to_bits(b'a', Mode::Private);
let _null_flag = validate_byte::<CurrentEnvironment>(&bits, true);
assert_scope!(0, 0, 23, 29);
});
Circuit::reset();
Circuit::scope("validate_byte_non_first", || {
let bits = byte_to_bits(b'a', Mode::Private);
let _null_flag = validate_byte::<CurrentEnvironment>(&bits, false);
assert_scope!(0, 0, 20, 25);
});
Circuit::reset();
}
#[test]
fn test_validate_trailing_nulls_constraint_counts() {
Circuit::scope("trailing_nulls_3", || {
let null_flags: Vec<Boolean<CurrentEnvironment>> =
[false, false, true].iter().map(|&b| Boolean::new(Mode::Private, b)).collect();
validate_trailing_nulls::<CurrentEnvironment>(&null_flags);
assert_scope!(0, 0, 3, 5);
});
Circuit::reset();
Circuit::scope("trailing_nulls_31", || {
let null_flags: Vec<Boolean<CurrentEnvironment>> =
(0..31).map(|_| Boolean::new(Mode::Private, false)).collect();
validate_trailing_nulls::<CurrentEnvironment>(&null_flags);
assert_scope!(0, 0, 31, 61);
});
Circuit::reset();
}
#[test]
fn test_validate_padding_bits_constraint_counts() {
Circuit::scope("padding_2_bits", || {
let bits: Vec<Boolean<CurrentEnvironment>> = (0..10).map(|_| Boolean::new(Mode::Private, false)).collect();
validate_padding_bits(&bits, 8);
assert_scope!(0, 0, 10, 12);
});
Circuit::reset();
}
#[test]
fn test_validate_identifier_bits_constraint_counts() {
Circuit::scope("validate_identifier_bits", || {
let mut bits: Vec<Boolean<CurrentEnvironment>> = Vec::with_capacity(SIZE_IN_BITS);
bits.extend(byte_to_bits(b'a', Mode::Private));
for _ in 1..SIZE_IN_BYTES {
bits.extend(byte_to_bits(0x00, Mode::Private));
}
validate_identifier_bits::<CurrentEnvironment>(&bits);
assert_scope!(0, 0, 623, 809);
});
assert!(Circuit::is_satisfied());
Circuit::reset();
}
#[test]
fn test_validate_byte_lowercase_a_to_z() {
for ch in b'a'..=b'z' {
assert!(check_validate_byte(ch, true), "Lowercase {ch} should be valid as first byte");
assert!(check_validate_byte(ch, false), "Lowercase {ch} should be valid as non-first byte");
}
}
#[test]
fn test_validate_byte_uppercase_a_to_z() {
for ch in b'A'..=b'Z' {
assert!(check_validate_byte(ch, true), "Uppercase {ch} should be valid as first byte");
assert!(check_validate_byte(ch, false), "Uppercase {ch} should be valid as non-first byte");
}
}
#[test]
fn test_validate_byte_digits_0_to_9() {
for ch in b'0'..=b'9' {
assert!(!check_validate_byte(ch, true), "Digit {ch} should be invalid as first byte");
assert!(check_validate_byte(ch, false), "Digit {ch} should be valid as non-first byte");
}
}
#[test]
fn test_validate_byte_underscore() {
assert!(!check_validate_byte(b'_', true), "Underscore should be invalid as first byte");
assert!(check_validate_byte(b'_', false), "Underscore should be valid as non-first byte");
}
#[test]
fn test_validate_byte_null() {
assert!(!check_validate_byte(0x00, true), "Null should be invalid as first byte");
assert!(check_validate_byte(0x00, false), "Null should be valid as non-first byte");
}
#[test]
fn test_validate_byte_invalid_chars() {
let invalid_chars = [b' ', b'!', b'@', b'#', b'$', b'%', b'^', b'&', b'*', b'(', b')', b'-', b'+', b'='];
for ch in invalid_chars {
assert!(!check_validate_byte(ch, true), "Char {ch} should be invalid as first byte");
assert!(!check_validate_byte(ch, false), "Char {ch} should be invalid as non-first byte");
}
}
#[test]
fn test_validate_byte_non_ascii() {
for ch in 128u8..=255 {
assert!(!check_validate_byte(ch, true), "Non-ASCII {ch} should be invalid as first byte");
assert!(!check_validate_byte(ch, false), "Non-ASCII {ch} should be invalid as non-first byte");
}
}
#[test]
fn test_validate_trailing_nulls_valid() {
let patterns: &[&[bool]] = &[
&[false, false, false], &[false, false, true], &[false, true, true], &[true, true, true], ];
for pattern in patterns {
Circuit::scope("trailing_nulls_valid", || {
let null_flags: Vec<Boolean<CurrentEnvironment>> =
pattern.iter().map(|&b| Boolean::new(Mode::Private, b)).collect();
validate_trailing_nulls::<CurrentEnvironment>(&null_flags);
assert_scope!(0, 0, 3, 5);
});
assert!(Circuit::is_satisfied(), "Pattern {pattern:?} should be valid");
Circuit::reset();
}
}
#[test]
fn test_validate_trailing_nulls_invalid() {
let patterns: &[&[bool]] = &[
&[true, false, false], &[false, true, false], &[true, false, true], &[true, true, false], ];
for pattern in patterns {
Circuit::scope("trailing_nulls_invalid", || {
let null_flags: Vec<Boolean<CurrentEnvironment>> =
pattern.iter().map(|&b| Boolean::new(Mode::Private, b)).collect();
validate_trailing_nulls::<CurrentEnvironment>(&null_flags);
assert_scope_fails!(0, 0, 3, 5);
});
assert!(!Circuit::is_satisfied(), "Pattern {pattern:?} should be invalid");
Circuit::reset();
}
}
#[test]
fn test_validate_padding_bits_valid() {
Circuit::scope("padding_valid", || {
let mut bits: Vec<Boolean<CurrentEnvironment>> =
(0..10).map(|_| Boolean::new(Mode::Private, false)).collect();
bits[0] = Boolean::new(Mode::Private, true); validate_padding_bits(&bits, 8);
assert_scope!(0, 0, 11, 13);
});
assert!(Circuit::is_satisfied());
Circuit::reset();
}
#[test]
fn test_validate_padding_bits_invalid() {
Circuit::scope("padding_invalid", || {
let mut bits: Vec<Boolean<CurrentEnvironment>> =
(0..10).map(|_| Boolean::new(Mode::Private, false)).collect();
bits[9] = Boolean::new(Mode::Private, true); validate_padding_bits(&bits, 8);
assert_scope_fails!(0, 0, 11, 13);
});
assert!(!Circuit::is_satisfied());
Circuit::reset();
}
#[test]
fn test_validate_byte_exhaustive() {
for byte_value in 0u8..=255 {
for is_first in [true, false] {
let expected_valid = match (is_first, byte_value) {
(true, b'A'..=b'Z') | (true, b'a'..=b'z') => true,
(true, _) => false,
(false, b'A'..=b'Z') | (false, b'a'..=b'z') => true,
(false, b'0'..=b'9') | (false, b'_') | (false, 0x00) => true,
(false, _) => false,
};
Circuit::scope(format!("byte_{byte_value}_first_{is_first}"), || {
let bits = byte_to_bits(byte_value, Mode::Private);
let _null_flag = validate_byte::<CurrentEnvironment>(&bits, is_first);
if expected_valid {
if is_first {
assert_scope!(0, 0, 23, 29);
} else {
assert_scope!(0, 0, 20, 25);
}
} else if is_first {
assert_scope_fails!(0, 0, 23, 29);
} else {
assert_scope_fails!(0, 0, 20, 25);
}
});
assert_eq!(
Circuit::is_satisfied(),
expected_valid,
"byte={byte_value} (0x{byte_value:02x}, '{}'), is_first={is_first}: expected {}",
if byte_value.is_ascii_graphic() { byte_value as char } else { '?' },
if expected_valid { "satisfied" } else { "unsatisfied" }
);
Circuit::reset();
}
}
}
#[test]
fn test_validate_trailing_nulls_exhaustive() {
for n in 1..=6usize {
for pattern in 0..(1 << n) {
let null_flags: Vec<bool> = (0..n).map(|i| (pattern >> i) & 1 == 1).collect();
let expected_valid = {
let mut seen_null = false;
let mut valid = true;
for &is_null in &null_flags {
if seen_null && !is_null {
valid = false;
break;
}
seen_null |= is_null;
}
valid
};
Circuit::scope(format!("trailing_n{n}_p{pattern}"), || {
let flags: Vec<Boolean<CurrentEnvironment>> =
null_flags.iter().map(|&b| Boolean::new(Mode::Private, b)).collect();
validate_trailing_nulls::<CurrentEnvironment>(&flags);
if expected_valid {
assert_scope!(0, 0, n as u64, (2 * n - 1) as u64);
} else {
assert_scope_fails!(0, 0, n as u64, (2 * n - 1) as u64);
}
});
assert_eq!(
Circuit::is_satisfied(),
expected_valid,
"n={n}, pattern={pattern:0width$b}: expected {}",
if expected_valid { "satisfied" } else { "unsatisfied" },
width = n
);
Circuit::reset();
}
}
}
#[test]
fn test_is_invalid_digit_offset_exhaustive() {
for nibble in 0u8..16 {
let b1 = (nibble >> 1) & 1 == 1;
let b2 = (nibble >> 2) & 1 == 1;
let b3 = (nibble >> 3) & 1 == 1;
let expected_invalid = nibble > 9;
Circuit::scope(format!("digit_nibble_{nibble}"), || {
let b1_circuit = Boolean::new(Mode::Private, b1);
let b2_circuit = Boolean::new(Mode::Private, b2);
let b3_circuit = Boolean::new(Mode::Private, b3);
let result = is_invalid_digit_offset::<CurrentEnvironment>(&b1_circuit, &b2_circuit, &b3_circuit);
assert_eq!(
result.eject_value(),
expected_invalid,
"nibble={nibble}: expected is_invalid={expected_invalid}"
);
assert_scope!(0, 0, 5, 5);
});
assert!(Circuit::is_satisfied(), "Circuit should be satisfied for nibble={nibble}");
Circuit::reset();
}
}
#[test]
fn test_is_invalid_uppercase_offset_exhaustive() {
for offset in 0u8..32 {
let b0 = offset & 1 == 1;
let b1 = (offset >> 1) & 1 == 1;
let b2 = (offset >> 2) & 1 == 1;
let b3 = (offset >> 3) & 1 == 1;
let b4 = (offset >> 4) & 1 == 1;
let expected_invalid = (27..=30).contains(&offset);
Circuit::scope(format!("upper_offset_{offset}"), || {
let b0_c = Boolean::new(Mode::Private, b0);
let b1_c = Boolean::new(Mode::Private, b1);
let b2_c = Boolean::new(Mode::Private, b2);
let b3_c = Boolean::new(Mode::Private, b3);
let b4_c = Boolean::new(Mode::Private, b4);
let data = ByteValidationData::new(&b0_c, &b1_c, &b2_c, &b3_c, &b4_c);
let result = is_invalid_uppercase_offset::<CurrentEnvironment>(&data, &b2_c);
assert_eq!(
result.eject_value(),
expected_invalid,
"offset={offset}: expected is_invalid={expected_invalid}"
);
assert_scope!(0, 0, 11, 11);
});
assert!(Circuit::is_satisfied(), "Circuit should be satisfied for offset={offset}");
Circuit::reset();
}
}
#[test]
fn test_is_invalid_lowercase_offset_exhaustive() {
for offset in 0u8..32 {
let b0 = offset & 1 == 1;
let b1 = (offset >> 1) & 1 == 1;
let b2 = (offset >> 2) & 1 == 1;
let b3 = (offset >> 3) & 1 == 1;
let b4 = (offset >> 4) & 1 == 1;
let expected_invalid = offset >= 27;
Circuit::scope(format!("lower_offset_{offset}"), || {
let b0_c = Boolean::new(Mode::Private, b0);
let b1_c = Boolean::new(Mode::Private, b1);
let b2_c = Boolean::new(Mode::Private, b2);
let b3_c = Boolean::new(Mode::Private, b3);
let b4_c = Boolean::new(Mode::Private, b4);
let data = ByteValidationData::new(&b0_c, &b1_c, &b2_c, &b3_c, &b4_c);
let result = is_invalid_lowercase_offset::<CurrentEnvironment>(&data, &b2_c);
assert_eq!(
result.eject_value(),
expected_invalid,
"offset={offset}: expected is_invalid={expected_invalid}"
);
assert_scope!(0, 0, 11, 11);
});
assert!(Circuit::is_satisfied(), "Circuit should be satisfied for offset={offset}");
Circuit::reset();
}
}
}