From 016ede017322f57aa5f3c86727f85914bf94fe18 Mon Sep 17 00:00:00 2001 From: Constance Beguier Date: Thu, 30 Apr 2026 09:53:50 +0200 Subject: [PATCH 1/3] Add NonIdentityPoint::new_from_constant --- halo2_gadgets/src/ecc.rs | 18 +++++++++++ halo2_gadgets/src/ecc/chip.rs | 12 ++++++++ halo2_gadgets/src/ecc/chip/witness_point.rs | 34 ++++++++++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/halo2_gadgets/src/ecc.rs b/halo2_gadgets/src/ecc.rs index 326b6e4f5e..ec3054a4de 100644 --- a/halo2_gadgets/src/ecc.rs +++ b/halo2_gadgets/src/ecc.rs @@ -77,6 +77,14 @@ pub trait EccInstructions: value: Value, ) -> Result; + /// Witnesses the given constant point as a private input to the circuit. + /// This returns an error if the point is the identity. + fn witness_point_non_id_from_constant( + &self, + layouter: &mut impl Layouter, + value: C, + ) -> Result; + /// Witnesses a full-width scalar to be used in variable-base multiplication. fn witness_scalar_var( &self, @@ -290,6 +298,16 @@ impl> NonIdentityPoint { point.map(|inner| NonIdentityPoint { chip, inner }) } + /// Constructs a new point with the given value. + pub fn new_from_constant( + chip: EccChip, + mut layouter: impl Layouter, + value: C, + ) -> Result { + 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> + Clone>( &self, diff --git a/halo2_gadgets/src/ecc/chip.rs b/halo2_gadgets/src/ecc/chip.rs index 469c19ebec..12ba4a7d72 100644 --- a/halo2_gadgets/src/ecc/chip.rs +++ b/halo2_gadgets/src/ecc/chip.rs @@ -492,6 +492,18 @@ where ) } + fn witness_point_non_id_from_constant( + &self, + layouter: &mut impl Layouter, + value: pallas::Affine, + ) -> Result { + 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, diff --git a/halo2_gadgets/src/ecc/chip/witness_point.rs b/halo2_gadgets/src/ecc/chip/witness_point.rs index 98f865a6dc..4c3e27a767 100644 --- a/halo2_gadgets/src/ecc/chip/witness_point.rs +++ b/halo2_gadgets/src/ecc/chip/witness_point.rs @@ -184,6 +184,30 @@ impl Config { self.assign_xy(value, offset, region) .map(|(x, y)| NonIdentityEccPoint::from_coordinates_unchecked(x, y)) } + + /// Assigns a constant non-identity point. + pub(super) fn constant_point_non_id( + &self, + value: pallas::Affine, + offset: usize, + region: &mut Region<'_, pallas::Base>, + ) -> Result { + // 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)] @@ -202,10 +226,18 @@ pub mod tests { ) { // Witnessing the identity should return an error. NonIdentityPoint::new( - chip, + chip.clone(), layouter.namespace(|| "witness identity"), Value::known(pallas::Affine::identity()), ) .expect_err("witnessing 𝒪 should return an error"); + + // Witnessing the identity from a constant should return an error. + NonIdentityPoint::new_from_constant( + chip, + layouter.namespace(|| "witness identity"), + pallas::Affine::identity(), + ) + .expect_err("witnessing 𝒪 should return an error"); } } From 46687fe39adfb7c7cc316b7e179726a7d692bdd9 Mon Sep 17 00:00:00 2001 From: Constance Beguier Date: Mon, 4 May 2026 12:49:18 +0200 Subject: [PATCH 2/3] Update comments --- halo2_gadgets/src/ecc.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/halo2_gadgets/src/ecc.rs b/halo2_gadgets/src/ecc.rs index ec3054a4de..1070aad187 100644 --- a/halo2_gadgets/src/ecc.rs +++ b/halo2_gadgets/src/ecc.rs @@ -77,8 +77,8 @@ pub trait EccInstructions: value: Value, ) -> Result; - /// Witnesses the given constant point as a private input to the circuit. - /// This returns an error if the point is the identity. + /// 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, @@ -288,7 +288,8 @@ pub struct NonIdentityPoint> { } impl> NonIdentityPoint { - /// 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, @@ -298,7 +299,7 @@ impl> NonIdentityPoint { point.map(|inner| NonIdentityPoint { chip, inner }) } - /// Constructs a new point with the given value. + /// Witnesses the given constant point with both coordinates pinned via fixed columns. pub fn new_from_constant( chip: EccChip, mut layouter: impl Layouter, From 943aa5945b416db1a39b2426c002cd2bae096fe1 Mon Sep 17 00:00:00 2001 From: Constance Beguier Date: Mon, 4 May 2026 12:50:39 +0200 Subject: [PATCH 3/3] Update comment --- halo2_gadgets/src/ecc/chip/witness_point.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/halo2_gadgets/src/ecc/chip/witness_point.rs b/halo2_gadgets/src/ecc/chip/witness_point.rs index 4c3e27a767..d7d279d2e4 100644 --- a/halo2_gadgets/src/ecc/chip/witness_point.rs +++ b/halo2_gadgets/src/ecc/chip/witness_point.rs @@ -185,7 +185,7 @@ impl Config { .map(|(x, y)| NonIdentityEccPoint::from_coordinates_unchecked(x, y)) } - /// Assigns a constant non-identity point. + /// Assigns a constant non-identity point with both coordinates pinned via fixed columns. pub(super) fn constant_point_non_id( &self, value: pallas::Affine,