Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add variable-base sign-scalar multiplication (upstream) #7

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion halo2_gadgets/src/ecc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::fmt::Debug;

use halo2_proofs::{
arithmetic::CurveAffine,
circuit::{Chip, Layouter, Value},
circuit::{AssignedCell, Chip, Layouter, Value},
plonk::Error,
};

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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(
Expand Down
18 changes: 18 additions & 0 deletions halo2_gadgets/src/ecc/chip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
279 changes: 277 additions & 2 deletions halo2_gadgets/src/ecc/chip/mul_fixed/short.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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()),
],
}
])
);
}
}