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::{
|
||||
arithmetic::CurveAffine,
|
||||
circuit::{Chip, Layouter, Value},
|
||||
circuit::{AssignedCell, Chip, Layouter, Value},
|
||||
plonk::Error,
|
||||
};
|
||||
|
||||
|
@ -111,6 +111,15 @@ pub trait EccInstructions<C: CurveAffine>:
|
|||
b: &B,
|
||||
) -> 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`.
|
||||
fn mul(
|
||||
&self,
|
||||
|
@ -432,6 +441,21 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
|
|||
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.
|
||||
|
@ -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
|
||||
{
|
||||
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(
|
||||
&self,
|
||||
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 halo2_proofs::{
|
||||
circuit::{Layouter, Region},
|
||||
circuit::{AssignedCell, Layouter, Region},
|
||||
plonk::{ConstraintSystem, Constraints, Error, Expression, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
|
@ -241,11 +241,73 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
|||
|
||||
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)]
|
||||
pub mod tests {
|
||||
use group::{ff::PrimeField, Curve};
|
||||
use group::{ff::PrimeField, Curve, Group};
|
||||
use halo2_proofs::{
|
||||
arithmetic::CurveAffine,
|
||||
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