Skip to main content

snarkvm_console_program/data/future/
parse.rs

1// Copyright (c) 2019-2026 Provable Inc.
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 Future<N> {
19    /// Parses a string into a future value.
20    #[inline]
21    fn parse(string: &str) -> ParserResult<Self> {
22        // Parse the future from the string.
23        Self::parse_internal(string, 0)
24    }
25}
26
27impl<N: Network> Future<N> {
28    /// Parses an array of future arguments: `[arg_0, ..., arg_1]`, while tracking the depth of the data.
29    fn parse_arguments(string: &str, depth: usize) -> ParserResult<Vec<Argument<N>>> {
30        // Parse the whitespace and comments from the string.
31        let (string, _) = Sanitizer::parse(string)?;
32        // Parse the "[" from the string.
33        let (string, _) = tag("[")(string)?;
34        // Parse the whitespace from the string.
35        let (string, _) = Sanitizer::parse(string)?;
36        // Parse the members.
37        let (string, arguments) = separated_list0(
38            pair(pair(Sanitizer::parse_whitespaces, tag(",")), Sanitizer::parse),
39            alt((
40                map(|input| Self::parse_internal(input, depth + 1), Argument::Future),
41                map(DynamicFuture::parse, Argument::DynamicFuture),
42                map(Plaintext::parse, Argument::Plaintext),
43            )),
44        )(string)?;
45        // Parse the whitespace and comments from the string.
46        let (string, _) = Sanitizer::parse(string)?;
47        // Parse the ']' from the string.
48        let (string, _) = tag("]")(string)?;
49        // Output the plaintext.
50        Ok((string, arguments))
51    }
52
53    /// Parses a string into a future value, while tracking the depth of the data.
54    #[inline]
55    fn parse_internal(string: &str, depth: usize) -> ParserResult<Self> {
56        // Ensure that the depth is within the maximum limit.
57        // Note: `N::MAX_DATA_DEPTH` is an upper bound on the number of nested futures.
58        //  The true maximum is defined by `Transaction::<N>::MAX_TRANSITIONS`, however, that object is not accessible in this crate.
59        //  In practice, `MAX_DATA_DEPTH` is 32, while `MAX_TRANSITIONS` is 31.
60        if depth > N::MAX_DATA_DEPTH {
61            return map_res(take(0usize), |_| {
62                Err(error(format!("Found a future that exceeds maximum data depth ({})", N::MAX_DATA_DEPTH)))
63            })(string);
64        }
65        // Parse the whitespace and comments from the string.
66        let (string, _) = Sanitizer::parse(string)?;
67        // Parse the "{" from the string.
68        let (string, _) = tag("{")(string)?;
69
70        // Parse the whitespace and comments from the string.
71        let (string, _) = Sanitizer::parse(string)?;
72        // Parse the "program_id" from the string.
73        let (string, _) = tag("program_id")(string)?;
74        // Parse the whitespace from the string.
75        let (string, _) = Sanitizer::parse_whitespaces(string)?;
76        // Parse the ":" from the string.
77        let (string, _) = tag(":")(string)?;
78        // Parse the whitespace from the string.
79        let (string, _) = Sanitizer::parse_whitespaces(string)?;
80        // Parse the program ID from the string.
81        let (string, program_id) = ProgramID::parse(string)?;
82        // Parse the whitespace from the string.
83        let (string, _) = Sanitizer::parse_whitespaces(string)?;
84        // Parse the "," from the string.
85        let (string, _) = tag(",")(string)?;
86
87        // Parse the whitespace and comments from the string.
88        let (string, _) = Sanitizer::parse(string)?;
89        // Parse the "function_name" from the string.
90        let (string, _) = tag("function_name")(string)?;
91        // Parse the whitespace from the string.
92        let (string, _) = Sanitizer::parse_whitespaces(string)?;
93        // Parse the ":" from the string.
94        let (string, _) = tag(":")(string)?;
95        // Parse the whitespace from the string.
96        let (string, _) = Sanitizer::parse_whitespaces(string)?;
97        // Parse the function name from the string.
98        let (string, function_name) = Identifier::parse(string)?;
99        // Parse the whitespace from the string.
100        let (string, _) = Sanitizer::parse_whitespaces(string)?;
101        // Parse the "," from the string.
102        let (string, _) = tag(",")(string)?;
103
104        // Parse the whitespace and comments from the string.
105        let (string, _) = Sanitizer::parse(string)?;
106        // Parse the "arguments" from the string.
107        let (string, _) = tag("arguments")(string)?;
108        // Parse the whitespace from the string.
109        let (string, _) = Sanitizer::parse_whitespaces(string)?;
110        // Parse the ":" from the string.
111        let (string, _) = tag(":")(string)?;
112        // Parse the whitespace from the string.
113        let (string, _) = Sanitizer::parse_whitespaces(string)?;
114        // Parse the arguments from the string.
115        let (string, arguments) = Self::parse_arguments(string, depth)?;
116
117        // Parse the whitespace and comments from the string.
118        let (string, _) = Sanitizer::parse(string)?;
119        // Parse the "}" from the string.
120        let (string, _) = tag("}")(string)?;
121
122        Ok((string, Self::new(program_id, function_name, arguments)))
123    }
124}
125
126impl<N: Network> FromStr for Future<N> {
127    type Err = Error;
128
129    /// Returns a future from a string literal.
130    fn from_str(string: &str) -> Result<Self> {
131        match Self::parse(string) {
132            Ok((remainder, object)) => {
133                // Ensure the remainder is empty.
134                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
135                // Return the object.
136                Ok(object)
137            }
138            Err(error) => bail!("Failed to parse string. {error}"),
139        }
140    }
141}
142
143impl<N: Network> Debug for Future<N> {
144    /// Prints the future as a string.
145    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
146        Display::fmt(self, f)
147    }
148}
149
150impl<N: Network> Display for Future<N> {
151    /// Prints the future as a string.
152    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
153        self.fmt_internal(f, 0)
154    }
155}
156
157impl<N: Network> Future<N> {
158    /// Prints the future with the given indentation depth.
159    fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
160        /// The number of spaces to indent.
161        const INDENT: usize = 2;
162
163        // Print the opening brace.
164        write!(f, "{{")?;
165
166        // Print the program ID.
167        write!(
168            f,
169            "\n{:indent$}program_id: {program_id},",
170            "",
171            indent = (depth + 1) * INDENT,
172            program_id = self.program_id()
173        )?;
174        // Print the function name.
175        write!(
176            f,
177            "\n{:indent$}function_name: {function_name},",
178            "",
179            indent = (depth + 1) * INDENT,
180            function_name = self.function_name()
181        )?;
182        // Print the arguments.
183        // If the arguments are empty, print an empty array.
184        if self.arguments.is_empty() {
185            write!(f, "\n{:indent$}arguments: []", "", indent = (depth + 1) * INDENT)?;
186        } else {
187            write!(f, "\n{:indent$}arguments: [", "", indent = (depth + 1) * INDENT)?;
188            self.arguments.iter().enumerate().try_for_each(|(i, argument)| {
189                match argument {
190                    Argument::Plaintext(plaintext) => match i == self.arguments.len() - 1 {
191                        true => {
192                            // Print the last argument without a comma.
193                            write!(f, "\n{:indent$}{plaintext}", "", indent = (depth + 2) * INDENT)
194                        }
195                        // Print the argument with a comma.
196                        false => {
197                            write!(f, "\n{:indent$}{plaintext},", "", indent = (depth + 2) * INDENT)
198                        }
199                    },
200                    Argument::Future(future) => {
201                        // Print a newline.
202                        write!(f, "\n{:indent$}", "", indent = (depth + 2) * INDENT)?;
203                        // Print the argument.
204                        future.fmt_internal(f, depth + 2)?;
205                        // Print the closing brace.
206                        match i == self.arguments.len() - 1 {
207                            // Print the last member without a comma.
208                            true => write!(f, "\n{:indent$}", "", indent = (depth + 1) * INDENT),
209                            // Print the member with a comma.
210                            false => write!(f, ","),
211                        }
212                    }
213                    Argument::DynamicFuture(dynamic_future) => {
214                        match i == self.arguments.len() - 1 {
215                            true => {
216                                // Print the last argument without a comma.
217                                write!(f, "\n{:indent$}{dynamic_future}", "", indent = (depth + 2) * INDENT)
218                            }
219                            // Print the argument with a comma.
220                            false => write!(f, "\n{:indent$}{dynamic_future},", "", indent = (depth + 2) * INDENT),
221                        }
222                    }
223                }
224            })?;
225            // Print the closing bracket.
226            write!(f, "\n{:indent$}]", "", indent = (depth + 1) * INDENT)?;
227        }
228
229        // Print the closing brace.
230        write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
231    }
232}
233
234#[cfg(test)]
235mod tests {
236    use super::*;
237    use snarkvm_console_network::MainnetV0;
238
239    use core::str::FromStr;
240
241    type CurrentNetwork = MainnetV0;
242
243    #[test]
244    fn test_parse_future() {
245        // No argument case.
246        let expected = r"{
247  program_id: credits.aleo,
248  function_name: transfer,
249  arguments: []
250}";
251        let (remainder, candidate) =
252            Future::<CurrentNetwork>::parse("{ program_id: credits.aleo, function_name: transfer, arguments: [] }")
253                .unwrap();
254        assert!(remainder.is_empty());
255        assert_eq!(expected, candidate.to_string());
256        assert_eq!("", remainder);
257
258        // Literal arguments.
259        let expected = r"{
260  program_id: credits.aleo,
261  function_name: transfer_public_to_private,
262  arguments: [
263    aleo1g8qul5a44vk22u9uuvaewdcjw4v6xg8wx0llru39nnjn7eu08yrscxe4e2,
264    100000000u64
265  ]
266}";
267        let (remainder, candidate) = Future::<CurrentNetwork>::parse(
268            "{ program_id: credits.aleo, function_name: transfer_public_to_private, arguments: [ aleo1g8qul5a44vk22u9uuvaewdcjw4v6xg8wx0llru39nnjn7eu08yrscxe4e2, 100000000u64 ] }",
269        ).unwrap();
270        assert!(remainder.is_empty());
271        assert_eq!(expected, candidate.to_string());
272        assert_eq!("", remainder);
273    }
274
275    #[test]
276    fn test_parse_display_roundtrip() {
277        // Create a future with various argument types.
278        let inner = Future::<CurrentNetwork>::new(
279            ProgramID::from_str("inner.aleo").unwrap(),
280            Identifier::from_str("bar").unwrap(),
281            vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
282        );
283
284        let expected = Future::<CurrentNetwork>::new(
285            ProgramID::from_str("test.aleo").unwrap(),
286            Identifier::from_str("foo").unwrap(),
287            vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap()), Argument::Future(inner)],
288        );
289
290        // Convert to string and parse back.
291        let string = expected.to_string();
292        let candidate = Future::<CurrentNetwork>::from_str(&string).unwrap();
293
294        assert_eq!(expected, candidate);
295    }
296
297    #[test]
298    fn test_parse_future_with_dynamic_future_argument() {
299        // Create an inner future and convert to dynamic.
300        let inner = Future::<CurrentNetwork>::new(
301            ProgramID::from_str("inner.aleo").unwrap(),
302            Identifier::from_str("bar").unwrap(),
303            vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
304        );
305        let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
306
307        // Create a future with a dynamic future argument.
308        let expected = Future::<CurrentNetwork>::new(
309            ProgramID::from_str("test.aleo").unwrap(),
310            Identifier::from_str("foo").unwrap(),
311            vec![Argument::DynamicFuture(dynamic_inner)],
312        );
313
314        // Convert to string and parse back.
315        let string = expected.to_string();
316        let candidate = Future::<CurrentNetwork>::from_str(&string).unwrap();
317
318        // Verify structure.
319        assert_eq!(expected.program_id(), candidate.program_id());
320        assert_eq!(expected.function_name(), candidate.function_name());
321        assert_eq!(expected.arguments().len(), candidate.arguments().len());
322
323        // Verify the dynamic future argument matches.
324        match (&expected.arguments()[0], &candidate.arguments()[0]) {
325            (Argument::DynamicFuture(e), Argument::DynamicFuture(c)) => {
326                assert_eq!(e.program_name(), c.program_name());
327                assert_eq!(e.program_network(), c.program_network());
328                assert_eq!(e.function_name(), c.function_name());
329                assert_eq!(e.checksum(), c.checksum());
330            }
331            _ => panic!("Expected DynamicFuture argument"),
332        }
333    }
334
335    #[test]
336    fn test_deeply_nested_future() {
337        // A helper function to iteratively create a deeply nested future.
338        fn create_nested_future(depth: usize) -> String {
339            // Define the base case.
340            let root = r"{
341                program_id: foo.aleo,
342                function_name: bar,
343                arguments: []
344            }";
345            // Define the prefix and suffix for the nested future.
346            let prefix = r"{
347                program_id: foo.aleo,
348                function_name: bar,
349                arguments: ["
350                .repeat(depth);
351            let suffix = r"]}".repeat(depth);
352            // Concatenate the prefix, root, and suffix to create the nested future.
353            format!("{prefix}{root}{suffix}")
354        }
355
356        // A helper function to test the parsing of a deeply nested future.
357        fn run_test(depth: usize, expected_error: bool) {
358            // Create the nested future string.
359            let nested_future_string = create_nested_future(depth);
360            // Parse the nested future.
361            let result = Future::<CurrentNetwork>::parse(&nested_future_string);
362            // Check if the result is an error.
363            match expected_error {
364                true => {
365                    assert!(result.is_err());
366                    return;
367                }
368                false => assert!(result.is_ok()),
369            };
370            // Unwrap the result.
371            let (remainder, candidate) = result.unwrap();
372            // Ensure the remainder is empty.
373            assert!(
374                remainder.is_empty(),
375                "Failed to parse deeply nested future. Found invalid character in: \"{remainder}\""
376            );
377            // Strip the expected string of whitespace.
378            let expected = nested_future_string.replace("\n", "").replace(" ", "").replace("\t", "");
379            // Strip the candidate string of whitespace.
380            let candidate_str = candidate.to_string().replace("\n", "").replace(" ", "").replace("\t", "");
381            // Ensure the expected and candidate strings are equal.
382            assert_eq!(expected, candidate_str, "Expected: {expected}, Candidate: {candidate_str}");
383        }
384
385        // Initialize a set of depths to test.
386        let mut depths = (0usize..100).collect_vec();
387        depths.extend((100..1000).step_by(100));
388        depths.extend((1000..10000).step_by(1000));
389        depths.extend((10000..100000).step_by(10000));
390
391        // For each depth, test the parsing of a deeply nested future.
392        for depth in depths {
393            run_test(depth, depth > CurrentNetwork::MAX_DATA_DEPTH);
394        }
395    }
396}