gadget::ecc.rs: Add EccInstructions::constrain_equal() instruction.

This allows us to constrain two points to be equal in value at the
gadget level.

Co-authored-by: Jack Grigg <jack@electriccoin.co>
This commit is contained in:
therealyingtong 2021-06-13 21:26:30 +08:00
parent a11c2066ef
commit 7341996d2c
3 changed files with 70 additions and 6 deletions

View File

@ -43,6 +43,14 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> {
/// Enumeration of the set of fixed bases to be used in short signed scalar mul.
type FixedPointsShort: Clone + Debug;
/// Constrains point `a` to be equal in value to point `b`.
fn constrain_equal(
&self,
layouter: &mut impl Layouter<C::Base>,
a: &Self::Point,
b: &Self::Point,
) -> Result<(), Error>;
/// Witnesses the given base field element as a private input to the circuit
/// for variable-base scalar mul.
fn witness_scalar_var(
@ -237,6 +245,16 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
point.map(|inner| Point { chip, inner })
}
/// Constrains this point to be equal in value to another point.
pub fn constrain_equal(
&self,
mut layouter: impl Layouter<C::Base>,
other: &Self,
) -> Result<(), Error> {
self.chip
.constrain_equal(&mut layouter, &self.inner, &other.inner)
}
/// Extracts the x-coordinate of a point.
pub fn extract_p(&self) -> X<C, EccChip> {
X::from_inner(self.chip.clone(), EccChip::extract_p(&self.inner).clone())

View File

@ -178,6 +178,24 @@ impl EccInstructions<pallas::Affine> for EccChip {
type FixedPoints = (); // TODO
type FixedPointsShort = (); // TODO
fn constrain_equal(
&self,
layouter: &mut impl Layouter<pallas::Base>,
a: &Self::Point,
b: &Self::Point,
) -> Result<(), Error> {
let config = self.config().clone();
layouter.assign_region(
|| "constrain equal",
|mut region| {
// Constrain x-coordinates
region.constrain_equal(&config.perm, a.x().cell(), b.x().cell())?;
// Constrain x-coordinates
region.constrain_equal(&config.perm, a.y().cell(), b.y().cell())
},
)
}
fn witness_scalar_var(
&self,
_layouter: &mut impl Layouter<pallas::Base>,

View File

@ -407,22 +407,50 @@ pub mod tests {
assert_ne!(p_val, q_val);
// Check complete addition P + (-P)
p.add(layouter.namespace(|| "P + (-P)"), p_neg)?;
{
let result = p.add(layouter.namespace(|| "P + (-P)"), p_neg)?;
result.constrain_equal(layouter.namespace(|| "P + (-P) = 0"), zero)?;
}
// Check complete addition 𝒪 + 𝒪
zero.add(layouter.namespace(|| "𝒪 + 𝒪"), zero)?;
{
let result = zero.add(layouter.namespace(|| "𝒪 + 𝒪"), zero)?;
result.constrain_equal(layouter.namespace(|| "P + (-P) = 0"), zero)?;
}
// Check P + Q
p.add(layouter.namespace(|| "P + Q"), q)?;
{
let result = p.add(layouter.namespace(|| "P + Q"), q)?;
let witnessed_result = Point::new(
chip.clone(),
layouter.namespace(|| "witnessed P + Q"),
Some((p_val + q_val).to_affine()),
)?;
result.constrain_equal(layouter.namespace(|| "constrain P + Q"), &witnessed_result)?;
}
// P + P
p.add(layouter.namespace(|| "P + P"), p)?;
{
let result = p.add(layouter.namespace(|| "P + P"), p)?;
let witnessed_result = Point::new(
chip.clone(),
layouter.namespace(|| "witnessed P + P"),
Some((p_val + p_val).to_affine()),
)?;
result.constrain_equal(layouter.namespace(|| "constrain P + P"), &witnessed_result)?;
}
// P + 𝒪
p.add(layouter.namespace(|| "P + 𝒪"), zero)?;
{
let result = p.add(layouter.namespace(|| "P + 𝒪"), zero)?;
result.constrain_equal(layouter.namespace(|| "P + 𝒪 = P"), p)?;
}
// 𝒪 + P
zero.add(layouter.namespace(|| "𝒪 + 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.
let endo_p = p_val.to_curve().endo();