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"]
test-dependencies = ["proptest"]
test-sinsemilla = ["sinsemilla/testing"]
test-ecc = ["ecc/testing"]
[[bench]]
name = "note_decryption"

View File

@ -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 = []

View File

@ -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}$.
/// <https://github.com/zcash/pasta>

View File

@ -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<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> {
// 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.

View File

@ -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<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> {
// 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(())

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 {
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<OrchardFixedBases>,
pub fn test_mul<F: FixedPoints<pallas::Affine>>(
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>,
p: &NonIdentityPoint<pallas::Affine, EccChip<OrchardFixedBases>>,
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<

View File

@ -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);

View File

@ -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<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
offset,
scalar,
true,
L_ORCHARD_BASE,
L_PALLAS_BASE,
NUM_WINDOWS,
)?;
EccBaseFieldElemFixed {
@ -375,9 +375,9 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
}
}
#[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<OrchardFixedBases>,
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<F: FixedPoints<pallas::Affine>>(
base: F,
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>,
) -> 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<OrchardFixedBases>,
fn test_single_base<F: FixedPoints<pallas::Affine>>(
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>,
base: FixedPoint<pallas::Affine, EccChip<OrchardFixedBases>>,
base: FixedPoint<pallas::Affine, EccChip<F>>,
base_val: pallas::Affine,
) -> Result<(), Error> {
let column = chip.config().advices[0];
fn constrain_equal_non_id(
chip: EccChip<OrchardFixedBases>,
fn constrain_equal_non_id<F: FixedPoints<pallas::Affine>>(
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine,
scalar_val: pallas::Base,
result: Point<pallas::Affine, EccChip<OrchardFixedBases>>,
result: Point<pallas::Affine, EccChip<F>>,
) -> 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();

View File

@ -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<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
offset: usize,
scalar: Option<pallas::Scalar>,
) -> 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 {
value: scalar,
@ -162,7 +162,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
}
}
#[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<OrchardFixedBases>,
pub fn test_mul_fixed<F: FixedPoints<pallas::Affine>>(
base: F,
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>,
) -> 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<OrchardFixedBases>,
fn test_single_base<F: FixedPoints<pallas::Affine>>(
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>,
base: FixedPoint<pallas::Affine, EccChip<OrchardFixedBases>>,
base: FixedPoint<pallas::Affine, EccChip<F>>,
base_val: pallas::Affine,
) -> Result<(), Error> {
fn constrain_equal_non_id(
chip: EccChip<OrchardFixedBases>,
fn constrain_equal_non_id<F: FixedPoints<pallas::Affine>>(
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine,
scalar_val: pallas::Scalar,
result: Point<pallas::Affine, EccChip<OrchardFixedBases>>,
result: Point<pallas::Affine, EccChip<F>>,
) -> Result<(), Error> {
let expected = NonIdentityPoint::new(
chip,

View File

@ -232,12 +232,12 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
}
}
#[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<OrchardFixedBases>,
pub fn test_mul_fixed_short<F: FixedPoints<pallas::Affine>>(
base: F,
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>,
) -> 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<OrchardFixedBases>,
fn load_magnitude_sign<F: FixedPoints<pallas::Affine>>(
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>,
magnitude: pallas::Base,
sign: pallas::Base,
@ -272,12 +270,12 @@ pub mod tests {
Ok((magnitude, sign))
}
fn constrain_equal_non_id(
chip: EccChip<OrchardFixedBases>,
fn constrain_equal_non_id<F: FixedPoints<pallas::Affine>>(
chip: EccChip<F>,
mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine,
scalar_val: pallas::Scalar,
result: Point<pallas::Affine, EccChip<OrchardFixedBases>>,
result: Point<pallas::Affine, EccChip<F>>,
) -> 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<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)]
struct MyCircuit {
@ -432,7 +466,7 @@ pub mod tests {
meta.enable_constant(constants);
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(
@ -442,7 +476,7 @@ pub mod tests {
) -> Result<(), Error> {
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 = 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(())
}

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$ 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<C: CurveAffine>(
base: C,
num_windows: usize,

View File

@ -377,7 +377,8 @@ impl<C: CurveAffine, EccChip: EccInstructions<C>> X<C, EccChip> {
#[derive(Clone, Debug)]
pub struct FixedPoint<C: CurveAffine, EccChip: EccInstructions<C>> {
chip: EccChip,
inner: EccChip::FixedPoints,
/// UNDO THIS pub.
pub inner: EccChip::FixedPoints,
}
impl<C: CurveAffine, EccChip: EccInstructions<C>> FixedPoint<C, EccChip> {
@ -448,30 +449,31 @@ impl<C: CurveAffine, EccChip: EccInstructions<C>> FixedPoint<C, EccChip> {
}
}
#[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<S: EccTest<F>, F: FixedPoints<pallas::Affine>>(pub PhantomData<(S, F)>);
#[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 FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
MyCircuit {}
MyCircuit(PhantomData)
}
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
@ -503,7 +505,7 @@ mod tests {
meta.enable_constant(constants);
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(
@ -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<F: FixedPoints<pallas::Affine>> {
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.
{
let _ = super::Point::new(
chip.clone(),
layouter.namespace(|| "identity"),
Some(pallas::Affine::identity()),
)?;
fn test_witness_non_id(
chip: EccChip<F>,
layouter: impl Layouter<pallas::Base>,
) -> 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<F>, layouter: impl Layouter<pallas::Base>) -> 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<F>,
layouter: impl Layouter<pallas::Base>,
) -> 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<F>, layouter: impl Layouter<pallas::Base>) -> 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<F>,
mut layouter: impl Layouter<pallas::Base>,
) -> 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<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(
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<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(
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<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]
fn ecc_chip() {
use halo2::dev::MockProver;
let k = 13;
let circuit = MyCircuit {};
let circuit = super::testing::MyCircuit::<Test, FixedBase>(std::marker::PhantomData);
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
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(()))
}