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