use alloc::vec::Vec; use core::ops::Mul; use plonky2::field::types::{Field, PrimeField}; use crate::curve::curve_types::{Curve, CurveScalar, ProjectivePoint}; const WINDOW_BITS: usize = 4; const BASE: usize = 1 << WINDOW_BITS; fn digits_per_scalar() -> usize { (C::ScalarField::BITS + WINDOW_BITS - 1) / WINDOW_BITS } /// Precomputed state used for scalar x ProjectivePoint multiplications, /// specific to a particular generator. #[derive(Clone)] pub struct MultiplicationPrecomputation { /// [(2^w)^i] g for each i < digits_per_scalar. powers: Vec>, } impl ProjectivePoint { pub fn mul_precompute(&self) -> MultiplicationPrecomputation { let num_digits = digits_per_scalar::(); let mut powers = Vec::with_capacity(num_digits); powers.push(*self); for i in 1..num_digits { let mut power_i = powers[i - 1]; for _j in 0..WINDOW_BITS { power_i = power_i.double(); } powers.push(power_i); } MultiplicationPrecomputation { powers } } #[must_use] pub fn mul_with_precomputation( &self, scalar: C::ScalarField, precomputation: MultiplicationPrecomputation, ) -> Self { // Yao's method; see https://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf let precomputed_powers = precomputation.powers; let digits = to_digits::(&scalar); let mut y = ProjectivePoint::ZERO; let mut u = ProjectivePoint::ZERO; let mut all_summands = Vec::new(); for j in (1..BASE).rev() { let mut u_summands = Vec::new(); for (i, &digit) in digits.iter().enumerate() { if digit == j as u64 { u_summands.push(precomputed_powers[i]); } } all_summands.push(u_summands); } let all_sums: Vec> = all_summands .iter() .cloned() .map(|vec| vec.iter().fold(ProjectivePoint::ZERO, |a, &b| a + b)) .collect(); for i in 0..all_sums.len() { u = u + all_sums[i]; y = y + u; } y } } impl Mul> for CurveScalar { type Output = ProjectivePoint; fn mul(self, rhs: ProjectivePoint) -> Self::Output { let precomputation = rhs.mul_precompute(); rhs.mul_with_precomputation(self.0, precomputation) } } #[allow(clippy::assertions_on_constants)] fn to_digits(x: &C::ScalarField) -> Vec { debug_assert!( 64 % WINDOW_BITS == 0, "For simplicity, only power-of-two window sizes are handled for now" ); let digits_per_u64 = 64 / WINDOW_BITS; let mut digits = Vec::with_capacity(digits_per_scalar::()); for limb in x.to_canonical_biguint().to_u64_digits() { for j in 0..digits_per_u64 { digits.push((limb >> (j * WINDOW_BITS) as u64) % BASE as u64); } } digits }