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 }