lib.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 #![forbid(unsafe_code)] 17 #![cfg_attr(test, allow(clippy::assertions_on_result_states))] 18 19 extern crate alphavm_console_types_string as console; 20 21 mod equal; 22 mod helpers; 23 24 #[cfg(test)] 25 use alphavm_circuit_environment::assert_scope; 26 #[cfg(test)] 27 use console::TestRng; 28 29 use alphavm_circuit_environment::prelude::*; 30 use alphavm_circuit_types_boolean::Boolean; 31 use alphavm_circuit_types_field::Field; 32 use alphavm_circuit_types_integers::U8; 33 34 #[derive(Clone)] 35 pub struct StringType<E: Environment> { 36 mode: Mode, 37 bytes: Vec<U8<E>>, 38 size_in_bytes: Field<E>, 39 } 40 41 impl<E: Environment> StringTrait for StringType<E> {} 42 43 impl<E: Environment> Inject for StringType<E> { 44 type Primitive = console::StringType<E::Network>; 45 46 /// Initializes a new instance of a string. 47 fn new(mode: Mode, string: Self::Primitive) -> Self { 48 // Cast the number of bytes in the 'string' as a field element. 49 let num_bytes = 50 console::Field::from_u32(u32::try_from(string.len()).unwrap_or_else(|error| E::halt(error.to_string()))); 51 52 // "Load-bearing witness allocation - Please do not optimize me." - Pratyush :) 53 54 // Inject the number of bytes as a constant. 55 let expected_size_in_bytes = Field::constant(num_bytes); 56 // Inject the number of bytes as a witness. 57 let size_in_bytes = match mode.is_constant() { 58 true => expected_size_in_bytes.clone(), 59 false => Field::new(Mode::Private, num_bytes), 60 }; 61 // Ensure the witness matches the constant. 62 E::assert_eq(&expected_size_in_bytes, &size_in_bytes); 63 64 Self { 65 mode, 66 bytes: string.as_bytes().iter().map(|byte| U8::new(mode, console::Integer::new(*byte))).collect(), 67 size_in_bytes, 68 } 69 } 70 } 71 72 impl<E: Environment> Eject for StringType<E> { 73 type Primitive = console::StringType<E::Network>; 74 75 /// Ejects the mode of the string. 76 fn eject_mode(&self) -> Mode { 77 match self.bytes.is_empty() { 78 true => self.mode, 79 false => self.bytes.eject_mode(), 80 } 81 } 82 83 /// Ejects the string as a string literal. 84 fn eject_value(&self) -> Self::Primitive { 85 // Ensure the string is within the allowed capacity. 86 let num_bytes = self.bytes.len(); 87 match num_bytes <= E::MAX_STRING_BYTES as usize { 88 true => console::StringType::new( 89 &String::from_utf8(self.bytes.eject_value().into_iter().map(|byte| *byte).collect()) 90 .unwrap_or_else(|error| E::halt(format!("Failed to eject a string value: {error}"))), 91 ), 92 false => E::halt(format!("Attempted to eject a string of size {num_bytes}")), 93 } 94 } 95 } 96 97 impl<E: Environment> Parser for StringType<E> { 98 /// Parses a string into a string circuit. 99 #[inline] 100 fn parse(string: &str) -> ParserResult<Self> { 101 // Parse the content from the string. 102 let (string, content) = console::StringType::parse(string)?; 103 // Parse the mode from the string. 104 let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?; 105 106 match mode { 107 Some((_, mode)) => Ok((string, StringType::new(mode, content))), 108 None => Ok((string, StringType::new(Mode::Constant, content))), 109 } 110 } 111 } 112 113 impl<E: Environment> FromStr for StringType<E> { 114 type Err = Error; 115 116 /// Parses a string into a string circuit. 117 #[inline] 118 fn from_str(string: &str) -> Result<Self> { 119 match Self::parse(string) { 120 Ok((remainder, object)) => { 121 // Ensure the remainder is empty. 122 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\""); 123 // Return the object. 124 Ok(object) 125 } 126 Err(error) => bail!("Failed to parse string. {error}"), 127 } 128 } 129 } 130 131 impl<E: Environment> TypeName for StringType<E> { 132 /// Returns the type name of the circuit as a string. 133 #[inline] 134 fn type_name() -> &'static str { 135 console::StringType::<E::Network>::type_name() 136 } 137 } 138 139 impl<E: Environment> Debug for StringType<E> { 140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 141 Display::fmt(self, f) 142 } 143 } 144 145 impl<E: Environment> Display for StringType<E> { 146 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 147 write!(f, "{}.{}", self.eject_value(), self.eject_mode()) 148 } 149 }