Merge pull request #209 from zcash/circuit-bugfixes

Circuit bugfixes
This commit is contained in:
str4d 2021-09-29 10:06:25 +13:00 committed by GitHub
commit 2c8241f25b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 4129 additions and 2984 deletions

View File

@ -38,7 +38,7 @@ use crate::{
use gadget::{
ecc::{
chip::{EccChip, EccConfig},
FixedPoint, FixedPointBaseField, FixedPointShort, Point,
FixedPoint, FixedPointBaseField, FixedPointShort, NonIdentityPoint, Point,
},
poseidon::{
Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig,
@ -356,7 +356,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
)?;
// Witness g_d_old
let g_d_old = Point::new(
let g_d_old = NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "gd_old"),
self.g_d_old.as_ref().map(|gd| gd.to_affine()),
@ -364,7 +364,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
// Witness ak.
let ak: Option<pallas::Point> = self.ak.as_ref().map(|ak| ak.into());
let ak = Point::new(
let ak = NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "ak"),
ak.map(|ak| ak.to_affine()),
@ -621,7 +621,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
g_d_old.mul(layouter.namespace(|| "[ivk] g_d_old"), ivk.inner())?;
// Constrain derived pk_d_old to equal witnessed pk_d_old
let pk_d_old = Point::new(
let pk_d_old = NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "witness pk_d_old"),
self.pk_d_old.map(|pk_d_old| pk_d_old.inner().to_affine()),
@ -666,7 +666,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let g_d_new = self
.g_d_new_star
.map(|bytes| pallas::Affine::from_bytes(&bytes).unwrap());
Point::new(
NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "witness g_d_new_star"),
g_d_new,
@ -678,7 +678,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let pk_d_new = self
.pk_d_new_star
.map(|bytes| pallas::Affine::from_bytes(&bytes).unwrap());
Point::new(
NonIdentityPoint::new(
ecc_chip,
layouter.namespace(|| "witness pk_d_new"),
pk_d_new,

View File

@ -35,7 +35,9 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
/// A `ScalarFixedShort` must be in the range [-(2^64 - 1), 2^64 - 1].
type ScalarFixedShort: Clone + Debug;
/// Variable representing an elliptic curve point.
type Point: Clone + Debug;
type Point: From<Self::NonIdentityPoint> + Clone + Debug;
/// Variable representing a non-identity elliptic curve point.
type NonIdentityPoint: Clone + Debug;
/// Variable representing the affine short Weierstrass x-coordinate of an
/// elliptic curve point.
type X: Clone + Debug;
@ -55,15 +57,24 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
) -> Result<(), Error>;
/// Witnesses the given point as a private input to the circuit.
/// This maps the identity to (0, 0) in affine coordinates.
/// This allows the point to be the identity, mapped to (0, 0) in
/// affine coordinates.
fn witness_point(
&self,
layouter: &mut impl Layouter<C::Base>,
value: Option<C>,
) -> Result<Self::Point, Error>;
/// Witnesses the given point as a private input to the circuit.
/// This returns an error if the point is the identity.
fn witness_point_non_id(
&self,
layouter: &mut impl Layouter<C::Base>,
value: Option<C>,
) -> Result<Self::NonIdentityPoint, Error>;
/// Extracts the x-coordinate of a point.
fn extract_p(point: &Self::Point) -> &Self::X;
fn extract_p<Point: Into<Self::Point> + Clone>(point: &Point) -> Self::X;
/// Performs incomplete point addition, returning `a + b`.
///
@ -71,25 +82,24 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
fn add_incomplete(
&self,
layouter: &mut impl Layouter<C::Base>,
a: &Self::Point,
b: &Self::Point,
) -> Result<Self::Point, Error>;
a: &Self::NonIdentityPoint,
b: &Self::NonIdentityPoint,
) -> Result<Self::NonIdentityPoint, Error>;
/// Performs complete point addition, returning `a + b`.
fn add(
fn add<A: Into<Self::Point> + Clone, B: Into<Self::Point> + Clone>(
&self,
layouter: &mut impl Layouter<C::Base>,
a: &Self::Point,
b: &Self::Point,
a: &A,
b: &B,
) -> Result<Self::Point, Error>;
/// Performs variable-base scalar multiplication, returning `[scalar] base`.
/// Multiplication of the identity `[scalar] 𝒪 ` returns an error.
fn mul(
&self,
layouter: &mut impl Layouter<C::Base>,
scalar: &Self::Var,
base: &Self::Point,
base: &Self::NonIdentityPoint,
) -> Result<(Self::Point, Self::ScalarVar), Error>;
/// Performs fixed-base scalar multiplication using a full-width scalar, returning `[scalar] base`.
@ -157,6 +167,125 @@ where
inner: EccChip::ScalarFixedShort,
}
/// A non-identity elliptic curve point over the given curve.
#[derive(Copy, Clone, Debug)]
pub struct NonIdentityPoint<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> {
chip: EccChip,
inner: EccChip::NonIdentityPoint,
}
impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq>
NonIdentityPoint<C, EccChip>
{
/// Constructs a new point with the given value.
pub fn new(
chip: EccChip,
mut layouter: impl Layouter<C::Base>,
value: Option<C>,
) -> Result<Self, Error> {
let point = chip.witness_point_non_id(&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,
mut layouter: impl Layouter<C::Base>,
other: &Other,
) -> Result<(), Error> {
let other: Point<C, EccChip> = (other.clone()).into();
self.chip.constrain_equal(
&mut layouter,
&Point::<C, EccChip>::from(self.clone()).inner,
&other.inner,
)
}
/// Returns the inner point.
pub fn inner(&self) -> &EccChip::NonIdentityPoint {
&self.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))
}
/// Wraps the given point (obtained directly from an instruction) in a gadget.
pub fn from_inner(chip: EccChip, inner: EccChip::NonIdentityPoint) -> Self {
NonIdentityPoint { chip, inner }
}
/// Returns `self + other` using complete addition.
pub fn add<Other: Into<Point<C, EccChip>> + Clone>(
&self,
mut layouter: impl Layouter<C::Base>,
other: &Other,
) -> Result<Point<C, EccChip>, Error> {
let other: Point<C, EccChip> = (other.clone()).into();
assert_eq!(self.chip, other.chip);
self.chip
.add(&mut layouter, &self.inner, &other.inner)
.map(|inner| Point {
chip: self.chip.clone(),
inner,
})
}
/// Returns `self + other` using incomplete addition.
/// The arguments are type-constrained not to be the identity point,
/// and since exceptional cases return an Error, the result also cannot
/// be the identity point.
pub fn add_incomplete(
&self,
mut layouter: impl Layouter<C::Base>,
other: &Self,
) -> Result<Self, Error> {
assert_eq!(self.chip, other.chip);
self.chip
.add_incomplete(&mut layouter, &self.inner, &other.inner)
.map(|inner| NonIdentityPoint {
chip: self.chip.clone(),
inner,
})
}
/// Returns `[by] self`.
#[allow(clippy::type_complexity)]
pub fn mul(
&self,
mut layouter: impl Layouter<C::Base>,
by: &EccChip::Var,
) -> Result<(Point<C, EccChip>, ScalarVar<C, EccChip>), Error> {
self.chip
.mul(&mut layouter, by, &self.inner.clone())
.map(|(point, scalar)| {
(
Point {
chip: self.chip.clone(),
inner: point,
},
ScalarVar {
chip: self.chip.clone(),
inner: scalar,
},
)
})
}
}
impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq>
From<NonIdentityPoint<C, EccChip>> for Point<C, EccChip>
{
fn from(non_id_point: NonIdentityPoint<C, EccChip>) -> Self {
Self {
chip: non_id_point.chip,
inner: non_id_point.inner.into(),
}
}
}
/// An elliptic curve point over the given curve.
#[derive(Copy, Clone, Debug)]
pub struct Point<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> {
@ -176,11 +305,12 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
}
/// Constrains this point to be equal in value to another point.
pub fn constrain_equal(
pub fn constrain_equal<Other: Into<Point<C, EccChip>> + Clone>(
&self,
mut layouter: impl Layouter<C::Base>,
other: &Self,
other: &Other,
) -> Result<(), Error> {
let other: Point<C, EccChip> = (other.clone()).into();
self.chip
.constrain_equal(&mut layouter, &self.inner, &other.inner)
}
@ -192,7 +322,7 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
/// 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())
X::from_inner(self.chip.clone(), EccChip::extract_p(&self.inner))
}
/// Wraps the given point (obtained directly from an instruction) in a gadget.
@ -201,7 +331,13 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
}
/// Returns `self + other` using complete addition.
pub fn add(&self, mut layouter: impl Layouter<C::Base>, other: &Self) -> Result<Self, Error> {
pub fn add<Other: Into<Point<C, EccChip>> + Clone>(
&self,
mut layouter: impl Layouter<C::Base>,
other: &Other,
) -> Result<Point<C, EccChip>, Error> {
let other: Point<C, EccChip> = (other.clone()).into();
assert_eq!(self.chip, other.chip);
self.chip
.add(&mut layouter, &self.inner, &other.inner)
@ -210,43 +346,6 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
inner,
})
}
/// Returns `self + other` using incomplete addition.
pub fn add_incomplete(
&self,
mut layouter: impl Layouter<C::Base>,
other: &Self,
) -> Result<Self, Error> {
assert_eq!(self.chip, other.chip);
self.chip
.add_incomplete(&mut layouter, &self.inner, &other.inner)
.map(|inner| Point {
chip: self.chip.clone(),
inner,
})
}
/// Returns `[by] self`.
pub fn mul(
&self,
mut layouter: impl Layouter<C::Base>,
by: &EccChip::Var,
) -> Result<(Self, ScalarVar<C, EccChip>), Error> {
self.chip
.mul(&mut layouter, by, &self.inner)
.map(|(point, scalar)| {
(
Point {
chip: self.chip.clone(),
inner: point,
},
ScalarVar {
chip: self.chip.clone(),
inner: scalar,
},
)
})
}
}
/// The affine short Weierstrass x-coordinate of an elliptic curve point over the
@ -463,34 +562,60 @@ mod tests {
// provided by the Sinsemilla chip.
config.lookup_config.load(&mut layouter)?;
// Generate a random point P
// Generate a random non-identity point P
let p_val = pallas::Point::random(rand::rngs::OsRng).to_affine(); // P
let p = super::Point::new(chip.clone(), layouter.namespace(|| "P"), Some(p_val))?;
let p = super::NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "P"),
Some(p_val),
)?;
let p_neg = -p_val;
let p_neg = super::Point::new(chip.clone(), layouter.namespace(|| "-P"), Some(p_neg))?;
let p_neg = super::NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "-P"),
Some(p_neg),
)?;
// Generate a random point Q
// Generate a random non-identity point Q
let q_val = pallas::Point::random(rand::rngs::OsRng).to_affine(); // Q
let q = super::Point::new(chip.clone(), layouter.namespace(|| "Q"), Some(q_val))?;
let q = super::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 = {
super::Point::new(
// 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()),
)?
};
)?;
super::NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "identity"),
Some(pallas::Affine::identity()),
)
.expect_err("Trying to witness the identity should return an error");
}
// Test witness non-identity point
{
super::chip::witness_point::tests::test_witness_non_id(
chip.clone(),
layouter.namespace(|| "witness non-identity point"),
)
}
// Test complete addition
{
super::chip::add::tests::test_add(
chip.clone(),
layouter.namespace(|| "complete addition"),
&zero,
p_val,
&p,
q_val,
@ -504,7 +629,6 @@ mod tests {
super::chip::add_incomplete::tests::test_add_incomplete(
chip.clone(),
layouter.namespace(|| "incomplete addition"),
&zero,
p_val,
&p,
q_val,
@ -518,7 +642,6 @@ mod tests {
super::chip::mul::tests::test_mul(
chip.clone(),
layouter.namespace(|| "variable-base scalar mul"),
&zero,
&p,
p_val,
)?;

View File

@ -22,9 +22,10 @@ pub(super) mod mul;
pub(super) mod mul_fixed;
pub(super) mod witness_point;
/// A curve point represented in affine (x, y) coordinates. Each coordinate is
/// assigned to a cell.
#[derive(Clone, Debug)]
/// A curve point represented in affine (x, y) coordinates, or the
/// identity represented as (0, 0).
/// Each coordinate is assigned to a cell.
#[derive(Copy, Clone, Debug)]
pub struct EccPoint {
/// x-coordinate
x: CellValue<pallas::Base>,
@ -67,6 +68,62 @@ impl EccPoint {
pub fn y(&self) -> CellValue<pallas::Base> {
self.y
}
#[cfg(test)]
fn is_identity(&self) -> Option<bool> {
self.x.value().map(|x| x == pallas::Base::zero())
}
}
/// A non-identity point represented in affine (x, y) coordinates.
/// Each coordinate is assigned to a cell.
#[derive(Copy, Clone, Debug)]
pub struct NonIdentityEccPoint {
/// x-coordinate
x: CellValue<pallas::Base>,
/// y-coordinate
y: CellValue<pallas::Base>,
}
impl NonIdentityEccPoint {
/// Constructs a point from its coordinates, without checking they are on the curve.
///
/// This is an internal API that we only use where we know we have a valid non-identity
/// curve point (specifically inside Sinsemilla).
pub(in crate::circuit::gadget) fn from_coordinates_unchecked(
x: CellValue<pallas::Base>,
y: CellValue<pallas::Base>,
) -> Self {
NonIdentityEccPoint { x, y }
}
/// Returns the value of this curve point, if known.
pub fn point(&self) -> Option<pallas::Affine> {
match (self.x.value(), self.y.value()) {
(Some(x), Some(y)) => {
assert!(x != pallas::Base::zero() && y != pallas::Base::zero());
Some(pallas::Affine::from_xy(x, y).unwrap())
}
_ => None,
}
}
/// The cell containing the affine short-Weierstrass x-coordinate.
pub fn x(&self) -> CellValue<pallas::Base> {
self.x
}
/// The cell containing the affine short-Weierstrass y-coordinate.
pub fn y(&self) -> CellValue<pallas::Base> {
self.y
}
}
impl From<NonIdentityEccPoint> for EccPoint {
fn from(non_id_point: NonIdentityEccPoint) -> Self {
Self {
x: non_id_point.x,
y: non_id_point.y,
}
}
}
/// Configuration for the ECC chip
@ -108,8 +165,10 @@ pub struct EccConfig {
/// when the scalar is a signed short exponent or a base-field element.
pub q_mul_fixed_running_sum: Selector,
/// Witness point
/// Witness point (can be identity)
pub q_point: Selector,
/// Witness non-identity point
pub q_point_non_id: Selector,
/// Lookup range check using 10-bit lookup table
pub lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
@ -206,6 +265,7 @@ impl EccChip {
q_mul_fixed_base_field: meta.selector(),
q_mul_fixed_running_sum,
q_point: meta.selector(),
q_point_non_id: meta.selector(),
lookup_config: range_check,
running_sum_config,
};
@ -320,6 +380,7 @@ impl EccInstructions<pallas::Affine> for EccChip {
type ScalarFixedShort = EccScalarFixedShort;
type ScalarVar = CellValue<pallas::Base>;
type Point = EccPoint;
type NonIdentityPoint = NonIdentityEccPoint;
type X = CellValue<pallas::Base>;
type FixedPoints = OrchardFixedBasesFull;
type FixedPointsBaseField = NullifierK;
@ -350,20 +411,33 @@ impl EccInstructions<pallas::Affine> for EccChip {
let config: witness_point::Config = self.config().into();
layouter.assign_region(
|| "witness point",
|mut region| config.assign_region(value, 0, &mut region),
|mut region| config.point(value, 0, &mut region),
)
}
fn extract_p(point: &Self::Point) -> &Self::X {
&point.x
fn witness_point_non_id(
&self,
layouter: &mut impl Layouter<pallas::Base>,
value: Option<pallas::Affine>,
) -> Result<Self::NonIdentityPoint, Error> {
let config: witness_point::Config = self.config().into();
layouter.assign_region(
|| "witness non-identity point",
|mut region| config.point_non_id(value, 0, &mut region),
)
}
fn extract_p<Point: Into<Self::Point> + Clone>(point: &Point) -> Self::X {
let point: EccPoint = (point.clone()).into();
point.x()
}
fn add_incomplete(
&self,
layouter: &mut impl Layouter<pallas::Base>,
a: &Self::Point,
b: &Self::Point,
) -> Result<Self::Point, Error> {
a: &Self::NonIdentityPoint,
b: &Self::NonIdentityPoint,
) -> Result<Self::NonIdentityPoint, Error> {
let config: add_incomplete::Config = self.config().into();
layouter.assign_region(
|| "incomplete point addition",
@ -371,16 +445,18 @@ impl EccInstructions<pallas::Affine> for EccChip {
)
}
fn add(
fn add<A: Into<Self::Point> + Clone, B: Into<Self::Point> + Clone>(
&self,
layouter: &mut impl Layouter<pallas::Base>,
a: &Self::Point,
b: &Self::Point,
a: &A,
b: &B,
) -> Result<Self::Point, Error> {
let config: add::Config = self.config().into();
layouter.assign_region(
|| "complete point addition",
|mut region| config.assign_region(a, b, 0, &mut region),
|mut region| {
config.assign_region(&(a.clone()).into(), &(b.clone()).into(), 0, &mut region)
},
)
}
@ -388,7 +464,7 @@ impl EccInstructions<pallas::Affine> for EccChip {
&self,
layouter: &mut impl Layouter<pallas::Base>,
scalar: &Self::Var,
base: &Self::Point,
base: &Self::NonIdentityPoint,
) -> Result<(Self::Point, Self::ScalarVar), Error> {
let config: mul::Config = self.config().into();
config.assign(

View File

@ -391,38 +391,40 @@ pub mod tests {
use halo2::{circuit::Layouter, plonk::Error};
use pasta_curves::{arithmetic::CurveExt, pallas};
use crate::circuit::gadget::ecc::{EccInstructions, Point};
use crate::circuit::gadget::ecc::{chip::EccPoint, EccInstructions, NonIdentityPoint};
#[allow(clippy::too_many_arguments)]
pub fn test_add<EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug>(
pub fn test_add<
EccChip: EccInstructions<pallas::Affine, Point = EccPoint> + Clone + Eq + std::fmt::Debug,
>(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
zero: &Point<pallas::Affine, EccChip>,
p_val: pallas::Affine,
p: &Point<pallas::Affine, EccChip>,
p: &NonIdentityPoint<pallas::Affine, EccChip>,
q_val: pallas::Affine,
q: &Point<pallas::Affine, EccChip>,
p_neg: &Point<pallas::Affine, EccChip>,
q: &NonIdentityPoint<pallas::Affine, EccChip>,
p_neg: &NonIdentityPoint<pallas::Affine, EccChip>,
) -> Result<(), Error> {
// Make sure P and Q are not the same point.
assert_ne!(p_val, q_val);
// Check complete addition P + (-P)
{
let zero = {
let result = p.add(layouter.namespace(|| "P + (-P)"), p_neg)?;
result.constrain_equal(layouter.namespace(|| "P + (-P) = 𝒪"), zero)?;
}
assert!(result.inner().is_identity().unwrap());
result
};
// Check complete addition 𝒪 + 𝒪
{
let result = zero.add(layouter.namespace(|| "𝒪 + 𝒪"), zero)?;
result.constrain_equal(layouter.namespace(|| "𝒪 + 𝒪 = 𝒪"), zero)?;
let result = zero.add(layouter.namespace(|| "𝒪 + 𝒪"), &zero)?;
result.constrain_equal(layouter.namespace(|| "𝒪 + 𝒪 = 𝒪"), &zero)?;
}
// Check P + Q
{
let result = p.add(layouter.namespace(|| "P + Q"), q)?;
let witnessed_result = Point::new(
let witnessed_result = NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "witnessed P + Q"),
Some((p_val + q_val).to_affine()),
@ -433,7 +435,7 @@ pub mod tests {
// P + P
{
let result = p.add(layouter.namespace(|| "P + P"), p)?;
let witnessed_result = Point::new(
let witnessed_result = NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "witnessed P + P"),
Some((p_val + p_val).to_affine()),
@ -443,7 +445,7 @@ pub mod tests {
// P + 𝒪
{
let result = p.add(layouter.namespace(|| "P + 𝒪"), zero)?;
let result = p.add(layouter.namespace(|| "P + 𝒪"), &zero)?;
result.constrain_equal(layouter.namespace(|| "P + 𝒪 = P"), p)?;
}
@ -455,7 +457,7 @@ pub mod tests {
// (x, y) + (ζx, y) should behave like normal P + Q.
let endo_p = p_val.to_curve().endo();
let endo_p = Point::new(
let endo_p = NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "endo(P)"),
Some(endo_p.to_affine()),
@ -464,7 +466,7 @@ pub mod tests {
// (x, y) + (ζx, -y) should also behave like normal P + Q.
let endo_p_neg = (-p_val).to_curve().endo();
let endo_p_neg = Point::new(
let endo_p_neg = NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "endo(-P)"),
Some(endo_p_neg.to_affine()),
@ -473,7 +475,7 @@ pub mod tests {
// (x, y) + ((ζ^2)x, y)
let endo_2_p = p_val.to_curve().endo().endo();
let endo_2_p = Point::new(
let endo_2_p = NonIdentityPoint::new(
chip.clone(),
layouter.namespace(|| "endo^2(P)"),
Some(endo_2_p.to_affine()),
@ -482,7 +484,7 @@ pub mod tests {
// (x, y) + ((ζ^2)x, -y)
let endo_2_p_neg = (-p_val).to_curve().endo().endo();
let endo_2_p_neg = Point::new(
let endo_2_p_neg = NonIdentityPoint::new(
chip,
layouter.namespace(|| "endo^2(-P)"),
Some(endo_2_p_neg.to_affine()),

View File

@ -1,6 +1,6 @@
use std::{array, collections::HashSet};
use super::{copy, CellValue, EccConfig, EccPoint, Var};
use super::{copy, CellValue, EccConfig, NonIdentityEccPoint, Var};
use group::Curve;
use halo2::{
circuit::Region,
@ -67,11 +67,11 @@ impl Config {
pub(super) fn assign_region(
&self,
p: &EccPoint,
q: &EccPoint,
p: &NonIdentityEccPoint,
q: &NonIdentityEccPoint,
offset: usize,
region: &mut Region<'_, pallas::Base>,
) -> Result<EccPoint, Error> {
) -> Result<NonIdentityEccPoint, Error> {
// Enable `q_add_incomplete` selector
self.q_add_incomplete.enable(region, offset)?;
@ -134,7 +134,7 @@ impl Config {
|| y_r.ok_or(Error::SynthesisError),
)?;
let result = EccPoint {
let result = NonIdentityEccPoint {
x: CellValue::<pallas::Base>::new(x_r_var, x_r),
y: CellValue::<pallas::Base>::new(y_r_var, y_r),
};
@ -149,7 +149,7 @@ pub mod tests {
use halo2::{circuit::Layouter, plonk::Error};
use pasta_curves::pallas;
use crate::circuit::gadget::ecc::{EccInstructions, Point};
use crate::circuit::gadget::ecc::{EccInstructions, NonIdentityPoint};
#[allow(clippy::too_many_arguments)]
pub fn test_add_incomplete<
@ -157,17 +157,16 @@ pub mod tests {
>(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
zero: &Point<pallas::Affine, EccChip>,
p_val: pallas::Affine,
p: &Point<pallas::Affine, EccChip>,
p: &NonIdentityPoint<pallas::Affine, EccChip>,
q_val: pallas::Affine,
q: &Point<pallas::Affine, EccChip>,
p_neg: &Point<pallas::Affine, EccChip>,
q: &NonIdentityPoint<pallas::Affine, EccChip>,
p_neg: &NonIdentityPoint<pallas::Affine, EccChip>,
) -> Result<(), Error> {
// P + Q
{
let result = p.add_incomplete(layouter.namespace(|| "P + Q"), q)?;
let witnessed_result = Point::new(
let witnessed_result = NonIdentityPoint::new(
chip,
layouter.namespace(|| "witnessed P + Q"),
Some((p_val + q_val).to_affine()),
@ -183,18 +182,6 @@ pub mod tests {
p.add_incomplete(layouter.namespace(|| "P + (-P)"), p_neg)
.expect_err("P + (-P) should return an error");
// P + 𝒪 should return an error
p.add_incomplete(layouter.namespace(|| "P + 𝒪"), zero)
.expect_err("P + 0 should return an error");
// 𝒪 + P should return an error
zero.add_incomplete(layouter.namespace(|| "𝒪 + P"), p)
.expect_err("0 + P should return an error");
// 𝒪 + 𝒪 should return an error
zero.add_incomplete(layouter.namespace(|| "𝒪 + 𝒪"), zero)
.expect_err("𝒪 + 𝒪 should return an error");
Ok(())
}
}

View File

@ -1,4 +1,4 @@
use super::{add, CellValue, EccConfig, EccPoint, Var};
use super::{add, CellValue, EccConfig, EccPoint, NonIdentityEccPoint, Var};
use crate::{circuit::gadget::utilities::copy, constants::T_Q};
use std::ops::{Deref, Range};
@ -136,12 +136,16 @@ impl Config {
&self,
mut layouter: impl Layouter<pallas::Base>,
alpha: CellValue<pallas::Base>,
base: &EccPoint,
base: &NonIdentityEccPoint,
) -> Result<(EccPoint, CellValue<pallas::Base>), Error> {
let (result, zs): (EccPoint, Vec<Z<pallas::Base>>) = layouter.assign_region(
|| "variable-base scalar mul",
|mut region| {
let offset = 0;
// Case `base` into an `EccPoint` for later use.
let base_point: EccPoint = (*base).into();
// Decompose `k = alpha + t_q` bitwise (big-endian bit order).
let bits = decompose_for_scalar_mul(alpha.value());
@ -151,9 +155,9 @@ impl Config {
let lsb = bits[pallas::Scalar::NUM_BITS as usize - 1];
// Initialize the accumulator `acc = [2]base`
let acc = self
.add_config
.assign_region(base, base, offset, &mut region)?;
let acc =
self.add_config
.assign_region(&base_point, &base_point, offset, &mut region)?;
// Increase the offset by 1 after complete addition.
let offset = offset + 1;
@ -207,7 +211,7 @@ impl Config {
&mut region,
offset,
bits_complete,
base,
&base_point,
x_a,
y_a,
*z,
@ -282,7 +286,7 @@ impl Config {
&self,
region: &mut Region<'_, pallas::Base>,
offset: usize,
base: &EccPoint,
base: &NonIdentityEccPoint,
acc: EccPoint,
z_1: Z<pallas::Base>,
lsb: Option<bool>,
@ -449,21 +453,23 @@ pub mod tests {
use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::circuit::gadget::{
ecc::{chip::EccChip, EccInstructions, Point},
ecc::{
chip::{EccChip, EccPoint},
EccInstructions, NonIdentityPoint, Point,
},
utilities::UtilitiesInstructions,
};
pub fn test_mul(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
zero: &Point<pallas::Affine, EccChip>,
p: &Point<pallas::Affine, EccChip>,
p: &NonIdentityPoint<pallas::Affine, EccChip>,
p_val: pallas::Affine,
) -> Result<(), Error> {
let column = chip.config().advices[0];
fn constrain_equal<
EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug,
fn constrain_equal_non_id<
EccChip: EccInstructions<pallas::Affine, Point = EccPoint> + Clone + Eq + std::fmt::Debug,
>(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
@ -474,7 +480,7 @@ pub mod tests {
// Move scalar from base field into scalar field (which always fits
// for Pallas).
let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap();
let expected = Point::new(
let expected = NonIdentityPoint::new(
chip,
layouter.namespace(|| "expected point"),
Some((base_val * scalar).to_affine()),
@ -493,7 +499,7 @@ pub mod tests {
)?;
p.mul(layouter.namespace(|| "random [a]B"), &scalar)?
};
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| "random [a]B"),
p_val,
@ -502,19 +508,6 @@ pub mod tests {
)?;
}
// [a]𝒪 should return an error since variable-base scalar multiplication
// uses incomplete addition at the beginning of its double-and-add.
{
let scalar_val = pallas::Base::rand();
let scalar = chip.load_private(
layouter.namespace(|| "random scalar"),
column,
Some(scalar_val),
)?;
zero.mul(layouter.namespace(|| "[a]𝒪"), &scalar)
.expect_err("[a]𝒪 should return an error");
}
// [0]B should return (0,0) since variable-base scalar multiplication
// uses complete addition for the final bits of the scalar.
{
@ -524,13 +517,7 @@ pub mod tests {
chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_val))?;
p.mul(layouter.namespace(|| "[0]B"), &scalar)?
};
constrain_equal(
chip.clone(),
layouter.namespace(|| "[0]B"),
p_val,
scalar_val,
result,
)?;
assert!(result.inner().is_identity().unwrap());
}
// [-1]B (the largest possible base field element)
@ -541,7 +528,7 @@ pub mod tests {
chip.load_private(layouter.namespace(|| "-1"), column, Some(scalar_val))?;
p.mul(layouter.namespace(|| "[-1]B"), &scalar)?
};
constrain_equal(
constrain_equal_non_id(
chip,
layouter.namespace(|| "[-1]B"),
p_val,

View File

@ -1,6 +1,6 @@
use std::ops::Deref;
use super::super::{copy, CellValue, EccConfig, EccPoint, Var};
use super::super::{copy, CellValue, EccConfig, NonIdentityEccPoint, Var};
use super::{INCOMPLETE_HI_RANGE, INCOMPLETE_LO_RANGE, X, Y, Z};
use ff::Field;
use halo2::{
@ -198,18 +198,19 @@ impl Config {
});
}
// We perform incomplete addition on all but the last three bits of the
// decomposed scalar.
// We split the bits in the incomplete addition range into "hi" and "lo"
// halves and process them side by side, using the same rows but with
// non-overlapping columns.
// Returns (x, y, z).
/// We perform incomplete addition on all but the last three bits of the
/// decomposed scalar.
/// We split the bits in the incomplete addition range into "hi" and "lo"
/// halves and process them side by side, using the same rows but with
/// non-overlapping columns. The base is never the identity point even at
/// the boundary between halves.
/// Returns (x, y, z).
#[allow(clippy::type_complexity)]
pub(super) fn double_and_add(
&self,
region: &mut Region<'_, pallas::Base>,
offset: usize,
base: &EccPoint,
base: &NonIdentityEccPoint,
bits: &[Option<bool>],
acc: (X<pallas::Base>, Y<pallas::Base>, Z<pallas::Base>),
) -> Result<(X<pallas::Base>, Y<pallas::Base>, Vec<Z<pallas::Base>>), Error> {

View File

@ -1,6 +1,6 @@
use super::{
add, add_incomplete, CellValue, EccBaseFieldElemFixed, EccConfig, EccPoint, EccScalarFixed,
EccScalarFixedShort, Var,
add, add_incomplete, CellValue, EccBaseFieldElemFixed, EccConfig, EccScalarFixed,
EccScalarFixedShort, NonIdentityEccPoint, Var,
};
use crate::constants::{
self,
@ -220,7 +220,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
scalar: &ScalarFixed,
base: OrchardFixedBases,
coords_check_toggle: Selector,
) -> Result<(EccPoint, EccPoint), Error> {
) -> Result<(NonIdentityEccPoint, NonIdentityEccPoint), Error> {
// Assign fixed columns for given fixed base
self.assign_fixed_constants(region, offset, base, coords_check_toggle)?;
@ -320,7 +320,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
k: Option<pallas::Scalar>,
k_usize: Option<usize>,
base: OrchardFixedBases,
) -> Result<EccPoint, Error> {
) -> Result<NonIdentityEccPoint, Error> {
let base_value = base.generator();
let base_u = base.u();
@ -330,7 +330,11 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
k.map(|k| base_value * (k + *TWO_SCALAR) * H_SCALAR.pow(&[w as u64, 0, 0, 0]));
let mul_b = mul_b.map(|mul_b| mul_b.to_affine().coordinates().unwrap());
let x = mul_b.map(|mul_b| *mul_b.x());
let x = mul_b.map(|mul_b| {
let x = *mul_b.x();
assert!(x != pallas::Base::zero());
x
});
let x_cell = region.assign_advice(
|| format!("mul_b_x, window {}", w),
self.x_p,
@ -339,7 +343,11 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
)?;
let x = CellValue::new(x_cell, x);
let y = mul_b.map(|mul_b| *mul_b.y());
let y = mul_b.map(|mul_b| {
let y = *mul_b.y();
assert!(y != pallas::Base::zero());
y
});
let y_cell = region.assign_advice(
|| format!("mul_b_y, window {}", w),
self.y_p,
@ -348,7 +356,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
)?;
let y = CellValue::new(y_cell, y);
EccPoint { x, y }
NonIdentityEccPoint { x, y }
};
// Assign u = (y_p + z_w).sqrt()
@ -369,7 +377,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
offset: usize,
base: OrchardFixedBases,
scalar: &ScalarFixed,
) -> Result<EccPoint, Error> {
) -> Result<NonIdentityEccPoint, Error> {
// Recall that the message at each window `w` is represented as
// `m_w = [(k_w + 2) ⋅ 8^w]B`.
// When `w = 0`, we have `m_0 = [(k_0 + 2)]B`.
@ -383,10 +391,10 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
&self,
region: &mut Region<'_, pallas::Base>,
offset: usize,
mut acc: EccPoint,
mut acc: NonIdentityEccPoint,
base: OrchardFixedBases,
scalar: &ScalarFixed,
) -> Result<EccPoint, Error> {
) -> Result<NonIdentityEccPoint, Error> {
let scalar_windows_field = scalar.windows_field();
let scalar_windows_usize = scalar.windows_usize();
@ -414,7 +422,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
offset: usize,
base: OrchardFixedBases,
scalar: &ScalarFixed,
) -> Result<EccPoint, Error> {
) -> Result<NonIdentityEccPoint, Error> {
// Assign u = (y_p + z_w).sqrt() for the most significant window
{
let u_val =
@ -445,16 +453,25 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
let mul_b = scalar.map(|scalar| base.generator() * scalar);
let mul_b = mul_b.map(|mul_b| mul_b.to_affine().coordinates().unwrap());
let x = mul_b.map(|mul_b| *mul_b.x());
let x = mul_b.map(|mul_b| {
let x = *mul_b.x();
assert!(x != pallas::Base::zero());
x
});
let x_cell = region.assign_advice(
|| format!("mul_b_x, window {}", NUM_WINDOWS - 1),
self.x_p,
offset + NUM_WINDOWS - 1,
|| x.ok_or(Error::SynthesisError),
)?;
let x = CellValue::new(x_cell, x);
let y = mul_b.map(|mul_b| *mul_b.y());
let y = mul_b.map(|mul_b| {
let y = *mul_b.y();
assert!(y != pallas::Base::zero());
y
});
let y_cell = region.assign_advice(
|| format!("mul_b_y, window {}", NUM_WINDOWS - 1),
self.y_p,
@ -463,7 +480,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
)?;
let y = CellValue::new(y_cell, y);
EccPoint { x, y }
NonIdentityEccPoint { x, y }
};
Ok(mul_b)

View File

@ -196,9 +196,12 @@ impl Config {
let result = layouter.assign_region(
|| "Base-field elem fixed-base mul (complete addition)",
|mut region| {
self.super_config
.add_config
.assign_region(&mul_b, &acc, 0, &mut region)
self.super_config.add_config.assign_region(
&mul_b.into(),
&acc.into(),
0,
&mut region,
)
},
)?;
@ -386,7 +389,7 @@ pub mod tests {
use crate::circuit::gadget::{
ecc::{
chip::{EccChip, NullifierK},
FixedPointBaseField, Point,
FixedPointBaseField, NonIdentityPoint, Point,
},
utilities::UtilitiesInstructions,
};
@ -415,7 +418,7 @@ pub mod tests {
) -> Result<(), Error> {
let column = chip.config().advices[0];
fn constrain_equal(
fn constrain_equal_non_id(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine,
@ -424,7 +427,7 @@ pub mod tests {
) -> 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();
let expected = Point::new(
let expected = NonIdentityPoint::new(
chip,
layouter.namespace(|| "expected point"),
Some((base_val * scalar).to_affine()),
@ -443,7 +446,7 @@ pub mod tests {
)?;
base.mul(layouter.namespace(|| "random [a]B"), scalar_fixed)?
};
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| "random [a]B"),
base_val,
@ -471,7 +474,7 @@ pub mod tests {
)?;
base.mul(layouter.namespace(|| "mul with double"), scalar_fixed)?
};
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| "mul with double"),
base_val,
@ -489,13 +492,7 @@ pub mod tests {
chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_fixed))?;
base.mul(layouter.namespace(|| "mul by zero"), scalar_fixed)?
};
constrain_equal(
chip.clone(),
layouter.namespace(|| "mul by zero"),
base_val,
scalar_fixed,
result,
)?;
assert!(result.inner().is_identity().unwrap());
}
// [-1]B is the largest base field element
@ -506,7 +503,7 @@ pub mod tests {
chip.load_private(layouter.namespace(|| "-1"), column, Some(scalar_fixed))?;
base.mul(layouter.namespace(|| "mul by -1"), scalar_fixed)?
};
constrain_equal(
constrain_equal_non_id(
chip,
layouter.namespace(|| "mul by -1"),
base_val,

View File

@ -140,9 +140,12 @@ impl Config {
let result = layouter.assign_region(
|| "Full-width fixed-base mul (last window, complete addition)",
|mut region| {
self.super_config
.add_config
.assign_region(&mul_b, &acc, 0, &mut region)
self.super_config.add_config.assign_region(
&mul_b.into(),
&acc.into(),
0,
&mut region,
)
},
)?;
@ -172,7 +175,7 @@ pub mod tests {
use crate::circuit::gadget::ecc::{
chip::{EccChip, OrchardFixedBasesFull},
FixedPoint, Point,
FixedPoint, NonIdentityPoint, Point,
};
use crate::constants;
@ -226,14 +229,14 @@ pub mod tests {
base: FixedPoint<pallas::Affine, EccChip>,
base_val: pallas::Affine,
) -> Result<(), Error> {
fn constrain_equal(
fn constrain_equal_non_id(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine,
scalar_val: pallas::Scalar,
result: Point<pallas::Affine, EccChip>,
) -> Result<(), Error> {
let expected = Point::new(
let expected = NonIdentityPoint::new(
chip,
layouter.namespace(|| "expected point"),
Some((base_val * scalar_val).to_affine()),
@ -246,7 +249,7 @@ pub mod tests {
let scalar_fixed = pallas::Scalar::rand();
let (result, _) = base.mul(layouter.namespace(|| "random [a]B"), Some(scalar_fixed))?;
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| "random [a]B"),
base_val,
@ -269,7 +272,7 @@ pub mod tests {
let (result, _) =
base.mul(layouter.namespace(|| "mul with double"), Some(scalar_fixed))?;
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| "mul with double"),
base_val,
@ -283,20 +286,14 @@ pub mod tests {
{
let scalar_fixed = pallas::Scalar::zero();
let (result, _) = base.mul(layouter.namespace(|| "mul by zero"), Some(scalar_fixed))?;
constrain_equal(
chip.clone(),
layouter.namespace(|| "mul by zero"),
base_val,
scalar_fixed,
result,
)?;
assert!(result.inner().is_identity().unwrap());
}
// [-1]B is the largest scalar field element.
{
let scalar_fixed = -pallas::Scalar::one();
let (result, _) = base.mul(layouter.namespace(|| "mul by -1"), Some(scalar_fixed))?;
constrain_equal(
constrain_equal_non_id(
chip,
layouter.namespace(|| "mul by -1"),
base_val,

View File

@ -127,8 +127,8 @@ impl Config {
let offset = 0;
// Add to the cumulative sum to get `[magnitude]B`.
let magnitude_mul = self.super_config.add_config.assign_region(
&mul_b,
&acc,
&mul_b.into(),
&acc.into(),
offset,
&mut region,
)?;
@ -244,7 +244,7 @@ pub mod tests {
use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::circuit::gadget::{
ecc::{chip::EccChip, FixedPointShort, Point},
ecc::{chip::EccChip, FixedPointShort, NonIdentityPoint, Point},
utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions},
};
use crate::constants::load::ValueCommitV;
@ -273,14 +273,14 @@ pub mod tests {
Ok((magnitude, sign))
}
fn constrain_equal(
fn constrain_equal_non_id(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine,
scalar_val: pallas::Scalar,
result: Point<pallas::Affine, EccChip>,
) -> Result<(), Error> {
let expected = Point::new(
let expected = NonIdentityPoint::new(
chip,
layouter.namespace(|| "expected point"),
Some((base_val * scalar_val).to_affine()),
@ -289,8 +289,6 @@ pub mod tests {
}
let magnitude_signs = [
("mul by +zero", pallas::Base::zero(), pallas::Base::one()),
("mul by -zero", pallas::Base::zero(), -pallas::Base::one()),
(
"random [a]B",
pallas::Base::from_u64(rand::random::<u64>()),
@ -347,7 +345,7 @@ pub mod tests {
};
magnitude * sign
};
constrain_equal(
constrain_equal_non_id(
chip.clone(),
layouter.namespace(|| *name),
base_val,
@ -356,6 +354,24 @@ pub mod tests {
)?;
}
let zero_magnitude_signs = [
("mul by +zero", pallas::Base::zero(), pallas::Base::one()),
("mul by -zero", pallas::Base::zero(), -pallas::Base::one()),
];
for (name, magnitude, sign) in zero_magnitude_signs.iter() {
let (result, _) = {
let magnitude_sign = load_magnitude_sign(
chip.clone(),
layouter.namespace(|| *name),
*magnitude,
*sign,
)?;
value_commit_v.mul(layouter.namespace(|| *name), magnitude_sign)?
};
assert!(result.inner().is_identity().unwrap());
}
Ok(())
}
@ -489,7 +505,7 @@ pub mod tests {
Err(vec![
VerifyFailure::ConstraintNotSatisfied {
constraint: (
(16, "Short fixed-base mul gate").into(),
(17, "Short fixed-base mul gate").into(),
0,
"last_window_check"
)
@ -521,13 +537,13 @@ pub mod tests {
prover.verify(),
Err(vec![
VerifyFailure::ConstraintNotSatisfied {
constraint: ((16, "Short fixed-base mul gate").into(), 1, "sign_check")
constraint: ((17, "Short fixed-base mul gate").into(), 1, "sign_check")
.into(),
row: 26
},
VerifyFailure::ConstraintNotSatisfied {
constraint: (
(16, "Short fixed-base mul gate").into(),
(17, "Short fixed-base mul gate").into(),
3,
"negation_check"
)

View File

@ -1,10 +1,10 @@
use super::{CellValue, EccConfig, EccPoint, Var};
use super::{CellValue, EccConfig, EccPoint, NonIdentityEccPoint, Var};
use group::prime::PrimeCurveAffine;
use halo2::{
circuit::Region,
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector, VirtualCells},
poly::Rotation,
};
use pasta_curves::{arithmetic::CurveAffine, pallas};
@ -12,6 +12,7 @@ use pasta_curves::{arithmetic::CurveAffine, pallas};
#[derive(Clone, Debug)]
pub struct Config {
q_point: Selector,
q_point_non_id: Selector,
// x-coordinate
pub x: Column<Advice>,
// y-coordinate
@ -22,6 +23,7 @@ impl From<&EccConfig> for Config {
fn from(ecc_config: &EccConfig) -> Self {
Self {
q_point: ecc_config.q_point,
q_point_non_id: ecc_config.q_point_non_id,
x: ecc_config.advices[0],
y: ecc_config.advices[1],
}
@ -30,8 +32,16 @@ impl From<&EccConfig> for Config {
impl Config {
pub(super) fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
let curve_eqn = |meta: &mut VirtualCells<pallas::Base>| {
let x = meta.query_advice(self.x, Rotation::cur());
let y = meta.query_advice(self.y, Rotation::cur());
// y^2 = x^3 + b
y.square() - (x.clone().square() * x) - Expression::Constant(pallas::Affine::b())
};
meta.create_gate("witness point", |meta| {
// Check that either the point being witness is either:
// Check that the point being witnessed is either:
// - the identity, which is mapped to (0, 0) in affine coordinates; or
// - a valid curve point y^2 = x^3 + b, where b = 5 in the Pallas equation
@ -39,37 +49,28 @@ impl Config {
let x = meta.query_advice(self.x, Rotation::cur());
let y = meta.query_advice(self.y, Rotation::cur());
// y^2 = x^3 + b
let curve_eqn = y.clone().square()
- (x.clone().square() * x.clone())
- Expression::Constant(pallas::Affine::b());
vec![
("x == 0 on_curve", q_point.clone() * x * curve_eqn.clone()),
("y == 0 on_curve", q_point * y * curve_eqn),
("x == 0 v on_curve", q_point.clone() * x * curve_eqn(meta)),
("y == 0 v on_curve", q_point * y * curve_eqn(meta)),
]
});
meta.create_gate("witness non-identity point", |meta| {
// Check that the point being witnessed is a valid curve point y^2 = x^3 + b,
// where b = 5 in the Pallas equation
let q_point_non_id = meta.query_selector(self.q_point_non_id);
vec![("on_curve", q_point_non_id * curve_eqn(meta))]
});
}
pub(super) fn assign_region(
fn assign_xy(
&self,
value: Option<pallas::Affine>,
value: Option<(pallas::Base, pallas::Base)>,
offset: usize,
region: &mut Region<'_, pallas::Base>,
) -> Result<EccPoint, Error> {
// Enable `q_point` selector
self.q_point.enable(region, offset)?;
let value = value.map(|value| {
// Map the identity to (0, 0).
if value == pallas::Affine::identity() {
(pallas::Base::zero(), pallas::Base::zero())
} else {
let value = value.coordinates().unwrap();
(*value.x(), *value.y())
}
});
) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), Error> {
// Assign `x` value
let x_val = value.map(|value| value.0);
let x_var = region.assign_advice(
@ -88,9 +89,83 @@ impl Config {
|| y_val.ok_or(Error::SynthesisError),
)?;
Ok(EccPoint {
x: CellValue::<pallas::Base>::new(x_var, x_val),
y: CellValue::<pallas::Base>::new(y_var, y_val),
})
Ok((
CellValue::<pallas::Base>::new(x_var, x_val),
CellValue::<pallas::Base>::new(y_var, y_val),
))
}
/// Assigns a point that can be the identity.
pub(super) fn point(
&self,
value: Option<pallas::Affine>,
offset: usize,
region: &mut Region<'_, pallas::Base>,
) -> Result<EccPoint, Error> {
// Enable `q_point` selector
self.q_point.enable(region, offset)?;
let value = value.map(|value| {
// Map the identity to (0, 0).
if value == pallas::Affine::identity() {
(pallas::Base::zero(), pallas::Base::zero())
} else {
let value = value.coordinates().unwrap();
(*value.x(), *value.y())
}
});
self.assign_xy(value, offset, region)
.map(|(x, y)| EccPoint { x, y })
}
/// Assigns a non-identity point.
pub(super) fn point_non_id(
&self,
value: Option<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)?;
if let Some(value) = value {
// Return an error if the point is the identity.
if value == pallas::Affine::identity() {
return Err(Error::SynthesisError);
}
};
let value = value.map(|value| {
let value = value.coordinates().unwrap();
(*value.x(), *value.y())
});
self.assign_xy(value, offset, region)
.map(|(x, y)| NonIdentityEccPoint { x, y })
}
}
#[cfg(test)]
pub mod tests {
use halo2::circuit::Layouter;
use pasta_curves::pallas;
use super::*;
use crate::circuit::gadget::ecc::{EccInstructions, NonIdentityPoint};
pub fn test_witness_non_id<
EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug,
>(
chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
) {
// Witnessing the identity should return an error.
NonIdentityPoint::new(
chip,
layouter.namespace(|| "witness identity"),
Some(pallas::Affine::identity()),
)
.expect_err("witnessing 𝒪 should return an error");
}
}

View File

@ -47,7 +47,7 @@ pub trait SinsemillaInstructions<C: CurveAffine, const K: usize, const MAX_WORDS
/// The x-coordinate of a point output of [`Self::hash_to_point`].
type X;
/// A point output of [`Self::hash_to_point`].
type Point: Clone + Debug;
type NonIdentityPoint: Clone + Debug;
/// A type enumerating the fixed points used in `CommitDomains`.
type FixedPoints: Clone + Debug;
@ -82,10 +82,10 @@ pub trait SinsemillaInstructions<C: CurveAffine, const K: usize, const MAX_WORDS
layouter: impl Layouter<C::Base>,
Q: C,
message: Self::Message,
) -> Result<(Self::Point, Vec<Self::RunningSum>), Error>;
) -> Result<(Self::NonIdentityPoint, Vec<Self::RunningSum>), Error>;
/// Extracts the x-coordinate of the output of a Sinsemilla hash.
fn extract(point: &Self::Point) -> Self::X;
fn extract(point: &Self::NonIdentityPoint) -> Self::X;
}
/// A message to be hashed.
@ -238,7 +238,7 @@ pub struct HashDomain<
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions<
C,
Point = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::Point,
NonIdentityPoint = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::NonIdentityPoint,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone
+ Debug
@ -255,7 +255,7 @@ where
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions<
C,
Point = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::Point,
NonIdentityPoint = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::NonIdentityPoint,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone
+ Debug
@ -283,11 +283,11 @@ where
&self,
layouter: impl Layouter<C::Base>,
message: Message<C, SinsemillaChip, K, MAX_WORDS>,
) -> Result<(ecc::Point<C, EccChip>, Vec<SinsemillaChip::RunningSum>), Error> {
) -> Result<(ecc::NonIdentityPoint<C, EccChip>, Vec<SinsemillaChip::RunningSum>), Error> {
assert_eq!(self.sinsemilla_chip, message.chip);
self.sinsemilla_chip
.hash_to_point(layouter, self.Q, message.inner)
.map(|(point, zs)| (ecc::Point::from_inner(self.ecc_chip.clone(), point), zs))
.map(|(point, zs)| (ecc::NonIdentityPoint::from_inner(self.ecc_chip.clone(), point), zs))
}
/// $\mathsf{SinsemillaHash}$ from [§ 5.4.1.9][concretesinsemillahash].
@ -334,7 +334,7 @@ pub struct CommitDomain<
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions<
C,
Point = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::Point,
NonIdentityPoint = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::NonIdentityPoint,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone
+ Debug
@ -350,7 +350,7 @@ where
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions<
C,
Point = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::Point,
NonIdentityPoint = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::NonIdentityPoint,
FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone
+ Debug
@ -377,11 +377,17 @@ where
mut layouter: impl Layouter<C::Base>,
message: Message<C, SinsemillaChip, K, MAX_WORDS>,
r: Option<C::Scalar>,
) -> Result<(ecc::Point<C, EccChip>, Vec<SinsemillaChip::RunningSum>), Error> {
) -> Result<
(
ecc::Point<C, EccChip>,
Vec<SinsemillaChip::RunningSum>,
),
Error,
> {
assert_eq!(self.M.sinsemilla_chip, message.chip);
let (blind, _) = self.R.mul(layouter.namespace(|| "[r] R"), r)?;
let (p, zs) = self.M.hash_to_point(layouter.namespace(|| "M"), message)?;
let commitment = p.add_incomplete(layouter.namespace(|| "M ⸭ [r] R"), &blind)?;
let commitment = p.add(layouter.namespace(|| "M + [r] R"), &blind)?;
Ok((commitment, zs))
}
@ -418,7 +424,7 @@ mod tests {
circuit::gadget::{
ecc::{
chip::{EccChip, EccConfig},
Point,
NonIdentityPoint,
},
utilities::lookup_range_check::LookupRangeCheckConfig,
},
@ -572,7 +578,7 @@ mod tests {
None
};
Point::new(
NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "Witness expected parent"),
expected_parent,
@ -623,7 +629,7 @@ mod tests {
None
};
Point::new(
NonIdentityPoint::new(
ecc_chip,
layouter.namespace(|| "Witness expected result"),
expected_result,

View File

@ -4,7 +4,7 @@ use super::{
};
use crate::{
circuit::gadget::{
ecc::chip::EccPoint,
ecc::chip::NonIdentityEccPoint,
utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, Var},
},
constants::OrchardFixedBasesFull,
@ -247,7 +247,7 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
type RunningSum = Vec<Self::CellValue>;
type X = CellValue<pallas::Base>;
type Point = EccPoint;
type NonIdentityPoint = NonIdentityEccPoint;
type FixedPoints = OrchardFixedBasesFull;
type HashDomains = SinsemillaHashDomains;
@ -282,14 +282,14 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
mut layouter: impl Layouter<pallas::Base>,
Q: pallas::Affine,
message: Self::Message,
) -> Result<(Self::Point, Vec<Self::RunningSum>), Error> {
) -> Result<(Self::NonIdentityPoint, Vec<Self::RunningSum>), Error> {
layouter.assign_region(
|| "hash_to_point",
|mut region| self.hash_message(&mut region, Q, &message),
)
}
fn extract(point: &Self::Point) -> Self::X {
fn extract(point: &Self::NonIdentityPoint) -> Self::X {
point.x()
}
}

View File

@ -1,5 +1,5 @@
use super::super::SinsemillaInstructions;
use super::{CellValue, EccPoint, SinsemillaChip, Var};
use super::{CellValue, NonIdentityEccPoint, SinsemillaChip, Var};
use crate::primitives::sinsemilla::{self, lebs2ip_k, INV_TWO_POW_K, SINSEMILLA_S};
use halo2::{
circuit::{Chip, Region},
@ -26,7 +26,7 @@ impl SinsemillaChip {
{ sinsemilla::K },
{ sinsemilla::C },
>>::Message,
) -> Result<(EccPoint, Vec<Vec<CellValue<pallas::Base>>>), Error> {
) -> Result<(NonIdentityEccPoint, Vec<Vec<CellValue<pallas::Base>>>), Error> {
let config = self.config().clone();
let mut offset = 0;
@ -147,7 +147,17 @@ impl SinsemillaChip {
}
}
Ok((EccPoint::from_coordinates_unchecked(x_a.0, y_a), zs_sum))
if let Some(x_a) = x_a.value() {
if let Some(y_a) = y_a.value() {
if x_a == pallas::Base::zero() || y_a == pallas::Base::zero() {
return Err(Error::SynthesisError);
}
}
}
Ok((
NonIdentityEccPoint::from_coordinates_unchecked(x_a.0, y_a),
zs_sum,
))
}
#[allow(clippy::type_complexity)]

View File

@ -417,11 +417,11 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
{ sinsemilla::K },
{ sinsemilla::C },
>>::X;
type Point = <SinsemillaChip as SinsemillaInstructions<
type NonIdentityPoint = <SinsemillaChip as SinsemillaInstructions<
pallas::Affine,
{ sinsemilla::K },
{ sinsemilla::C },
>>::Point;
>>::NonIdentityPoint;
type FixedPoints = <SinsemillaChip as SinsemillaInstructions<
pallas::Affine,
{ sinsemilla::K },
@ -457,13 +457,13 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
layouter: impl Layouter<pallas::Base>,
Q: pallas::Affine,
message: Self::Message,
) -> Result<(Self::Point, Vec<Vec<Self::CellValue>>), Error> {
) -> Result<(Self::NonIdentityPoint, Vec<Vec<Self::CellValue>>), Error> {
let config = self.config().sinsemilla_config.clone();
let chip = SinsemillaChip::construct(config);
chip.hash_to_point(layouter, Q, message)
}
fn extract(point: &Self::Point) -> Self::X {
fn extract(point: &Self::NonIdentityPoint) -> Self::X {
SinsemillaChip::extract(point)
}
}

View File

@ -8,7 +8,7 @@ use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::{
circuit::gadget::{
ecc::{
chip::{EccChip, EccPoint},
chip::{EccChip, NonIdentityEccPoint},
Point,
},
utilities::{bitrange_subset, bool_check, copy, CellValue, Var},
@ -523,8 +523,8 @@ impl NoteCommitConfig {
mut layouter: impl Layouter<pallas::Base>,
chip: SinsemillaChip,
ecc_chip: EccChip,
g_d: &EccPoint,
pk_d: &EccPoint,
g_d: &NonIdentityEccPoint,
pk_d: &NonIdentityEccPoint,
value: CellValue<pallas::Base>,
rho: CellValue<pallas::Base>,
psi: CellValue<pallas::Base>,
@ -1432,7 +1432,7 @@ mod tests {
circuit::gadget::{
ecc::{
chip::{EccChip, EccConfig},
Point,
NonIdentityPoint,
},
sinsemilla::chip::SinsemillaChip,
utilities::{
@ -1566,7 +1566,11 @@ mod tests {
pallas::Affine::from_xy(x, y).unwrap()
});
Point::new(ecc_chip.clone(), layouter.namespace(|| "witness g_d"), g_d)?
NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "witness g_d"),
g_d,
)?
};
// Witness pk_d
@ -1580,7 +1584,7 @@ mod tests {
pallas::Affine::from_xy(x, y).unwrap()
});
Point::new(
NonIdentityPoint::new(
ecc_chip.clone(),
layouter.namespace(|| "witness pk_d"),
pk_d,
@ -1674,7 +1678,11 @@ mod tests {
)
.unwrap()
.to_affine();
Point::new(ecc_chip, layouter.namespace(|| "witness g_d"), Some(point))?
NonIdentityPoint::new(
ecc_chip,
layouter.namespace(|| "witness cm"),
Some(point),
)?
};
cm.constrain_equal(layouter.namespace(|| "cm == expected cm"), &expected_cm)
}

File diff suppressed because it is too large Load Diff

View File

@ -174,7 +174,9 @@ impl CommitDomain {
msg: impl Iterator<Item = bool>,
r: &pallas::Scalar,
) -> CtOption<pallas::Point> {
(self.M.hash_to_point_inner(msg) + Wnaf::new().scalar(r).base(self.R)).into()
// We use complete addition for the blinding factor.
CtOption::<pallas::Point>::from(self.M.hash_to_point_inner(msg))
.map(|p| p + Wnaf::new().scalar(r).base(self.R))
}
/// $\mathsf{SinsemillaShortCommit}$ from [§ 5.4.8.4][concretesinsemillacommit].

View File

@ -46,14 +46,6 @@ impl Add for IncompletePoint {
}
}
impl Add<pallas::Point> for IncompletePoint {
type Output = IncompletePoint;
fn add(self, rhs: pallas::Point) -> Self::Output {
self + IncompletePoint(CtOption::new(rhs, 1.into()))
}
}
impl Add<pallas::Affine> for IncompletePoint {
type Output = IncompletePoint;