Skip to main content

snarkvm_console_program/data/future/
bytes.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> FromBytes for Future<N> {
19    /// Reads in a future from a buffer.
20    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
21        // Read the future.
22        Self::read_le_internal(&mut reader, 0)
23    }
24}
25
26impl<N: Network> Future<N> {
27    /// Reads in a future from a buffer, while tracking the depth of the data.
28    fn read_le_internal<R: Read>(mut reader: R, depth: usize) -> IoResult<Self> {
29        // Ensure that the depth is within the maximum limit.
30        // Note: `N::MAX_DATA_DEPTH` is an upper bound on the number of nested futures.
31        //  The true maximum is defined by `Transaction::<N>::MAX_TRANSITIONS`, however, that object is not accessible in this crate.
32        //  In practice, `MAX_DATA_DEPTH` is 32, while `MAX_TRANSITIONS` is 31.
33        if depth > N::MAX_DATA_DEPTH {
34            return Err(error(format!(
35                "Failed to deserialize plaintext: Depth exceeds maximum limit: {}",
36                N::MAX_DATA_DEPTH
37            )));
38        }
39        // Read the program ID.
40        let program_id = ProgramID::read_le(&mut reader)?;
41        // Read the function name.
42        let function_name = Identifier::<N>::read_le(&mut reader)?;
43        // Read the number of arguments to the future.
44        let num_arguments = u8::read_le(&mut reader)? as usize;
45        if num_arguments > N::MAX_INPUTS {
46            return Err(error("Failed to read future: too many arguments"));
47        };
48        // Read the arguments.
49        let mut arguments = Vec::with_capacity(num_arguments);
50        for _ in 0..num_arguments {
51            // Read the argument (in 2 steps to prevent infinite recursion).
52            let num_bytes = u16::read_le(&mut reader)?;
53            // Read the argument bytes.
54            let mut bytes = Vec::new();
55            (&mut reader).take(num_bytes as u64).read_to_end(&mut bytes)?;
56            // Ensure the declared region is entirely present.
57            if bytes.len() != num_bytes as usize {
58                return Err(error("Failed to read future: argument is truncated"));
59            }
60            // Recover the argument.
61            let mut slice = bytes.as_slice();
62            let entry = Argument::read_le_internal(&mut slice, depth)?;
63            // Ensure the argument fills its region. Bytes left unread would be dropped
64            // on re-serialization, giving one value a second encoding.
65            if !slice.is_empty() {
66                return Err(error("Failed to read future: argument has trailing bytes"));
67            }
68            // Add the argument.
69            arguments.push(entry);
70        }
71        // Return the future.
72        Ok(Self::new(program_id, function_name, arguments))
73    }
74}
75
76impl<N: Network> ToBytes for Future<N> {
77    /// Writes a future to a buffer.
78    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
79        // Write the program ID.
80        self.program_id.write_le(&mut writer)?;
81        // Write the function name.
82        self.function_name.write_le(&mut writer)?;
83        // Write the number of arguments.
84        if self.arguments.len() > N::MAX_INPUTS {
85            return Err(error("Failed to write future: too many arguments"));
86        };
87        u8::try_from(self.arguments.len()).map_err(error)?.write_le(&mut writer)?;
88        // Write each argument.
89        for argument in &self.arguments {
90            // Write the argument (performed in 2 steps to prevent infinite recursion).
91            let bytes = argument.to_bytes_le().map_err(error)?;
92            // Write the number of bytes.
93            u16::try_from(bytes.len()).map_err(error)?.write_le(&mut writer)?;
94            // Write the bytes.
95            bytes.write_le(&mut writer)?;
96        }
97        Ok(())
98    }
99}
100
101impl<N: Network> Argument<N> {
102    fn read_le_internal<R: Read>(mut reader: R, depth: usize) -> IoResult<Self>
103    where
104        Self: Sized,
105    {
106        // Read the index.
107        let index = u8::read_le(&mut reader)?;
108        // Read the argument.
109        let argument = match index {
110            0 => Self::Plaintext(Plaintext::read_le(&mut reader)?),
111            1 => Self::Future(Future::read_le_internal(&mut reader, depth + 1)?),
112            2 => Self::DynamicFuture(DynamicFuture::read_le(&mut reader)?),
113            3.. => return Err(error(format!("Failed to decode future argument {index}"))),
114        };
115        Ok(argument)
116    }
117}
118
119impl<N: Network> ToBytes for Argument<N> {
120    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
121        match self {
122            Self::Plaintext(plaintext) => {
123                0u8.write_le(&mut writer)?;
124                plaintext.write_le(&mut writer)
125            }
126            Self::Future(future) => {
127                1u8.write_le(&mut writer)?;
128                future.write_le(&mut writer)
129            }
130            Self::DynamicFuture(dynamic_future) => {
131                2u8.write_le(&mut writer)?;
132                dynamic_future.write_le(&mut writer)
133            }
134        }
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use snarkvm_console_network::MainnetV0;
142
143    use core::str::FromStr;
144
145    type CurrentNetwork = MainnetV0;
146
147    #[test]
148    fn test_bytes() {
149        // Check the future manually.
150        let expected =
151            Future::<CurrentNetwork>::from_str("{ program_id: credits.aleo, function_name: transfer, arguments: [] }")
152                .unwrap();
153
154        // Check the byte representation.
155        let expected_bytes = expected.to_bytes_le().unwrap();
156        assert_eq!(expected, Future::read_le(&expected_bytes[..]).unwrap());
157    }
158
159    #[test]
160    fn test_bytes_with_plaintext_arguments() {
161        // Create a future with plaintext arguments.
162        let expected = Future::<CurrentNetwork>::new(
163            ProgramID::from_str("test.aleo").unwrap(),
164            Identifier::from_str("foo").unwrap(),
165            vec![
166                Argument::Plaintext(Plaintext::from_str("100u64").unwrap()),
167                Argument::Plaintext(Plaintext::from_str("true").unwrap()),
168            ],
169        );
170
171        // Check the byte representation.
172        let expected_bytes = expected.to_bytes_le().unwrap();
173        let candidate = Future::read_le(&expected_bytes[..]).unwrap();
174        assert_eq!(expected, candidate);
175    }
176
177    #[test]
178    fn test_bytes_with_dynamic_future_argument() {
179        // Create an inner future.
180        let inner = Future::<CurrentNetwork>::new(
181            ProgramID::from_str("inner.aleo").unwrap(),
182            Identifier::from_str("bar").unwrap(),
183            vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
184        );
185
186        // Convert to dynamic future.
187        let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
188
189        // Create a future with a dynamic future argument.
190        let expected = Future::<CurrentNetwork>::new(
191            ProgramID::from_str("outer.aleo").unwrap(),
192            Identifier::from_str("baz").unwrap(),
193            vec![Argument::DynamicFuture(dynamic_inner)],
194        );
195
196        // Check the byte representation.
197        let expected_bytes = expected.to_bytes_le().unwrap();
198        let candidate = Future::read_le(&expected_bytes[..]).unwrap();
199
200        // Verify the structure matches.
201        assert_eq!(expected.program_id(), candidate.program_id());
202        assert_eq!(expected.function_name(), candidate.function_name());
203        assert_eq!(expected.arguments().len(), candidate.arguments().len());
204
205        // Verify the dynamic future argument.
206        match (&expected.arguments()[0], &candidate.arguments()[0]) {
207            (Argument::DynamicFuture(e), Argument::DynamicFuture(c)) => {
208                assert_eq!(e.program_name(), c.program_name());
209                assert_eq!(e.program_network(), c.program_network());
210                assert_eq!(e.function_name(), c.function_name());
211                assert_eq!(e.checksum(), c.checksum());
212            }
213            _ => panic!("Expected DynamicFuture argument"),
214        }
215    }
216
217    #[test]
218    fn test_bytes_with_nested_future_argument() {
219        // Create an inner future.
220        let inner = Future::<CurrentNetwork>::new(
221            ProgramID::from_str("inner.aleo").unwrap(),
222            Identifier::from_str("bar").unwrap(),
223            vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
224        );
225
226        // Create a future with a nested future argument.
227        let expected = Future::<CurrentNetwork>::new(
228            ProgramID::from_str("outer.aleo").unwrap(),
229            Identifier::from_str("baz").unwrap(),
230            vec![Argument::Future(inner)],
231        );
232
233        // Check the byte representation.
234        let expected_bytes = expected.to_bytes_le().unwrap();
235        let candidate = Future::read_le(&expected_bytes[..]).unwrap();
236        assert_eq!(expected, candidate);
237    }
238
239    #[test]
240    fn test_bytes_with_mixed_arguments() {
241        // Create an inner future.
242        let inner = Future::<CurrentNetwork>::new(
243            ProgramID::from_str("inner.aleo").unwrap(),
244            Identifier::from_str("bar").unwrap(),
245            vec![],
246        );
247
248        // Convert to dynamic future.
249        let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
250
251        // Create a future with mixed arguments.
252        let expected = Future::<CurrentNetwork>::new(
253            ProgramID::from_str("test.aleo").unwrap(),
254            Identifier::from_str("mixed").unwrap(),
255            vec![
256                Argument::Plaintext(Plaintext::from_str("100u64").unwrap()),
257                Argument::Future(inner),
258                Argument::DynamicFuture(dynamic_inner),
259            ],
260        );
261
262        // Check the byte representation.
263        let expected_bytes = expected.to_bytes_le().unwrap();
264        let candidate = Future::read_le(&expected_bytes[..]).unwrap();
265
266        // Verify structure.
267        assert_eq!(expected.program_id(), candidate.program_id());
268        assert_eq!(expected.function_name(), candidate.function_name());
269        assert_eq!(expected.arguments().len(), candidate.arguments().len());
270    }
271
272    // A helper function to get the depth of a future.
273    fn get_depth<N: Network>(future: &Future<N>) -> usize {
274        // Count the number of nested futures.
275        future
276            .arguments
277            .iter()
278            .map(|arg| match arg {
279                Argument::Plaintext(_) => 0,
280                Argument::Future(future) => 1 + get_depth(future),
281                Argument::DynamicFuture(_) => panic!("Dynamic futures are not used in this test"),
282            })
283            .sum()
284    }
285
286    #[test]
287    fn test_deeply_nested_future() {
288        // Creates a nested `Future`.
289        // This method is iterative to avoid stack overflows.
290        fn create_nested_future(depth: usize) -> Vec<u8> {
291            // Start from the innermost value.
292            let mut result = Future::<CurrentNetwork>::from_str(
293                r"{
294                program_id: foo.aleo,
295                function_name: bar,
296                arguments: []
297            }",
298            )
299            .unwrap()
300            .to_bytes_le()
301            .unwrap();
302            // Reverse the bytes.
303            result.reverse();
304            // Build up the structure in reverse.
305            for _ in 0..depth {
306                // Write the variant of the argument in reverse.
307                let mut variant = 1u8.to_bytes_le().unwrap();
308                variant.reverse();
309                result.extend(variant);
310                // Write the size of the object in bytes in reverse.
311                let mut length = (u16::try_from(result.len()).unwrap()).to_bytes_le().unwrap();
312                length.reverse();
313                result.extend(length);
314                // Write the number of arguments in reverse.
315                let mut num_elements = 1u8.to_bytes_le().unwrap();
316                num_elements.reverse();
317                result.extend(num_elements);
318                // Write the function name in reverse.
319                let mut function_name = Identifier::<CurrentNetwork>::from_str("bar").unwrap().to_bytes_le().unwrap();
320                function_name.reverse();
321                result.extend(function_name);
322                // Write the program ID in reverse.
323                let mut program_id = ProgramID::<CurrentNetwork>::from_str("foo.aleo").unwrap().to_bytes_le().unwrap();
324                program_id.reverse();
325                result.extend(program_id);
326            }
327            // Reverse the result to get the correct order.
328            result.reverse();
329            result
330        }
331
332        // A helper function to run the test.
333        fn run_test(expected_depth: usize, input: Vec<u8>, expected_error: bool) {
334            // Parse the input string.
335            let result = Future::<CurrentNetwork>::read_le(&*input);
336            // // Check if the result is an error.
337            match expected_error {
338                true => {
339                    assert!(result.is_err());
340                    return;
341                }
342                false => assert!(result.is_ok()),
343            };
344            // Unwrap the result.
345            let candidate = result.unwrap();
346            // Check if the candidate is equal to the input, with whitespace removed.
347            assert_eq!(input, candidate.to_bytes_le().unwrap());
348            // Check if the candidate is equal to the expected depth.
349            assert_eq!(get_depth(&candidate), expected_depth);
350        }
351
352        // Initialize a sequence of depths to check.
353        // Note: It is not possible to create a `Future` of depth 4000 in this test as it's size would exceed `u16::MAX`.
354        let mut depths = (0usize..100).collect_vec();
355        depths.extend((100..3900).step_by(100));
356
357        // Test deeply nested arrays with different literal types.
358        for i in depths.iter().copied() {
359            run_test(i, create_nested_future(i), i > CurrentNetwork::MAX_DATA_DEPTH);
360            run_test(i, create_nested_future(i), i > CurrentNetwork::MAX_DATA_DEPTH);
361            run_test(i, create_nested_future(i), i > CurrentNetwork::MAX_DATA_DEPTH);
362            run_test(i, create_nested_future(i), i > CurrentNetwork::MAX_DATA_DEPTH);
363        }
364    }
365
366    /// Builds the encoding of a one-argument future, declaring `padding` more bytes
367    /// for the argument than the argument actually occupies.
368    fn future_with_padded_argument(padding: usize) -> Result<Vec<u8>> {
369        let argument = Argument::<CurrentNetwork>::Plaintext(Plaintext::from_str("7u8")?);
370        let argument_bytes = argument.to_bytes_le()?;
371
372        let mut bytes = Vec::new();
373        ProgramID::<CurrentNetwork>::from_str("credits.aleo")?.write_le(&mut bytes)?;
374        Identifier::<CurrentNetwork>::from_str("transfer")?.write_le(&mut bytes)?;
375        1u8.write_le(&mut bytes)?;
376        u16::try_from(argument_bytes.len() + padding)?.write_le(&mut bytes)?;
377        bytes.extend_from_slice(&argument_bytes);
378        bytes.extend(std::iter::repeat_n(0u8, padding));
379        Ok(bytes)
380    }
381
382    #[test]
383    fn test_future_argument_rejects_trailing_bytes() -> Result<()> {
384        // Unpadded, this is the encoding `to_bytes_le` produces, and it must keep working.
385        let exact = future_with_padded_argument(0)?;
386        let future = Future::<CurrentNetwork>::read_le(&exact[..])?;
387        assert_eq!(exact, future.to_bytes_le()?, "the unpadded encoding must round trip");
388
389        // An argument region larger than the argument fills leaves bytes unread.
390        // Accepting it gives one future -- and so one transaction -- a second
391        // encoding, since re-serializing emits the exact length.
392        for padding in [1, 8, 32] {
393            let padded = future_with_padded_argument(padding)?;
394            assert_ne!(padded, exact);
395            assert!(
396                Future::<CurrentNetwork>::read_le(&padded[..]).is_err(),
397                "a future argument declaring {padding} bytes more than it uses must be rejected"
398            );
399        }
400        Ok(())
401    }
402}