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::{ use gadget::{
ecc::{ ecc::{
chip::{EccChip, EccConfig}, chip::{EccChip, EccConfig},
FixedPoint, FixedPointBaseField, FixedPointShort, Point, FixedPoint, FixedPointBaseField, FixedPointShort, NonIdentityPoint, Point,
}, },
poseidon::{ poseidon::{
Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig, Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig,
@ -356,7 +356,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
)?; )?;
// Witness g_d_old // Witness g_d_old
let g_d_old = Point::new( let g_d_old = NonIdentityPoint::new(
ecc_chip.clone(), ecc_chip.clone(),
layouter.namespace(|| "gd_old"), layouter.namespace(|| "gd_old"),
self.g_d_old.as_ref().map(|gd| gd.to_affine()), self.g_d_old.as_ref().map(|gd| gd.to_affine()),
@ -364,7 +364,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
// Witness ak. // Witness ak.
let ak: Option<pallas::Point> = self.ak.as_ref().map(|ak| ak.into()); let ak: Option<pallas::Point> = self.ak.as_ref().map(|ak| ak.into());
let ak = Point::new( let ak = NonIdentityPoint::new(
ecc_chip.clone(), ecc_chip.clone(),
layouter.namespace(|| "ak"), layouter.namespace(|| "ak"),
ak.map(|ak| ak.to_affine()), 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())?; g_d_old.mul(layouter.namespace(|| "[ivk] g_d_old"), ivk.inner())?;
// Constrain derived pk_d_old to equal witnessed pk_d_old // 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(), ecc_chip.clone(),
layouter.namespace(|| "witness pk_d_old"), layouter.namespace(|| "witness pk_d_old"),
self.pk_d_old.map(|pk_d_old| pk_d_old.inner().to_affine()), 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 let g_d_new = self
.g_d_new_star .g_d_new_star
.map(|bytes| pallas::Affine::from_bytes(&bytes).unwrap()); .map(|bytes| pallas::Affine::from_bytes(&bytes).unwrap());
Point::new( NonIdentityPoint::new(
ecc_chip.clone(), ecc_chip.clone(),
layouter.namespace(|| "witness g_d_new_star"), layouter.namespace(|| "witness g_d_new_star"),
g_d_new, g_d_new,
@ -678,7 +678,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let pk_d_new = self let pk_d_new = self
.pk_d_new_star .pk_d_new_star
.map(|bytes| pallas::Affine::from_bytes(&bytes).unwrap()); .map(|bytes| pallas::Affine::from_bytes(&bytes).unwrap());
Point::new( NonIdentityPoint::new(
ecc_chip, ecc_chip,
layouter.namespace(|| "witness pk_d_new"), layouter.namespace(|| "witness pk_d_new"),
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]. /// A `ScalarFixedShort` must be in the range [-(2^64 - 1), 2^64 - 1].
type ScalarFixedShort: Clone + Debug; type ScalarFixedShort: Clone + Debug;
/// Variable representing an elliptic curve point. /// 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 /// Variable representing the affine short Weierstrass x-coordinate of an
/// elliptic curve point. /// elliptic curve point.
type X: Clone + Debug; type X: Clone + Debug;
@ -55,15 +57,24 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
) -> Result<(), Error>; ) -> Result<(), Error>;
/// Witnesses the given point as a private input to the circuit. /// 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( fn witness_point(
&self, &self,
layouter: &mut impl Layouter<C::Base>, layouter: &mut impl Layouter<C::Base>,
value: Option<C>, value: Option<C>,
) -> Result<Self::Point, Error>; ) -> 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. /// 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`. /// Performs incomplete point addition, returning `a + b`.
/// ///
@ -71,25 +82,24 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
fn add_incomplete( fn add_incomplete(
&self, &self,
layouter: &mut impl Layouter<C::Base>, layouter: &mut impl Layouter<C::Base>,
a: &Self::Point, a: &Self::NonIdentityPoint,
b: &Self::Point, b: &Self::NonIdentityPoint,
) -> Result<Self::Point, Error>; ) -> Result<Self::NonIdentityPoint, Error>;
/// Performs complete point addition, returning `a + b`. /// Performs complete point addition, returning `a + b`.
fn add( fn add<A: Into<Self::Point> + Clone, B: Into<Self::Point> + Clone>(
&self, &self,
layouter: &mut impl Layouter<C::Base>, layouter: &mut impl Layouter<C::Base>,
a: &Self::Point, a: &A,
b: &Self::Point, b: &B,
) -> Result<Self::Point, Error>; ) -> Result<Self::Point, Error>;
/// Performs variable-base scalar multiplication, returning `[scalar] base`. /// Performs variable-base scalar multiplication, returning `[scalar] base`.
/// Multiplication of the identity `[scalar] 𝒪 ` returns an error.
fn mul( fn mul(
&self, &self,
layouter: &mut impl Layouter<C::Base>, layouter: &mut impl Layouter<C::Base>,
scalar: &Self::Var, scalar: &Self::Var,
base: &Self::Point, base: &Self::NonIdentityPoint,
) -> Result<(Self::Point, Self::ScalarVar), Error>; ) -> Result<(Self::Point, Self::ScalarVar), Error>;
/// Performs fixed-base scalar multiplication using a full-width scalar, returning `[scalar] base`. /// Performs fixed-base scalar multiplication using a full-width scalar, returning `[scalar] base`.
@ -157,6 +167,125 @@ where
inner: EccChip::ScalarFixedShort, 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. /// An elliptic curve point over the given curve.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct Point<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> { 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. /// 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, &self,
mut layouter: impl Layouter<C::Base>, mut layouter: impl Layouter<C::Base>,
other: &Self, other: &Other,
) -> Result<(), Error> { ) -> Result<(), Error> {
let other: Point<C, EccChip> = (other.clone()).into();
self.chip self.chip
.constrain_equal(&mut layouter, &self.inner, &other.inner) .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. /// Extracts the x-coordinate of a point.
pub fn extract_p(&self) -> X<C, EccChip> { 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. /// 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. /// 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); assert_eq!(self.chip, other.chip);
self.chip self.chip
.add(&mut layouter, &self.inner, &other.inner) .add(&mut layouter, &self.inner, &other.inner)
@ -210,43 +346,6 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
inner, 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 /// The affine short Weierstrass x-coordinate of an elliptic curve point over the
@ -463,34 +562,60 @@ mod tests {
// provided by the Sinsemilla chip. // provided by the Sinsemilla chip.
config.lookup_config.load(&mut layouter)?; 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_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 = -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_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. // Make sure P and Q are not the same point.
assert_ne!(p_val, q_val); assert_ne!(p_val, q_val);
// Generate a (0,0) point to be used in other tests. // Test that we can witness the identity as a point, but not as a non-identity point.
let zero = { {
super::Point::new( let _ = super::Point::new(
chip.clone(), chip.clone(),
layouter.namespace(|| "identity"), layouter.namespace(|| "identity"),
Some(pallas::Affine::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 // Test complete addition
{ {
super::chip::add::tests::test_add( super::chip::add::tests::test_add(
chip.clone(), chip.clone(),
layouter.namespace(|| "complete addition"), layouter.namespace(|| "complete addition"),
&zero,
p_val, p_val,
&p, &p,
q_val, q_val,
@ -504,7 +629,6 @@ mod tests {
super::chip::add_incomplete::tests::test_add_incomplete( super::chip::add_incomplete::tests::test_add_incomplete(
chip.clone(), chip.clone(),
layouter.namespace(|| "incomplete addition"), layouter.namespace(|| "incomplete addition"),
&zero,
p_val, p_val,
&p, &p,
q_val, q_val,
@ -518,7 +642,6 @@ mod tests {
super::chip::mul::tests::test_mul( super::chip::mul::tests::test_mul(
chip.clone(), chip.clone(),
layouter.namespace(|| "variable-base scalar mul"), layouter.namespace(|| "variable-base scalar mul"),
&zero,
&p, &p,
p_val, p_val,
)?; )?;

View File

@ -22,9 +22,10 @@ pub(super) mod mul;
pub(super) mod mul_fixed; pub(super) mod mul_fixed;
pub(super) mod witness_point; pub(super) mod witness_point;
/// A curve point represented in affine (x, y) coordinates. Each coordinate is /// A curve point represented in affine (x, y) coordinates, or the
/// assigned to a cell. /// identity represented as (0, 0).
#[derive(Clone, Debug)] /// Each coordinate is assigned to a cell.
#[derive(Copy, Clone, Debug)]
pub struct EccPoint { pub struct EccPoint {
/// x-coordinate /// x-coordinate
x: CellValue<pallas::Base>, x: CellValue<pallas::Base>,
@ -67,6 +68,62 @@ impl EccPoint {
pub fn y(&self) -> CellValue<pallas::Base> { pub fn y(&self) -> CellValue<pallas::Base> {
self.y 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 /// 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. /// when the scalar is a signed short exponent or a base-field element.
pub q_mul_fixed_running_sum: Selector, pub q_mul_fixed_running_sum: Selector,
/// Witness point /// Witness point (can be identity)
pub q_point: Selector, pub q_point: Selector,
/// Witness non-identity point
pub q_point_non_id: Selector,
/// Lookup range check using 10-bit lookup table /// Lookup range check using 10-bit lookup table
pub lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>, pub lookup_config: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
@ -206,6 +265,7 @@ impl EccChip {
q_mul_fixed_base_field: meta.selector(), q_mul_fixed_base_field: meta.selector(),
q_mul_fixed_running_sum, q_mul_fixed_running_sum,
q_point: meta.selector(), q_point: meta.selector(),
q_point_non_id: meta.selector(),
lookup_config: range_check, lookup_config: range_check,
running_sum_config, running_sum_config,
}; };
@ -320,6 +380,7 @@ impl EccInstructions<pallas::Affine> for EccChip {
type ScalarFixedShort = EccScalarFixedShort; type ScalarFixedShort = EccScalarFixedShort;
type ScalarVar = CellValue<pallas::Base>; type ScalarVar = CellValue<pallas::Base>;
type Point = EccPoint; type Point = EccPoint;
type NonIdentityPoint = NonIdentityEccPoint;
type X = CellValue<pallas::Base>; type X = CellValue<pallas::Base>;
type FixedPoints = OrchardFixedBasesFull; type FixedPoints = OrchardFixedBasesFull;
type FixedPointsBaseField = NullifierK; type FixedPointsBaseField = NullifierK;
@ -350,20 +411,33 @@ impl EccInstructions<pallas::Affine> for EccChip {
let config: witness_point::Config = self.config().into(); let config: witness_point::Config = self.config().into();
layouter.assign_region( layouter.assign_region(
|| "witness point", || "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 { fn witness_point_non_id(
&point.x &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( fn add_incomplete(
&self, &self,
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
a: &Self::Point, a: &Self::NonIdentityPoint,
b: &Self::Point, b: &Self::NonIdentityPoint,
) -> Result<Self::Point, Error> { ) -> Result<Self::NonIdentityPoint, Error> {
let config: add_incomplete::Config = self.config().into(); let config: add_incomplete::Config = self.config().into();
layouter.assign_region( layouter.assign_region(
|| "incomplete point addition", || "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, &self,
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
a: &Self::Point, a: &A,
b: &Self::Point, b: &B,
) -> Result<Self::Point, Error> { ) -> Result<Self::Point, Error> {
let config: add::Config = self.config().into(); let config: add::Config = self.config().into();
layouter.assign_region( layouter.assign_region(
|| "complete point addition", || "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, &self,
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
scalar: &Self::Var, scalar: &Self::Var,
base: &Self::Point, base: &Self::NonIdentityPoint,
) -> Result<(Self::Point, Self::ScalarVar), Error> { ) -> Result<(Self::Point, Self::ScalarVar), Error> {
let config: mul::Config = self.config().into(); let config: mul::Config = self.config().into();
config.assign( config.assign(

View File

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

View File

@ -1,6 +1,6 @@
use std::{array, collections::HashSet}; use std::{array, collections::HashSet};
use super::{copy, CellValue, EccConfig, EccPoint, Var}; use super::{copy, CellValue, EccConfig, NonIdentityEccPoint, Var};
use group::Curve; use group::Curve;
use halo2::{ use halo2::{
circuit::Region, circuit::Region,
@ -67,11 +67,11 @@ impl Config {
pub(super) fn assign_region( pub(super) fn assign_region(
&self, &self,
p: &EccPoint, p: &NonIdentityEccPoint,
q: &EccPoint, q: &NonIdentityEccPoint,
offset: usize, offset: usize,
region: &mut Region<'_, pallas::Base>, region: &mut Region<'_, pallas::Base>,
) -> Result<EccPoint, Error> { ) -> Result<NonIdentityEccPoint, Error> {
// Enable `q_add_incomplete` selector // Enable `q_add_incomplete` selector
self.q_add_incomplete.enable(region, offset)?; self.q_add_incomplete.enable(region, offset)?;
@ -134,7 +134,7 @@ impl Config {
|| y_r.ok_or(Error::SynthesisError), || y_r.ok_or(Error::SynthesisError),
)?; )?;
let result = EccPoint { let result = NonIdentityEccPoint {
x: CellValue::<pallas::Base>::new(x_r_var, x_r), x: CellValue::<pallas::Base>::new(x_r_var, x_r),
y: CellValue::<pallas::Base>::new(y_r_var, y_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 halo2::{circuit::Layouter, plonk::Error};
use pasta_curves::pallas; use pasta_curves::pallas;
use crate::circuit::gadget::ecc::{EccInstructions, Point}; use crate::circuit::gadget::ecc::{EccInstructions, NonIdentityPoint};
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn test_add_incomplete< pub fn test_add_incomplete<
@ -157,17 +157,16 @@ pub mod tests {
>( >(
chip: EccChip, chip: EccChip,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
zero: &Point<pallas::Affine, EccChip>,
p_val: pallas::Affine, p_val: pallas::Affine,
p: &Point<pallas::Affine, EccChip>, p: &NonIdentityPoint<pallas::Affine, EccChip>,
q_val: pallas::Affine, q_val: pallas::Affine,
q: &Point<pallas::Affine, EccChip>, q: &NonIdentityPoint<pallas::Affine, EccChip>,
p_neg: &Point<pallas::Affine, EccChip>, p_neg: &NonIdentityPoint<pallas::Affine, EccChip>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// P + Q // 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 = Point::new( let witnessed_result = NonIdentityPoint::new(
chip, chip,
layouter.namespace(|| "witnessed P + Q"), layouter.namespace(|| "witnessed P + Q"),
Some((p_val + q_val).to_affine()), Some((p_val + q_val).to_affine()),
@ -183,18 +182,6 @@ pub mod tests {
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"); .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(()) 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 crate::{circuit::gadget::utilities::copy, constants::T_Q};
use std::ops::{Deref, Range}; use std::ops::{Deref, Range};
@ -136,12 +136,16 @@ impl Config {
&self, &self,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
alpha: CellValue<pallas::Base>, alpha: CellValue<pallas::Base>,
base: &EccPoint, base: &NonIdentityEccPoint,
) -> Result<(EccPoint, CellValue<pallas::Base>), Error> { ) -> Result<(EccPoint, CellValue<pallas::Base>), Error> {
let (result, zs): (EccPoint, Vec<Z<pallas::Base>>) = layouter.assign_region( let (result, zs): (EccPoint, Vec<Z<pallas::Base>>) = layouter.assign_region(
|| "variable-base scalar mul", || "variable-base scalar mul",
|mut region| { |mut region| {
let offset = 0; 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). // Decompose `k = alpha + t_q` bitwise (big-endian bit order).
let bits = decompose_for_scalar_mul(alpha.value()); let bits = decompose_for_scalar_mul(alpha.value());
@ -151,9 +155,9 @@ impl Config {
let lsb = bits[pallas::Scalar::NUM_BITS as usize - 1]; let lsb = bits[pallas::Scalar::NUM_BITS as usize - 1];
// Initialize the accumulator `acc = [2]base` // Initialize the accumulator `acc = [2]base`
let acc = self let acc =
.add_config self.add_config
.assign_region(base, base, offset, &mut region)?; .assign_region(&base_point, &base_point, offset, &mut region)?;
// Increase the offset by 1 after complete addition. // Increase the offset by 1 after complete addition.
let offset = offset + 1; let offset = offset + 1;
@ -207,7 +211,7 @@ impl Config {
&mut region, &mut region,
offset, offset,
bits_complete, bits_complete,
base, &base_point,
x_a, x_a,
y_a, y_a,
*z, *z,
@ -282,7 +286,7 @@ impl Config {
&self, &self,
region: &mut Region<'_, pallas::Base>, region: &mut Region<'_, pallas::Base>,
offset: usize, offset: usize,
base: &EccPoint, base: &NonIdentityEccPoint,
acc: EccPoint, acc: EccPoint,
z_1: Z<pallas::Base>, z_1: Z<pallas::Base>,
lsb: Option<bool>, lsb: Option<bool>,
@ -449,21 +453,23 @@ pub mod tests {
use pasta_curves::{arithmetic::FieldExt, pallas}; use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::circuit::gadget::{ use crate::circuit::gadget::{
ecc::{chip::EccChip, EccInstructions, Point}, ecc::{
chip::{EccChip, EccPoint},
EccInstructions, NonIdentityPoint, Point,
},
utilities::UtilitiesInstructions, utilities::UtilitiesInstructions,
}; };
pub fn test_mul( pub fn test_mul(
chip: EccChip, chip: EccChip,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
zero: &Point<pallas::Affine, EccChip>, p: &NonIdentityPoint<pallas::Affine, EccChip>,
p: &Point<pallas::Affine, EccChip>,
p_val: pallas::Affine, p_val: pallas::Affine,
) -> Result<(), Error> { ) -> Result<(), Error> {
let column = chip.config().advices[0]; let column = chip.config().advices[0];
fn constrain_equal< fn constrain_equal_non_id<
EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug, EccChip: EccInstructions<pallas::Affine, Point = EccPoint> + Clone + Eq + std::fmt::Debug,
>( >(
chip: EccChip, chip: EccChip,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
@ -474,7 +480,7 @@ pub mod tests {
// Move scalar from base field into scalar field (which always fits // Move scalar from base field into scalar field (which always fits
// for Pallas). // for Pallas).
let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap(); let scalar = pallas::Scalar::from_bytes(&scalar_val.to_bytes()).unwrap();
let expected = Point::new( let expected = NonIdentityPoint::new(
chip, chip,
layouter.namespace(|| "expected point"), layouter.namespace(|| "expected point"),
Some((base_val * scalar).to_affine()), Some((base_val * scalar).to_affine()),
@ -493,7 +499,7 @@ pub mod tests {
)?; )?;
p.mul(layouter.namespace(|| "random [a]B"), &scalar)? p.mul(layouter.namespace(|| "random [a]B"), &scalar)?
}; };
constrain_equal( constrain_equal_non_id(
chip.clone(), chip.clone(),
layouter.namespace(|| "random [a]B"), layouter.namespace(|| "random [a]B"),
p_val, 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 // [0]B should return (0,0) since variable-base scalar multiplication
// uses complete addition for the final bits of the scalar. // 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))?; chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_val))?;
p.mul(layouter.namespace(|| "[0]B"), &scalar)? p.mul(layouter.namespace(|| "[0]B"), &scalar)?
}; };
constrain_equal( assert!(result.inner().is_identity().unwrap());
chip.clone(),
layouter.namespace(|| "[0]B"),
p_val,
scalar_val,
result,
)?;
} }
// [-1]B (the largest possible base field element) // [-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))?; chip.load_private(layouter.namespace(|| "-1"), column, Some(scalar_val))?;
p.mul(layouter.namespace(|| "[-1]B"), &scalar)? p.mul(layouter.namespace(|| "[-1]B"), &scalar)?
}; };
constrain_equal( constrain_equal_non_id(
chip, chip,
layouter.namespace(|| "[-1]B"), layouter.namespace(|| "[-1]B"),
p_val, p_val,

View File

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

View File

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

View File

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

View File

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

View File

@ -127,8 +127,8 @@ impl Config {
let offset = 0; let offset = 0;
// Add to the cumulative sum to get `[magnitude]B`. // Add to the cumulative sum to get `[magnitude]B`.
let magnitude_mul = self.super_config.add_config.assign_region( let magnitude_mul = self.super_config.add_config.assign_region(
&mul_b, &mul_b.into(),
&acc, &acc.into(),
offset, offset,
&mut region, &mut region,
)?; )?;
@ -244,7 +244,7 @@ pub mod tests {
use pasta_curves::{arithmetic::FieldExt, pallas}; use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::circuit::gadget::{ use crate::circuit::gadget::{
ecc::{chip::EccChip, FixedPointShort, Point}, ecc::{chip::EccChip, FixedPointShort, NonIdentityPoint, Point},
utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions}, utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions},
}; };
use crate::constants::load::ValueCommitV; use crate::constants::load::ValueCommitV;
@ -273,14 +273,14 @@ pub mod tests {
Ok((magnitude, sign)) Ok((magnitude, sign))
} }
fn constrain_equal( fn constrain_equal_non_id(
chip: EccChip, chip: EccChip,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
base_val: pallas::Affine, base_val: pallas::Affine,
scalar_val: pallas::Scalar, scalar_val: pallas::Scalar,
result: Point<pallas::Affine, EccChip>, result: Point<pallas::Affine, EccChip>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let expected = Point::new( let expected = NonIdentityPoint::new(
chip, chip,
layouter.namespace(|| "expected point"), layouter.namespace(|| "expected point"),
Some((base_val * scalar_val).to_affine()), Some((base_val * scalar_val).to_affine()),
@ -289,8 +289,6 @@ pub mod tests {
} }
let magnitude_signs = [ let magnitude_signs = [
("mul by +zero", pallas::Base::zero(), pallas::Base::one()),
("mul by -zero", pallas::Base::zero(), -pallas::Base::one()),
( (
"random [a]B", "random [a]B",
pallas::Base::from_u64(rand::random::<u64>()), pallas::Base::from_u64(rand::random::<u64>()),
@ -347,7 +345,7 @@ pub mod tests {
}; };
magnitude * sign magnitude * sign
}; };
constrain_equal( constrain_equal_non_id(
chip.clone(), chip.clone(),
layouter.namespace(|| *name), layouter.namespace(|| *name),
base_val, 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(()) Ok(())
} }
@ -489,7 +505,7 @@ pub mod tests {
Err(vec![ Err(vec![
VerifyFailure::ConstraintNotSatisfied { VerifyFailure::ConstraintNotSatisfied {
constraint: ( constraint: (
(16, "Short fixed-base mul gate").into(), (17, "Short fixed-base mul gate").into(),
0, 0,
"last_window_check" "last_window_check"
) )
@ -521,13 +537,13 @@ pub mod tests {
prover.verify(), prover.verify(),
Err(vec![ Err(vec![
VerifyFailure::ConstraintNotSatisfied { 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(), .into(),
row: 26 row: 26
}, },
VerifyFailure::ConstraintNotSatisfied { VerifyFailure::ConstraintNotSatisfied {
constraint: ( constraint: (
(16, "Short fixed-base mul gate").into(), (17, "Short fixed-base mul gate").into(),
3, 3,
"negation_check" "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 group::prime::PrimeCurveAffine;
use halo2::{ use halo2::{
circuit::Region, circuit::Region,
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector, VirtualCells},
poly::Rotation, poly::Rotation,
}; };
use pasta_curves::{arithmetic::CurveAffine, pallas}; use pasta_curves::{arithmetic::CurveAffine, pallas};
@ -12,6 +12,7 @@ use pasta_curves::{arithmetic::CurveAffine, pallas};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Config { pub struct Config {
q_point: Selector, q_point: Selector,
q_point_non_id: Selector,
// x-coordinate // x-coordinate
pub x: Column<Advice>, pub x: Column<Advice>,
// y-coordinate // y-coordinate
@ -22,6 +23,7 @@ impl From<&EccConfig> for Config {
fn from(ecc_config: &EccConfig) -> Self { fn from(ecc_config: &EccConfig) -> Self {
Self { Self {
q_point: ecc_config.q_point, q_point: ecc_config.q_point,
q_point_non_id: ecc_config.q_point_non_id,
x: ecc_config.advices[0], x: ecc_config.advices[0],
y: ecc_config.advices[1], y: ecc_config.advices[1],
} }
@ -30,8 +32,16 @@ impl From<&EccConfig> for Config {
impl Config { impl Config {
pub(super) fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) { 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| { 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 // - 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 // - 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 x = meta.query_advice(self.x, Rotation::cur());
let y = meta.query_advice(self.y, 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![ vec![
("x == 0 on_curve", q_point.clone() * x * curve_eqn.clone()), ("x == 0 v on_curve", q_point.clone() * x * curve_eqn(meta)),
("y == 0 on_curve", q_point * y * curve_eqn), ("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, &self,
value: Option<pallas::Affine>, value: Option<(pallas::Base, pallas::Base)>,
offset: usize, offset: usize,
region: &mut Region<'_, pallas::Base>, region: &mut Region<'_, pallas::Base>,
) -> Result<EccPoint, Error> { ) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), 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())
}
});
// Assign `x` value // Assign `x` value
let x_val = value.map(|value| value.0); let x_val = value.map(|value| value.0);
let x_var = region.assign_advice( let x_var = region.assign_advice(
@ -88,9 +89,83 @@ impl Config {
|| y_val.ok_or(Error::SynthesisError), || y_val.ok_or(Error::SynthesisError),
)?; )?;
Ok(EccPoint { Ok((
x: CellValue::<pallas::Base>::new(x_var, x_val), CellValue::<pallas::Base>::new(x_var, x_val),
y: CellValue::<pallas::Base>::new(y_var, y_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`]. /// The x-coordinate of a point output of [`Self::hash_to_point`].
type X; type X;
/// A point output of [`Self::hash_to_point`]. /// 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`. /// A type enumerating the fixed points used in `CommitDomains`.
type FixedPoints: Clone + Debug; type FixedPoints: Clone + Debug;
@ -82,10 +82,10 @@ pub trait SinsemillaInstructions<C: CurveAffine, const K: usize, const MAX_WORDS
layouter: impl Layouter<C::Base>, layouter: impl Layouter<C::Base>,
Q: C, Q: C,
message: Self::Message, 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. /// 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. /// A message to be hashed.
@ -238,7 +238,7 @@ pub struct HashDomain<
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq, SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions< EccChip: EccInstructions<
C, 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, FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone > + Clone
+ Debug + Debug
@ -255,7 +255,7 @@ where
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq, SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions< EccChip: EccInstructions<
C, 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, FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone > + Clone
+ Debug + Debug
@ -283,11 +283,11 @@ where
&self, &self,
layouter: impl Layouter<C::Base>, layouter: impl Layouter<C::Base>,
message: Message<C, SinsemillaChip, K, MAX_WORDS>, 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); assert_eq!(self.sinsemilla_chip, message.chip);
self.sinsemilla_chip self.sinsemilla_chip
.hash_to_point(layouter, self.Q, message.inner) .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]. /// $\mathsf{SinsemillaHash}$ from [§ 5.4.1.9][concretesinsemillahash].
@ -334,7 +334,7 @@ pub struct CommitDomain<
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq, SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions< EccChip: EccInstructions<
C, 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, FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone > + Clone
+ Debug + Debug
@ -350,7 +350,7 @@ where
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq, SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
EccChip: EccInstructions< EccChip: EccInstructions<
C, 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, FixedPoints = <SinsemillaChip as SinsemillaInstructions<C, K, MAX_WORDS>>::FixedPoints,
> + Clone > + Clone
+ Debug + Debug
@ -377,11 +377,17 @@ where
mut layouter: impl Layouter<C::Base>, mut layouter: impl Layouter<C::Base>,
message: Message<C, SinsemillaChip, K, MAX_WORDS>, message: Message<C, SinsemillaChip, K, MAX_WORDS>,
r: Option<C::Scalar>, 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); assert_eq!(self.M.sinsemilla_chip, message.chip);
let (blind, _) = self.R.mul(layouter.namespace(|| "[r] R"), r)?; let (blind, _) = self.R.mul(layouter.namespace(|| "[r] R"), r)?;
let (p, zs) = self.M.hash_to_point(layouter.namespace(|| "M"), message)?; 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)) Ok((commitment, zs))
} }
@ -418,7 +424,7 @@ mod tests {
circuit::gadget::{ circuit::gadget::{
ecc::{ ecc::{
chip::{EccChip, EccConfig}, chip::{EccChip, EccConfig},
Point, NonIdentityPoint,
}, },
utilities::lookup_range_check::LookupRangeCheckConfig, utilities::lookup_range_check::LookupRangeCheckConfig,
}, },
@ -572,7 +578,7 @@ mod tests {
None None
}; };
Point::new( NonIdentityPoint::new(
ecc_chip.clone(), ecc_chip.clone(),
layouter.namespace(|| "Witness expected parent"), layouter.namespace(|| "Witness expected parent"),
expected_parent, expected_parent,
@ -623,7 +629,7 @@ mod tests {
None None
}; };
Point::new( NonIdentityPoint::new(
ecc_chip, ecc_chip,
layouter.namespace(|| "Witness expected result"), layouter.namespace(|| "Witness expected result"),
expected_result, expected_result,

View File

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

View File

@ -1,5 +1,5 @@
use super::super::SinsemillaInstructions; 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 crate::primitives::sinsemilla::{self, lebs2ip_k, INV_TWO_POW_K, SINSEMILLA_S};
use halo2::{ use halo2::{
circuit::{Chip, Region}, circuit::{Chip, Region},
@ -26,7 +26,7 @@ impl SinsemillaChip {
{ sinsemilla::K }, { sinsemilla::K },
{ sinsemilla::C }, { sinsemilla::C },
>>::Message, >>::Message,
) -> Result<(EccPoint, Vec<Vec<CellValue<pallas::Base>>>), Error> { ) -> Result<(NonIdentityEccPoint, Vec<Vec<CellValue<pallas::Base>>>), Error> {
let config = self.config().clone(); let config = self.config().clone();
let mut offset = 0; 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)] #[allow(clippy::type_complexity)]

View File

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

View File

@ -8,7 +8,7 @@ use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::{ use crate::{
circuit::gadget::{ circuit::gadget::{
ecc::{ ecc::{
chip::{EccChip, EccPoint}, chip::{EccChip, NonIdentityEccPoint},
Point, Point,
}, },
utilities::{bitrange_subset, bool_check, copy, CellValue, Var}, utilities::{bitrange_subset, bool_check, copy, CellValue, Var},
@ -523,8 +523,8 @@ impl NoteCommitConfig {
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
chip: SinsemillaChip, chip: SinsemillaChip,
ecc_chip: EccChip, ecc_chip: EccChip,
g_d: &EccPoint, g_d: &NonIdentityEccPoint,
pk_d: &EccPoint, pk_d: &NonIdentityEccPoint,
value: CellValue<pallas::Base>, value: CellValue<pallas::Base>,
rho: CellValue<pallas::Base>, rho: CellValue<pallas::Base>,
psi: CellValue<pallas::Base>, psi: CellValue<pallas::Base>,
@ -1432,7 +1432,7 @@ mod tests {
circuit::gadget::{ circuit::gadget::{
ecc::{ ecc::{
chip::{EccChip, EccConfig}, chip::{EccChip, EccConfig},
Point, NonIdentityPoint,
}, },
sinsemilla::chip::SinsemillaChip, sinsemilla::chip::SinsemillaChip,
utilities::{ utilities::{
@ -1566,7 +1566,11 @@ mod tests {
pallas::Affine::from_xy(x, y).unwrap() 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 // Witness pk_d
@ -1580,7 +1584,7 @@ mod tests {
pallas::Affine::from_xy(x, y).unwrap() pallas::Affine::from_xy(x, y).unwrap()
}); });
Point::new( NonIdentityPoint::new(
ecc_chip.clone(), ecc_chip.clone(),
layouter.namespace(|| "witness pk_d"), layouter.namespace(|| "witness pk_d"),
pk_d, pk_d,
@ -1674,7 +1678,11 @@ mod tests {
) )
.unwrap() .unwrap()
.to_affine(); .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) 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>, msg: impl Iterator<Item = bool>,
r: &pallas::Scalar, r: &pallas::Scalar,
) -> CtOption<pallas::Point> { ) -> 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]. /// $\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 { impl Add<pallas::Affine> for IncompletePoint {
type Output = IncompletePoint; type Output = IncompletePoint;