diff --git a/Cargo.toml b/Cargo.toml index 088c3e90..a8b984cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ bench = false dev-graph = ["halo2/dev-graph", "plotters"] test-dependencies = ["proptest"] test-sinsemilla = ["sinsemilla/testing"] +test-ecc = ["ecc/testing"] [[bench]] name = "note_decryption" diff --git a/halo2-gadgets/halo2_ecc/Cargo.toml b/halo2-gadgets/halo2_ecc/Cargo.toml index 9efc8eae..4ff54f48 100644 --- a/halo2-gadgets/halo2_ecc/Cargo.toml +++ b/halo2-gadgets/halo2_ecc/Cargo.toml @@ -26,5 +26,9 @@ pasta_curves = "0.1.2" rand = "0.8" utilities = { package = "halo2_utilities", version = "0.0", path = "../halo2_utilities" } -[dev-dependencies] -orchard = "0.0" +# Developer tooling dependencies +plotters = { version = "0.3.0", optional = true } + +[features] +dev-graph = ["halo2/dev-graph", "plotters"] +testing = [] diff --git a/halo2-gadgets/halo2_ecc/src/chip.rs b/halo2-gadgets/halo2_ecc/src/chip.rs index 94ec991b..4b9265e3 100644 --- a/halo2-gadgets/halo2_ecc/src/chip.rs +++ b/halo2-gadgets/halo2_ecc/src/chip.rs @@ -19,9 +19,11 @@ pub(super) mod mul; pub(super) mod mul_fixed; pub(super) mod witness_point; +pub use mul_fixed::{compute_lagrange_coeffs, compute_window_table, find_zs_and_us}; + /// Number of windows for a full-width scalar pub const NUM_WINDOWS: usize = - (L_ORCHARD_SCALAR + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE; + (L_PALLAS_SCALAR + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE; /// Number of windows for a short signed scalar pub const NUM_WINDOWS_SHORT: usize = @@ -31,13 +33,11 @@ pub const NUM_WINDOWS_SHORT: usize = /// Number of bits in an unsigned short scalar. pub(crate) const L_VALUE: usize = 64; -/// $\ell^\mathsf{Orchard}_\mathsf{base}$ /// Number of bits in a Pallas base field element. -pub(crate) const L_ORCHARD_BASE: usize = 255; +pub(crate) const L_PALLAS_BASE: usize = 255; -/// $\ell^\mathsf{Orchard}_\mathsf{scalar}$ /// Number of bits in a Pallas scalar field element. -pub(crate) const L_ORCHARD_SCALAR: usize = 255; +pub(crate) const L_PALLAS_SCALAR: usize = 255; /// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$. /// diff --git a/halo2-gadgets/halo2_ecc/src/chip/add.rs b/halo2-gadgets/halo2_ecc/src/chip/add.rs index 581cde1f..2f7eb8aa 100644 --- a/halo2-gadgets/halo2_ecc/src/chip/add.rs +++ b/halo2-gadgets/halo2_ecc/src/chip/add.rs @@ -385,9 +385,9 @@ impl Config { } } -#[cfg(test)] +#[cfg(feature = "testing")] pub mod tests { - use group::{prime::PrimeCurveAffine, Curve}; + use group::{prime::PrimeCurveAffine, Curve, Group}; use halo2::{circuit::Layouter, plonk::Error}; use pasta_curves::{arithmetic::CurveExt, pallas}; @@ -399,22 +399,35 @@ pub mod tests { >( chip: EccChip, mut layouter: impl Layouter, - p_val: pallas::Affine, - p: &NonIdentityPoint, - q_val: pallas::Affine, - q: &NonIdentityPoint, - p_neg: &NonIdentityPoint, ) -> Result<(), Error> { + // Generate a random point P + let p_val = pallas::Point::random(rand::rngs::OsRng).to_affine(); // P + let p = NonIdentityPoint::new(chip.clone(), layouter.namespace(|| "P"), Some(p_val))?; + let p_neg = -p_val; + let p_neg = NonIdentityPoint::new(chip.clone(), layouter.namespace(|| "-P"), Some(p_neg))?; + + // Generate a random point Q + let q_val = pallas::Point::random(rand::rngs::OsRng).to_affine(); // Q + let q = NonIdentityPoint::new(chip.clone(), layouter.namespace(|| "Q"), Some(q_val))?; + // Make sure P and Q are not the same point. assert_ne!(p_val, q_val); - // Check complete addition P + (-P) + // Generate a (0,0) point to be used in other tests. let zero = { - let result = p.add(layouter.namespace(|| "P + (-P)"), p_neg)?; - assert!(result.inner().is_identity().unwrap()); - result + Point::new( + chip.clone(), + layouter.namespace(|| "identity"), + Some(pallas::Affine::identity()), + )? }; + // Check complete addition P + (-P) + { + let result = p.add(layouter.namespace(|| "P + (-P)"), &p_neg)?; + result.constrain_equal(layouter.namespace(|| "P + (-P) = π’ͺ"), &zero)?; + } + // Check complete addition π’ͺ + π’ͺ { let result = zero.add(layouter.namespace(|| "π’ͺ + π’ͺ"), &zero)?; @@ -423,7 +436,7 @@ pub mod tests { // Check P + Q { - let result = p.add(layouter.namespace(|| "P + Q"), q)?; + let result = p.add(layouter.namespace(|| "P + Q"), &q)?; let witnessed_result = NonIdentityPoint::new( chip.clone(), layouter.namespace(|| "witnessed P + Q"), @@ -434,7 +447,7 @@ pub mod tests { // P + P { - let result = p.add(layouter.namespace(|| "P + P"), p)?; + let result = p.add(layouter.namespace(|| "P + P"), &p)?; let witnessed_result = NonIdentityPoint::new( chip.clone(), layouter.namespace(|| "witnessed P + P"), @@ -446,13 +459,13 @@ pub mod tests { // P + π’ͺ { let result = p.add(layouter.namespace(|| "P + π’ͺ"), &zero)?; - result.constrain_equal(layouter.namespace(|| "P + π’ͺ = P"), p)?; + result.constrain_equal(layouter.namespace(|| "P + π’ͺ = P"), &p)?; } // π’ͺ + P { - let result = zero.add(layouter.namespace(|| "π’ͺ + P"), p)?; - result.constrain_equal(layouter.namespace(|| "π’ͺ + P = P"), p)?; + let result = zero.add(layouter.namespace(|| "π’ͺ + P"), &p)?; + result.constrain_equal(layouter.namespace(|| "π’ͺ + P = P"), &p)?; } // (x, y) + (ΞΆx, y) should behave like normal P + Q. diff --git a/halo2-gadgets/halo2_ecc/src/chip/add_incomplete.rs b/halo2-gadgets/halo2_ecc/src/chip/add_incomplete.rs index 5e114421..8695c495 100644 --- a/halo2-gadgets/halo2_ecc/src/chip/add_incomplete.rs +++ b/halo2-gadgets/halo2_ecc/src/chip/add_incomplete.rs @@ -143,9 +143,9 @@ impl Config { } } -#[cfg(test)] +#[cfg(feature = "testing")] pub mod tests { - use group::Curve; + use group::{prime::PrimeCurveAffine, Curve, Group}; use halo2::{circuit::Layouter, plonk::Error}; use pasta_curves::pallas; @@ -157,15 +157,32 @@ pub mod tests { >( chip: EccChip, mut layouter: impl Layouter, - p_val: pallas::Affine, - p: &NonIdentityPoint, - q_val: pallas::Affine, - q: &NonIdentityPoint, - p_neg: &NonIdentityPoint, ) -> Result<(), Error> { + // Generate a random point P + let p_val = pallas::Point::random(rand::rngs::OsRng).to_affine(); // P + let p = NonIdentityPoint::new(chip.clone(), layouter.namespace(|| "P"), Some(p_val))?; + let p_neg = -p_val; + let p_neg = NonIdentityPoint::new(chip.clone(), layouter.namespace(|| "-P"), Some(p_neg))?; + + // Generate a random point Q + let q_val = pallas::Point::random(rand::rngs::OsRng).to_affine(); // Q + let q = NonIdentityPoint::new(chip.clone(), layouter.namespace(|| "Q"), Some(q_val))?; + + // Make sure P and Q are not the same point. + assert_ne!(p_val, q_val); + + // Generate a (0,0) point to be used in other tests. + let zero = { + Point::new( + chip.clone(), + layouter.namespace(|| "identity"), + Some(pallas::Affine::identity()), + )? + }; + // P + Q { - let result = p.add_incomplete(layouter.namespace(|| "P + Q"), q)?; + let result = p.add_incomplete(layouter.namespace(|| "P + Q"), &q)?; let witnessed_result = NonIdentityPoint::new( chip, layouter.namespace(|| "witnessed P + Q"), @@ -175,11 +192,11 @@ pub mod tests { } // P + P should return an error - p.add_incomplete(layouter.namespace(|| "P + P"), p) + p.add_incomplete(layouter.namespace(|| "P + P"), &p) .expect_err("P + P should return an error"); // P + (-P) should return an error - p.add_incomplete(layouter.namespace(|| "P + (-P)"), p_neg) + p.add_incomplete(layouter.namespace(|| "P + (-P)"), &p_neg) .expect_err("P + (-P) should return an error"); Ok(()) diff --git a/halo2-gadgets/halo2_ecc/src/chip/mul.rs b/halo2-gadgets/halo2_ecc/src/chip/mul.rs index 8ce76f19..5116ecb6 100644 --- a/halo2-gadgets/halo2_ecc/src/chip/mul.rs +++ b/halo2-gadgets/halo2_ecc/src/chip/mul.rs @@ -442,9 +442,9 @@ fn decompose_for_scalar_mul(scalar: Option) -> Vec> { } } -#[cfg(test)] +#[cfg(feature = "testing")] pub mod tests { - use group::Curve; + use group::{prime::PrimeCurveAffine, Curve, Group}; use halo2::{ circuit::{Chip, Layouter}, plonk::Error, @@ -453,17 +453,27 @@ pub mod tests { use crate::{ chip::EccChip, - gadget::{EccInstructions, NonIdentityPoint, Point}, + gadget::{EccInstructions, FixedPoints, NonIdentityPoint, Point}, }; - use orchard::constants::OrchardFixedBases; use utilities::UtilitiesInstructions; - pub fn test_mul( - chip: EccChip, + pub fn test_mul>( + chip: EccChip, mut layouter: impl Layouter, - p: &NonIdentityPoint>, - p_val: pallas::Affine, ) -> Result<(), Error> { + // Generate a random point P + let p_val = pallas::Point::random(rand::rngs::OsRng).to_affine(); // P + let p = NonIdentityPoint::new(chip.clone(), layouter.namespace(|| "P"), Some(p_val))?; + + // Generate a (0,0) point to be used in other tests. + let zero = { + Point::new( + chip.clone(), + layouter.namespace(|| "identity"), + Some(pallas::Affine::identity()), + )? + }; + let column = chip.config().advices[0]; fn constrain_equal_non_id< diff --git a/halo2-gadgets/halo2_ecc/src/chip/mul_fixed.rs b/halo2-gadgets/halo2_ecc/src/chip/mul_fixed.rs index 41b3111d..e6a480e5 100644 --- a/halo2-gadgets/halo2_ecc/src/chip/mul_fixed.rs +++ b/halo2-gadgets/halo2_ecc/src/chip/mul_fixed.rs @@ -19,6 +19,9 @@ use pasta_curves::{ pub mod base_field_elem; pub mod full_width; pub mod short; +pub mod util; + +pub use util::{compute_lagrange_coeffs, compute_window_table, find_zs_and_us}; lazy_static! { static ref TWO_SCALAR: pallas::Scalar = pallas::Scalar::from_u64(2); diff --git a/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/base_field_elem.rs b/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/base_field_elem.rs index 82e8de2d..06cfcd32 100644 --- a/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/base_field_elem.rs +++ b/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/base_field_elem.rs @@ -1,6 +1,6 @@ use super::super::{ EccBaseFieldElemFixed, EccConfig, EccPoint, FixedPoints, FIXED_BASE_WINDOW_SIZE, LOOKUP_K, - L_ORCHARD_BASE, NUM_WINDOWS, T_P, + L_PALLAS_BASE, NUM_WINDOWS, T_P, }; use super::H_BASE; use utilities::{ @@ -170,7 +170,7 @@ impl> Config { offset, scalar, true, - L_ORCHARD_BASE, + L_PALLAS_BASE, NUM_WINDOWS, )?; EccBaseFieldElemFixed { @@ -375,9 +375,9 @@ impl> Config { } } -#[cfg(test)] +#[cfg(feature = "testing")] pub mod tests { - use group::Curve; + use group::{Curve, Group}; use halo2::{ circuit::{Chip, Layouter}, plonk::Error, @@ -385,41 +385,47 @@ pub mod tests { use pasta_curves::{arithmetic::FieldExt, pallas}; use crate::{ - chip::EccChip, + chip::{EccChip, NUM_WINDOWS}, gadget::{FixedPoint, FixedPoints, NonIdentityPoint, Point, H}, }; - use orchard::constants::OrchardFixedBases; use utilities::UtilitiesInstructions; - pub fn test_mul_fixed_base_field( - chip: EccChip, + use lazy_static::lazy_static; + + lazy_static! { + static ref BASE: pallas::Affine = pallas::Point::generator().to_affine(); + static ref ZS_AND_US: Vec<(u64, [[u8; 32]; H])> = + crate::chip::find_zs_and_us(*BASE, NUM_WINDOWS).unwrap(); + } + + pub fn test_mul_fixed_base_field>( + base: F, + chip: EccChip, mut layouter: impl Layouter, ) -> Result<(), Error> { - // nullifier_k - let nullifier_k = OrchardFixedBases::NullifierK; test_single_base( chip.clone(), - layouter.namespace(|| "nullifier_k"), - FixedPoint::from_inner(chip, nullifier_k), - nullifier_k.generator(), + layouter.namespace(|| "fixed base"), + FixedPoint::from_inner(chip, base.clone()), + base.generator(), ) } #[allow(clippy::op_ref)] - fn test_single_base( - chip: EccChip, + fn test_single_base>( + chip: EccChip, mut layouter: impl Layouter, - base: FixedPoint>, + base: FixedPoint>, base_val: pallas::Affine, ) -> Result<(), Error> { let column = chip.config().advices[0]; - fn constrain_equal_non_id( - chip: EccChip, + fn constrain_equal_non_id>( + chip: EccChip, mut layouter: impl Layouter, base_val: pallas::Affine, scalar_val: pallas::Base, - result: Point>, + result: Point>, ) -> Result<(), Error> { // Move scalar from base field into scalar field (which always fits for Pallas). let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap(); diff --git a/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/full_width.rs b/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/full_width.rs index 17b2b3c2..dd9a7b8c 100644 --- a/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/full_width.rs +++ b/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/full_width.rs @@ -1,5 +1,5 @@ use super::super::{ - EccConfig, EccPoint, EccScalarFixed, FixedPoints, FIXED_BASE_WINDOW_SIZE, H, L_ORCHARD_SCALAR, + EccConfig, EccPoint, EccScalarFixed, FixedPoints, FIXED_BASE_WINDOW_SIZE, H, L_PALLAS_SCALAR, NUM_WINDOWS, }; @@ -54,7 +54,7 @@ impl> Config { offset: usize, scalar: Option, ) -> Result { - let windows = self.decompose_scalar_fixed::(scalar, offset, region)?; + let windows = self.decompose_scalar_fixed::(scalar, offset, region)?; Ok(EccScalarFixed { value: scalar, @@ -162,7 +162,7 @@ impl> Config { } } -#[cfg(test)] +#[cfg(feature = "testing")] pub mod tests { use group::Curve; use halo2::{circuit::Layouter, plonk::Error}; @@ -172,64 +172,35 @@ pub mod tests { chip::EccChip, gadget::{FixedPoint, FixedPoints, NonIdentityPoint, Point, H}, }; - use orchard::constants::OrchardFixedBases; - pub fn test_mul_fixed( - chip: EccChip, + pub fn test_mul_fixed>( + base: F, + chip: EccChip, mut layouter: impl Layouter, ) -> Result<(), Error> { - // commit_ivk_r - let commit_ivk_r = OrchardFixedBases::CommitIvkR; test_single_base( chip.clone(), - layouter.namespace(|| "commit_ivk_r"), - FixedPoint::from_inner(chip.clone(), commit_ivk_r), - commit_ivk_r.generator(), - )?; - - // note_commit_r - let note_commit_r = OrchardFixedBases::NoteCommitR; - test_single_base( - chip.clone(), - layouter.namespace(|| "note_commit_r"), - FixedPoint::from_inner(chip.clone(), note_commit_r), - note_commit_r.generator(), - )?; - - // value_commit_r - let value_commit_r = OrchardFixedBases::ValueCommitR; - test_single_base( - chip.clone(), - layouter.namespace(|| "value_commit_r"), - FixedPoint::from_inner(chip.clone(), value_commit_r), - value_commit_r.generator(), - )?; - - // spend_auth_g - let spend_auth_g = OrchardFixedBases::SpendAuthG; - test_single_base( - chip.clone(), - layouter.namespace(|| "spend_auth_g"), - FixedPoint::from_inner(chip, spend_auth_g), - spend_auth_g.generator(), + layouter.namespace(|| "fixed base"), + FixedPoint::from_inner(chip, base.clone()), + base.generator(), )?; Ok(()) } #[allow(clippy::op_ref)] - fn test_single_base( - chip: EccChip, + fn test_single_base>( + chip: EccChip, mut layouter: impl Layouter, - base: FixedPoint>, + base: FixedPoint>, base_val: pallas::Affine, ) -> Result<(), Error> { - fn constrain_equal_non_id( - chip: EccChip, + fn constrain_equal_non_id>( + chip: EccChip, mut layouter: impl Layouter, base_val: pallas::Affine, scalar_val: pallas::Scalar, - result: Point>, + result: Point>, ) -> Result<(), Error> { let expected = NonIdentityPoint::new( chip, diff --git a/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/short.rs b/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/short.rs index f6d5814c..167702ba 100644 --- a/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/short.rs +++ b/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/short.rs @@ -232,12 +232,12 @@ impl> Config { } } -#[cfg(test)] +#[cfg(feature = "testing")] pub mod tests { use group::Curve; use halo2::{ circuit::{Chip, Layouter}, - plonk::{Any, Error}, + plonk::Error, }; use pasta_curves::{arithmetic::FieldExt, pallas}; @@ -245,21 +245,19 @@ pub mod tests { chip::EccChip, gadget::{FixedPoint, FixedPoints, NonIdentityPoint, Point}, }; - use orchard::constants::OrchardFixedBases; - use utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions}; + use utilities::{CellValue, UtilitiesInstructions}; #[allow(clippy::op_ref)] - pub fn test_mul_fixed_short( - chip: EccChip, + pub fn test_mul_fixed_short>( + base: F, + chip: EccChip, mut layouter: impl Layouter, ) -> Result<(), Error> { - // value_commit_v - let value_commit_v = OrchardFixedBases::ValueCommitV; - let base_val = value_commit_v.generator(); - let value_commit_v = FixedPoint::from_inner(chip.clone(), value_commit_v); + let base_val = base.generator(); + let base = FixedPoint::from_inner(chip.clone(), base); - fn load_magnitude_sign( - chip: EccChip, + fn load_magnitude_sign>( + chip: EccChip, mut layouter: impl Layouter, magnitude: pallas::Base, sign: pallas::Base, @@ -272,12 +270,12 @@ pub mod tests { Ok((magnitude, sign)) } - fn constrain_equal_non_id( - chip: EccChip, + fn constrain_equal_non_id>( + chip: EccChip, mut layouter: impl Layouter, base_val: pallas::Affine, scalar_val: pallas::Scalar, - result: Point>, + result: Point>, ) -> Result<(), Error> { let expected = NonIdentityPoint::new( chip, @@ -332,7 +330,7 @@ pub mod tests { *magnitude, *sign, )?; - value_commit_v.mul_short(layouter.namespace(|| *name), magnitude_sign)? + base.mul_short(layouter.namespace(|| *name), magnitude_sign)? }; // Move from base field into scalar field let scalar = { @@ -380,9 +378,45 @@ pub mod tests { use halo2::{ circuit::{Layouter, SimpleFloorPlanner}, dev::{MockProver, VerifyFailure}, - plonk::{Circuit, ConstraintSystem, Error}, + plonk::{Any, Circuit, ConstraintSystem, Error}, }; - use utilities::{CellValue, UtilitiesInstructions}; + use utilities::{ + lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, + }; + + use crate::{ + chip::{compute_lagrange_coeffs, NUM_WINDOWS_SHORT}, + gadget::H, + }; + use group::{Curve, Group}; + use lazy_static::lazy_static; + + lazy_static! { + static ref BASE: pallas::Affine = pallas::Point::generator().to_affine(); + static ref ZS_AND_US: Vec<(u64, [[u8; 32]; H])> = + crate::chip::find_zs_and_us(*BASE, NUM_WINDOWS_SHORT).unwrap(); + } + + #[derive(Debug, Eq, PartialEq, Clone)] + struct FixedBase; + + impl FixedPoints for FixedBase { + fn generator(&self) -> pallas::Affine { + *BASE + } + + fn u(&self) -> Vec<[[u8; 32]; H]> { + ZS_AND_US.iter().map(|(_, us)| *us).collect() + } + + fn z(&self) -> Vec { + ZS_AND_US.iter().map(|(z, _)| *z).collect() + } + + fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> { + compute_lagrange_coeffs(self.generator(), NUM_WINDOWS_SHORT) + } + } #[derive(Default)] struct MyCircuit { @@ -432,7 +466,7 @@ pub mod tests { meta.enable_constant(constants); let range_check = LookupRangeCheckConfig::configure(meta, advices[9], lookup_table); - EccChip::::configure(meta, advices, lagrange_coeffs, range_check) + EccChip::::configure(meta, advices, lagrange_coeffs, range_check) } fn synthesize( @@ -442,7 +476,7 @@ pub mod tests { ) -> Result<(), Error> { let column = config.advices[0]; - let short_config: super::Config = (&config).into(); + let short_config: super::Config = (&config).into(); let magnitude_sign = { let magnitude = self.load_private( layouter.namespace(|| "load magnitude"), @@ -454,7 +488,7 @@ pub mod tests { (magnitude, sign) }; - short_config.assign(layouter, magnitude_sign, &OrchardFixedBases::ValueCommitV)?; + short_config.assign(layouter, magnitude_sign, &FixedBase)?; Ok(()) } diff --git a/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/util.rs b/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/util.rs index 1bb99882..7a287f7a 100644 --- a/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/util.rs +++ b/halo2-gadgets/halo2_ecc/src/chip/mul_fixed/util.rs @@ -85,11 +85,6 @@ pub fn compute_lagrange_coeffs(base: C, num_windows: usize) -> V /// - $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, diff --git a/halo2-gadgets/halo2_ecc/src/gadget.rs b/halo2-gadgets/halo2_ecc/src/gadget.rs index b4d7f2a3..9ee523b7 100644 --- a/halo2-gadgets/halo2_ecc/src/gadget.rs +++ b/halo2-gadgets/halo2_ecc/src/gadget.rs @@ -377,7 +377,8 @@ impl> X { #[derive(Clone, Debug)] pub struct FixedPoint> { chip: EccChip, - inner: EccChip::FixedPoints, + /// UNDO THIS pub. + pub inner: EccChip::FixedPoints, } impl> FixedPoint { @@ -448,30 +449,31 @@ impl> FixedPoint { } } -#[cfg(test)] -mod tests { - use group::{prime::PrimeCurveAffine, Curve, Group}; +#[cfg(feature = "testing")] +pub mod testing { + use crate::{ + chip::{EccChip, EccConfig}, + gadget::FixedPoints, + }; + use utilities::lookup_range_check::LookupRangeCheckConfig; use halo2::{ circuit::{Layouter, SimpleFloorPlanner}, - dev::MockProver, plonk::{Circuit, ConstraintSystem, Error}, }; use pasta_curves::pallas; - use crate::chip::{EccChip, EccConfig}; - use orchard::constants::OrchardFixedBases; - use utilities::lookup_range_check::LookupRangeCheckConfig; + use std::marker::PhantomData; - struct MyCircuit {} + pub struct MyCircuit, F: FixedPoints>(pub PhantomData<(S, F)>); #[allow(non_snake_case)] - impl Circuit for MyCircuit { + impl, F: FixedPoints> Circuit for MyCircuit { type Config = EccConfig; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { - MyCircuit {} + MyCircuit(PhantomData) } fn configure(meta: &mut ConstraintSystem) -> Self::Config { @@ -503,7 +505,7 @@ mod tests { meta.enable_constant(constants); let range_check = LookupRangeCheckConfig::configure(meta, advices[9], lookup_table); - EccChip::::configure(meta, advices, lagrange_coeffs, range_check) + EccChip::::configure(meta, advices, lagrange_coeffs, range_check) } fn synthesize( @@ -517,123 +519,183 @@ mod tests { // provided by the Sinsemilla chip. config.lookup_config.load(&mut layouter)?; - // Generate a random non-identity point P - let p_val = pallas::Point::random(rand::rngs::OsRng).to_affine(); // P - let p = super::NonIdentityPoint::new( + S::test_witness_non_id( chip.clone(), - layouter.namespace(|| "P"), - Some(p_val), + layouter.namespace(|| "witness non-identity point"), )?; - let p_neg = -p_val; - let p_neg = super::NonIdentityPoint::new( + S::test_add(chip.clone(), layouter.namespace(|| "addition"))?; + S::test_add_incomplete(chip.clone(), layouter.namespace(|| "incomplete addition"))?; + S::test_mul( chip.clone(), - layouter.namespace(|| "-P"), - Some(p_neg), + layouter.namespace(|| "variable-base scalar multiplication"), + )?; + S::test_mul_fixed( + chip.clone(), + layouter.namespace(|| "fixed-base scalar multiplication with full-width scalar"), + )?; + S::test_mul_fixed_short( + chip.clone(), + layouter.namespace(|| "fixed-base scalar multiplication with short signed scalar"), + )?; + S::test_mul_fixed_base_field( + chip, + layouter.namespace(|| "fixed-base scalar multiplication with base field element"), )?; - // Generate a random non-identity point Q - let q_val = pallas::Point::random(rand::rngs::OsRng).to_affine(); // Q - let q = super::NonIdentityPoint::new( - chip.clone(), - layouter.namespace(|| "Q"), - Some(q_val), - )?; + Ok(()) + } + } - // Make sure P and Q are not the same point. - assert_ne!(p_val, q_val); + pub trait EccTest> { + fn fixed_bases_full() -> Vec; + fn fixed_bases_short() -> Vec; + fn fixed_bases_base_field() -> Vec; - // Test that we can witness the identity as a point, but not as a non-identity point. - { - let _ = super::Point::new( - chip.clone(), - layouter.namespace(|| "identity"), - Some(pallas::Affine::identity()), - )?; + fn test_witness_non_id( + chip: EccChip, + layouter: impl Layouter, + ) -> Result<(), Error> { + crate::chip::witness_point::tests::test_witness_non_id(chip, layouter) + } - super::NonIdentityPoint::new( - chip.clone(), - layouter.namespace(|| "identity"), - Some(pallas::Affine::identity()), - ) - .expect_err("Trying to witness the identity should return an error"); - } + fn test_add(chip: EccChip, layouter: impl Layouter) -> Result<(), Error> { + crate::chip::add::tests::test_add(chip, layouter) + } - // Test witness non-identity point - { - super::chip::witness_point::tests::test_witness_non_id( - chip.clone(), - layouter.namespace(|| "witness non-identity point"), - ) - } + fn test_add_incomplete( + chip: EccChip, + layouter: impl Layouter, + ) -> Result<(), Error> { + crate::chip::add_incomplete::tests::test_add_incomplete(chip, layouter) + } - // Test complete addition - { - crate::chip::add::tests::test_add( - chip.clone(), - layouter.namespace(|| "complete addition"), - p_val, - &p, - q_val, - &q, - &p_neg, - )?; - } + fn test_mul(chip: EccChip, layouter: impl Layouter) -> Result<(), Error> { + crate::chip::mul::tests::test_mul(chip, layouter) + } - // Test incomplete addition - { - crate::chip::add_incomplete::tests::test_add_incomplete( - chip.clone(), - layouter.namespace(|| "incomplete addition"), - p_val, - &p, - q_val, - &q, - &p_neg, - )?; - } - - // Test variable-base scalar multiplication - { - crate::chip::mul::tests::test_mul( - chip.clone(), - layouter.namespace(|| "variable-base scalar mul"), - &p, - p_val, - )?; - } - - // Test full-width fixed-base scalar multiplication - { + fn test_mul_fixed( + chip: EccChip, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + for base in Self::fixed_bases_full().into_iter() { crate::chip::mul_fixed::full_width::tests::test_mul_fixed( + base, chip.clone(), layouter.namespace(|| "full-width fixed-base scalar mul"), )?; } - // Test signed short fixed-base scalar multiplication - { + Ok(()) + } + + fn test_mul_fixed_short( + chip: EccChip, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + for base in Self::fixed_bases_short().into_iter() { crate::chip::mul_fixed::short::tests::test_mul_fixed_short( + base, chip.clone(), - layouter.namespace(|| "signed short fixed-base scalar mul"), + layouter.namespace(|| "full-width fixed-base scalar mul"), )?; } - // Test fixed-base scalar multiplication with a base field element - { + Ok(()) + } + + fn test_mul_fixed_base_field( + chip: EccChip, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + for base in Self::fixed_bases_base_field().into_iter() { crate::chip::mul_fixed::base_field_elem::tests::test_mul_fixed_base_field( - chip, - layouter.namespace(|| "fixed-base scalar mul with base field element"), + base, + chip.clone(), + layouter.namespace(|| "full-width fixed-base scalar mul"), )?; } Ok(()) } } +} + +#[cfg(feature = "testing")] +mod tests { + use group::{Curve, Group}; + + use pasta_curves::pallas; + + use crate::{ + chip::{compute_lagrange_coeffs, find_zs_and_us, NUM_WINDOWS, NUM_WINDOWS_SHORT}, + gadget::{FixedPoints, H}, + }; + use lazy_static::lazy_static; + + #[derive(Debug, Eq, PartialEq, Clone)] + enum FixedBase { + FullWidth, + Short, + } + + lazy_static! { + static ref BASE: pallas::Affine = pallas::Point::generator().to_affine(); + static ref ZS_AND_US: Vec<(u64, [[u8; 32]; H])> = + find_zs_and_us(*BASE, NUM_WINDOWS).unwrap(); + static ref ZS_AND_US_SHORT: Vec<(u64, [[u8; 32]; H])> = + find_zs_and_us(*BASE, NUM_WINDOWS_SHORT).unwrap(); + static ref LAGRANGE_COEFFS: Vec<[pallas::Base; H]> = + compute_lagrange_coeffs(*BASE, NUM_WINDOWS); + static ref LAGRANGE_COEFFS_SHORT: Vec<[pallas::Base; H]> = + compute_lagrange_coeffs(*BASE, NUM_WINDOWS_SHORT); + } + + impl FixedPoints for FixedBase { + fn generator(&self) -> pallas::Affine { + *BASE + } + + fn u(&self) -> Vec<[[u8; 32]; H]> { + match self { + FixedBase::FullWidth => ZS_AND_US.iter().map(|(_, us)| *us).collect(), + FixedBase::Short => ZS_AND_US_SHORT.iter().map(|(_, us)| *us).collect(), + } + } + + fn z(&self) -> Vec { + match self { + FixedBase::FullWidth => ZS_AND_US.iter().map(|(z, _)| *z).collect(), + FixedBase::Short => ZS_AND_US_SHORT.iter().map(|(z, _)| *z).collect(), + } + } + + fn lagrange_coeffs(&self) -> Vec<[pallas::Base; H]> { + match self { + FixedBase::FullWidth => LAGRANGE_COEFFS.to_vec(), + FixedBase::Short => LAGRANGE_COEFFS_SHORT.to_vec(), + } + } + } + + struct Test; + impl super::testing::EccTest for Test { + fn fixed_bases_full() -> Vec { + vec![FixedBase::FullWidth] + } + fn fixed_bases_short() -> Vec { + vec![FixedBase::Short] + } + fn fixed_bases_base_field() -> Vec { + vec![FixedBase::FullWidth] + } + } #[test] fn ecc_chip() { + use halo2::dev::MockProver; + let k = 13; - let circuit = MyCircuit {}; + let circuit = super::testing::MyCircuit::(std::marker::PhantomData); let prover = MockProver::run(k, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())) } diff --git a/src/constants/fixed_bases.rs b/src/constants/fixed_bases.rs index 30424ce8..8af74955 100644 --- a/src/constants/fixed_bases.rs +++ b/src/constants/fixed_bases.rs @@ -110,3 +110,33 @@ impl FixedPoints for OrchardFixedBases { } } } + +#[cfg(feature = "test-ecc")] +#[test] +fn test_orchard_fixed_bases() { + use ecc::gadget::testing; + use halo2::dev::MockProver; + + struct OrchardTest; + impl testing::EccTest for OrchardTest { + fn fixed_bases_full() -> Vec { + vec![ + OrchardFixedBases::CommitIvkR, + OrchardFixedBases::NoteCommitR, + OrchardFixedBases::SpendAuthG, + OrchardFixedBases::ValueCommitR, + ] + } + fn fixed_bases_short() -> Vec { + vec![OrchardFixedBases::ValueCommitV] + } + fn fixed_bases_base_field() -> Vec { + vec![OrchardFixedBases::NullifierK] + } + } + + let k = 13; + let circuit = testing::MyCircuit::(std::marker::PhantomData); + let prover = MockProver::run(k, &circuit, vec![]).unwrap(); + assert_eq!(prover.verify(), Ok(())) +}