Refactor ECC tests to be generic over fixed bases.

This removes the need to import OrchardFixedBases to the halo2_ecc
crate.
This commit is contained in:
therealyingtong 2021-08-25 12:38:53 +08:00
parent a997364545
commit 1acf0c2c15
13 changed files with 372 additions and 226 deletions

View File

@ -65,6 +65,7 @@ bench = false
dev-graph = ["halo2/dev-graph", "plotters"] dev-graph = ["halo2/dev-graph", "plotters"]
test-dependencies = ["proptest"] test-dependencies = ["proptest"]
test-sinsemilla = ["sinsemilla/testing"] test-sinsemilla = ["sinsemilla/testing"]
test-ecc = ["ecc/testing"]
[[bench]] [[bench]]
name = "note_decryption" name = "note_decryption"

View File

@ -26,5 +26,9 @@ pasta_curves = "0.1.2"
rand = "0.8" rand = "0.8"
utilities = { package = "halo2_utilities", version = "0.0", path = "../halo2_utilities" } utilities = { package = "halo2_utilities", version = "0.0", path = "../halo2_utilities" }
[dev-dependencies] # Developer tooling dependencies
orchard = "0.0" plotters = { version = "0.3.0", optional = true }
[features]
dev-graph = ["halo2/dev-graph", "plotters"]
testing = []

View File

@ -19,9 +19,11 @@ pub(super) mod mul;
pub(super) mod mul_fixed; pub(super) mod mul_fixed;
pub(super) mod witness_point; 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 /// Number of windows for a full-width scalar
pub const NUM_WINDOWS: usize = 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 /// Number of windows for a short signed scalar
pub const NUM_WINDOWS_SHORT: usize = pub const NUM_WINDOWS_SHORT: usize =
@ -31,13 +33,11 @@ pub const NUM_WINDOWS_SHORT: usize =
/// Number of bits in an unsigned short scalar. /// Number of bits in an unsigned short scalar.
pub(crate) const L_VALUE: usize = 64; pub(crate) const L_VALUE: usize = 64;
/// $\ell^\mathsf{Orchard}_\mathsf{base}$
/// Number of bits in a Pallas base field element. /// 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. /// 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}$. /// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$.
/// <https://github.com/zcash/pasta> /// <https://github.com/zcash/pasta>

View File

