/ console / types / scalar / src / from_bits.rs
from_bits.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the alphavm 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  
 16  use super::*;
 17  
 18  impl<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)]
 79  mod tests {
 80      use super::*;
 81      use alphavm_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  }