From fa11f3b09dc15d893e7e33b8d01e8594ad2045dc Mon Sep 17 00:00:00 2001 From: Xin Gao Date: Sat, 20 Nov 2021 12:36:26 +0800 Subject: [PATCH] feature: sketch ecc add --- Cargo.toml | 1 + src/circuit/ecc.rs | 309 ++++++++++++++++++++++++++++++-- src/circuit/ecc/add.rs | 136 ++++++++++++++ src/circuit/ecc/external_ecc.rs | 45 ++--- src/circuit/integer.rs | 4 +- src/circuit/main_gate.rs | 55 ++++++ src/rns.rs | 6 + 7 files changed, 517 insertions(+), 39 deletions(-) create mode 100644 src/circuit/ecc/add.rs diff --git a/Cargo.toml b/Cargo.toml index afa7795..f3657fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ num-bigint = { version = "0.4", features = ["rand"] } num-integer = "0.1" num-traits = "0.2" rand = "0.8" +group = "0.11" [dev-dependencies] diff --git a/src/circuit/ecc.rs b/src/circuit/ecc.rs index ae723e5..d154539 100644 --- a/src/circuit/ecc.rs +++ b/src/circuit/ecc.rs @@ -1,40 +1,315 @@ use crate::rns::Integer; +use crate::circuit::main_gate::{MainGateConfig}; -use super::{integer::IntegerConfig, AssignedInteger}; -use halo2::arithmetic::CurveAffine; -use halo2::circuit::Region; +use super::{AssignedCondition, AssignedInteger}; +use super::main_gate::{MainGate, MainGateInstructions}; +use super::integer::{IntegerConfig, IntegerChip, IntegerInstructions}; +use halo2::arithmetic::{FieldExt, CurveAffine}; +use halo2::circuit::{Region, Layouter}; use halo2::plonk::Error; +use std::marker::PhantomData; +use crate::NUMBER_OF_LIMBS; mod base_field_ecc; mod external_ecc; -pub struct Point { - x: Integer, - y: Integer, +// Ecc operation mods +mod add; + +/* Emulate CurveAffine point undert field F */ +#[derive(Default, Clone, Debug)] +pub struct Point { + x: Integer, + y: Integer, + _marker: PhantomData } -impl Point { - fn new(x: Integer, y: Integer) -> Self { - Point { x, y } +impl Point { + fn new(x: Integer, y: Integer) -> Self { + Point { x, y, _marker:PhantomData } } } -pub struct AssignedPoint { - x: AssignedInteger, - y: AssignedInteger, +pub struct AssignedPoint { + x: AssignedInteger, + y: AssignedInteger, + // indicate whether the poinit is the identity point of curve or not + z: AssignedCondition, + _marker: PhantomData +} + +impl AssignedPoint { + pub fn new( + x:AssignedInteger, + y:AssignedInteger, + z:AssignedCondition + ) -> AssignedPoint { + AssignedPoint{ x, y, z, _marker:PhantomData } + } + pub fn is_identity(&self) -> AssignedCondition { + self.z.clone() + } } /// Linear combination term -pub enum Term { - Assigned(AssignedPoint, C::ScalarExt), - Unassigned(Option>, C::ScalarExt), +pub enum Term { + Assigned(AssignedPoint, F), + Unassigned(Option>, F), } #[derive(Clone, Debug)] pub struct EccConfig { integer_chip_config: IntegerConfig, + integer_gate_config: MainGateConfig, } -pub struct EccChip { - config: IntegerConfig, +// we need template arg C to extract curve constants including a and b +pub struct EccChip { + config: EccConfig, + integer_chip: IntegerChip, + // We need to assign following integers based on constants of curve C + a: AssignedInteger, + b: AssignedInteger, +} + +/* Now moved to external_ecc +impl EccInstruction for EccChip { + fn assign_point(&self, region: &mut Region<'_, F>, point: Point, offset: &mut usize) -> Result, Error> { + let x = self.integer_chip.assign_integer(region, Some(point.x.clone()), offset)?.clone(); + let y = self.integer_chip.assign_integer(region, Some(point.y.clone()), offset)?.clone(); + let z = self.integer_gate().assign_bit(region, Some(F::zero()), offset)?.clone(); + Ok(AssignedPoint::new(x,y,z)) + } + + fn assert_is_on_curve(&self, region: &mut Region<'_, F>, point: AssignedPoint, offset: &mut usize) -> Result<(), Error> { + unimplemented!(); + } + + fn assert_equal( + &self, + region: &mut Region<'_, F>, + p0: &AssignedPoint, + p1: &AssignedPoint, + offset: &mut usize, + ) -> Result, Error> { + unimplemented!(); + } + + fn add(&self, region: &mut Region<'_, F>, p0: &AssignedPoint, p1: &AssignedPoint, offset: &mut usize) -> Result, Error> { + self._add(region, p0, p1, offset) + } + + fn double(&self, region: &mut Region<'_, F>, p: AssignedPoint, offset: &mut usize) -> Result, Error> { + unimplemented!(); + } + + fn mul_var(&self, region: &mut Region<'_, F>, p: AssignedPoint, e: F, offset: &mut usize) -> Result, Error> { + unimplemented!(); + } + + fn mul_fix(&self, region: &mut Region<'_, F>, p: C, e: F, offset: &mut usize) -> Result, Error> { + unimplemented!(); + } + + fn multi_exp(&self, region: &mut Region<'_, F>, terms: Vec>, offset: &mut usize) -> Result, Error> { + unimplemented!(); + } + + fn combine(&self, region: &mut Region<'_, F>, terms: Vec>, u: F, offset: &mut usize) -> Result, Error> { + unimplemented!(); + } +} +*/ + +impl EccChip { + fn new( + layouter: &mut impl Layouter, + config: EccConfig, + integer_chip: IntegerChip + ) -> Result { + let ca = Integer::::from_bytes_le( + &C::a().to_bytes(), + NUMBER_OF_LIMBS, + integer_chip.rns.bit_len_limb + ); + let cb = Integer::::from_bytes_le( + &C::b().to_bytes(), + NUMBER_OF_LIMBS, + integer_chip.rns.bit_len_limb + ); + + let (a, b) = { + let mut a: Option> = None; + let mut b: Option> = None; + layouter.assign_region( + || "region 0", + |mut region| { + let offset = &mut 0; + a = Some (integer_chip.assign_integer(&mut region, Some(ca.clone()), offset)?); + b = Some (integer_chip.assign_integer(&mut region, Some(cb.clone()), offset)?); + Ok(()) + }, + )?; + (a.unwrap(), b.unwrap()) + }; + Ok(EccChip {config, integer_chip, a, b}) + } +} + +impl EccChip { + fn integer_gate(&self) -> MainGate { + let main_gate_config = self.config.integer_gate_config.clone(); + MainGate::::new(main_gate_config) + } +} + +#[cfg(test)] +mod tests { + use halo2::arithmetic::{CurveAffine, FieldExt, Field}; + use super::{IntegerChip, IntegerConfig, IntegerInstructions}; + use crate::circuit::AssignedValue; + use crate::circuit::main_gate::{MainGate, MainGateConfig, MainGateInstructions}; + use crate::circuit::range::{RangeChip, RangeInstructions, RangeConfig}; + use crate::circuit::ecc::{Point, EccChip, EccInstruction, EccConfig}; + use crate::rns::{Integer, Limb, Rns}; + use halo2::circuit::{Layouter, SimpleFloorPlanner}; + use halo2::dev::MockProver; + use halo2::plonk::{Circuit, ConstraintSystem, Error}; + use group::{Curve, prime::PrimeCurveAffine}; + use crate::NUMBER_OF_LIMBS; + + #[derive(Clone, Debug)] + struct TestCircuitConfig { + integer_gate_config: MainGateConfig, + integer_chip_config: IntegerConfig, + ecc_chip_config: EccConfig, + range_config: RangeConfig, + } + + impl TestCircuitConfig { + fn overflow_bit_lengths() -> Vec { + vec![2, 3] + } + } + + #[derive(Default, Clone, Debug)] + struct TestEcc { + x: Point, + y: Point, + z: Point, + rns: Rns, + } + + impl Circuit for TestEcc { + type Config = TestCircuitConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let integer_gate_config = MainGate::::configure(meta); + let overflow_bit_lengths = TestCircuitConfig::overflow_bit_lengths(); + let range_config = RangeChip::::configure(meta, &integer_gate_config, overflow_bit_lengths); + let integer_chip_config = IntegerChip::::configure(meta, &range_config, &integer_gate_config); + let ecc_chip_config = EccConfig { + integer_gate_config: integer_gate_config.clone(), + integer_chip_config: integer_chip_config.clone() + }; + TestCircuitConfig { + range_config, + integer_chip_config, + integer_gate_config, + ecc_chip_config, + } + } + + fn synthesize(&self, config: Self::Config, mut layouter: impl Layouter) -> Result<(), Error> { + let integer_chip = IntegerChip::::new(config.integer_chip_config.clone(), self.rns.clone()); + + let ecc_chip = EccChip::::new(&mut layouter, config.ecc_chip_config, integer_chip)?; + let offset = &mut 0; + layouter.assign_region( + || "region 0", + |mut region| { + let px = ecc_chip.assign_point(&mut region, self.x.clone(), offset)?; + let py = ecc_chip.assign_point(&mut region, self.y.clone(), offset)?; + let pz = ecc_chip.assign_point(&mut region, self.z.clone(), offset)?; + let r = ecc_chip.add(&mut region, &px, &py, offset)?; + ecc_chip.assert_equal(&mut region, &r, &pz, offset)?; + Ok(()) + }, + )?; + + + let range_chip = RangeChip::::new(config.range_config, self.rns.bit_len_lookup); + #[cfg(not(feature = "no_lookup"))] + range_chip.load_limb_range_table(&mut layouter)?; + #[cfg(not(feature = "no_lookup"))] + range_chip.load_overflow_range_tables(&mut layouter)?; + + Ok(()) + } + } + + #[test] + fn test_ecc_add_circuit() { + use halo2::pasta::EpAffine as C; + use halo2::pasta::Fq as Native; + let bit_len_limb = 64; + + let rns_base = Rns::<::Base, Native>::construct(bit_len_limb); + let rns_scalar = Rns::<::ScalarExt, Native>::construct(bit_len_limb); + + #[cfg(not(feature = "no_lookup"))] + let k: u32 = (rns_base.bit_len_lookup + 1) as u32; + #[cfg(feature = "no_lookup")] + let k: u32 = 8; + + let sk = ::ScalarExt::rand(); + let generator = :: generator(); + let pk = generator * sk; + + let pbase = // :: generator() + pk + .to_affine() + .coordinates() + .unwrap(); + let x = { + let x = Integer::<::ScalarExt>::from_bytes_le( + &pbase.x().to_bytes(), NUMBER_OF_LIMBS, bit_len_limb); + let y = Integer::<::ScalarExt>::from_bytes_le( + &pbase.y().to_bytes(), NUMBER_OF_LIMBS, bit_len_limb); + Point::new(x, y) + }; + let y = { + let x = Integer::<::ScalarExt>::from_bytes_le( + &pbase.x().to_bytes(), NUMBER_OF_LIMBS, bit_len_limb); + let y = Integer::<::ScalarExt>::from_bytes_le( + &pbase.y().to_bytes(), NUMBER_OF_LIMBS, bit_len_limb); + Point::new(x, y) + }; + let z = { + let x = Integer::<::ScalarExt>::from_bytes_le( + &pbase.x().to_bytes(), NUMBER_OF_LIMBS, bit_len_limb); + let y = Integer::<::ScalarExt>::from_bytes_le( + &pbase.y().to_bytes(), NUMBER_OF_LIMBS, bit_len_limb); + Point::new(x, y) + }; + + let circuit = TestEcc:: { + x: x, + y: y, + z: z, + rns: rns_base.clone(), + }; + + let prover = match MockProver::run(k, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{:#?}", e), + }; + + assert_eq!(prover.verify(), Ok(())); + } } diff --git a/src/circuit/ecc/add.rs b/src/circuit/ecc/add.rs new file mode 100644 index 0000000..bcf3072 --- /dev/null +++ b/src/circuit/ecc/add.rs @@ -0,0 +1,136 @@ +use super::EccChip; +use super::{AssignedPoint}; +use super::super::integer::{IntegerConfig, IntegerChip, IntegerInstructions}; +use crate::circuit::main_gate::{MainGateConfig, MainGateInstructions}; +use crate::circuit::{AssignedInteger, AssignedLimb, AssignedCondition}; +use halo2::arithmetic::{CurveAffine, FieldExt}; +use halo2::circuit::Region; +use halo2::plonk::Error; + + +impl EccChip { + fn curvature( + &self, + region: &mut Region<'_, F>, + a: &AssignedPoint, + offset: &mut usize + ) -> Result , Error> { + // (3 * a.x^2 + self.a) / 2 * a.y + let cst3 = { + let rns3 = self.integer_chip.rns.new_from_big(3u32.into()); + &self.integer_chip.assign_integer(region, Some(rns3), offset)? + }; + let cst2 = { + let rns2 = self.integer_chip.rns.new_from_big(2u32.into()); + &self.integer_chip.assign_integer(region, Some(rns2), offset)? + }; + + let xsqm = { + let xsq = self.integer_chip.mul(region, &a.x, &a.x, offset)?; + self.integer_chip.mul(region, &xsq, cst3, offset)? + }; + let curvature = { + let numerator = self.integer_chip.add(region, &xsqm, &self.a, offset)?; + let denominator = self.integer_chip.mul(region, &a.y, cst2, offset)?; + let (lambda, _) = self.integer_chip.div(region, + &numerator, + &denominator, + offset + )?; + lambda + }; + Ok(curvature) + } + + fn lambda( + &self, + region: &mut Region<'_, F>, + a: &AssignedPoint, + b: &AssignedPoint, + offset: &mut usize + ) -> Result<(AssignedInteger, AssignedCondition), Error> { + let integer_gate = self.integer_gate(); + + let numerator = self.integer_chip.sub(region, &a.y, &b.y, offset)?; + let (_, eqy_cond) = self.integer_chip.invert(region, &numerator, offset)?; + + let (lambda_neq, eqx_cond) = { + let denominator = self.integer_chip.sub(region, &a.x, &b.x, offset)?; + self.integer_chip.div(region, &numerator, &denominator, offset)? + }; + + // When eqx_cond == 1, we calculated the tangent curvature + let lambda_eq = self.curvature(region, a, offset)?; + let lambda = self.integer_chip.cond_select(region, &lambda_neq, &lambda_eq, &eqx_cond, offset)?; + + // eqx_cond == 1 and eqy_cond == 0 means tangent is infinity + let not_eqy_cond = integer_gate.cond_not(region, &eqy_cond, offset)?; + let zero_cond = integer_gate.cond_and(region, + ¬_eqy_cond, + &eqx_cond, + offset, + )?; + Ok((lambda, zero_cond)) + } + + /* We use affine coordinates since invert cost almost the same as mul in + * halo circuts gates while projective coordinates involves more multiplication + * than affine coordinates. + * Thus coordinate z in point is used as an indicator of whether the point is + * identity(infinity) or not. + */ + pub(crate) fn _add( + &self, + region: &mut Region<'_, F>, + a: &AssignedPoint, + b: &AssignedPoint, + offset: &mut usize + ) -> Result, Error> { + let integer_gate = self.integer_gate(); + + let (lambda, zero_cond) = self.lambda(region, a, b, offset)?; + let lambda_square = self.integer_chip._square(region, &lambda, offset)?; + + // cx = λ^2 - a.x - b.x + let sqsub = self.integer_chip._sub(region, &lambda_square, &a.x, offset)?; + let cx = self.integer_chip._sub( + region, + &sqsub, + &b.x, + offset + )?; + + // cy = λ(a.x - c.x) - a.y + let xsub = self.integer_chip._sub(region, &a.x, &cx, offset)?; + let yi = self.integer_chip._mul( + region, + &lambda, + &xsub, + offset, + )?; + let cy = self.integer_chip._sub(region, &yi, &a.y, offset)?; + let cx_sel = self.integer_chip.cond_select(region, + &b.x, + &cx, + &a.is_identity(), + offset + )?; + let p = AssignedPoint::new(cx, cy, zero_cond); + + /* Now combine the calculation using the following cond table + * a.is_identity() -> b + * b.is_identity() -> a + * zero_cond -> self.identity() + * otherwise -> p + */ + + let id_sel = integer_gate.cond_or( + region, + &a.is_identity(), + &b.is_identity(), + offset + )?; + + Ok(p) + } +} diff --git a/src/circuit/ecc/external_ecc.rs b/src/circuit/ecc/external_ecc.rs index 0f81a77..da8695e 100644 --- a/src/circuit/ecc/external_ecc.rs +++ b/src/circuit/ecc/external_ecc.rs @@ -1,60 +1,63 @@ use crate::circuit::AssignedInteger; -use halo2::arithmetic::CurveAffine; +use halo2::arithmetic::{FieldExt, CurveAffine}; use halo2::circuit::Region; use halo2::plonk::Error; use super::{AssignedPoint, EccChip, Point}; -pub trait ExternalEccInstruction { - fn assign_point(&self, region: &mut Region<'_, Native::ScalarExt>, point: Point, offset: &mut usize) -> Result, Error>; +pub trait ExternalEccInstruction { + fn assign_point(&self, region: &mut Region<'_, Native>, point: Point, offset: &mut usize) -> Result, Error>; - fn assert_is_on_curve(&self, region: &mut Region<'_, Native::ScalarExt>, point: AssignedPoint, offset: &mut usize) -> Result<(), Error>; + fn assert_is_on_curve(&self, region: &mut Region<'_, Native>, point: AssignedPoint, offset: &mut usize) -> Result<(), Error>; fn assert_equal( &self, - region: &mut Region<'_, Native::ScalarExt>, - p0: AssignedPoint, - p1: AssignedPoint, + region: &mut Region<'_, Native>, + p0: AssignedPoint, + p1: AssignedPoint, offset: &mut usize, ) -> Result<(), Error>; fn add( &self, - region: &mut Region<'_, Native::ScalarExt>, + region: &mut Region<'_, Native>, p0: AssignedPoint, p1: AssignedPoint, offset: &mut usize, ) -> Result, Error>; - fn double(&self, region: &mut Region<'_, Native::ScalarExt>, p: AssignedPoint, offset: &mut usize) -> Result, Error>; + fn double(&self, region: &mut Region<'_, Native>, p: AssignedPoint, offset: &mut usize) -> Result, Error>; fn mul_var( &self, - region: &mut Region<'_, Native::ScalarExt>, + region: &mut Region<'_, Native>, p: AssignedPoint, e: AssignedInteger, offset: &mut usize, ) -> Result, Error>; fn mul_fix( &self, - region: &mut Region<'_, Native::ScalarExt>, + region: &mut Region<'_, Native>, p: Point, e: AssignedInteger, offset: &mut usize, ) -> Result, Error>; } -impl ExternalEccInstruction for EccChip { - fn assign_point(&self, region: &mut Region<'_, Native::ScalarExt>, point: Point, offset: &mut usize) -> Result, Error> { - unimplemented!(); +impl ExternalEccInstruction for EccChip { + fn assign_point(&self, region: &mut Region<'_, Native>, point: Point, offset: &mut usize) -> Result, Error> { + let x = self.integer_chip.assign_integer(region, Some(point.x.clone()), offset)?.clone(); + let y = self.integer_chip.assign_integer(region, Some(point.y.clone()), offset)?.clone(); + let z = self.integer_gate().assign_bit(region, Some(F::zero()), offset)?.clone(); + Ok(AssignedPoint::new(x,y,z)) } - fn assert_is_on_curve(&self, region: &mut Region<'_, Native::ScalarExt>, point: AssignedPoint, offset: &mut usize) -> Result<(), Error> { + fn assert_is_on_curve(&self, region: &mut Region<'_, Native>, point: AssignedPoint, offset: &mut usize) -> Result<(), Error> { unimplemented!(); } fn assert_equal( &self, - region: &mut Region<'_, Native::ScalarExt>, + region: &mut Region<'_, Native>, p0: AssignedPoint, p1: AssignedPoint, offset: &mut usize, @@ -64,7 +67,7 @@ impl ExternalEccInstruction, + region: &mut Region<'_, Native>, p0: AssignedPoint, p1: AssignedPoint, offset: &mut usize, @@ -72,13 +75,13 @@ impl ExternalEccInstruction, p: AssignedPoint, offset: &mut usize) -> Result, Error> { + fn double(&self, region: &mut Region<'_, Native>, p: AssignedPoint, offset: &mut usize) -> Result, Error> { unimplemented!(); } fn mul_var( &self, - region: &mut Region<'_, Native::ScalarExt>, + region: &mut Region<'_, Native>, p: AssignedPoint, e: AssignedInteger, offset: &mut usize, @@ -88,7 +91,7 @@ impl ExternalEccInstruction, + region: &mut Region<'_, Native>, p: Point, e: AssignedInteger, offset: &mut usize, @@ -96,3 +99,5 @@ impl ExternalEccInstruction { config: IntegerConfig, - rns: Rns, + pub rns: Rns, } -trait IntegerInstructions { +pub trait IntegerInstructions { fn assign_integer(&self, region: &mut Region<'_, N>, integer: Option>, offset: &mut usize) -> Result, Error>; fn range_assign_integer( &self, diff --git a/src/circuit/main_gate.rs b/src/circuit/main_gate.rs index 4ed728a..d7fa234 100644 --- a/src/circuit/main_gate.rs +++ b/src/circuit/main_gate.rs @@ -111,6 +111,29 @@ pub trait MainGateInstructions { fn one_or_one(&self, region: &mut Region<'_, F>, a: impl Assigned, b: impl Assigned, offset: &mut usize) -> Result<(), Error>; + fn cond_or( + &self, + region: &mut Region<'_, F>, + c1: &AssignedCondition, + c2: &AssignedCondition, + offset: &mut usize, + ) -> Result, Error>; + + fn cond_and( + &self, + region: &mut Region<'_, F>, + c1: &AssignedCondition, + c2: &AssignedCondition, + offset: &mut usize, + ) -> Result, Error>; + + fn cond_not( + &self, + region: &mut Region<'_, F>, + c: &AssignedCondition, + offset: &mut usize, + ) -> Result, Error>; + fn cond_select( &self, region: &mut Region<'_, F>, @@ -522,6 +545,38 @@ impl MainGateInstructions for MainGate { Ok(is_zero) } + fn cond_or( + &self, + region: &mut Region<'_, F>, + c1: &AssignedCondition, + c2: &AssignedCondition, + offset: &mut usize, + ) -> Result, Error> { + unimplemented!(); + } + + fn cond_and( + &self, + region: &mut Region<'_, F>, + c1: &AssignedCondition, + c2: &AssignedCondition, + offset: &mut usize, + ) -> Result, Error> { + unimplemented!(); + } + + fn cond_not( + &self, + region: &mut Region<'_, F>, + c: &AssignedCondition, + offset: &mut usize, + ) -> Result, Error> { + unimplemented!(); + } + + + + fn cond_select( &self, region: &mut Region<'_, F>, diff --git a/src/rns.rs b/src/rns.rs index f060f46..65d2530 100644 --- a/src/rns.rs +++ b/src/rns.rs @@ -490,6 +490,12 @@ impl Integer { Self { limbs } } + pub fn from_bytes_le(e: &[u8], number_of_limbs: usize, bit_len: usize) -> Self { + let x = num_bigint::BigUint::from_bytes_le(e); + Self::from_big(x, number_of_limbs, bit_len) + } + + pub fn limbs(&self) -> Vec { self.limbs.iter().map(|limb| limb.fe()).collect() }