diff --git a/src/circuit/gadget/ecc/chip.rs b/src/circuit/gadget/ecc/chip.rs index 386e031f..b265371f 100644 --- a/src/circuit/gadget/ecc/chip.rs +++ b/src/circuit/gadget/ecc/chip.rs @@ -6,7 +6,6 @@ use crate::{ primitives::sinsemilla, }; use arrayvec::ArrayVec; -use ff::PrimeField; use ff::Field; use group::prime::PrimeCurveAffine; @@ -20,35 +19,12 @@ use std::convert::TryInto; pub(super) mod add; pub(super) mod add_incomplete; +pub mod constants; pub(super) mod mul; pub(super) mod mul_fixed; pub(super) mod witness_point; -/// Window size for fixed-base scalar multiplication -pub const FIXED_BASE_WINDOW_SIZE: usize = 3; - -/// $2^{`FIXED_BASE_WINDOW_SIZE`}$ -pub const H: usize = 1 << FIXED_BASE_WINDOW_SIZE; - -/// Number of windows for a full-width scalar -pub const NUM_WINDOWS: usize = - (pallas::Scalar::NUM_BITS as usize + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE; - -/// Number of windows for a short signed scalar -pub const NUM_WINDOWS_SHORT: usize = - (L_SCALAR_SHORT + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE; - -/// $\ell_\mathsf{value}$ -/// Number of bits in an unsigned short scalar. -pub(crate) const L_SCALAR_SHORT: usize = 64; - -/// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$. -/// -pub(crate) const T_Q: u128 = 45560315531506369815346746415080538113; - -/// The Pallas base field modulus is $p = 2^{254} + \mathsf{t_p}$. -/// -pub(crate) const T_P: u128 = 45560315531419706090280762371685220353; +pub use constants::*; /// A curve point represented in affine (x, y) coordinates, or the /// identity represented as (0, 0). @@ -184,16 +160,49 @@ pub struct EccConfig> { pub lookup_config: LookupRangeCheckConfig, } +/// A trait representing the kind of scalar used with a particular `FixedPoint`. +/// +/// This trait exists because of limitations around const generics. +pub trait ScalarKind { + const NUM_WINDOWS: usize; +} + +/// Type marker representing a full-width scalar for use in fixed-base scalar +/// multiplication. +pub enum FullScalar {} +impl ScalarKind for FullScalar { + const NUM_WINDOWS: usize = NUM_WINDOWS; +} + +/// Type marker representing a signed 64-bit scalar for use in fixed-base scalar +/// multiplication. +pub enum ShortScalar {} +impl ScalarKind for ShortScalar { + const NUM_WINDOWS: usize = NUM_WINDOWS_SHORT; +} + +/// Type marker representing a base field element being used as a scalar in fixed-base +/// scalar multiplication. +pub enum BaseFieldElem {} +impl ScalarKind for BaseFieldElem { + const NUM_WINDOWS: usize = NUM_WINDOWS; +} + /// Returns information about a fixed point. /// /// TODO: When associated consts can be used as const generics, introduce a /// `const NUM_WINDOWS: usize` associated const, and return `NUM_WINDOWS`-sized /// arrays instead of `Vec`s. pub trait FixedPoint: std::fmt::Debug + Eq + Clone { + type ScalarKind: ScalarKind; + fn generator(&self) -> C; fn u(&self) -> Vec<[[u8; 32]; H]>; fn z(&self) -> Vec; - fn lagrange_coeffs(&self) -> Vec<[C::Base; H]>; + + fn lagrange_coeffs(&self) -> Vec<[C::Base; H]> { + compute_lagrange_coeffs(self.generator(), Self::ScalarKind::NUM_WINDOWS) + } } /// A chip implementing EccInstructions @@ -353,9 +362,12 @@ impl EccBaseFieldElemFixed { impl> EccInstructions for EccChip where - >::Base: FixedPoint, - >::FullScalar: FixedPoint, - >::ShortScalar: FixedPoint, + >::Base: + FixedPoint, + >::FullScalar: + FixedPoint, + >::ShortScalar: + FixedPoint, { type ScalarFixed = EccScalarFixed; type ScalarFixedShort = EccScalarFixedShort; diff --git a/src/circuit/gadget/ecc/chip/constants.rs b/src/circuit/gadget/ecc/chip/constants.rs new file mode 100644 index 00000000..b0d55a9e --- /dev/null +++ b/src/circuit/gadget/ecc/chip/constants.rs @@ -0,0 +1,273 @@ +use arrayvec::ArrayVec; +use group::{ + ff::{Field, PrimeField}, + Curve, +}; +use halo2::arithmetic::lagrange_interpolate; +use pasta_curves::{ + arithmetic::{CurveAffine, FieldExt}, + pallas, +}; + +/// Window size for fixed-base scalar multiplication +pub const FIXED_BASE_WINDOW_SIZE: usize = 3; + +/// $2^{`FIXED_BASE_WINDOW_SIZE`}$ +pub const H: usize = 1 << FIXED_BASE_WINDOW_SIZE; + +/// Number of windows for a full-width scalar +pub const NUM_WINDOWS: usize = + (pallas::Scalar::NUM_BITS as usize + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE; + +/// Number of windows for a short signed scalar +pub const NUM_WINDOWS_SHORT: usize = + (L_SCALAR_SHORT + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE; + +/// $\ell_\mathsf{value}$ +/// Number of bits in an unsigned short scalar. +pub(crate) const L_SCALAR_SHORT: usize = 64; + +/// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$. +/// +pub(crate) const T_Q: u128 = 45560315531506369815346746415080538113; + +/// The Pallas base field modulus is $p = 2^{254} + \mathsf{t_p}$. +/// +pub(crate) const T_P: u128 = 45560315531419706090280762371685220353; + +/// For each fixed base, we calculate its scalar multiples in three-bit windows. +/// Each window will have $2^3 = 8$ points. +pub fn compute_window_table(base: C, num_windows: usize) -> Vec<[C; H]> { + let mut window_table: Vec<[C; H]> = Vec::with_capacity(num_windows); + + // Generate window table entries for all windows but the last. + // For these first `num_windows - 1` windows, we compute the multiple [(k+2)*(2^3)^w]B. + // Here, w ranges from [0..`num_windows - 1`) + for w in 0..(num_windows - 1) { + window_table.push( + (0..H) + .map(|k| { + // scalar = (k+2)*(8^w) + let scalar = C::Scalar::from(k as u64 + 2) + * C::Scalar::from(H as u64).pow(&[w as u64, 0, 0, 0]); + (base * scalar).to_affine() + }) + .collect::>() + .into_inner() + .unwrap(), + ); + } + + // Generate window table entries for the last window, w = `num_windows - 1`. + // For the last window, we compute [k * (2^3)^w - sum]B, where sum is defined + // as sum = \sum_{j = 0}^{`num_windows - 2`} 2^{3j+1} + let sum = (0..(num_windows - 1)).fold(C::Scalar::zero(), |acc, j| { + acc + C::Scalar::from(2).pow(&[FIXED_BASE_WINDOW_SIZE as u64 * j as u64 + 1, 0, 0, 0]) + }); + window_table.push( + (0..H) + .map(|k| { + // scalar = k * (2^3)^w - sum, where w = `num_windows - 1` + let scalar = C::Scalar::from(k as u64) + * C::Scalar::from(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0]) + - sum; + (base * scalar).to_affine() + }) + .collect::>() + .into_inner() + .unwrap(), + ); + + window_table +} + +/// For each window, we interpolate the $x$-coordinate. +/// Here, we pre-compute and store the coefficients of the interpolation polynomial. +pub fn compute_lagrange_coeffs(base: C, num_windows: usize) -> Vec<[C::Base; H]> { + // We are interpolating over the 3-bit window, k \in [0..8) + let points: Vec<_> = (0..H).map(|i| C::Base::from(i as u64)).collect(); + + let window_table = compute_window_table(base, num_windows); + + window_table + .iter() + .map(|window_points| { + let x_window_points: Vec<_> = window_points + .iter() + .map(|point| *point.coordinates().unwrap().x()) + .collect(); + lagrange_interpolate(&points, &x_window_points) + .into_iter() + .collect::>() + .into_inner() + .unwrap() + }) + .collect() +} + +/// For each window, $z$ is a field element such that for each point $(x, y)$ in the window: +/// - $z + y = u^2$ (some square in the field); and +/// - $z - y$ is not a square. +/// If successful, return a vector of `(z: u64, us: [C::Base; H])` for each window. +/// +/// This function was used to generate the `z`s and `u`s for the Orchard fixed +/// bases. The outputs of this function have been stored as constants, and it +/// is not called anywhere in this codebase. However, we keep this function here +/// as a utility for those who wish to use it with different parameters. +pub fn find_zs_and_us( + base: C, + num_windows: usize, +) -> Option> { + // Closure to find z and u's for one window + let find_z_and_us = |window_points: &[C]| { + assert_eq!(H, window_points.len()); + + let ys: Vec<_> = window_points + .iter() + .map(|point| *point.coordinates().unwrap().y()) + .collect(); + (0..(1000 * (1 << (2 * H)))).find_map(|z| { + ys.iter() + .map(|&y| { + if (-y + C::Base::from(z)).sqrt().is_none().into() { + (y + C::Base::from(z)).sqrt().into() + } else { + None + } + }) + .collect::>>() + .map(|us| (z, us.into_inner().unwrap())) + }) + }; + + let window_table = compute_window_table(base, num_windows); + window_table + .iter() + .map(|window_points| find_z_and_us(window_points)) + .collect() +} + +// Test that the z-values and u-values satisfy the conditions: +// 1. z + y = u^2, +// 2. z - y is not a square +// for the y-coordinate of each fixed-base multiple in each window. +pub fn test_zs_and_us(base: C, z: &[u64], u: &[[[u8; 32]; H]], num_windows: usize) { + let window_table = compute_window_table(base, num_windows); + + for ((u, z), window_points) in u.iter().zip(z.iter()).zip(window_table) { + for (u, point) in u.iter().zip(window_points.iter()) { + let y = *point.coordinates().unwrap().y(); + let u = C::Base::from_bytes(u).unwrap(); + assert_eq!(C::Base::from_u64(*z) + y, u * u); // allow either square root + assert!(bool::from((C::Base::from_u64(*z) - y).sqrt().is_none())); + } + } +} + +// Test that Lagrange interpolation coefficients reproduce the correct x-coordinate +// for each fixed-base multiple in each window. +pub fn test_lagrange_coeffs(base: C, num_windows: usize) { + /// Evaluate y = f(x) given the coefficients of f(x) + fn evaluate(x: u8, coeffs: &[C::Base]) -> C::Base { + let x = C::Base::from(x as u64); + coeffs + .iter() + .rev() + .cloned() + .reduce(|acc, coeff| acc * x + coeff) + .unwrap_or_else(C::Base::zero) + } + + let lagrange_coeffs = compute_lagrange_coeffs(base, num_windows); + + // Check first 84 windows, i.e. `k_0, k_1, ..., k_83` + for (idx, coeffs) in lagrange_coeffs[0..(num_windows - 1)].iter().enumerate() { + // Test each three-bit chunk in this window. + for bits in 0..(H as u8) { + { + // Interpolate the x-coordinate using this window's coefficients + let interpolated_x = evaluate::(bits, coeffs); + + // Compute the actual x-coordinate of the multiple [(k+2)*(8^w)]B. + let point = base + * C::Scalar::from(bits as u64 + 2) + * C::Scalar::from(H as u64).pow(&[idx as u64, 0, 0, 0]); + let x = *point.to_affine().coordinates().unwrap().x(); + + // Check that the interpolated x-coordinate matches the actual one. + assert_eq!(x, interpolated_x); + } + } + } + + // Check last window. + for bits in 0..(H as u8) { + // Interpolate the x-coordinate using the last window's coefficients + let interpolated_x = evaluate::(bits, &lagrange_coeffs[num_windows - 1]); + + // Compute the actual x-coordinate of the multiple [k * (8^84) - offset]B, + // where offset = \sum_{j = 0}^{83} 2^{3j+1} + let offset = (0..(num_windows - 1)).fold(C::Scalar::zero(), |acc, w| { + acc + C::Scalar::from(2).pow(&[FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1, 0, 0, 0]) + }); + let scalar = C::Scalar::from(bits as u64) + * C::Scalar::from(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0]) + - offset; + let point = base * scalar; + let x = *point.to_affine().coordinates().unwrap().x(); + + // Check that the interpolated x-coordinate matches the actual one. + assert_eq!(x, interpolated_x); + } +} + +#[cfg(test)] +mod tests { + use group::{ff::Field, Curve, Group}; + use pasta_curves::{ + arithmetic::{CurveAffine, FieldExt}, + pallas, + }; + use proptest::prelude::*; + + use super::{compute_window_table, find_zs_and_us, test_lagrange_coeffs, H, NUM_WINDOWS}; + + prop_compose! { + /// Generate an arbitrary Pallas point. + pub fn arb_point()(bytes in prop::array::uniform32(0u8..)) -> pallas::Point { + // Instead of rejecting out-of-range bytes, let's reduce them. + let mut buf = [0; 64]; + buf[..32].copy_from_slice(&bytes); + let scalar = pallas::Scalar::from_bytes_wide(&buf); + pallas::Point::generator() * scalar + } + } + + proptest! { + #[test] + fn lagrange_coeffs( + base in arb_point(), + ) { + test_lagrange_coeffs(base.to_affine(), NUM_WINDOWS); + } + } + + #[test] + fn zs_and_us() { + let base = pallas::Point::random(rand::rngs::OsRng); + let (z, u): (Vec, Vec<[pallas::Base; H]>) = + find_zs_and_us(base.to_affine(), NUM_WINDOWS) + .unwrap() + .into_iter() + .unzip(); + let window_table = compute_window_table(base.to_affine(), NUM_WINDOWS); + + for ((u, z), window_points) in u.iter().zip(z.iter()).zip(window_table) { + for (u, point) in u.iter().zip(window_points.iter()) { + let y = *point.coordinates().unwrap().y(); + assert_eq!(pallas::Base::from(*z) + y, u * u); // allow either square root + assert!(bool::from((pallas::Base::from(*z) - y).sqrt().is_none())); + } + } + } +} diff --git a/src/circuit/gadget/ecc/chip/mul_fixed.rs b/src/circuit/gadget/ecc/chip/mul_fixed.rs index f4365b79..1a82a23f 100644 --- a/src/circuit/gadget/ecc/chip/mul_fixed.rs +++ b/src/circuit/gadget/ecc/chip/mul_fixed.rs @@ -25,8 +25,8 @@ pub mod short; lazy_static! { static ref TWO_SCALAR: pallas::Scalar = pallas::Scalar::from(2); // H = 2^3 (3-bit window) - static ref H_SCALAR: pallas::Scalar = pallas::Scalar::from_u64(H as u64); - static ref H_BASE: pallas::Base = pallas::Base::from_u64(H as u64); + static ref H_SCALAR: pallas::Scalar = pallas::Scalar::from(H as u64); + static ref H_BASE: pallas::Base = pallas::Base::from(H as u64); } #[derive(Clone, Debug, Eq, PartialEq)] @@ -259,7 +259,7 @@ impl> Config { window + offset, || { let z = &constants.as_ref().unwrap().1; - Ok(pallas::Base::from_u64(z[window])) + Ok(pallas::Base::from(z[window])) }, )?; } diff --git a/src/circuit/gadget/ecc/chip/mul_fixed/full_width.rs b/src/circuit/gadget/ecc/chip/mul_fixed/full_width.rs index eca975bf..5dcef72d 100644 --- a/src/circuit/gadget/ecc/chip/mul_fixed/full_width.rs +++ b/src/circuit/gadget/ecc/chip/mul_fixed/full_width.rs @@ -182,7 +182,7 @@ pub mod tests { use rand::rngs::OsRng; use crate::circuit::gadget::ecc::{ - chip::{EccChip, FixedPoint as FixedPointTrait, H}, + chip::{EccChip, FixedPoint as _, H}, FixedPoint, NonIdentityPoint, Point, }; use crate::constants::{OrchardFixedBases, OrchardFixedBasesFull}; diff --git a/src/circuit/gadget/utilities/decompose_running_sum.rs b/src/circuit/gadget/utilities/decompose_running_sum.rs index cc37c18e..170655c1 100644 --- a/src/circuit/gadget/utilities/decompose_running_sum.rs +++ b/src/circuit/gadget/utilities/decompose_running_sum.rs @@ -216,7 +216,7 @@ impl #[cfg(test)] mod tests { use super::*; - use group::ff::Field; + use group::ff::{Field, PrimeField}; use halo2::{ circuit::{Layouter, SimpleFloorPlanner}, dev::{MockProver, VerifyFailure}, @@ -225,11 +225,11 @@ mod tests { use pasta_curves::{arithmetic::FieldExt, pallas}; use rand::rngs::OsRng; - const FIXED_BASE_WINDOW_SIZE: usize = 3; - const NUM_WINDOWS: usize = 85; - const NUM_WINDOWS_SHORT: usize = 22; - const L_BASE: usize = 255; - const L_SHORT: usize = 64; + use crate::circuit::gadget::ecc::chip::{ + FIXED_BASE_WINDOW_SIZE, L_SCALAR_SHORT as L_SHORT, NUM_WINDOWS, NUM_WINDOWS_SHORT, + }; + + const L_BASE: usize = pallas::Base::NUM_BITS as usize; #[test] fn test_running_sum() { diff --git a/src/constants.rs b/src/constants.rs index 8c3825bc..5af82257 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -5,7 +5,6 @@ pub mod util; pub use fixed_bases::{NullifierK, OrchardFixedBases, OrchardFixedBasesFull, ValueCommitV, H}; pub use sinsemilla::{OrchardCommitDomains, OrchardHashDomains}; -pub use util::{evaluate, gen_const_array}; /// $\mathsf{MerkleDepth^{Orchard}}$ pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32; diff --git a/src/constants/fixed_bases.rs b/src/constants/fixed_bases.rs index e7b70ee3..90a15858 100644 --- a/src/constants/fixed_bases.rs +++ b/src/constants/fixed_bases.rs @@ -1,16 +1,12 @@ //! Orchard fixed bases. use super::{L_ORCHARD_SCALAR, L_VALUE}; -use crate::circuit::gadget::ecc::{chip::FixedPoint, FixedPoints}; - -use arrayvec::ArrayVec; -use ff::Field; -use group::Curve; -use halo2::arithmetic::lagrange_interpolate; -use pasta_curves::{ - arithmetic::{CurveAffine, FieldExt}, - pallas, +use crate::circuit::gadget::ecc::{ + chip::{BaseFieldElem, FixedPoint, FullScalar, ShortScalar}, + FixedPoints, }; +use pasta_curves::pallas; + pub mod commit_ivk_r; pub mod note_commit_r; pub mod nullifier_k; @@ -51,120 +47,6 @@ pub const NUM_WINDOWS: usize = pub const NUM_WINDOWS_SHORT: usize = (L_VALUE + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE; -/// For each fixed base, we calculate its scalar multiples in three-bit windows. -/// Each window will have $2^3 = 8$ points. -fn compute_window_table(base: C, num_windows: usize) -> Vec<[C; H]> { - let mut window_table: Vec<[C; H]> = Vec::with_capacity(num_windows); - - // Generate window table entries for all windows but the last. - // For these first `num_windows - 1` windows, we compute the multiple [(k+2)*(2^3)^w]B. - // Here, w ranges from [0..`num_windows - 1`) - for w in 0..(num_windows - 1) { - window_table.push( - (0..H) - .map(|k| { - // scalar = (k+2)*(8^w) - let scalar = C::ScalarExt::from_u64(k as u64 + 2) - * C::ScalarExt::from_u64(H as u64).pow(&[w as u64, 0, 0, 0]); - (base * scalar).to_affine() - }) - .collect::>() - .into_inner() - .unwrap(), - ); - } - - // Generate window table entries for the last window, w = `num_windows - 1`. - // For the last window, we compute [k * (2^3)^w - sum]B, where sum is defined - // as sum = \sum_{j = 0}^{`num_windows - 2`} 2^{3j+1} - let sum = (0..(num_windows - 1)).fold(C::ScalarExt::zero(), |acc, j| { - acc + C::ScalarExt::from_u64(2).pow(&[ - FIXED_BASE_WINDOW_SIZE as u64 * j as u64 + 1, - 0, - 0, - 0, - ]) - }); - window_table.push( - (0..H) - .map(|k| { - // scalar = k * (2^3)^w - sum, where w = `num_windows - 1` - let scalar = C::ScalarExt::from_u64(k as u64) - * C::ScalarExt::from_u64(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0]) - - sum; - (base * scalar).to_affine() - }) - .collect::>() - .into_inner() - .unwrap(), - ); - - window_table -} - -/// For each window, we interpolate the $x$-coordinate. -/// Here, we pre-compute and store the coefficients of the interpolation polynomial. -fn compute_lagrange_coeffs(base: C, num_windows: usize) -> Vec<[C::Base; H]> { - // We are interpolating over the 3-bit window, k \in [0..8) - let points: Vec<_> = (0..H).map(|i| C::Base::from_u64(i as u64)).collect(); - - let window_table = compute_window_table(base, num_windows); - - window_table - .iter() - .map(|window_points| { - let x_window_points: Vec<_> = window_points - .iter() - .map(|point| *point.coordinates().unwrap().x()) - .collect(); - lagrange_interpolate(&points, &x_window_points) - .into_iter() - .collect::>() - .into_inner() - .unwrap() - }) - .collect() -} - -/// For each window, $z$ is a field element such that for each point $(x, y)$ in the window: -/// - $z + y = u^2$ (some square in the field); and -/// - $z - y$ is not a square. -/// If successful, return a vector of `(z: u64, us: [C::Base; H])` for each window. -/// -/// This function was used to generate the `z`s and `u`s for the Orchard fixed -/// bases. The outputs of this function have been stored as constants, and it -/// is not called anywhere in this codebase. However, we keep this function here -/// as a utility for those who wish to use it with different parameters. -fn find_zs_and_us(base: C, num_windows: usize) -> Option> { - // Closure to find z and u's for one window - let find_z_and_us = |window_points: &[C]| { - assert_eq!(H, window_points.len()); - - let ys: Vec<_> = window_points - .iter() - .map(|point| *point.coordinates().unwrap().y()) - .collect(); - (0..(1000 * (1 << (2 * H)))).find_map(|z| { - ys.iter() - .map(|&y| { - if (-y + C::Base::from_u64(z)).sqrt().is_none().into() { - (y + C::Base::from_u64(z)).sqrt().into() - } else { - None - } - }) - .collect::>>() - .map(|us| (z, us.into_inner().unwrap())) - }) - }; - - let window_table = compute_window_table(base, num_windows); - window_table - .iter() - .map(|window_points| find_z_and_us(window_points)) - .collect() -} - #[derive(Copy, Clone, Debug, Eq, PartialEq)] // A sum type for both full-width and short bases. This enables us to use the // shared functionality of full-width and short fixed-base scalar multiplication. @@ -216,6 +98,8 @@ impl FixedPoints for OrchardFixedBases { } impl FixedPoint for OrchardFixedBasesFull { + type ScalarKind = FullScalar; + fn generator(&self) -> pallas::Affine { match self { Self::CommitIvkR => commit_ivk_r::generator(), @@ -242,13 +126,11 @@ impl FixedPoint for OrchardFixedBasesFull { Self::SpendAuthG => spend_auth_g::Z.to_vec(), } } - - fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> { - compute_lagrange_coeffs(self.generator(), NUM_WINDOWS) - } } impl FixedPoint for NullifierK { + type ScalarKind = BaseFieldElem; + fn generator(&self) -> pallas::Affine { nullifier_k::generator() } @@ -260,13 +142,11 @@ impl FixedPoint for NullifierK { fn z(&self) -> Vec { nullifier_k::Z.to_vec() } - - fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> { - compute_lagrange_coeffs(self.generator(), NUM_WINDOWS) - } } impl FixedPoint for ValueCommitV { + type ScalarKind = ShortScalar; + fn generator(&self) -> pallas::Affine { value_commit_v::generator() } @@ -278,78 +158,4 @@ impl FixedPoint for ValueCommitV { fn z(&self) -> Vec { value_commit_v::Z_SHORT.to_vec() } - - fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> { - compute_lagrange_coeffs(self.generator(), NUM_WINDOWS_SHORT) - } -} - -#[cfg(test)] -// Test that Lagrange interpolation coefficients reproduce the correct x-coordinate -// for each fixed-base multiple in each window. -fn test_lagrange_coeffs(base: C, num_windows: usize) { - let lagrange_coeffs = compute_lagrange_coeffs(base, num_windows); - - // Check first 84 windows, i.e. `k_0, k_1, ..., k_83` - for (idx, coeffs) in lagrange_coeffs[0..(num_windows - 1)].iter().enumerate() { - // Test each three-bit chunk in this window. - for bits in 0..(1 << FIXED_BASE_WINDOW_SIZE) { - { - // Interpolate the x-coordinate using this window's coefficients - let interpolated_x = super::evaluate::(bits, coeffs); - - // Compute the actual x-coordinate of the multiple [(k+2)*(8^w)]B. - let point = base - * C::Scalar::from_u64(bits as u64 + 2) - * C::Scalar::from_u64(H as u64).pow(&[idx as u64, 0, 0, 0]); - let x = *point.to_affine().coordinates().unwrap().x(); - - // Check that the interpolated x-coordinate matches the actual one. - assert_eq!(x, interpolated_x); - } - } - } - - // Check last window. - for bits in 0..(1 << FIXED_BASE_WINDOW_SIZE) { - // Interpolate the x-coordinate using the last window's coefficients - let interpolated_x = super::evaluate::(bits, &lagrange_coeffs[num_windows - 1]); - - // Compute the actual x-coordinate of the multiple [k * (8^84) - offset]B, - // where offset = \sum_{j = 0}^{83} 2^{3j+1} - let offset = (0..(num_windows - 1)).fold(C::Scalar::zero(), |acc, w| { - acc + C::Scalar::from_u64(2).pow(&[ - FIXED_BASE_WINDOW_SIZE as u64 * w as u64 + 1, - 0, - 0, - 0, - ]) - }); - let scalar = C::Scalar::from_u64(bits as u64) - * C::Scalar::from_u64(H as u64).pow(&[(num_windows - 1) as u64, 0, 0, 0]) - - offset; - let point = base * scalar; - let x = *point.to_affine().coordinates().unwrap().x(); - - // Check that the interpolated x-coordinate matches the actual one. - assert_eq!(x, interpolated_x); - } -} - -#[cfg(test)] -// Test that the z-values and u-values satisfy the conditions: -// 1. z + y = u^2, -// 2. z - y is not a square -// for the y-coordinate of each fixed-base multiple in each window. -fn test_zs_and_us(base: C, z: &[u64], u: &[[[u8; 32]; H]], num_windows: usize) { - let window_table = compute_window_table(base, num_windows); - - for ((u, z), window_points) in u.iter().zip(z.iter()).zip(window_table) { - for (u, point) in u.iter().zip(window_points.iter()) { - let y = *point.coordinates().unwrap().y(); - let u = C::Base::from_bytes(u).unwrap(); - assert_eq!(C::Base::from_u64(*z) + y, u * u); // allow either square root - assert!(bool::from((C::Base::from_u64(*z) - y).sqrt().is_none())); - } - } } diff --git a/src/constants/fixed_bases/commit_ivk_r.rs b/src/constants/fixed_bases/commit_ivk_r.rs index 976ed22e..d4b23ae7 100644 --- a/src/constants/fixed_bases/commit_ivk_r.rs +++ b/src/constants/fixed_bases/commit_ivk_r.rs @@ -2928,10 +2928,9 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { - use super::super::{ - test_lagrange_coeffs, test_zs_and_us, COMMIT_IVK_PERSONALIZATION, NUM_WINDOWS, - }; + use super::super::{COMMIT_IVK_PERSONALIZATION, NUM_WINDOWS}; use super::*; + use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}; use crate::primitives::sinsemilla::CommitDomain; use group::Curve; use pasta_curves::{arithmetic::CurveAffine, pallas}; diff --git a/src/constants/fixed_bases/note_commit_r.rs b/src/constants/fixed_bases/note_commit_r.rs index d2dde504..179f4af8 100644 --- a/src/constants/fixed_bases/note_commit_r.rs +++ b/src/constants/fixed_bases/note_commit_r.rs @@ -2928,11 +2928,11 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { - use super::super::{ - test_lagrange_coeffs, test_zs_and_us, NOTE_COMMITMENT_PERSONALIZATION, NUM_WINDOWS, - }; + use super::super::{NOTE_COMMITMENT_PERSONALIZATION, NUM_WINDOWS}; use super::*; + use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}; use crate::primitives::sinsemilla::CommitDomain; + use group::Curve; use pasta_curves::{arithmetic::CurveAffine, pallas}; diff --git a/src/constants/fixed_bases/nullifier_k.rs b/src/constants/fixed_bases/nullifier_k.rs index 47f69068..0f494984 100644 --- a/src/constants/fixed_bases/nullifier_k.rs +++ b/src/constants/fixed_bases/nullifier_k.rs @@ -2927,10 +2927,9 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { - use super::super::{ - test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS, ORCHARD_PERSONALIZATION, - }; + use super::super::{NUM_WINDOWS, ORCHARD_PERSONALIZATION}; use super::*; + use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}; use group::Curve; use pasta_curves::{arithmetic::CurveExt, pallas}; diff --git a/src/constants/fixed_bases/spend_auth_g.rs b/src/constants/fixed_bases/spend_auth_g.rs index 6deba8f6..b7daf323 100644 --- a/src/constants/fixed_bases/spend_auth_g.rs +++ b/src/constants/fixed_bases/spend_auth_g.rs @@ -2929,10 +2929,9 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { - use super::super::{ - test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS, ORCHARD_PERSONALIZATION, - }; + use super::super::{NUM_WINDOWS, ORCHARD_PERSONALIZATION}; use super::*; + use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}; use group::Curve; use pasta_curves::{ arithmetic::{CurveAffine, CurveExt}, diff --git a/src/constants/fixed_bases/value_commit_r.rs b/src/constants/fixed_bases/value_commit_r.rs index b12030f0..f25a369a 100644 --- a/src/constants/fixed_bases/value_commit_r.rs +++ b/src/constants/fixed_bases/value_commit_r.rs @@ -2929,10 +2929,9 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { - use super::super::{ - test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS, VALUE_COMMITMENT_PERSONALIZATION, - }; + use super::super::{NUM_WINDOWS, VALUE_COMMITMENT_PERSONALIZATION}; use super::*; + use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}; use group::Curve; use pasta_curves::{ arithmetic::{CurveAffine, CurveExt}, diff --git a/src/constants/fixed_bases/value_commit_v.rs b/src/constants/fixed_bases/value_commit_v.rs index c17bdaed..83cbe37a 100644 --- a/src/constants/fixed_bases/value_commit_v.rs +++ b/src/constants/fixed_bases/value_commit_v.rs @@ -782,10 +782,9 @@ pub fn generator() -> pallas::Affine { #[cfg(test)] mod tests { - use super::super::{ - test_lagrange_coeffs, test_zs_and_us, NUM_WINDOWS_SHORT, VALUE_COMMITMENT_PERSONALIZATION, - }; + use super::super::{NUM_WINDOWS_SHORT, VALUE_COMMITMENT_PERSONALIZATION}; use super::*; + use crate::circuit::gadget::ecc::chip::constants::{test_lagrange_coeffs, test_zs_and_us}; use group::Curve; use pasta_curves::{ arithmetic::{CurveAffine, CurveExt}, @@ -803,13 +802,13 @@ mod tests { } #[test] - fn lagrange_coeffs_short() { + fn lagrange_coeffs() { let base = super::generator(); test_lagrange_coeffs(base, NUM_WINDOWS_SHORT); } #[test] - fn z_short() { + fn z() { let base = super::generator(); test_zs_and_us(base, &Z_SHORT, &U_SHORT, NUM_WINDOWS_SHORT); } diff --git a/src/constants/util.rs b/src/constants/util.rs index 8ceec799..756f6852 100644 --- a/src/constants/util.rs +++ b/src/constants/util.rs @@ -1,17 +1,3 @@ -use ff::Field; -use halo2::arithmetic::CurveAffine; - -/// Evaluate y = f(x) given the coefficients of f(x) -pub fn evaluate(x: u8, coeffs: &[C::Base]) -> C::Base { - let x = C::Base::from(x as u64); - coeffs - .iter() - .rev() - .cloned() - .reduce(|acc, coeff| acc * x + coeff) - .unwrap_or_else(C::Base::zero) -} - /// Takes in an FnMut closure and returns a constant-length array with elements of /// type `Output`. pub fn gen_const_array(