snarkvm_console_program/data/record/
parse_plaintext.rs

1// Copyright 2024 Aleo Network Foundation
2// This file is part of the snarkVM 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
16use super::*;
17
18impl<N: Network> Parser for Record<N, Plaintext<N>> {
19    /// Parses a string as a record: `{ owner: address, identifier_0: entry_0, ..., identifier_n: entry_n, _nonce: field }`.
20    #[inline]
21    fn parse(string: &str) -> ParserResult<Self> {
22        /// Parses a sanitized pair: `identifier: entry`.
23        #[allow(clippy::type_complexity)]
24        fn parse_pair<N: Network>(string: &str) -> ParserResult<(Identifier<N>, Entry<N, Plaintext<N>>)> {
25            // Parse the whitespace and comments from the string.
26            let (string, _) = Sanitizer::parse(string)?;
27            // Parse the identifier from the string.
28            let (string, identifier) = Identifier::parse(string)?;
29            // Parse the whitespace from the string.
30            let (string, _) = Sanitizer::parse_whitespaces(string)?;
31            // Parse the ":" from the string.
32            let (string, _) = tag(":")(string)?;
33            // Parse the whitespace and comments from the string.
34            let (string, _) = Sanitizer::parse(string)?;
35            // Parse the entry from the string.
36            let (string, entry) = Entry::parse(string)?;
37            // Parse the whitespace from the string.
38            let (string, _) = Sanitizer::parse_whitespaces(string)?;
39            // Return the identifier and entry.
40            Ok((string, (identifier, entry)))
41        }
42
43        // Parse the whitespace and comments from the string.
44        let (string, _) = Sanitizer::parse(string)?;
45        // Parse the "{" from the string.
46        let (string, _) = tag("{")(string)?;
47
48        // Parse the whitespace and comments from the string.
49        let (string, _) = Sanitizer::parse(string)?;
50        // Parse the "owner" tag from the string.
51        let (string, _) = tag("owner")(string)?;
52        // Parse the whitespace from the string.
53        let (string, _) = Sanitizer::parse_whitespaces(string)?;
54        // Parse the ":" from the string.
55        let (string, _) = tag(":")(string)?;
56        // Parse the whitespace and comments from the string.
57        let (string, _) = Sanitizer::parse(string)?;
58        // Parse the owner from the string.
59        let (string, owner) = alt((
60            map(pair(Address::parse, tag(".public")), |(owner, _)| Owner::Public(owner)),
61            map(pair(Address::parse, tag(".private")), |(owner, _)| {
62                Owner::Private(Plaintext::from(Literal::Address(owner)))
63            }),
64        ))(string)?;
65        // Parse the "," from the string.
66        let (string, _) = tag(",")(string)?;
67
68        // Parse the entries.
69        let (string, entries) = map_res(separated_list0(tag(","), parse_pair), |entries: Vec<_>| {
70            // Prepare the reserved entry names.
71            let reserved = [Identifier::from_str("owner").map_err(|e| error(e.to_string()))?];
72            // Ensure the entries has no duplicate names.
73            if has_duplicates(entries.iter().map(|(identifier, _)| identifier).chain(reserved.iter())) {
74                return Err(error("Duplicate entry type found in record"));
75            }
76            // Ensure the number of entries is within the maximum limit.
77            match entries.len() <= N::MAX_DATA_ENTRIES {
78                true => Ok(entries),
79                false => Err(error(format!("Found a record that exceeds size ({})", entries.len()))),
80            }
81        })(string)?;
82
83        // If there are entries, then parse the "," from the string.
84        let string = match !entries.is_empty() {
85            // Parse the "," from the string.
86            true => tag(",")(string)?.0,
87            false => string,
88        };
89
90        // Parse the whitespace and comments from the string.
91        let (string, _) = Sanitizer::parse(string)?;
92        // Parse the "_nonce" tag from the string.
93        let (string, _) = tag("_nonce")(string)?;
94        // Parse the ":" from the string.
95        let (string, _) = tag(":")(string)?;
96        // Parse the whitespace and comments from the string.
97        let (string, _) = Sanitizer::parse(string)?;
98        // Parse the nonce from the string.
99        let (string, (nonce, _)) = pair(Group::parse, tag(".public"))(string)?;
100
101        // Parse the whitespace and comments from the string.
102        let (string, _) = Sanitizer::parse(string)?;
103        // Parse the '}' from the string.
104        let (string, _) = tag("}")(string)?;
105        // Output the record.
106        Ok((string, Record { owner, data: IndexMap::from_iter(entries), nonce }))
107    }
108}
109
110impl<N: Network> FromStr for Record<N, Plaintext<N>> {
111    type Err = Error;
112
113    /// Returns a record from a string literal.
114    fn from_str(string: &str) -> Result<Self> {
115        match Self::parse(string) {
116            Ok((remainder, object)) => {
117                // Ensure the remainder is empty.
118                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
119                // Return the object.
120                Ok(object)
121            }
122            Err(error) => bail!("Failed to parse string. {error}"),
123        }
124    }
125}
126
127impl<N: Network> Debug for Record<N, Plaintext<N>> {
128    /// Prints the record as a string.
129    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
130        Display::fmt(self, f)
131    }
132}
133
134impl<N: Network> Display for Record<N, Plaintext<N>> {
135    /// Prints the record as a string.
136    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
137        self.fmt_internal(f, 0)
138    }
139}
140
141impl<N: Network> Record<N, Plaintext<N>> {
142    /// Prints the record with the given indentation depth.
143    fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
144        /// The number of spaces to indent.
145        const INDENT: usize = 2;
146
147        // Print the opening brace.
148        write!(f, "{{")?;
149        // Print the owner with a comma.
150        write!(f, "\n{:indent$}owner: {},", "", self.owner, indent = (depth + 1) * INDENT)?;
151        // Print the data with a comma.
152        for (identifier, entry) in self.data.iter() {
153            // Print the identifier.
154            write!(f, "\n{:indent$}{identifier}: ", "", indent = (depth + 1) * INDENT)?;
155            // Print the entry.
156            match entry {
157                // If the entry is a literal, print the entry without indentation.
158                Entry::Constant(Plaintext::Literal(..))
159                | Entry::Public(Plaintext::Literal(..))
160                | Entry::Private(Plaintext::Literal(..)) => write!(f, "{entry}")?,
161                // If the entry is a struct or an array, print the entry with indentation.
162                Entry::Constant(Plaintext::Struct(..))
163                | Entry::Public(Plaintext::Struct(..))
164                | Entry::Private(Plaintext::Struct(..))
165                | Entry::Constant(Plaintext::Array(..))
166                | Entry::Public(Plaintext::Array(..))
167                | Entry::Private(Plaintext::Array(..)) => entry.fmt_internal(f, depth + 1)?,
168            }
169            // Print the comma.
170            write!(f, ",")?;
171        }
172        // Print the nonce without a comma.
173        write!(f, "\n{:indent$}_nonce: {}.public", "", self.nonce, indent = (depth + 1) * INDENT)?;
174        // Print the closing brace.
175        write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182    use snarkvm_console_network::MainnetV0;
183
184    type CurrentNetwork = MainnetV0;
185
186    #[test]
187    fn test_parse_without_data_entries() -> Result<()> {
188        // Sanity check.
189        let expected = r"{
190  owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.private,
191  _nonce: 0group.public
192}";
193        let given =
194            "{ owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.private, _nonce: 0group.public }";
195        let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?;
196        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
197        assert_eq!(expected, candidate.to_string());
198        assert_eq!("", remainder);
199        Ok(())
200    }
201
202    #[test]
203    fn test_parse_with_literal_entry() -> Result<()> {
204        let expected = r"{
205  owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.public,
206  foo: 5u8.constant,
207  _nonce: 0group.public
208}";
209        let given = "{ owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.public, foo: 5u8.constant, _nonce: 0group.public }";
210        let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?;
211        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
212        assert_eq!(expected, candidate.to_string());
213        assert_eq!("", remainder);
214        Ok(())
215    }
216
217    #[test]
218    fn test_parse_with_struct_entry() -> Result<()> {
219        let expected = r"{
220  owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.public,
221  foo: 5u8.public,
222  bar: {
223    baz: 6u8.constant,
224    qux: 7u8.constant
225  },
226  quux: 8u8.private,
227  corge: {
228    grault: 9u8.constant,
229    garply: {
230      waldo: 10u8.constant,
231      fred: 11u8.constant
232    }
233  },
234  xyzzy: {
235    thud: 12u8.public
236  },
237  _nonce: 2293253577170800572742339369209137467208538700597121244293392265726446806023group.public
238}";
239        let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?;
240        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
241        assert_eq!(expected, candidate.to_string());
242        assert_eq!("", remainder);
243
244        let expected = r"{
245  owner: aleo1lhcpfumagern97esytsgdva2ytme043zydlzyprhejsd0gw5vypqqz0zkw.private,
246  foo: {
247    bar: 0u128.private
248  },
249  baz: {
250    quine: {
251      flop: 0u64.private
252    },
253    slice: 0u16.private,
254    flag: true.private,
255    square: {
256      first: 0u128.private,
257      second: 1u128.private,
258      third: 2u128.private,
259      fourth: 3u128.private
260    }
261  },
262  _nonce: 0group.public
263}";
264        let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?;
265        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
266        assert_eq!(expected, candidate.to_string());
267        assert_eq!("", remainder);
268
269        let expected = r"{
270  owner: aleo18ttcegpydcs95yw4je0u400j3u7r26yqr9h8evqps3qa9slrvyrsqjwt9l.private,
271  c: {
272    c: {
273      a: 0u8.private,
274      b: 1u8.private
275    },
276    d: {
277      a: 0u8.private,
278      b: 1u8.private
279    }
280  },
281  d: {
282    c: {
283      a: 0u8.private,
284      b: 1u8.private
285    },
286    d: {
287      a: 0u8.private,
288      b: 1u8.private
289    }
290  },
291  _nonce: 8102307625287186026775464343238779600702564007094834161216556016558567413871group.public
292}";
293        let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?;
294        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
295        assert_eq!(expected, candidate.to_string());
296        assert_eq!("", remainder);
297
298        Ok(())
299    }
300
301    #[test]
302    fn test_parse_with_array_entry() -> Result<()> {
303        let expected = r"{
304  owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.public,
305  foo: 5u8.public,
306  bar: [
307    6u8.private,
308    7u8.private,
309    8u8.private
310  ],
311  _nonce: 0group.public
312}";
313        let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?;
314        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
315        assert_eq!(expected, candidate.to_string());
316        assert_eq!("", remainder);
317        Ok(())
318    }
319
320    #[test]
321    fn test_parse_fails() -> Result<()> {
322        // Missing owner.
323        let expected = "{ foo: 5u8.private, _nonce: 0group.public }";
324        assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err());
325
326        // Missing nonce.
327        let expected =
328            "{ owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.public, foo: 5u8.private }";
329        assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err());
330
331        // Entry 'd' contains members with different visibility.
332        let expected = r"{
333    owner: aleo14tlamssdmg3d0p5zmljma573jghe2q9n6wz29qf36re2glcedcpqfg4add.private,
334    a: true.private,
335    b: 123456789field.private,
336    c: 0group.private,
337    d: {
338        e: true.private,
339        f: 123456789field.public,
340        g: 0group.private
341    },
342    _nonce: 0group.public
343}";
344        assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err());
345        Ok(())
346    }
347}