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