use super::*;
impl<E: Environment> FromBits for StringType<E> {
type Boolean = Boolean<E>;
fn from_bits_le(bits_le: &[Self::Boolean]) -> Self {
let mode = bits_le.eject_mode();
let size_in_bytes = Self::inject_size_in_bytes(mode, bits_le);
StringType { mode, bytes: bits_le.chunks(8).map(U8::from_bits_le).collect(), size_in_bytes }
}
fn from_bits_be(bits_be: &[Self::Boolean]) -> Self {
let mode = bits_be.eject_mode();
let size_in_bytes = Self::inject_size_in_bytes(mode, bits_be);
StringType { mode, bytes: bits_be.chunks(8).map(U8::from_bits_be).collect(), size_in_bytes }
}
}
impl<E: Environment> StringType<E> {
fn inject_size_in_bytes(mode: Mode, bits: &[Boolean<E>]) -> Field<E> {
let num_bits = bits.len();
if num_bits % 8 != 0 {
E::halt(format!("Attempted to instantiate a {num_bits}-bit string, which is not byte-aligned"))
}
let num_bytes =
console::Field::from_u32(u32::try_from(num_bits / 8).unwrap_or_else(|error| E::halt(error.to_string())));
let expected_size_in_bytes = Field::constant(num_bytes);
let size_in_bytes = match mode.is_constant() {
true => expected_size_in_bytes.clone(),
false => Field::new(Mode::Private, num_bytes),
};
E::assert_eq(&expected_size_in_bytes, &size_in_bytes)
.expect("String size witness does not match expected size");
size_in_bytes
}
}
#[cfg(test)]
mod tests {
use super::*;
use snarkvm_circuit_environment::Circuit;
const ITERATIONS: u32 = 10;
fn check_from_bits_le(mode: Mode, num_constants: u64, num_public: u64, num_private: u64, num_constraints: u64) {
let rng = &mut TestRng::default();
for i in 0..ITERATIONS {
let expected = rng.next_string(Circuit::MAX_STRING_BYTES / 4, true);
let expected_num_bytes = expected.len();
assert!(expected_num_bytes <= Circuit::MAX_STRING_BYTES as usize);
let candidate = StringType::<Circuit>::new(mode, console::StringType::new(&expected)).to_bits_le();
Circuit::scope(format!("{mode} {i}"), || {
let candidate = StringType::<Circuit>::from_bits_le(&candidate);
assert_eq!(expected, *candidate.eject_value());
assert_scope!(num_constants, num_public, num_private, num_constraints);
});
Circuit::reset();
}
}
fn check_from_bits_be(mode: Mode, num_constants: u64, num_public: u64, num_private: u64, num_constraints: u64) {
let rng = &mut TestRng::default();
for i in 0..ITERATIONS {
let expected = rng.next_string(Circuit::MAX_STRING_BYTES / 4, true);
let expected_num_bytes = expected.len();
assert!(expected_num_bytes <= Circuit::MAX_STRING_BYTES as usize);
let candidate = StringType::<Circuit>::new(mode, console::StringType::new(&expected)).to_bits_be();
Circuit::scope(format!("{mode} {i}"), || {
let candidate = StringType::<Circuit>::from_bits_be(&candidate);
assert_eq!(expected, *candidate.eject_value());
assert_scope!(num_constants, num_public, num_private, num_constraints);
});
Circuit::reset();
}
}
#[test]
fn test_from_bits_le_constant() {
check_from_bits_le(Mode::Constant, 1, 0, 0, 0);
}
#[test]
fn test_from_bits_le_public() {
check_from_bits_le(Mode::Public, 1, 0, 1, 1);
}
#[test]
fn test_from_bits_le_private() {
check_from_bits_le(Mode::Private, 1, 0, 1, 1);
}
#[test]
fn test_from_bits_be_constant() {
check_from_bits_be(Mode::Constant, 1, 0, 0, 0);
}
#[test]
fn test_from_bits_be_public() {
check_from_bits_be(Mode::Public, 1, 0, 1, 1);
}
#[test]
fn test_from_bits_be_private() {
check_from_bits_be(Mode::Private, 1, 0, 1, 1);
}
}