mirror of https://github.com/zcash/orchard.git
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:
parent
a997364545
commit
1acf0c2c15
|
@ -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"
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(()))
|
||||
}
|
||||
|
|
|
@ -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(()))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue