Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 58 additions & 9 deletions halo2_gadgets/src/ecc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ pub trait EccInstructions<C: CurveAffine>:
value: Value<C>,
) -> Result<Self::NonIdentityPoint, Error>;

/// Witnesses the given constant point with both coordinates pinned via fixed columns.
/// Returns an error if the point is the identity.
fn witness_point_non_id_from_constant(
&self,
layouter: &mut impl Layouter<C::Base>,
value: C,
) -> Result<Self::NonIdentityPoint, Error>;

/// Witnesses a full-width scalar to be used in variable-base multiplication.
fn witness_scalar_var(
&self,
Expand Down Expand Up @@ -283,7 +291,8 @@ pub struct NonIdentityPoint<C: CurveAffine, EccChip: EccInstructions<C>> {
}

impl<C: CurveAffine, EccChip: EccInstructions<C>> NonIdentityPoint<C, EccChip> {
/// Constructs a new point with the given value.
/// Witnesses the given point with only on-curve / non-identity constraints.
/// For known-constant points use [`NonIdentityPoint::new_from_constant`].
pub fn new(
chip: EccChip,
mut layouter: impl Layouter<C::Base>,
Expand All @@ -293,6 +302,16 @@ impl<C: CurveAffine, EccChip: EccInstructions<C>> NonIdentityPoint<C, EccChip> {
point.map(|inner| NonIdentityPoint { chip, inner })
}

/// Witnesses the given constant point with both coordinates pinned via fixed columns.
pub fn new_from_constant(
chip: EccChip,
mut layouter: impl Layouter<C::Base>,
value: C,
) -> Result<Self, Error> {
let point = chip.witness_point_non_id_from_constant(&mut layouter, value);
point.map(|inner| NonIdentityPoint { chip, inner })
}

/// Constrains this point to be equal in value to another point.
pub fn constrain_equal<Other: Into<Point<C, EccChip>> + Clone>(
&self,
Expand Down Expand Up @@ -778,18 +797,28 @@ pub(crate) mod tests {

struct MyEccCircuit<Lookup: PallasLookupRangeCheck> {
test_errors: bool,
test_zsa_additions: bool,
circuit_version: CircuitVersion,
_lookup_marker: PhantomData<Lookup>,
}

impl<Lookup: PallasLookupRangeCheck> MyEccCircuit<Lookup> {
fn new(test_errors: bool) -> Self {
Self::with_version(test_errors, CircuitVersion::AnchoredBase)
fn new(test_errors: bool, test_zsa_additions: bool) -> Self {
Self::with_version(
test_errors,
test_zsa_additions,
CircuitVersion::AnchoredBase,
)
}

fn with_version(test_errors: bool, circuit_version: CircuitVersion) -> Self {
fn with_version(
test_errors: bool,
test_zsa_additions: bool,
circuit_version: CircuitVersion,
) -> Self {
Self {
test_errors,
test_zsa_additions,
circuit_version,
_lookup_marker: PhantomData,
}
Expand All @@ -802,7 +831,7 @@ pub(crate) mod tests {
type FloorPlanner = SimpleFloorPlanner;

fn without_witnesses(&self) -> Self {
MyEccCircuit::with_version(false, self.circuit_version)
MyEccCircuit::with_version(false, false, self.circuit_version)
}

fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
Expand Down Expand Up @@ -902,6 +931,14 @@ pub(crate) mod tests {
)
}

// Test constant witness
if self.test_zsa_additions {
super::chip::witness_point::tests::test_witness_constant(
chip.clone(),
layouter.namespace(|| "witness constant"),
)
}

// Test complete addition
{
super::chip::add::tests::test_add(
Expand Down Expand Up @@ -978,14 +1015,15 @@ pub(crate) mod tests {
#[test]
fn ecc_chip() {
let k = 13;
let circuit = MyEccCircuit::<PallasLookupRangeCheckConfig>::new(true);
let circuit = MyEccCircuit::<PallasLookupRangeCheckConfig>::new(true, false);
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
assert_eq!(prover.verify(), Ok(()))
}

#[test]
fn test_ecc_chip_fixed_against_stored_circuit() {
let circuit = MyEccCircuit::<PallasLookupRangeCheckConfig>::with_version(
false,
false,
CircuitVersion::AnchoredBase,
);
Expand All @@ -998,12 +1036,21 @@ pub(crate) mod tests {
#[test]
fn test_ecc_chip_insecure_against_stored_circuit() {
let circuit = MyEccCircuit::<PallasLookupRangeCheckConfig>::with_version(
false,
false,
CircuitVersion::InsecureUnanchoredBase,
);
test_against_stored_circuit(circuit, "ecc_chip_insecure", 3872);
}

#[test]
fn ecc_chip_with_zsa_additions() {
let k = 13;
let circuit = MyEccCircuit::<PallasLookupRangeCheckConfig>::new(true, true);
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
assert_eq!(prover.verify(), Ok(()))
}

#[cfg(feature = "test-dev-graph")]
#[test]
fn print_ecc_chip() {
Expand All @@ -1013,7 +1060,7 @@ pub(crate) mod tests {
root.fill(&WHITE).unwrap();
let root = root.titled("Ecc Chip Layout", ("sans-serif", 60)).unwrap();

let circuit = MyEccCircuit::<PallasLookupRangeCheckConfig>::new(false);
let circuit = MyEccCircuit::<PallasLookupRangeCheckConfig>::new(false, false);
halo2_proofs::dev::CircuitLayout::default()
.render(13, &circuit, &root)
.unwrap();
Expand All @@ -1022,7 +1069,7 @@ pub(crate) mod tests {
#[test]
fn ecc_chip_4_5b() {
let k = 13;
let circuit = MyEccCircuit::<PallasLookupRangeCheck4_5BConfig>::new(true);
let circuit = MyEccCircuit::<PallasLookupRangeCheck4_5BConfig>::new(true, false);
let prover = MockProver::run(k, &circuit, vec![]).unwrap();

assert_eq!(prover.verify(), Ok(()))
Expand All @@ -1031,6 +1078,7 @@ pub(crate) mod tests {
#[test]
fn test_against_stored_ecc_chip_4_5b_fixed() {
let circuit = MyEccCircuit::<PallasLookupRangeCheck4_5BConfig>::with_version(
false,
false,
CircuitVersion::AnchoredBase,
);
Expand All @@ -1040,6 +1088,7 @@ pub(crate) mod tests {
#[test]
fn test_against_stored_ecc_chip_4_5b_insecure() {
let circuit = MyEccCircuit::<PallasLookupRangeCheck4_5BConfig>::with_version(
false,
false,
CircuitVersion::InsecureUnanchoredBase,
);
Expand All @@ -1055,7 +1104,7 @@ pub(crate) mod tests {
root.fill(&WHITE).unwrap();
let root = root.titled("Ecc Chip Layout", ("sans-serif", 60)).unwrap();

let circuit = MyEccCircuit::<PallasLookupRangeCheck4_5BConfig>::new(false);
let circuit = MyEccCircuit::<PallasLookupRangeCheck4_5BConfig>::new(false, false);
halo2_proofs::dev::CircuitLayout::default()
.render(13, &circuit, &root)
.unwrap();
Expand Down
12 changes: 12 additions & 0 deletions halo2_gadgets/src/ecc/chip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,18 @@ where
)
}

fn witness_point_non_id_from_constant(
&self,
layouter: &mut impl Layouter<pallas::Base>,
value: pallas::Affine,
) -> Result<Self::NonIdentityPoint, Error> {
let config = self.config().witness_point;
layouter.assign_region(
|| "witness constant non-identity point",
|mut region| config.constant_point_non_id(value, 0, &mut region),
)
}

fn witness_scalar_var(
&self,
_layouter: &mut impl Layouter<pallas::Base>,
Expand Down
66 changes: 65 additions & 1 deletion halo2_gadgets/src/ecc/chip/witness_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,15 +184,40 @@ impl Config {
self.assign_xy(value, offset, region)
.map(|(x, y)| NonIdentityEccPoint::from_coordinates_unchecked(x, y))
}

/// Assigns a constant non-identity point with both coordinates pinned via fixed columns.
pub(super) fn constant_point_non_id(
&self,
value: pallas::Affine,
offset: usize,
region: &mut Region<'_, pallas::Base>,
) -> Result<NonIdentityEccPoint, Error> {
// Enable `q_point_non_id` selector
self.q_point_non_id.enable(region, offset)?;

// Return an error if the point is the identity.
if value == pallas::Affine::identity() {
return Err(Error::Synthesis);
}

let value = {
let value = value.coordinates().unwrap();
(value.x().into(), value.y().into())
};

self.assign_xy_from_constant(value, offset, region)
.map(|(x, y)| NonIdentityEccPoint::from_coordinates_unchecked(x, y))
}
}

#[cfg(test)]
pub mod tests {
use group::{Curve, Group};
use halo2_proofs::circuit::Layouter;
use pasta_curves::pallas;

use super::*;
use crate::ecc::{EccInstructions, NonIdentityPoint};
use crate::ecc::{EccInstructions, NonIdentityPoint, Point};

pub fn test_witness_non_id<
EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug,
Expand All @@ -208,4 +233,43 @@ pub mod tests {
)
.expect_err("witnessing 𝒪 should return an error");
}

pub fn test_witness_constant<
EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug,
>(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
) {
// `NonIdentityPoint::new_from_constant` must reject the identity point.
NonIdentityPoint::new_from_constant(
chip.clone(),
layouter.namespace(|| "witness constant identity"),
pallas::Affine::identity(),
)
.expect_err("witnessing 𝒪 should return an error");

// `NonIdentityPoint::new_from_constant` must accept a non-identity point.
let _ = NonIdentityPoint::new_from_constant(
chip.clone(),
layouter.namespace(|| "witness a constant non-identity point"),
pallas::Point::generator().to_affine(),
)
.unwrap();

// `Point::new_from_constant` must accept the identity point.
let _ = Point::new_from_constant(
chip.clone(),
layouter.namespace(|| "witness constant identity"),
pallas::Affine::identity(),
)
.unwrap();

// `Point::new_from_constant` must accept a non-identity point.
let _ = Point::new_from_constant(
chip,
layouter.namespace(|| "witness a constant non-identity point"),
pallas::Point::generator().to_affine(),
)
.unwrap();
}
}
Loading