snarkvm_circuit_program/data/plaintext/
from_bits.rs

1// Copyright (c) 2019-2025 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<A: Aleo> FromBits for Plaintext<A> {
19    type Boolean = Boolean<A>;
20
21    /// Initializes a new plaintext from a list of little-endian bits *without* trailing zeros.
22    fn from_bits_le(bits_le: &[Boolean<A>]) -> Self {
23        Self::from_bits_le_internal(bits_le, 0)
24    }
25
26    /// Initializes a new plaintext from a list of big-endian bits *without* trailing zeros.
27    fn from_bits_be(bits_be: &[Boolean<A>]) -> Self {
28        Self::from_bits_be_internal(bits_be, 0)
29    }
30}
31
32impl<A: Aleo> Plaintext<A> {
33    /// Initializes a new plaintext from a list of little-endian bits *without* trailing zeros, while tracking the depth of the data.
34    fn from_bits_le_internal(bits_le: &[Boolean<A>], depth: usize) -> Self {
35        // Ensure that the depth is within the maximum limit.
36        if depth > <A::Network as console::Network>::MAX_DATA_DEPTH {
37            A::halt(format!(
38                "Plaintext depth exceeds maximum limit: {}",
39                <A::Network as console::Network>::MAX_DATA_DEPTH
40            ))
41        }
42
43        let bits = bits_le;
44
45        // The starting index used to create subsequent subslices of the `bits` slice.
46        let mut index = 0;
47
48        // Helper function to get the next n bits as a slice.
49        let mut next_bits = |n: usize| -> &[Boolean<A>] {
50            // Safely procure a subslice with the length `n` starting at `index`.
51            let subslice = bits.get(index..index + n);
52            // Check if the range is within bounds.
53            if let Some(next_bits) = subslice {
54                // Move the starting index.
55                index += n;
56                // Return the subslice.
57                next_bits
58            } else {
59                A::halt("Insufficient bits.")
60            }
61        };
62
63        let mut variant = next_bits(2).iter().map(|b| b.eject_value());
64        let variant1 = variant.next().unwrap();
65        let variant2 = variant.next().unwrap();
66        let variant = [variant1, variant2];
67
68        // Literal
69        if variant == [false, false] {
70            let literal_variant = U8::from_bits_le(next_bits(8));
71            let literal_size = U16::from_bits_le(next_bits(16)).eject_value();
72            let literal = Literal::from_bits_le(&literal_variant, next_bits(*literal_size as usize));
73
74            // Cache the plaintext bits, and return the literal.
75            Self::Literal(literal, OnceCell::with_value(bits_le.to_vec()))
76        }
77        // Struct
78        else if variant == [false, true] {
79            let num_members = U8::from_bits_le(next_bits(8)).eject_value();
80
81            let mut members = IndexMap::with_capacity(*num_members as usize);
82            for _ in 0..*num_members {
83                let identifier_size = U8::from_bits_le(next_bits(8)).eject_value();
84                let identifier = Identifier::from_bits_le(next_bits(*identifier_size as usize));
85
86                let member_size = U16::from_bits_le(next_bits(16)).eject_value();
87                let value = Plaintext::from_bits_le_internal(next_bits(*member_size as usize), depth + 1);
88
89                members.insert(identifier, value);
90            }
91
92            // Cache the plaintext bits, and return the struct.
93            Self::Struct(members, OnceCell::with_value(bits_le.to_vec()))
94        }
95        // Array
96        else if variant == [true, false] {
97            let num_elements = U32::from_bits_le(next_bits(32)).eject_value();
98
99            let mut elements = Vec::with_capacity(*num_elements as usize);
100            for _ in 0..*num_elements {
101                let element_size = U16::from_bits_le(next_bits(16)).eject_value();
102                let value = Plaintext::from_bits_le_internal(next_bits(*element_size as usize), depth + 1);
103
104                elements.push(value);
105            }
106
107            // Cache the plaintext bits, and return the array.
108            Self::Array(elements, OnceCell::with_value(bits_le.to_vec()))
109        }
110        // Unknown variant.
111        else {
112            A::halt("Unknown plaintext variant.")
113        }
114    }
115
116    /// Initializes a new plaintext from a list of big-endian bits *without* trailing zeros, while tracking the depth of the data.
117    fn from_bits_be_internal(bits_be: &[Boolean<A>], depth: usize) -> Self {
118        // Ensure that the depth is within the maximum limit.
119        if depth > <A::Network as console::Network>::MAX_DATA_DEPTH {
120            A::halt(format!(
121                "Plaintext depth exceeds maximum limit: {}",
122                <A::Network as console::Network>::MAX_DATA_DEPTH
123            ))
124        }
125
126        let bits = bits_be;
127
128        // The starting index used to create subsequent subslices of the `bits` slice.
129        let mut index = 0;
130
131        // Helper function to get the next n bits as a slice.
132        let mut next_bits = |n: usize| -> &[Boolean<A>] {
133            // Safely procure a subslice with the length `n` starting at `index`.
134            let subslice = bits.get(index..index + n);
135            // Check if the range is within bounds.
136            if let Some(next_bits) = subslice {
137                // Move the starting index.
138                index += n;
139                // Return the subslice.
140                next_bits
141            } else {
142                A::halt("Insufficient bits.")
143            }
144        };
145
146        let mut variant = next_bits(2).iter().map(|b| b.eject_value());
147        let variant1 = variant.next().unwrap();
148        let variant2 = variant.next().unwrap();
149        let variant = [variant1, variant2];
150
151        // Literal
152        if variant == [false, false] {
153            let literal_variant = U8::from_bits_be(next_bits(8));
154            let literal_size = U16::from_bits_be(next_bits(16)).eject_value();
155            let literal = Literal::from_bits_be(&literal_variant, next_bits(*literal_size as usize));
156
157            // Cache the plaintext bits, and return the literal.
158            Self::Literal(literal, OnceCell::with_value(bits_be.to_vec()))
159        }
160        // Struct
161        else if variant == [false, true] {
162            let num_members = U8::from_bits_be(next_bits(8)).eject_value();
163
164            let mut members = IndexMap::with_capacity(*num_members as usize);
165            for _ in 0..*num_members {
166                let identifier_size = U8::from_bits_be(next_bits(8)).eject_value();
167                let identifier = Identifier::from_bits_be(next_bits(*identifier_size as usize));
168
169                let member_size = U16::from_bits_be(next_bits(16)).eject_value();
170                let value = Plaintext::from_bits_be_internal(next_bits(*member_size as usize), depth + 1);
171
172                members.insert(identifier, value);
173            }
174
175            // Cache the plaintext bits, and return the struct.
176            Self::Struct(members, OnceCell::with_value(bits_be.to_vec()))
177        }
178        // Array
179        else if variant == [true, false] {
180            let num_elements = U32::from_bits_be(next_bits(32)).eject_value();
181
182            let mut elements = Vec::with_capacity(*num_elements as usize);
183            for _ in 0..*num_elements {
184                let element_size = U16::from_bits_be(next_bits(16)).eject_value();
185                let value = Plaintext::from_bits_be_internal(next_bits(*element_size as usize), depth + 1);
186
187                elements.push(value);
188            }
189
190            // Cache the plaintext bits, and return the array.
191            Self::Array(elements, OnceCell::with_value(bits_be.to_vec()))
192        }
193        // Unknown variant.
194        else {
195            A::halt("Unknown plaintext variant.")
196        }
197    }
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203    use console_root::network::{MainnetV0, Network};
204    use snarkvm_circuit_network::AleoV0;
205
206    type CurrentAleo = AleoV0;
207    type CurrentNetwork = MainnetV0;
208
209    // A helper function to get the depth of the plaintext.
210    fn get_depth(plaintext: &Plaintext<CurrentAleo>) -> usize {
211        match plaintext {
212            Plaintext::Literal(_, _) => 0,
213            Plaintext::Struct(members, _) => members.values().map(get_depth).max().unwrap_or(0) + 1,
214            Plaintext::Array(elements, _) => elements.iter().map(get_depth).max().unwrap_or(0) + 1,
215        }
216    }
217
218    #[test]
219    fn test_deeply_nested_plaintext_bits_le() {
220        // Creates a nested array-like `Plaintext` structure by wrapping a root value `depth` times.
221        fn create_nested_array(mode: Mode, depth: usize, root: impl Display) -> Vec<Boolean<CurrentAleo>> {
222            // Start from the innermost value.
223            let mut result = console::Plaintext::<CurrentNetwork>::from_str(&root.to_string()).unwrap().to_bits_le();
224            // Reverse the bytes.
225            result.reverse();
226            // Build up the structure in reverse.
227            for _ in 0..depth {
228                // Write the size of the object in bits in reverse.
229                let mut length = (u16::try_from(result.len()).unwrap()).to_bits_le();
230                length.reverse();
231                result.extend(length);
232                // Write the number of elements in the array in reverse.
233                let mut num_elements = 1u32.to_bits_le();
234                num_elements.reverse();
235                result.extend(num_elements);
236                // Write the plaintext variant in reverse.
237                result.extend([false, true]);
238            }
239            // Reverse the result to get the correct order.
240            result.reverse();
241            // Transform the bits into a vector of booleans.
242            let result: Vec<Boolean<CurrentAleo>> = result.into_iter().map(|input| Boolean::new(mode, input)).collect();
243            result
244        }
245
246        // Creates a nested struct-like `Plaintext` structure by wrapping a root value `depth` times.
247        fn create_nested_struct(mode: Mode, depth: usize, root: impl Display) -> Vec<Boolean<CurrentAleo>> {
248            // Start from the innermost value.
249            let mut result = console::Plaintext::<CurrentNetwork>::from_str(&root.to_string()).unwrap().to_bits_le();
250            // Reverse the bytes.
251            result.reverse();
252            // Build up the structure in reverse.
253            for _ in 0..depth {
254                // Write the size of the object in bits in reverse.
255                let mut length = (u16::try_from(result.len()).unwrap()).to_bits_le();
256                length.reverse();
257                result.extend(length);
258                // Write the member name in reverse.
259                let mut member_name = console::Identifier::<CurrentNetwork>::from_str("inner").unwrap().to_bits_le();
260                let mut member_name_length = u8::try_from(member_name.len()).unwrap().to_bits_le();
261                member_name.reverse();
262                result.extend(member_name);
263                // Write the length of the member name in reverse.
264                member_name_length.reverse();
265                result.extend(member_name_length);
266                // Write the number of members in the struct in reverse.
267                let mut num_members = 1u8.to_bits_le();
268                num_members.reverse();
269                result.extend(num_members);
270                // Write the plaintext variant in reverse.
271                result.extend([true, false]);
272            }
273            // Reverse the result to get the correct order.
274            result.reverse();
275            // Transform the bits into a vector of booleans.
276            let result: Vec<Boolean<CurrentAleo>> = result.into_iter().map(|input| Boolean::new(mode, input)).collect();
277            result
278        }
279
280        // Creates a nested `Plaintext` structure with alternating array and struct wrappers.
281        fn create_alternated_nested(mode: Mode, depth: usize, root: impl Display) -> Vec<Boolean<CurrentAleo>> {
282            // Start from the innermost value.
283            let mut result = console::Plaintext::<CurrentNetwork>::from_str(&root.to_string()).unwrap().to_bits_le();
284            // Reverse the bytes.
285            result.reverse();
286            // Build up the structure in reverse.
287            for i in 0..depth {
288                // Write the size of the object in bits in reverse.
289                let mut length = (u16::try_from(result.len()).unwrap()).to_bits_le();
290                length.reverse();
291                result.extend(length);
292                // Determine the type of the wrapper (array or struct) and handle accordingly.
293                if i % 2 == 0 {
294                    // Write the number of elements in the array in reverse.
295                    let mut num_elements = 1u32.to_bits_le();
296                    num_elements.reverse();
297                    result.extend(num_elements);
298                    // Write the plaintext variant for array in reverse.
299                    result.extend([false, true]);
300                } else {
301                    // Write the member name in reverse.
302                    let mut member_name =
303                        console::Identifier::<CurrentNetwork>::from_str("inner").unwrap().to_bits_le();
304                    let mut member_name_length = u8::try_from(member_name.len()).unwrap().to_bits_le();
305                    member_name.reverse();
306                    result.extend(member_name);
307                    // Write the member name length in reverse.
308                    member_name_length.reverse();
309                    result.extend(member_name_length);
310                    // Write the number of members in the struct in reverse.
311                    let mut num_members = 1u8.to_bits_le();
312                    num_members.reverse();
313                    result.extend(num_members);
314                    // Write the plaintext variant for struct in reverse.
315                    result.extend([true, false]);
316                }
317            }
318            // Reverse the result to get the correct order.
319            result.reverse();
320            // Transform the bits into a vector of booleans.
321            let result: Vec<Boolean<CurrentAleo>> = result.into_iter().map(|input| Boolean::new(mode, input)).collect();
322            result
323        }
324
325        // A helper function to run the test.
326        fn run_test(expected_depth: usize, input: Vec<Boolean<CurrentAleo>>, expected_error: bool) {
327            // Parse the input string, catching any panics.
328            let result = std::panic::catch_unwind(|| Plaintext::<CurrentAleo>::from_bits_le(&input));
329            CurrentAleo::reset();
330            // Check if the result is an error.
331            match expected_error {
332                true => {
333                    assert!(result.is_err());
334                    return;
335                }
336                false => assert!(result.is_ok()),
337            };
338            // Unwrap the result.
339            let candidate = result.unwrap();
340            // Check if the candidate is equal to the input.
341            for (i, expected_bit) in input.iter().enumerate() {
342                assert_eq!(expected_bit.eject_value(), candidate.to_bits_le()[i].eject_value());
343            }
344            // Check if the depth of the candidate is equal to the expected depth.
345            assert_eq!(get_depth(&candidate), expected_depth);
346        }
347
348        // Initialize a sequence of depths to check.
349        // Note that 890 is approximate maximum depth that can be constructed in this test.
350        let mut depths = (0usize..100).collect_vec();
351        depths.extend((100..890).step_by(10));
352
353        // Test deeply nested arrays with different literal types.
354        for i in depths.iter().copied() {
355            run_test(i, create_nested_array(Mode::Constant, i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
356            run_test(i, create_nested_array(Mode::Public, i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
357            run_test(i, create_nested_array(Mode::Private, i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
358            run_test(i, create_nested_array(Mode::Constant, i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
359        }
360
361        // Test deeply nested structs with different literal types.
362        for i in depths.iter().copied() {
363            run_test(i, create_nested_struct(Mode::Public, i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
364            run_test(i, create_nested_struct(Mode::Private, i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
365            run_test(i, create_nested_struct(Mode::Constant, i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
366            run_test(i, create_nested_struct(Mode::Public, i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
367        }
368
369        // Test alternating nested arrays and structs.
370        for i in depths.iter().copied() {
371            run_test(i, create_alternated_nested(Mode::Private, i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
372            run_test(i, create_alternated_nested(Mode::Constant, i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
373            run_test(i, create_alternated_nested(Mode::Public, i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
374            run_test(i, create_alternated_nested(Mode::Private, i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
375        }
376    }
377
378    #[test]
379    fn test_deeply_nested_plaintext_bits_be() {
380        // Creates a nested array-like `Plaintext` structure by wrapping a root value `depth` times.
381        fn create_nested_array(mode: Mode, depth: usize, root: impl Display) -> Vec<Boolean<CurrentAleo>> {
382            // Start from the innermost value.
383            let mut result = console::Plaintext::<CurrentNetwork>::from_str(&root.to_string()).unwrap().to_bits_be();
384            // Reverse the bits.
385            result.reverse();
386            // Build up the structure in reverse.
387            for _ in 0..depth {
388                // Write the size of the object in bits in reverse.
389                let mut length = (u16::try_from(result.len()).unwrap()).to_bits_be();
390                length.reverse();
391                result.extend(length);
392                // Write the number of elements in the array in reverse.
393                let mut num_elements = 1u32.to_bits_be();
394                num_elements.reverse();
395                result.extend(num_elements);
396                // Write the plaintext variant in reverse.
397                result.extend([false, true]);
398            }
399            // Reverse the result to get the correct order.
400            result.reverse();
401            // Transform the bits into a vector of booleans.
402            let result: Vec<Boolean<CurrentAleo>> = result.into_iter().map(|input| Boolean::new(mode, input)).collect();
403            result
404        }
405
406        // Creates a nested struct-like `Plaintext` structure by wrapping a root value `depth` times.
407        fn create_nested_struct(mode: Mode, depth: usize, root: impl Display) -> Vec<Boolean<CurrentAleo>> {
408            // Start from the innermost value.
409            let mut result = console::Plaintext::<CurrentNetwork>::from_str(&root.to_string()).unwrap().to_bits_be();
410            // Reverse the bytes.
411            result.reverse();
412            // Build up the structure in reverse.
413            for _ in 0..depth {
414                // Write the size of the object in bits in reverse.
415                let mut length = (u16::try_from(result.len()).unwrap()).to_bits_be();
416                length.reverse();
417                result.extend(length);
418                // Write the member name in reverse.
419                let mut member_name = console::Identifier::<CurrentNetwork>::from_str("inner").unwrap().to_bits_be();
420                let mut member_name_length = u8::try_from(member_name.len()).unwrap().to_bits_be();
421                member_name.reverse();
422                result.extend(member_name);
423                // Write the length of the member name in reverse.
424                member_name_length.reverse();
425                result.extend(member_name_length);
426                // Write the number of members in the struct in reverse.
427                let mut num_members = 1u8.to_bits_be();
428                num_members.reverse();
429                result.extend(num_members);
430                // Write the plaintext variant in reverse.
431                result.extend([true, false]);
432            }
433            // Reverse the result to get the correct order.
434            result.reverse();
435            // Transform the bits into a vector of booleans.
436            let result: Vec<Boolean<CurrentAleo>> = result.into_iter().map(|input| Boolean::new(mode, input)).collect();
437            result
438        }
439
440        // Creates a nested `Plaintext` structure with alternating array and struct wrappers.
441        fn create_alternated_nested(mode: Mode, depth: usize, root: impl Display) -> Vec<Boolean<CurrentAleo>> {
442            // Start from the innermost value.
443            let mut result = console::Plaintext::<CurrentNetwork>::from_str(&root.to_string()).unwrap().to_bits_be();
444            // Reverse the bytes.
445            result.reverse();
446            // Build up the structure in reverse.
447            for i in 0..depth {
448                // Write the size of the object in bits in reverse.
449                let mut length = (u16::try_from(result.len()).unwrap()).to_bits_be();
450                length.reverse();
451                result.extend(length);
452                // Determine the type of the wrapper (array or struct) and handle accordingly.
453                if i % 2 == 0 {
454                    // Write the number of elements in the array in reverse.
455                    let mut num_elements = 1u32.to_bits_be();
456                    num_elements.reverse();
457                    result.extend(num_elements);
458                    // Write the plaintext variant for array in reverse.
459                    result.extend([false, true]);
460                } else {
461                    // Write the member name in reverse.
462                    let mut member_name =
463                        console::Identifier::<CurrentNetwork>::from_str("inner").unwrap().to_bits_be();
464                    let mut member_name_length = u8::try_from(member_name.len()).unwrap().to_bits_be();
465                    member_name.reverse();
466                    result.extend(member_name);
467                    // Write the member name length in reverse.
468                    member_name_length.reverse();
469                    result.extend(member_name_length);
470                    // Write the number of members in the struct in reverse.
471                    let mut num_members = 1u8.to_bits_be();
472                    num_members.reverse();
473                    result.extend(num_members);
474                    // Write the plaintext variant for struct in reverse.
475                    result.extend([true, false]);
476                }
477            }
478            // Reverse the result to get the correct order.
479            result.reverse();
480            // Transform the bits into a vector of booleans.
481            let result: Vec<Boolean<CurrentAleo>> = result.into_iter().map(|input| Boolean::new(mode, input)).collect();
482            result
483        }
484
485        // A helper function to run the test.
486        fn run_test(expected_depth: usize, input: Vec<Boolean<CurrentAleo>>, expected_error: bool) {
487            // Parse the input string, catching any panics.
488            let result = std::panic::catch_unwind(|| Plaintext::<CurrentAleo>::from_bits_be(&input));
489            CurrentAleo::reset();
490            // Check if the result is an error.
491            match expected_error {
492                true => {
493                    assert!(result.is_err());
494                    return;
495                }
496                false => assert!(result.is_ok()),
497            };
498            // Unwrap the result.
499            let candidate = result.unwrap();
500            // Check if the candidate is equal to the input.
501            for (i, expected_bit) in input.iter().enumerate() {
502                assert_eq!(expected_bit.eject_value(), candidate.to_bits_be()[i].eject_value());
503            }
504            // Check if the depth of the candidate is equal to the expected depth.
505            assert_eq!(get_depth(&candidate), expected_depth);
506        }
507
508        // Initialize a sequence of depths to check.
509        // Note that 890 is approximate maximum depth that can be constructed in this test.
510        let mut depths = (0usize..100).collect_vec();
511        depths.extend((100..890).step_by(10));
512
513        // Test deeply nested arrays with different literal types.
514        for i in depths.iter().copied() {
515            run_test(i, create_nested_array(Mode::Constant, i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
516            run_test(i, create_nested_array(Mode::Public, i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
517            run_test(i, create_nested_array(Mode::Private, i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
518            run_test(i, create_nested_array(Mode::Constant, i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
519        }
520
521        // Test deeply nested structs with different literal types.
522        for i in depths.iter().copied() {
523            run_test(i, create_nested_struct(Mode::Public, i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
524            run_test(i, create_nested_struct(Mode::Private, i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
525            run_test(i, create_nested_struct(Mode::Constant, i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
526            run_test(i, create_nested_struct(Mode::Public, i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
527        }
528
529        // Test alternating nested arrays and structs.
530        for i in depths.iter().copied() {
531            run_test(i, create_alternated_nested(Mode::Private, i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
532            run_test(i, create_alternated_nested(Mode::Constant, i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
533            run_test(i, create_alternated_nested(Mode::Public, i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
534            run_test(i, create_alternated_nested(Mode::Private, i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
535        }
536    }
537}