mirror of https://github.com/zcash/halo2.git
Add variable-base sign-scalar multiplication
This commit is contained in:
parent
7fd2ce259e
commit
35c815d36e
|
@ -4,7 +4,7 @@ use std::fmt::Debug;
|
||||||
|
|
||||||
use halo2_proofs::{
|
use halo2_proofs::{
|
||||||
arithmetic::CurveAffine,
|
arithmetic::CurveAffine,
|
||||||
circuit::{Chip, Layouter, Value},
|
circuit::{AssignedCell, Chip, Layouter, Value},
|
||||||
plonk::Error,
|
plonk::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,6 +111,15 @@ pub trait EccInstructions<C: CurveAffine>:
|
||||||
b: &B,
|
b: &B,
|
||||||
) -> Result<Self::Point, Error>;
|
) -> Result<Self::Point, Error>;
|
||||||
|
|
||||||
|
/// Performs variable-base sign-scalar multiplication, returning `[sign] point`
|
||||||
|
/// `sign` must be in {-1, 1}.
|
||||||
|
fn mul_sign(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<C::Base>,
|
||||||
|
sign: &AssignedCell<C::Base, C::Base>,
|
||||||
|
point: &Self::Point,
|
||||||
|
) -> Result<Self::Point, Error>;
|
||||||
|
|
||||||
/// Performs variable-base scalar multiplication, returning `[scalar] base`.
|
/// Performs variable-base scalar multiplication, returning `[scalar] base`.
|
||||||
fn mul(
|
fn mul(
|
||||||
&self,
|
&self,
|
||||||
|
@ -432,6 +441,21 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
|
||||||
inner,
|
inner,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `[sign] self`.
|
||||||
|
/// `sign` must be in {-1, 1}.
|
||||||
|
pub fn mul_sign(
|
||||||
|
&self,
|
||||||
|
mut layouter: impl Layouter<C::Base>,
|
||||||
|
sign: &AssignedCell<C::Base, C::Base>,
|
||||||
|
) -> Result<Point<C, EccChip>, Error> {
|
||||||
|
self.chip
|
||||||
|
.mul_sign(&mut layouter, sign, &self.inner)
|
||||||
|
.map(|point| Point {
|
||||||
|
chip: self.chip.clone(),
|
||||||
|
inner: point,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The affine short Weierstrass x-coordinate of a point on a specific elliptic curve.
|
/// The affine short Weierstrass x-coordinate of a point on a specific elliptic curve.
|
||||||
|
@ -865,6 +889,14 @@ pub(crate) mod tests {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test variable-base sign-scalar multiplication
|
||||||
|
{
|
||||||
|
super::chip::mul_fixed::short::tests::test_mul_sign(
|
||||||
|
chip.clone(),
|
||||||
|
layouter.namespace(|| "variable-base sign-scalar mul"),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Test full-width fixed-base scalar multiplication
|
// Test full-width fixed-base scalar multiplication
|
||||||
{
|
{
|
||||||
super::chip::mul_fixed::full_width::tests::test_mul_fixed(
|
super::chip::mul_fixed::full_width::tests::test_mul_fixed(
|
||||||
|
|
|
@ -532,6 +532,24 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Performs variable-base sign-scalar multiplication, returning `[sign] point`
|
||||||
|
/// `sign` must be in {-1, 1}.
|
||||||
|
fn mul_sign(
|
||||||
|
&self,
|
||||||
|
layouter: &mut impl Layouter<pallas::Base>,
|
||||||
|
sign: &AssignedCell<pallas::Base, pallas::Base>,
|
||||||
|
point: &Self::Point,
|
||||||
|
) -> Result<Self::Point, Error> {
|
||||||
|
// Multiply point by sign, using the same gate as mul_fixed::short.
|
||||||
|
// This also constrains sign to be in {-1, 1}.
|
||||||
|
let config_short = self.config().mul_fixed_short.clone();
|
||||||
|
config_short.assign_scalar_sign(
|
||||||
|
layouter.namespace(|| "variable-base sign-scalar mul"),
|
||||||
|
sign,
|
||||||
|
point,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn mul(
|
fn mul(
|
||||||
&self,
|
&self,
|
||||||
layouter: &mut impl Layouter<pallas::Base>,
|
layouter: &mut impl Layouter<pallas::Base>,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use super::super::{EccPoint, EccScalarFixedShort, FixedPoints, L_SCALAR_SHORT, N
|
||||||
use crate::{ecc::chip::MagnitudeSign, utilities::bool_check};
|
use crate::{ecc::chip::MagnitudeSign, utilities::bool_check};
|
||||||
|
|
||||||
use halo2_proofs::{
|
use halo2_proofs::{
|
||||||
circuit::{Layouter, Region},
|
circuit::{AssignedCell, Layouter, Region},
|
||||||
plonk::{ConstraintSystem, Constraints, Error, Expression, Selector},
|
plonk::{ConstraintSystem, Constraints, Error, Expression, Selector},
|
||||||
poly::Rotation,
|
poly::Rotation,
|
||||||
};
|
};
|
||||||
|
@ -241,11 +241,73 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||||
|
|
||||||
Ok((result, scalar))
|
Ok((result, scalar))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Multiply the point by sign, using the q_mul_fixed_short gate.
|
||||||
|
/// Constraints `sign` in {-1, 1}
|
||||||
|
pub fn assign_scalar_sign(
|
||||||
|
&self,
|
||||||
|
mut layouter: impl Layouter<pallas::Base>,
|
||||||
|
sign: &AssignedCell<pallas::Base, pallas::Base>,
|
||||||
|
point: &EccPoint,
|
||||||
|
) -> Result<EccPoint, Error> {
|
||||||
|
let signed_point = layouter.assign_region(
|
||||||
|
|| "Signed point",
|
||||||
|
|mut region| {
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
// Enable mul_fixed_short selector to check the sign logic.
|
||||||
|
self.q_mul_fixed_short.enable(&mut region, offset)?;
|
||||||
|
|
||||||
|
// Set "last window" to 0 (this field is irrelevant here).
|
||||||
|
region.assign_advice_from_constant(
|
||||||
|
|| "u=0",
|
||||||
|
self.super_config.u,
|
||||||
|
offset,
|
||||||
|
pallas::Base::zero(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Copy sign to `window` column
|
||||||
|
sign.copy_advice(|| "sign", &mut region, self.super_config.window, offset)?;
|
||||||
|
|
||||||
|
// Assign the input y-coordinate.
|
||||||
|
point.y.copy_advice(
|
||||||
|
|| "unsigned y",
|
||||||
|
&mut region,
|
||||||
|
self.super_config.add_config.y_qr,
|
||||||
|
offset,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Conditionally negate y-coordinate according to the value of sign
|
||||||
|
let signed_y_val = sign.value().and_then(|sign| {
|
||||||
|
if sign == &-pallas::Base::one() {
|
||||||
|
-point.y.value()
|
||||||
|
} else {
|
||||||
|
point.y.value().cloned()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assign the output signed y-coordinate.
|
||||||
|
let signed_y = region.assign_advice(
|
||||||
|
|| "signed y",
|
||||||
|
self.super_config.add_config.y_p,
|
||||||
|
offset,
|
||||||
|
|| signed_y_val,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(EccPoint {
|
||||||
|
x: point.x.clone(),
|
||||||
|
y: signed_y,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(signed_point)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use group::{ff::PrimeField, Curve};
|
use group::{ff::PrimeField, Curve, Group};
|
||||||
use halo2_proofs::{
|
use halo2_proofs::{
|
||||||
arithmetic::CurveAffine,
|
arithmetic::CurveAffine,
|
||||||
circuit::{AssignedCell, Chip, Layouter, Value},
|
circuit::{AssignedCell, Chip, Layouter, Value},
|
||||||
|
@ -657,4 +719,217 @@ pub mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn test_mul_sign(
|
||||||
|
chip: EccChip<TestFixedBases>,
|
||||||
|
mut layouter: impl Layouter<pallas::Base>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// Generate a random non-identity point P
|
||||||
|
let p_val = pallas::Point::random(rand::rngs::OsRng).to_affine();
|
||||||
|
let p = Point::new(
|
||||||
|
chip.clone(),
|
||||||
|
layouter.namespace(|| "P"),
|
||||||
|
Value::known(p_val),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Create -P
|
||||||
|
let p_neg_val = -p_val;
|
||||||
|
let p_neg = Point::new(
|
||||||
|
chip.clone(),
|
||||||
|
layouter.namespace(|| "-P"),
|
||||||
|
Value::known(p_neg_val),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Create the identity point
|
||||||
|
let identity = Point::new(
|
||||||
|
chip.clone(),
|
||||||
|
layouter.namespace(|| "identity"),
|
||||||
|
Value::known(pallas::Point::identity().to_affine()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Create -1 and 1 scalars
|
||||||
|
let pos_sign = chip.load_private(
|
||||||
|
layouter.namespace(|| "positive sign"),
|
||||||
|
chip.config().advices[0],
|
||||||
|
Value::known(pallas::Base::one()),
|
||||||
|
)?;
|
||||||
|
let neg_sign = chip.load_private(
|
||||||
|
layouter.namespace(|| "negative sign"),
|
||||||
|
chip.config().advices[1],
|
||||||
|
Value::known(-pallas::Base::one()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// [1] P == P
|
||||||
|
{
|
||||||
|
let result = p.mul_sign(layouter.namespace(|| "[1] P"), &pos_sign)?;
|
||||||
|
result.constrain_equal(layouter.namespace(|| "constrain [1] P"), &p)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [-1] P == -P
|
||||||
|
{
|
||||||
|
let result = p.mul_sign(layouter.namespace(|| "[1] P"), &neg_sign)?;
|
||||||
|
result.constrain_equal(layouter.namespace(|| "constrain [1] P"), &p_neg)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [1] 0 == 0
|
||||||
|
{
|
||||||
|
let result = identity.mul_sign(layouter.namespace(|| "[1] O"), &pos_sign)?;
|
||||||
|
result.constrain_equal(layouter.namespace(|| "constrain [1] 0"), &identity)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [-1] 0 == 0
|
||||||
|
{
|
||||||
|
let result = identity.mul_sign(layouter.namespace(|| "[-1] O"), &neg_sign)?;
|
||||||
|
result.constrain_equal(layouter.namespace(|| "constrain [1] 0"), &identity)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_sign_in_mul_sign() {
|
||||||
|
use crate::{ecc::chip::EccConfig, utilities::UtilitiesInstructions};
|
||||||
|
use halo2_proofs::{
|
||||||
|
circuit::{Layouter, SimpleFloorPlanner},
|
||||||
|
dev::{FailureLocation, MockProver, VerifyFailure},
|
||||||
|
plonk::{Circuit, ConstraintSystem, Error},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct MyCircuit {
|
||||||
|
base: Value<pallas::Affine>,
|
||||||
|
sign: Value<pallas::Base>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UtilitiesInstructions<pallas::Base> for MyCircuit {
|
||||||
|
type Var = AssignedCell<pallas::Base, pallas::Base>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Circuit<pallas::Base> for MyCircuit {
|
||||||
|
type Config = EccConfig<TestFixedBases>;
|
||||||
|
type FloorPlanner = SimpleFloorPlanner;
|
||||||
|
|
||||||
|
fn without_witnesses(&self) -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
|
||||||
|
let advices = [
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
meta.advice_column(),
|
||||||
|
];
|
||||||
|
let lookup_table = meta.lookup_table_column();
|
||||||
|
let lagrange_coeffs = [
|
||||||
|
meta.fixed_column(),
|
||||||
|
meta.fixed_column(),
|
||||||
|
meta.fixed_column(),
|
||||||
|
meta.fixed_column(),
|
||||||
|
meta.fixed_column(),
|
||||||
|
meta.fixed_column(),
|
||||||
|
meta.fixed_column(),
|
||||||
|
meta.fixed_column(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Shared fixed column for loading constants
|
||||||
|
let constants = meta.fixed_column();
|
||||||
|
meta.enable_constant(constants);
|
||||||
|
|
||||||
|
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], lookup_table);
|
||||||
|
EccChip::<TestFixedBases>::configure(meta, advices, lagrange_coeffs, range_check)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn synthesize(
|
||||||
|
&self,
|
||||||
|
config: Self::Config,
|
||||||
|
mut layouter: impl Layouter<pallas::Base>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let chip = EccChip::construct(config.clone());
|
||||||
|
|
||||||
|
let column = config.advices[0];
|
||||||
|
|
||||||
|
//let short_config = config.mul_fixed_short.clone();
|
||||||
|
let base = Point::new(chip, layouter.namespace(|| "load base"), self.base)?;
|
||||||
|
|
||||||
|
let sign =
|
||||||
|
self.load_private(layouter.namespace(|| "load sign"), column, self.sign)?;
|
||||||
|
|
||||||
|
base.mul_sign(layouter.namespace(|| "[sign] base"), &sign)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied from halo2_proofs::dev::util
|
||||||
|
fn format_value(v: pallas::Base) -> String {
|
||||||
|
use ff::Field;
|
||||||
|
if v.is_zero_vartime() {
|
||||||
|
"0".into()
|
||||||
|
} else if v == pallas::Base::one() {
|
||||||
|
"1".into()
|
||||||
|
} else if v == -pallas::Base::one() {
|
||||||
|
"-1".into()
|
||||||
|
} else {
|
||||||
|
// Format value as hex.
|
||||||
|
let s = format!("{:?}", v);
|
||||||
|
// Remove leading zeroes.
|
||||||
|
let s = s.strip_prefix("0x").unwrap();
|
||||||
|
let s = s.trim_start_matches('0');
|
||||||
|
format!("0x{}", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign that is not +/- 1 should fail
|
||||||
|
// Generate a random non-identity point
|
||||||
|
let point = pallas::Point::random(rand::rngs::OsRng);
|
||||||
|
let circuit = MyCircuit {
|
||||||
|
base: Value::known(point.to_affine()),
|
||||||
|
sign: Value::known(pallas::Base::zero()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let prover = MockProver::<pallas::Base>::run(11, &circuit, vec![]).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
prover.verify(),
|
||||||
|
Err(vec![
|
||||||
|
VerifyFailure::ConstraintNotSatisfied {
|
||||||
|
constraint: ((17, "Short fixed-base mul gate").into(), 1, "sign_check").into(),
|
||||||
|
location: FailureLocation::InRegion {
|
||||||
|
region: (2, "Signed point").into(),
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
cell_values: vec![(((Any::Advice, 4).into(), 0).into(), "0".to_string())],
|
||||||
|
},
|
||||||
|
VerifyFailure::ConstraintNotSatisfied {
|
||||||
|
constraint: (
|
||||||
|
(17, "Short fixed-base mul gate").into(),
|
||||||
|
3,
|
||||||
|
"negation_check"
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
location: FailureLocation::InRegion {
|
||||||
|
region: (2, "Signed point").into(),
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
cell_values: vec![
|
||||||
|
(
|
||||||
|
((Any::Advice, 1).into(), 0).into(),
|
||||||
|
format_value(*point.to_affine().coordinates().unwrap().y()),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
((Any::Advice, 3).into(), 0).into(),
|
||||||
|
format_value(*point.to_affine().coordinates().unwrap().y()),
|
||||||
|
),
|
||||||
|
(((Any::Advice, 4).into(), 0).into(), "0".to_string()),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue