snarkvm_console_types_field/
from_bits.rs

1// Copyright 2024 Aleo Network Foundation
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<E: Environment> FromBits for Field<E> {
19    /// Initializes a new field from a list of **little-endian** bits.
20    ///   - If `bits_le` is longer than `E::Field::size_in_bits()`, the excess bits are enforced to be `0`s.
21    ///   - If `bits_le` is shorter than `E::Field::size_in_bits()`, it is padded with `0`s up to field size.
22    fn from_bits_le(bits_le: &[bool]) -> Result<Self> {
23        // Retrieve the data and field size.
24        let size_in_data_bits = Field::<E>::size_in_data_bits();
25        let size_in_bits = Field::<E>::size_in_bits();
26
27        // Ensure the list of booleans is within the allowed size in bits.
28        let num_bits = bits_le.len();
29        if num_bits > size_in_bits {
30            // Check if all excess bits are zero.
31            let should_be_zero = bits_le[size_in_bits..].iter().fold(false, |acc, bit| acc | bit);
32            // Ensure `should_be_zero` is `false`.
33            ensure!(!should_be_zero, "The excess bits are not zero.");
34        }
35
36        // If `num_bits` is greater than `size_in_data_bits`, check it is less than `Field::MODULUS`.
37        if num_bits > size_in_data_bits {
38            // Recover the field as a `BigInteger` for comparison.
39            // As `bits_le[size_in_bits..]` is guaranteed to be zero from the above logic,
40            // and `bits_le` is greater than `size_in_data_bits`, it is safe to truncate `bits_le` to `size_in_bits`.
41            let field = E::BigInteger::from_bits_le(&bits_le[..size_in_bits])?;
42
43            // Ensure the field is less than `Field::MODULUS`.
44            ensure!(field < E::Field::modulus(), "The field is greater than or equal to the modulus.");
45
46            // Return the field.
47            Ok(Field { field: E::Field::from_bigint(field).ok_or_else(|| anyhow!("Invalid field from bits"))? })
48        } else {
49            // Construct the sanitized list of bits padded with `false`
50            let mut sanitized_bits = vec![false; size_in_bits];
51            // Note: This is safe, because we just checked that the length of bits isn't bigger
52            // than `size_in_data_bits` which is equal to `size_in_bits - 1`.
53            sanitized_bits[..num_bits].copy_from_slice(bits_le);
54
55            // Recover the native field.
56            let field = E::Field::from_bigint(E::BigInteger::from_bits_le(&sanitized_bits)?)
57                .ok_or_else(|| anyhow!("Invalid field from bits"))?;
58
59            // Return the field.
60            Ok(Field { field })
61        }
62    }
63
64    /// Initializes a new field from a list of big-endian bits *without* leading zeros.
65    fn from_bits_be(bits_be: &[bool]) -> Result<Self> {
66        // Reverse the given bits from big-endian into little-endian.
67        // Note: This is safe as the bit representation is consistent (there are no leading zeros).
68        let mut bits_le = bits_be.to_vec();
69        bits_le.reverse();
70
71        Self::from_bits_le(&bits_le)
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use snarkvm_console_network_environment::Console;
79
80    type CurrentEnvironment = Console;
81
82    const ITERATIONS: usize = 100;
83
84    fn check_from_bits_le() -> Result<()> {
85        let mut rng = TestRng::default();
86
87        for i in 0..ITERATIONS {
88            // Sample a random element.
89            let expected: Field<CurrentEnvironment> = Uniform::rand(&mut rng);
90            let given_bits = expected.to_bits_le();
91            assert_eq!(Field::<CurrentEnvironment>::size_in_bits(), given_bits.len());
92
93            let candidate = Field::<CurrentEnvironment>::from_bits_le(&given_bits)?;
94            assert_eq!(expected, candidate);
95
96            // Add excess zero bits.
97            let candidate = [given_bits, vec![false; i]].concat();
98
99            let candidate = Field::<CurrentEnvironment>::from_bits_le(&candidate)?;
100            assert_eq!(expected, candidate);
101            assert_eq!(Field::<CurrentEnvironment>::size_in_bits(), candidate.to_bits_le().len());
102        }
103        Ok(())
104    }
105
106    fn check_from_bits_be() -> Result<()> {
107        let mut rng = TestRng::default();
108
109        for i in 0..ITERATIONS {
110            // Sample a random element.
111            let expected: Field<CurrentEnvironment> = Uniform::rand(&mut rng);
112            let given_bits = expected.to_bits_be();
113            assert_eq!(Field::<CurrentEnvironment>::size_in_bits(), given_bits.len());
114
115            let candidate = Field::<CurrentEnvironment>::from_bits_be(&given_bits)?;
116            assert_eq!(expected, candidate);
117
118            // Add excess zero bits.
119            let candidate = [vec![false; i], given_bits].concat();
120
121            let candidate = Field::<CurrentEnvironment>::from_bits_be(&candidate)?;
122            assert_eq!(expected, candidate);
123            assert_eq!(Field::<CurrentEnvironment>::size_in_bits(), candidate.to_bits_be().len());
124        }
125        Ok(())
126    }
127
128    #[test]
129    fn test_from_bits_le() -> Result<()> {
130        check_from_bits_le()
131    }
132
133    #[test]
134    fn test_from_bits_be() -> Result<()> {
135        check_from_bits_be()
136    }
137}