parse_plaintext.rs
1 // Copyright (c) 2025-2026 ACDC Network 2 // This file is part of the alphavm library. 3 // 4 // Alpha Chain | Delta Chain Protocol 5 // International Monetary Graphite. 6 // 7 // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com). 8 // They built world-class ZK infrastructure. We installed the EASY button. 9 // Their cryptography: elegant. Our modifications: bureaucracy-compatible. 10 // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours. 11 // 12 // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0 13 // All modifications and new work: CC0 1.0 Universal Public Domain Dedication. 14 // No rights reserved. No permission required. No warranty. No refunds. 15 // 16 // https://creativecommons.org/publicdomain/zero/1.0/ 17 // SPDX-License-Identifier: CC0-1.0 18 19 use super::*; 20 21 impl<N: Network> Parser for Record<N, Plaintext<N>> { 22 /// Parses a string as a record: `{ owner: address, identifier_0: entry_0, ..., identifier_n: entry_n, _nonce: field, _version: u8 }`. 23 #[inline] 24 fn parse(string: &str) -> ParserResult<'_, Self> { 25 /// Parses a sanitized pair: `identifier: entry`. 26 #[allow(clippy::type_complexity)] 27 fn parse_pair<N: Network>(string: &str) -> ParserResult<'_, (Identifier<N>, Entry<N, Plaintext<N>>)> { 28 // Parse the whitespace and comments from the string. 29 let (string, _) = Sanitizer::parse(string)?; 30 // Parse the identifier from the string. 31 let (string, identifier) = Identifier::parse(string)?; 32 // Parse the whitespace from the string. 33 let (string, _) = Sanitizer::parse_whitespaces(string)?; 34 // Parse the ":" from the string. 35 let (string, _) = tag(":")(string)?; 36 // Parse the whitespace and comments from the string. 37 let (string, _) = Sanitizer::parse(string)?; 38 // Parse the entry from the string. 39 let (string, entry) = Entry::parse(string)?; 40 // Parse the whitespace from the string. 41 let (string, _) = Sanitizer::parse_whitespaces(string)?; 42 // Return the identifier and entry. 43 Ok((string, (identifier, entry))) 44 } 45 46 // Parse the whitespace and comments from the string. 47 let (string, _) = Sanitizer::parse(string)?; 48 // Parse the "{" from the string. 49 let (string, _) = tag("{")(string)?; 50 51 // Parse the whitespace and comments from the string. 52 let (string, _) = Sanitizer::parse(string)?; 53 // Parse the "owner" tag from the string. 54 let (string, _) = tag("owner")(string)?; 55 // Parse the whitespace from the string. 56 let (string, _) = Sanitizer::parse_whitespaces(string)?; 57 // Parse the ":" from the string. 58 let (string, _) = tag(":")(string)?; 59 // Parse the whitespace and comments from the string. 60 let (string, _) = Sanitizer::parse(string)?; 61 // Parse the owner from the string. 62 let (string, owner) = alt(( 63 map(pair(Address::parse, tag(".public")), |(owner, _)| Owner::Public(owner)), 64 map(pair(Address::parse, tag(".private")), |(owner, _)| { 65 Owner::Private(Plaintext::from(Literal::Address(owner))) 66 }), 67 ))(string)?; 68 // Parse the "," from the string. 69 let (string, _) = tag(",")(string)?; 70 71 // Parse the entries. 72 let (string, entries) = map_res(separated_list0(tag(","), parse_pair), |entries: Vec<_>| { 73 // Prepare the reserved entry names. 74 let reserved = [Identifier::from_str("owner").map_err(|e| error(e.to_string()))?]; 75 // Ensure the entries has no duplicate names. 76 if has_duplicates(entries.iter().map(|(identifier, _)| identifier).chain(reserved.iter())) { 77 return Err(error("Duplicate entry type found in record")); 78 } 79 // Ensure the number of entries is within the maximum limit. 80 match entries.len() <= N::MAX_DATA_ENTRIES { 81 true => Ok(entries), 82 false => Err(error(format!("Found a record that exceeds size ({})", entries.len()))), 83 } 84 })(string)?; 85 86 // If there are entries, then parse the "," from the string. 87 let string = match !entries.is_empty() { 88 // Parse the "," from the string. 89 true => tag(",")(string)?.0, 90 false => string, 91 }; 92 93 // Parse the whitespace and comments from the string. 94 let (string, _) = Sanitizer::parse(string)?; 95 // Parse the "_nonce" tag from the string. 96 let (string, _) = tag("_nonce")(string)?; 97 // Parse the ":" from the string. 98 let (string, _) = tag(":")(string)?; 99 // Parse the whitespace and comments from the string. 100 let (string, _) = Sanitizer::parse(string)?; 101 // Parse the nonce from the string. 102 let (string, (nonce, _)) = pair(Group::parse, tag(".public"))(string)?; 103 104 // There may be an optional "_version" tag. Parse the "," from the string if it exists. 105 let string = match opt(tag(","))(string)? { 106 // If there is a version, then parse the "," from the string. 107 (string, Some(_)) => string, 108 // If there is no version, then keep the string as is. 109 (string, None) => string, 110 }; 111 112 // Parse the whitespace and comments from the string. 113 let (string, _) = Sanitizer::parse(string)?; 114 // Parse the optional "_version" tag from the string. 115 let (string, version) = match opt(tag("_version"))(string)? { 116 // If there is no version, then set the version to zero. 117 (string, None) => (string, U8::zero()), 118 // If there is a version, then parse the version from the string. 119 (string, Some(_)) => { 120 // Parse the whitespace and comments from the string. 121 let (string, _) = Sanitizer::parse(string)?; 122 // Parse the ":" from the string. 123 let (string, _) = tag(":")(string)?; 124 // Parse the whitespace and comments from the string. 125 let (string, _) = Sanitizer::parse(string)?; 126 // Parse the version from the string. 127 terminated(U8::parse, tag(".public"))(string)? 128 } 129 }; 130 131 // Parse the whitespace and comments from the string. 132 let (string, _) = Sanitizer::parse(string)?; 133 // Parse the '}' from the string. 134 let (string, _) = tag("}")(string)?; 135 // Output the record. 136 Ok((string, Record { owner, data: IndexMap::from_iter(entries), nonce, version })) 137 } 138 } 139 140 impl<N: Network> FromStr for Record<N, Plaintext<N>> { 141 type Err = Error; 142 143 /// Returns a record from a string literal. 144 fn from_str(string: &str) -> Result<Self> { 145 match Self::parse(string) { 146 Ok((remainder, object)) => { 147 // Ensure the remainder is empty. 148 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\""); 149 // Return the object. 150 Ok(object) 151 } 152 Err(error) => bail!("Failed to parse string. {error}"), 153 } 154 } 155 } 156 157 impl<N: Network> Debug for Record<N, Plaintext<N>> { 158 /// Prints the record as a string. 159 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 160 Display::fmt(self, f) 161 } 162 } 163 164 impl<N: Network> Display for Record<N, Plaintext<N>> { 165 /// Prints the record as a string. 166 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 167 self.fmt_internal(f, 0) 168 } 169 } 170 171 impl<N: Network> Record<N, Plaintext<N>> { 172 /// Prints the record with the given indentation depth. 173 fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result { 174 /// The number of spaces to indent. 175 const INDENT: usize = 2; 176 177 // Print the opening brace. 178 write!(f, "{{")?; 179 // Print the owner with a comma. 180 write!(f, "\n{:indent$}owner: {},", "", self.owner, indent = (depth + 1) * INDENT)?; 181 // Print the data with a comma. 182 for (identifier, entry) in self.data.iter() { 183 // Print the identifier. 184 write!(f, "\n{:indent$}{identifier}: ", "", indent = (depth + 1) * INDENT)?; 185 // Print the entry. 186 match entry { 187 // If the entry is a literal, print the entry without indentation. 188 Entry::Constant(Plaintext::Literal(..)) 189 | Entry::Public(Plaintext::Literal(..)) 190 | Entry::Private(Plaintext::Literal(..)) => write!(f, "{entry}")?, 191 // If the entry is a struct or an array, print the entry with indentation. 192 Entry::Constant(Plaintext::Struct(..)) 193 | Entry::Public(Plaintext::Struct(..)) 194 | Entry::Private(Plaintext::Struct(..)) 195 | Entry::Constant(Plaintext::Array(..)) 196 | Entry::Public(Plaintext::Array(..)) 197 | Entry::Private(Plaintext::Array(..)) => entry.fmt_internal(f, depth + 1)?, 198 } 199 // Print the comma. 200 write!(f, ",")?; 201 } 202 // Print the nonce with a comma. 203 write!(f, "\n{:indent$}_nonce: {}.public,", "", self.nonce, indent = (depth + 1) * INDENT)?; 204 // Print the version without a comma. 205 write!(f, "\n{:indent$}_version: {}.public", "", self.version, indent = (depth + 1) * INDENT)?; 206 // Print the closing brace. 207 write!(f, "\n{:indent$}}}", "", indent = depth * INDENT) 208 } 209 } 210 211 #[cfg(test)] 212 mod tests { 213 use super::*; 214 use alphavm_console_network::MainnetV0; 215 216 type CurrentNetwork = MainnetV0; 217 218 #[test] 219 fn test_parse_without_version() -> Result<()> { 220 // Sanity check. 221 let expected = r"{ 222 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 223 _nonce: 0group.public, 224 _version: 0u8.public 225 }"; 226 let given = 227 "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public }"; 228 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?; 229 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 230 assert_eq!(expected, candidate.to_string()); 231 assert_eq!("", remainder); 232 Ok(()) 233 } 234 235 #[test] 236 fn test_parse_with_version() -> Result<()> { 237 // Sanity check. 238 let expected = r"{ 239 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 240 _nonce: 0group.public, 241 _version: 0u8.public 242 }"; 243 let given = "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public, _version: 0u8.public }"; 244 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?; 245 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 246 assert_eq!(expected, candidate.to_string()); 247 assert_eq!("", remainder); 248 249 // Sanity check. 250 let expected = r"{ 251 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 252 _nonce: 0group.public, 253 _version: 1u8.public 254 }"; 255 let given = "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public, _version: 1u8.public }"; 256 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?; 257 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 258 assert_eq!(expected, candidate.to_string()); 259 assert_eq!("", remainder); 260 Ok(()) 261 } 262 263 #[test] 264 fn test_parse_without_data_entries() -> Result<()> { 265 // Sanity check. 266 let expected = r"{ 267 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 268 _nonce: 0group.public, 269 _version: 0u8.public 270 }"; 271 let given = 272 "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public }"; 273 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?; 274 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 275 assert_eq!(expected, candidate.to_string()); 276 assert_eq!("", remainder); 277 Ok(()) 278 } 279 280 #[test] 281 fn test_parse_with_literal_entry() -> Result<()> { 282 let expected = r"{ 283 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, 284 foo: 5u8.constant, 285 _nonce: 0group.public, 286 _version: 0u8.public 287 }"; 288 let given = "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, foo: 5u8.constant, _nonce: 0group.public }"; 289 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?; 290 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 291 assert_eq!(expected, candidate.to_string()); 292 assert_eq!("", remainder); 293 Ok(()) 294 } 295 296 #[test] 297 fn test_parse_with_struct_entry() -> Result<()> { 298 let expected = r"{ 299 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, 300 foo: 5u8.public, 301 bar: { 302 baz: 6u8.constant, 303 qux: 7u8.constant 304 }, 305 quux: 8u8.private, 306 corge: { 307 grault: 9u8.constant, 308 garply: { 309 waldo: 10u8.constant, 310 fred: 11u8.constant 311 } 312 }, 313 xyzzy: { 314 thud: 12u8.public 315 }, 316 _nonce: 2293253577170800572742339369209137467208538700597121244293392265726446806023group.public, 317 _version: 0u8.public 318 }"; 319 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?; 320 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 321 assert_eq!(expected, candidate.to_string()); 322 assert_eq!("", remainder); 323 324 let expected = r"{ 325 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 326 foo: { 327 bar: 0u128.private 328 }, 329 baz: { 330 quine: { 331 flop: 0u64.private 332 }, 333 slice: 0u16.private, 334 flag: true.private, 335 square: { 336 first: 0u128.private, 337 second: 1u128.private, 338 third: 2u128.private, 339 fourth: 3u128.private 340 } 341 }, 342 _nonce: 0group.public, 343 _version: 0u8.public 344 }"; 345 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?; 346 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 347 assert_eq!(expected, candidate.to_string()); 348 assert_eq!("", remainder); 349 350 let expected = r"{ 351 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 352 c: { 353 c: { 354 a: 0u8.private, 355 b: 1u8.private 356 }, 357 d: { 358 a: 0u8.private, 359 b: 1u8.private 360 } 361 }, 362 d: { 363 c: { 364 a: 0u8.private, 365 b: 1u8.private 366 }, 367 d: { 368 a: 0u8.private, 369 b: 1u8.private 370 } 371 }, 372 _nonce: 8102307625287186026775464343238779600702564007094834161216556016558567413871group.public, 373 _version: 0u8.public 374 }"; 375 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?; 376 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 377 assert_eq!(expected, candidate.to_string()); 378 assert_eq!("", remainder); 379 380 Ok(()) 381 } 382 383 #[test] 384 fn test_parse_with_array_entry() -> Result<()> { 385 let expected = r"{ 386 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, 387 foo: 5u8.public, 388 bar: [ 389 6u8.private, 390 7u8.private, 391 8u8.private 392 ], 393 _nonce: 0group.public, 394 _version: 0u8.public 395 }"; 396 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?; 397 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 398 assert_eq!(expected, candidate.to_string()); 399 assert_eq!("", remainder); 400 Ok(()) 401 } 402 403 #[test] 404 fn test_parse_fails() -> Result<()> { 405 // Missing owner. 406 let expected = "{ foo: 5u8.private, _nonce: 0group.public }"; 407 assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err()); 408 409 // Missing nonce. 410 let expected = 411 "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, foo: 5u8.private }"; 412 assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err()); 413 414 // Entry 'd' contains members with different visibility. 415 let expected = r"{ 416 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 417 a: true.private, 418 b: 123456789field.private, 419 c: 0group.private, 420 d: { 421 e: true.private, 422 f: 123456789field.public, 423 g: 0group.private 424 }, 425 _nonce: 0group.public, 426 _version: 0u8.public 427 }"; 428 assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err()); 429 Ok(()) 430 } 431 }