parse.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 static CIPHERTEXT_PREFIX: &str = "ciphertext"; 22 23 impl<N: Network> Parser for Ciphertext<N> { 24 /// Parses a string into an ciphertext. 25 #[inline] 26 fn parse(string: &str) -> ParserResult<'_, Self> { 27 // Prepare a parser for the Alpha ciphertext. 28 let parse_ciphertext = recognize(pair( 29 pair(tag(CIPHERTEXT_PREFIX), tag("1")), 30 many1(terminated(one_of("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), many0(char('_')))), 31 )); 32 33 // Parse the ciphertext from the string. 34 map_res(parse_ciphertext, |ciphertext: &str| -> Result<_, Error> { 35 Self::from_str(&ciphertext.replace('_', "")) 36 })(string) 37 } 38 } 39 40 impl<N: Network> FromStr for Ciphertext<N> { 41 type Err = Error; 42 43 /// Reads in the ciphertext string. 44 fn from_str(ciphertext: &str) -> Result<Self, Self::Err> { 45 // Decode the ciphertext string from bech32m. 46 let (hrp, data, variant) = bech32::decode(ciphertext)?; 47 if hrp != CIPHERTEXT_PREFIX { 48 bail!("Failed to decode ciphertext: '{hrp}' is an invalid prefix") 49 } else if data.is_empty() { 50 bail!("Failed to decode ciphertext: data field is empty") 51 } else if variant != bech32::Variant::Bech32m { 52 bail!("Found an ciphertext that is not bech32m encoded: {ciphertext}"); 53 } 54 // Decode the ciphertext data from u5 to u8, and into the ciphertext. 55 Ok(Self::read_le(&Vec::from_base32(&data)?[..])?) 56 } 57 } 58 59 impl<N: Network> Debug for Ciphertext<N> { 60 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 61 Display::fmt(self, f) 62 } 63 } 64 65 impl<N: Network> Display for Ciphertext<N> { 66 /// Writes the ciphertext as a bech32m string. 67 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 68 // Convert the ciphertext to bytes. 69 let bytes = self.to_bytes_le().map_err(|_| fmt::Error)?; 70 // Encode the bytes into bech32m. 71 let string = 72 bech32::encode(CIPHERTEXT_PREFIX, bytes.to_base32(), bech32::Variant::Bech32m).map_err(|_| fmt::Error)?; 73 // Output the string. 74 Display::fmt(&string, f) 75 } 76 } 77 78 #[cfg(test)] 79 mod tests { 80 use super::*; 81 use alphavm_console_network::MainnetV0; 82 83 type CurrentNetwork = MainnetV0; 84 85 const ITERATIONS: u64 = 1_000; 86 87 #[test] 88 fn test_parse() -> Result<()> { 89 // Ensure type and empty value fails. 90 assert!(Ciphertext::<CurrentNetwork>::parse(&format!("{CIPHERTEXT_PREFIX}1")).is_err()); 91 assert!(Ciphertext::<CurrentNetwork>::parse("").is_err()); 92 93 let mut rng = TestRng::default(); 94 95 for _ in 0..ITERATIONS { 96 // Sample a new ciphertext. 97 let ciphertext = 98 Ciphertext::<CurrentNetwork>((0..100).map(|_| Uniform::rand(&mut rng)).collect::<Vec<_>>()); 99 100 let expected = format!("{ciphertext}"); 101 let (remainder, candidate) = Ciphertext::<CurrentNetwork>::parse(&expected).unwrap(); 102 assert_eq!(format!("{expected}"), candidate.to_string()); 103 assert_eq!(CIPHERTEXT_PREFIX, candidate.to_string().split('1').next().unwrap()); 104 assert_eq!("", remainder); 105 } 106 Ok(()) 107 } 108 109 #[test] 110 fn test_string() -> Result<()> { 111 let mut rng = TestRng::default(); 112 113 for _ in 0..ITERATIONS { 114 // Sample a new ciphertext. 115 let expected = Ciphertext::<CurrentNetwork>((0..100).map(|_| Uniform::rand(&mut rng)).collect::<Vec<_>>()); 116 117 // Check the string representation. 118 let candidate = format!("{expected}"); 119 assert_eq!(expected, Ciphertext::from_str(&candidate)?); 120 assert_eq!(CIPHERTEXT_PREFIX, candidate.split('1').next().unwrap()); 121 } 122 Ok(()) 123 } 124 125 #[test] 126 fn test_display() -> Result<()> { 127 let mut rng = TestRng::default(); 128 129 for _ in 0..ITERATIONS { 130 // Sample a new ciphertext. 131 let expected = Ciphertext::<CurrentNetwork>((0..100).map(|_| Uniform::rand(&mut rng)).collect::<Vec<_>>()); 132 133 let candidate = expected.to_string(); 134 assert_eq!(format!("{expected}"), candidate); 135 assert_eq!(CIPHERTEXT_PREFIX, candidate.split('1').next().unwrap()); 136 137 let candidate_recovered = Ciphertext::<CurrentNetwork>::from_str(&candidate.to_string())?; 138 assert_eq!(expected, candidate_recovered); 139 } 140 Ok(()) 141 } 142 }