@ -385,9 +385,9 @@ impl Config {
} }
} }
#[cfg(test)] #[cfg(feature = "testing")]
pub mod tests { pub mod tests {
use group::{prime::PrimeCurveAffine, Curve}; use group::{prime::PrimeCurveAffine, Curve, Group};
use halo2::{circuit::Layouter, plonk::Error}; use halo2::{circuit::Layouter, plonk::Error};
use pasta_curves::{arithmetic::CurveExt, pallas}; use pasta_curves::{arithmetic::CurveExt, pallas};
@ -399,22 +399,35 @@ pub mod tests {
>( >(
chip: EccChip, chip: EccChip,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
p_val: pallas::Affine,
p: &NonIdentityPoint<pallas::Affine, EccChip>,
q_val: pallas::Affine,
q: &NonIdentityPoint<pallas::Affine, EccChip>,
p_neg: &NonIdentityPoint<pallas::Affine, EccChip>,
) -> Result<(), Error> { ) -> 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. // Make sure P and Q are not the same point.
assert_ne!(p_val, q_val); 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 zero = {
let result = p.add(layouter.namespace(|| "P + (-P)"), p_neg)?; Point::new(
assert!(result.inner().is_identity().unwrap()); chip.clone(),
result 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 𝒪 + 𝒪 // Check complete addition 𝒪 + 𝒪
{ {
let result = zero.add(layouter.namespace(|| "𝒪 + 𝒪"), &zero)?; let result = zero.add(layouter.namespace(|| "𝒪 + 𝒪"), &zero)?;
@ -423,7 +436,7 @@ pub mod tests {
// Check P + Q // 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( let witnessed_result = NonIdentityPoint::new(
chip.clone(), chip.clone(),
layouter.namespace(|| "witnessed P + Q"), layouter.namespace(|| "witnessed P + Q"),
@ -434,7 +447,7 @@ pub mod tests {
// P + P // 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( let witnessed_result = NonIdentityPoint::new(
chip.clone(), chip.clone(),
layouter.namespace(|| "witnessed P + P"), layouter.namespace(|| "witnessed P + P"),
@ -446,13 +459,13 @@ pub mod tests {
// P + 𝒪 // P + 𝒪
{ {
let result = p.add(layouter.namespace(|| "P + 𝒪"), &zero)?; 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 // 𝒪 + P
{ {
let result = zero.add(layouter.namespace(|| "𝒪 + P"), p)?; let result = zero.add(layouter.namespace(|| "𝒪 + P"), &p)?;
result.constrain_equal(layouter.namespace(|| "𝒪 + P = P"), p)?; result.constrain_equal(layouter.namespace(|| "𝒪 + P = P"), &p)?;
} }
// (x, y) + (ζx, y) should behave like normal P + Q. // (x, y) + (ζx, y) should behave like normal P + Q.

View File

@ -143,9 +143,9 @@ impl Config {
} }
} }
#[cfg(test)] #[cfg(feature = "testing")]
pub mod tests { pub mod tests {
use group::Curve; use group::{prime::PrimeCurveAffine, Curve, Group};
use halo2::{circuit::Layouter, plonk::Error}; use halo2::{circuit::Layouter, plonk::Error};
use pasta_curves::pallas; use pasta_curves::pallas;
@ -157,15 +157,32 @@ pub mod tests {
>( >(
chip: EccChip, chip: EccChip,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
p_val: pallas::Affine,
p: &NonIdentityPoint<pallas::Affine, EccChip>,
q_val: pallas::Affine,
q: &NonIdentityPoint<pallas::Affine, EccChip>,
p_neg: &NonIdentityPoint<pallas::Affine, EccChip>,
) -> Result<(), Error> { ) -> 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 // 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( let witnessed_result = NonIdentityPoint::new(
chip, chip,
layouter.namespace(|| "witnessed P + Q"), layouter.namespace(|| "witnessed P + Q"),
@ -175,11 +192,11 @@ pub mod tests {
} }
// P + P should return an error // 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"); .expect_err("P + P should return an error");
// 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"); .expect_err("P + (-P) should return an error");
Ok(()) Ok(())

View File

@ -442,9 +442,9 @@ fn decompose_for_scalar_mul(scalar: Option<pallas::Base>) -> Vec<Option<bool>> {
} }
} }
#[cfg(test)] #[cfg(feature = "testing")]
pub mod tests { pub mod tests {
use group::Curve; use group::{prime::PrimeCurveAffine, Curve, Group};
use halo2::{ use halo2::{
circuit::{Chip, Layouter}, circuit::{Chip, Layouter},
plonk::Error, plonk::Error,
@ -453,17 +453,27 @@ pub mod tests {
use crate::{ use crate::{
chip::EccChip, chip::EccChip,
gadget::{EccInstructions, NonIdentityPoint, Point}, gadget::{EccInstructions, FixedPoints, NonIdentityPoint, Point},
}; };
use orchard::constants::OrchardFixedBases;
use utilities::UtilitiesInstructions; use utilities::UtilitiesInstructions;
pub fn test_mul( pub fn test_mul<F: FixedPoints<pallas::Affine>>(
chip: EccChip<OrchardFixedBases>, chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
p: &NonIdentityPoint<pallas::Affine, EccChip<OrchardFixedBases>>,
p_val: pallas::Affine,
) -> Result<(), Error> { ) -> 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]; let column = chip.config().advices[0];
fn constrain_equal_non_id< fn constrain_equal_non_id<

View File

@ -19,6 +19,9 @@ use pasta_curves::{
pub mod base_field_elem; pub mod base_field_elem;
pub mod full_width; pub mod full_width;
pub mod short; pub mod short;
pub mod util;
pub use util::{compute_lagrange_coeffs, compute_window_table, find_zs_and_us};
lazy_static! { lazy_static! {
static ref TWO_SCALAR: pallas::Scalar = pallas::Scalar::from_u64(2); static ref TWO_SCALAR: pallas::Scalar = pallas::Scalar::from_u64(2);

View File

@ -1,6 +1,6 @@
use super::super::{ use super::super::{
EccBaseFieldElemFixed, EccConfig, EccPoint, FixedPoints, FIXED_BASE_WINDOW_SIZE, LOOKUP_K, 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 super::H_BASE;
use utilities::{ use utilities::{
@ -170,7 +170,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
offset, offset,
scalar, scalar,
true, true,
L_ORCHARD_BASE, L_PALLAS_BASE,
NUM_WINDOWS, NUM_WINDOWS,
)?; )?;
EccBaseFieldElemFixed { EccBaseFieldElemFixed {
@ -375,9 +375,9 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
} }
} }
#[cfg(test)] #[cfg(feature = "testing")]
pub mod tests { pub mod tests {
use group::Curve; use group::{Curve, Group};
use halo2::{ use halo2::{
circuit::{Chip, Layouter}, circuit::{Chip, Layouter},
plonk::Error, plonk::Error,
@ -385,41 +385,47 @@ pub mod tests {
use pasta_curves::{arithmetic::FieldExt, pallas}; use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::{ use crate::{
chip::EccChip, chip::{EccChip, NUM_WINDOWS},
gadget::{FixedPoint, FixedPoints, NonIdentityPoint, Point, H}, gadget::{FixedPoint, FixedPoints, NonIdentityPoint, Point, H},
}; };
use orchard::constants::OrchardFixedBases;
use utilities::UtilitiesInstructions; use utilities::UtilitiesInstructions;
pub fn test_mul_fixed_base_field( use lazy_static::lazy_static;
chip: EccChip<OrchardFixedBases>,
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<F: FixedPoints<pallas::Affine>>(
base: F,
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// nullifier_k
let nullifier_k = OrchardFixedBases::NullifierK;
test_single_base( test_single_base(
chip.clone(), chip.clone(),
layouter.namespace(|| "nullifier_k"), layouter.namespace(|| "fixed base"),
FixedPoint::from_inner(chip, nullifier_k), FixedPoint::from_inner(chip, base.clone()),
nullifier_k.generator(), base.generator(),
) )
} }
#[allow(clippy::op_ref)] #[allow(clippy::op_ref)]
fn test_single_base( fn test_single_base<F: FixedPoints<pallas::Affine>>(
chip: EccChip<OrchardFixedBases>, chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
base: FixedPoint<pallas::Affine, EccChip<OrchardFixedBases>>, base: FixedPoint<pallas::Affine, EccChip<F>>,
base_val: pallas::Affine, base_val: pallas::Affine,
) -> Result<(), Error> { ) -> Result<(), Error> {
let column = chip.config().advices[0]; let column = chip.config().advices[0];
fn constrain_equal_non_id( fn constrain_equal_non_id<F: FixedPoints<pallas::Affine>>(
chip: EccChip<OrchardFixedBases>, chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine, base_val: pallas::Affine,
scalar_val: pallas::Base, scalar_val: pallas::Base,
result: Point<pallas::Affine, EccChip<OrchardFixedBases>>, result: Point<pallas::Affine, EccChip<F>>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// Move scalar from base field into scalar field (which always fits for Pallas). // Move scalar from base field into scalar field (which always fits for Pallas).
let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap(); let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap();

View File

@ -1,5 +1,5 @@
use super::super::{ 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, NUM_WINDOWS,
}; };
@ -54,7 +54,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
offset: usize, offset: usize,
scalar: Option<pallas::Scalar>, scalar: Option<pallas::Scalar>,
) -> Result<EccScalarFixed, Error> { ) -> Result<EccScalarFixed, Error> {
let windows = self.decompose_scalar_fixed::<L_ORCHARD_SCALAR>(scalar, offset, region)?; let windows = self.decompose_scalar_fixed::<L_PALLAS_SCALAR>(scalar, offset, region)?;
Ok(EccScalarFixed { Ok(EccScalarFixed {
value: scalar, value: scalar,
@ -162,7 +162,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
} }
} }
#[cfg(test)] #[cfg(feature = "testing")]
pub mod tests { pub mod tests {
use group::Curve; use group::Curve;
use halo2::{circuit::Layouter, plonk::Error}; use halo2::{circuit::Layouter, plonk::Error};
@ -172,64 +172,35 @@ pub mod tests {
chip::EccChip, chip::EccChip,
gadget::{FixedPoint, FixedPoints, NonIdentityPoint, Point, H}, gadget::{FixedPoint, FixedPoints, NonIdentityPoint, Point, H},
}; };
use orchard::constants::OrchardFixedBases;
pub fn test_mul_fixed( pub fn test_mul_fixed<F: FixedPoints<pallas::Affine>>(
chip: EccChip<OrchardFixedBases>, base: F,
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// commit_ivk_r
let commit_ivk_r = OrchardFixedBases::CommitIvkR;
test_single_base( test_single_base(
chip.clone(), chip.clone(),
layouter.namespace(|| "commit_ivk_r"), layouter.namespace(|| "fixed base"),
FixedPoint::from_inner(chip.clone(), commit_ivk_r), FixedPoint::from_inner(chip, base.clone()),
commit_ivk_r.generator(), base.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(),
)?; )?;
Ok(()) Ok(())
} }
#[allow(clippy::op_ref)] #[allow(clippy::op_ref)]
fn test_single_base( fn test_single_base<F: FixedPoints<pallas::Affine>>(
chip: EccChip<OrchardFixedBases>, chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
base: FixedPoint<pallas::Affine, EccChip<OrchardFixedBases>>, base: FixedPoint<pallas::Affine, EccChip<F>>,
base_val: pallas::Affine, base_val: pallas::Affine,
) -> Result<(), Error> { ) -> Result<(), Error> {
fn constrain_equal_non_id( fn constrain_equal_non_id<F: FixedPoints<pallas::Affine>>(
chip: EccChip<OrchardFixedBases>, chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine, base_val: pallas::Affine,
scalar_val: pallas::Scalar, scalar_val: pallas::Scalar,
result: Point<pallas::Affine, EccChip<OrchardFixedBases>>, result: Point<pallas::Affine, EccChip<F>>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let expected = NonIdentityPoint::new( let expected = NonIdentityPoint::new(
chip, chip,

View File

@ -232,12 +232,12 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
} }
} }
#[cfg(test)] #[cfg(feature = "testing")]
pub mod tests { pub mod tests {
use group::Curve; use group::Curve;
use halo2::{ use halo2::{
circuit::{Chip, Layouter}, circuit::{Chip, Layouter},
plonk::{Any, Error}, plonk::Error,
}; };
use pasta_curves::{arithmetic::FieldExt, pallas}; use pasta_curves::{arithmetic::FieldExt, pallas};
@ -245,21 +245,19 @@ pub mod tests {
chip::EccChip, chip::EccChip,
gadget::{FixedPoint, FixedPoints, NonIdentityPoint, Point}, gadget::{FixedPoint, FixedPoints, NonIdentityPoint, Point},
}; };
use orchard::constants::OrchardFixedBases; use utilities::{CellValue, UtilitiesInstructions};
use utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions};
#[allow(clippy::op_ref)] #[allow(clippy::op_ref)]
pub fn test_mul_fixed_short( pub fn test_mul_fixed_short<F: FixedPoints<pallas::Affine>>(
chip: EccChip<OrchardFixedBases>, base: F,
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// value_commit_v let base_val = base.generator();
let value_commit_v = OrchardFixedBases::ValueCommitV; let base = FixedPoint::from_inner(chip.clone(), base);
let base_val = value_commit_v.generator();
let value_commit_v = FixedPoint::from_inner(chip.clone(), value_commit_v);
fn load_magnitude_sign( fn load_magnitude_sign<F: FixedPoints<pallas::Affine>>(
chip: EccChip<OrchardFixedBases>, chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
magnitude: pallas::Base, magnitude: pallas::Base,
sign: pallas::Base, sign: pallas::Base,
@ -272,12 +270,12 @@ pub mod tests {
Ok((magnitude, sign)) Ok((magnitude, sign))
} }
fn constrain_equal_non_id( fn constrain_equal_non_id<F: FixedPoints<pallas::Affine>>(
chip: EccChip<OrchardFixedBases>, chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine, base_val: pallas::Affine,
scalar_val: pallas::Scalar, scalar_val: pallas::Scalar,
result: Point<pallas::Affine, EccChip<OrchardFixedBases>>, result: Point<pallas::Affine, EccChip<F>>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let expected = NonIdentityPoint::new( let expected = NonIdentityPoint::new(
chip, chip,
@ -332,7 +330,7 @@ pub mod tests {
*magnitude, *magnitude,
*sign, *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 // Move from base field into scalar field
let scalar = { let scalar = {
@ -380,9 +378,45 @@ pub mod tests {
use halo2::{ use halo2::{
circuit::{Layouter, SimpleFloorPlanner}, circuit::{Layouter, SimpleFloorPlanner},
dev::{MockProver, VerifyFailure}, 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<pallas::Affine> 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<u64> {
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)] #[derive(Default)]
struct MyCircuit { struct MyCircuit {
@ -432,7 +466,7 @@ pub mod tests {
meta.enable_constant(constants); meta.enable_constant(constants);
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], lookup_table); let range_check = LookupRangeCheckConfig::configure(meta, advices[9], lookup_table);
EccChip::<OrchardFixedBases>::configure(meta, advices, lagrange_coeffs, range_check) EccChip::<FixedBase>::configure(meta, advices, lagrange_coeffs, range_check)
} }
fn synthesize( fn synthesize(
@ -442,7 +476,7 @@ pub mod tests {
) -> Result<(), Error> { ) -> Result<(), Error> {
let column = config.advices[0]; let column = config.advices[0];
let short_config: super::Config<OrchardFixedBases> = (&config).into(); let short_config: super::Config<FixedBase> = (&config).into();
let magnitude_sign = { let magnitude_sign = {
let magnitude = self.load_private( let magnitude = self.load_private(
layouter.namespace(|| "load magnitude"), layouter.namespace(|| "load magnitude"),
@ -454,7 +488,7 @@ pub mod tests {
(magnitude, sign) (magnitude, sign)
}; };
short_config.assign(layouter, magnitude_sign, &OrchardFixedBases::ValueCommitV)?; short_config.assign(layouter, magnitude_sign, &FixedBase)?;
Ok(()) Ok(())
} }

View File

@ -85,11 +85,6 @@ pub fn compute_lagrange_coeffs<C: CurveAffine>(base: C, num_windows: usize) -> V
/// - $z + y = u^2$ (some square in the field); and /// - $z + y = u^2$ (some square in the field); and
/// - $z - y$ is not a square. /// - $z - y$ is not a square.
/// If successful, return a vector of `(z: u64, us: [C::Base; H])` for each window. /// 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<C: CurveAffine>( pub fn find_zs_and_us<C: CurveAffine>(
base: C, base: C,
num_windows: usize, num_windows: usize,

View File

@ -377,7 +377,8 @@ impl<C: CurveAffine, EccChip: EccInstructions<C>> X<C, EccChip> {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct FixedPoint<C: CurveAffine, EccChip: EccInstructions<C>> { pub struct FixedPoint<C: CurveAffine, EccChip: EccInstructions<C>> {
chip: EccChip, chip: EccChip,
inner: EccChip::FixedPoints, /// UNDO THIS pub.
pub inner: EccChip::FixedPoints,
} }
impl<C: CurveAffine, EccChip: EccInstructions<C>> FixedPoint<C, EccChip> { impl<C: CurveAffine, EccChip: EccInstructions<C>> FixedPoint<C, EccChip> {
@ -448,30 +449,31 @@ impl<C: CurveAffine, EccChip: EccInstructions<C>> FixedPoint<C, EccChip> {
} }
} }
#[cfg(test)] #[cfg(feature = "testing")]
mod tests { pub mod testing {
use group::{prime::PrimeCurveAffine, Curve, Group}; use crate::{
chip::{EccChip, EccConfig},
gadget::FixedPoints,
};
use utilities::lookup_range_check::LookupRangeCheckConfig;
use halo2::{ use halo2::{
circuit::{Layouter, SimpleFloorPlanner}, circuit::{Layouter, SimpleFloorPlanner},
dev::MockProver,
plonk::{Circuit, ConstraintSystem, Error}, plonk::{Circuit, ConstraintSystem, Error},
}; };
use pasta_curves::pallas; use pasta_curves::pallas;
use crate::chip::{EccChip, EccConfig}; use std::marker::PhantomData;
use orchard::constants::OrchardFixedBases;
use utilities::lookup_range_check::LookupRangeCheckConfig;
struct MyCircuit {} pub struct MyCircuit<S: EccTest<F>, F: FixedPoints<pallas::Affine>>(pub PhantomData<(S, F)>);
#[allow(non_snake_case)] #[allow(non_snake_case)]
impl Circuit<pallas::Base> for MyCircuit { impl<S: EccTest<F>, F: FixedPoints<pallas::Affine>> Circuit<pallas::Base> for MyCircuit<S, F> {
type Config = EccConfig; type Config = EccConfig;
type FloorPlanner = SimpleFloorPlanner; type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self { fn without_witnesses(&self) -> Self {
MyCircuit {} MyCircuit(PhantomData)
} }
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config { fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
@ -503,7 +505,7 @@ mod tests {
meta.enable_constant(constants); meta.enable_constant(constants);
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], lookup_table); let range_check = LookupRangeCheckConfig::configure(meta, advices[9], lookup_table);
EccChip::<OrchardFixedBases>::configure(meta, advices, lagrange_coeffs, range_check) EccChip::<F>::configure(meta, advices, lagrange_coeffs, range_check)
} }
fn synthesize( fn synthesize(
@ -517,123 +519,183 @@ mod tests {
// provided by the Sinsemilla chip. // provided by the Sinsemilla chip.
config.lookup_config.load(&mut layouter)?; config.lookup_config.load(&mut layouter)?;
// Generate a random non-identity point P S::test_witness_non_id(
let p_val = pallas::Point::random(rand::rngs::OsRng).to_affine(); // P
let p = super::NonIdentityPoint::new(
chip.clone(), chip.clone(),
layouter.namespace(|| "P"), layouter.namespace(|| "witness non-identity point"),
Some(p_val),
)?; )?;
let p_neg = -p_val; S::test_add(chip.clone(), layouter.namespace(|| "addition"))?;
let p_neg = super::NonIdentityPoint::new( S::test_add_incomplete(chip.clone(), layouter.namespace(|| "incomplete addition"))?;
S::test_mul(
chip.clone(), chip.clone(),
layouter.namespace(|| "-P"), layouter.namespace(|| "variable-base scalar multiplication"),
Some(p_neg), )?;
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 Ok(())
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),
)?;
// Make sure P and Q are not the same point. pub trait EccTest<F: FixedPoints<pallas::Affine>> {
assert_ne!(p_val, q_val); fn fixed_bases_full() -> Vec<F>;
fn fixed_bases_short() -> Vec<F>;
fn fixed_bases_base_field() -> Vec<F>;
// Test that we can witness the identity as a point, but not as a non-identity point. fn test_witness_non_id(
{ chip: EccChip<F>,
let _ = super::Point::new( layouter: impl Layouter<pallas::Base>,
chip.clone(), ) -> Result<(), Error> {
layouter.namespace(|| "identity"), crate::chip::witness_point::tests::test_witness_non_id(chip, layouter)
Some(pallas::Affine::identity()), }
)?;
super::NonIdentityPoint::new( fn test_add(chip: EccChip<F>, layouter: impl Layouter<pallas::Base>) -> Result<(), Error> {
chip.clone(), crate::chip::add::tests::test_add(chip, layouter)
layouter.namespace(|| "identity"), }
Some(pallas::Affine::identity()),
)
.expect_err("Trying to witness the identity should return an error");
}
// Test witness non-identity point fn test_add_incomplete(
{ chip: EccChip<F>,
super::chip::witness_point::tests::test_witness_non_id( layouter: impl Layouter<pallas::Base>,
chip.clone(), ) -> Result<(), Error> {
layouter.namespace(|| "witness non-identity point"), crate::chip::add_incomplete::tests::test_add_incomplete(chip, layouter)
) }
}
// Test complete addition fn test_mul(chip: EccChip<F>, layouter: impl Layouter<pallas::Base>) -> Result<(), Error> {
{ crate::chip::mul::tests::test_mul(chip, layouter)
crate::chip::add::tests::test_add( }
chip.clone(),
layouter.namespace(|| "complete addition"),
p_val,
&p,
q_val,
&q,
&p_neg,
)?;
}
// Test incomplete addition fn test_mul_fixed(
{ chip: EccChip<F>,
crate::chip::add_incomplete::tests::test_add_incomplete( mut layouter: impl Layouter<pallas::Base>,
chip.clone(), ) -> Result<(), Error> {
layouter.namespace(|| "incomplete addition"), for base in Self::fixed_bases_full().into_iter() {
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
{
crate::chip::mul_fixed::full_width::tests::test_mul_fixed( crate::chip::mul_fixed::full_width::tests::test_mul_fixed(
base,
chip.clone(), chip.clone(),
layouter.namespace(|| "full-width fixed-base scalar mul"), layouter.namespace(|| "full-width fixed-base scalar mul"),
)?; )?;
} }
// Test signed short fixed-base scalar multiplication Ok(())
{ }
fn test_mul_fixed_short(
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>,
) -> Result<(), Error> {
for base in Self::fixed_bases_short().into_iter() {
crate::chip::mul_fixed::short::tests::test_mul_fixed_short( crate::chip::mul_fixed::short::tests::test_mul_fixed_short(
base,
chip.clone(), 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<F>,
mut layouter: impl Layouter<pallas::Base>,
) -> 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( crate::chip::mul_fixed::base_field_elem::tests::test_mul_fixed_base_field(
chip, base,
layouter.namespace(|| "fixed-base scalar mul with base field element"), chip.clone(),
layouter.namespace(|| "full-width fixed-base scalar mul"),
)?; )?;
} }
Ok(()) 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<pallas::Affine> 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<u64> {
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<FixedBase> for Test {
fn fixed_bases_full() -> Vec<FixedBase> {
vec![FixedBase::FullWidth]
}
fn fixed_bases_short() -> Vec<FixedBase> {
vec![FixedBase::Short]
}
fn fixed_bases_base_field() -> Vec<FixedBase> {
vec![FixedBase::FullWidth]
}
}
#[test] #[test]
fn ecc_chip() { fn ecc_chip() {
use halo2::dev::MockProver;
let k = 13; let k = 13;
let circuit = MyCircuit {}; let circuit = super::testing::MyCircuit::<Test, FixedBase>(std::marker::PhantomData);
let prover = MockProver::run(k, &circuit, vec![]).unwrap(); let prover = MockProver::run(k, &circuit, vec![]).unwrap();
assert_eq!(prover.verify(), Ok(())) assert_eq!(prover.verify(), Ok(()))
} }

View File

@ -110,3 +110,33 @@ impl FixedPoints<pallas::Affine> 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<OrchardFixedBases> for OrchardTest {
fn fixed_bases_full() -> Vec<OrchardFixedBases> {
vec![
OrchardFixedBases::CommitIvkR,
OrchardFixedBases::NoteCommitR,
OrchardFixedBases::SpendAuthG,
OrchardFixedBases::ValueCommitR,
]
}
fn fixed_bases_short() -> Vec<OrchardFixedBases> {
vec![OrchardFixedBases::ValueCommitV]
}
fn fixed_bases_base_field() -> Vec<OrchardFixedBases> {
vec![OrchardFixedBases::NullifierK]
}
}
let k = 13;
let circuit = testing::MyCircuit::<OrchardTest, OrchardFixedBases>(std::marker::PhantomData);
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
assert_eq!(prover.verify(), Ok(()))
}