Skip to content

Commit 5473a0f

Browse files
change From<QM31Felt> implementation + add test
1 parent 0dc0d27 commit 5473a0f

File tree

1 file changed

+50
-44
lines changed
  • crates/starknet-types-core/src/felt

1 file changed

+50
-44
lines changed

crates/starknet-types-core/src/felt/qm31.rs

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ use crate::felt::Felt;
44

55
pub const STWO_PRIME: u64 = (1 << 31) - 1;
66
const STWO_PRIME_U128: u128 = STWO_PRIME as u128;
7-
const MASK_36: u64 = (1 << 36) - 1;
8-
const MASK_8: u64 = (1 << 8) - 1;
97

108
#[derive(Debug)]
119
pub enum QM31Error {
@@ -44,28 +42,24 @@ impl QM31Felt {
4442
/// into a single Felt252. STWO_PRIME fits in 36 bits, hence each coordinate can be represented
4543
/// by 36 bits and a QM31 element can be stored in the first 144 bits of a Felt252.
4644
pub fn from_raw(coordinates: [u64; 4]) -> QM31Felt {
47-
let bytes_part1 = ((coordinates[0] % STWO_PRIME) as u128
48-
+ (((coordinates[1] % STWO_PRIME) as u128) << 36))
49-
.to_le_bytes();
50-
let bytes_part2 = ((coordinates[2] % STWO_PRIME) as u128
51-
+ (((coordinates[3] % STWO_PRIME) as u128) << 36))
52-
.to_le_bytes();
53-
let mut result_bytes = [0; 32];
45+
Self([
46+
coordinates[0] % STWO_PRIME,
47+
coordinates[1] % STWO_PRIME,
48+
coordinates[2] % STWO_PRIME,
49+
coordinates[3] % STWO_PRIME,
50+
])
51+
}
5452

53+
pub fn pack_into_felt(&self) -> Felt {
54+
let coordinates = self.0;
55+
56+
let bytes_part1 = (coordinates[0] as u128 + ((coordinates[1] as u128) << 36)).to_le_bytes();
57+
let bytes_part2 = (coordinates[2] as u128 + ((coordinates[3] as u128) << 36)).to_le_bytes();
58+
let mut result_bytes = [0u8; 32];
5559
result_bytes[0..9].copy_from_slice(&bytes_part1[0..9]);
5660
result_bytes[9..18].copy_from_slice(&bytes_part2[0..9]);
5761

58-
let limbs = {
59-
let felt = Felt::from_bytes_le_slice(&result_bytes);
60-
felt.to_le_digits()
61-
};
62-
63-
Self([
64-
(limbs[0] & MASK_36),
65-
((limbs[0] >> 36) + ((limbs[1] & MASK_8) << 28)),
66-
((limbs[1] >> 8) & MASK_36),
67-
((limbs[1] >> 44) + (limbs[2] << 20)),
68-
])
62+
Felt::from_bytes_le(&result_bytes)
6963
}
7064

7165
/// Computes the addition of two QM31 elements in reduced form.
@@ -215,13 +209,13 @@ impl QM31Felt {
215209

216210
impl From<&QM31Felt> for Felt {
217211
fn from(value: &QM31Felt) -> Self {
218-
Felt::from_raw(value.0)
212+
value.pack_into_felt()
219213
}
220214
}
221215

222216
impl From<QM31Felt> for Felt {
223217
fn from(value: QM31Felt) -> Self {
224-
Felt::from_raw(value.0)
218+
value.pack_into_felt()
225219
}
226220
}
227221

@@ -261,6 +255,8 @@ impl TryFrom<&Felt> for QM31Felt {
261255

262256
#[cfg(test)]
263257
mod test {
258+
use core::{u128, u16, u8};
259+
264260
use proptest::{
265261
array::uniform4,
266262
prelude::{BoxedStrategy, Just, Strategy},
@@ -273,41 +269,51 @@ mod test {
273269
};
274270

275271
#[test]
276-
fn from_positive_felt_to_qm31_to_felt() {
277-
let felt_expected = Felt::from(2i128.pow(126));
272+
fn qm31_to_felt_packed() {
273+
let u64_max_reduced = u64::MAX % STWO_PRIME;
274+
275+
let value = u8::MAX;
276+
let felt = Felt::from(value);
277+
let qm31: QM31Felt = felt.try_into().unwrap();
278+
let qm31_to_felt = qm31.pack_into_felt();
278279

279-
let qm31: QM31Felt = felt_expected.try_into().unwrap();
280-
let felt = qm31.into();
280+
assert_eq!(qm31_to_felt, Felt::from(value));
281281

282-
assert_eq!(qm31, felt);
282+
let value = u16::MAX;
283+
let felt = Felt::from(value);
284+
let qm31: QM31Felt = felt.try_into().unwrap();
285+
let qm31_to_felt = qm31.pack_into_felt();
283286

284-
let felt_expected = Felt::from(2i64.pow(62));
285-
let qm31: QM31Felt = felt_expected.try_into().unwrap();
286-
let felt = qm31.into();
287+
assert_eq!(qm31_to_felt, Felt::from(value));
287288

288-
assert_eq!(qm31, felt);
289+
let value = u32::MAX;
290+
let felt = Felt::from(value);
291+
let qm31: QM31Felt = felt.try_into().unwrap();
292+
let qm31_to_felt = qm31.pack_into_felt();
289293

290-
let felt_expected = Felt::from(2i32.pow(30));
291-
let qm31: QM31Felt = felt_expected.try_into().unwrap();
292-
let felt = qm31.into();
294+
assert_eq!(qm31_to_felt, Felt::from(value as u64 % STWO_PRIME));
293295

294-
assert_eq!(qm31, felt);
296+
let felt = Felt::from(u64::MAX);
297+
let qm31: QM31Felt = felt.try_into().unwrap();
298+
let qm31_to_felt = qm31.pack_into_felt();
299+
dbg!(felt.to_le_digits());
295300

296-
let felt_expected = Felt::from(2i8.pow(6));
297-
let qm31: QM31Felt = felt_expected.try_into().unwrap();
298-
let felt = qm31.into();
301+
assert_eq!(qm31_to_felt, Felt::from(u64_max_reduced));
299302

300-
assert_eq!(qm31, felt);
303+
let felt = Felt::from(u128::MAX);
304+
let qm31: QM31Felt = felt.try_into().unwrap();
305+
let qm31_to_felt = qm31.pack_into_felt();
301306

302-
let felt_expected = Felt::ZERO;
303-
let qm31: QM31Felt = felt_expected.try_into().unwrap();
304-
let felt = qm31.into();
307+
let mut bytes = [0u8; 32];
308+
let bytes_part1 =
309+
((u64_max_reduced) as u128 + (((u64_max_reduced) as u128) << 36)).to_le_bytes();
310+
bytes[0..9].copy_from_slice(&bytes_part1[0..9]);
305311

306-
assert_eq!(qm31, felt);
312+
assert_eq!(qm31_to_felt, Felt::from_bytes_le(&bytes));
307313
}
308314

309315
#[test]
310-
fn qm31_packed_reduced_coordinates_over_144_bits() {
316+
fn qm31_coordinates_over_144_bits() {
311317
let mut felt_bytes = [0u8; 32];
312318
felt_bytes[18] = 1;
313319
let felt = Felt::from_bytes_le(&felt_bytes);

0 commit comments

Comments
 (0)