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