diff --git a/ecc/src/base_field_ecc.rs b/ecc/src/base_field_ecc.rs index c38e8855..123249c1 100644 --- a/ecc/src/base_field_ecc.rs +++ b/ecc/src/base_field_ecc.rs @@ -263,7 +263,7 @@ impl let integer_chip = self.integer_chip(); let x = integer_chip.reduce(ctx, point.x())?; let y = integer_chip.reduce(ctx, point.y())?; - Ok(AssignedPoint::new(x, y)) + Ok(AssignedPoint::new(x.0, y.0)) } /// Adds 2 distinct `AssignedPoints` diff --git a/ecc/src/general_ecc.rs b/ecc/src/general_ecc.rs index 89fd5ee6..6099d875 100644 --- a/ecc/src/general_ecc.rs +++ b/ecc/src/general_ecc.rs @@ -7,11 +7,13 @@ use halo2::arithmetic::CurveAffine; use halo2::circuit::{Layouter, Value}; use halo2::halo2curves::ff::PrimeField; use halo2::plonk::Error; -use integer::maingate::RegionCtx; +use integer::maingate::{RegionCtx, MainGateInstructions}; use maingate::{AssignedCondition, MainGate}; use std::collections::BTreeMap; use std::rc::Rc; +use num_bigint::BigUint as big_uint; + mod add; mod mul; @@ -97,6 +99,15 @@ impl< e.map(|e| Integer::from_fe(e, self.rns_scalar())).into() } + /// Assign integer for chip + pub fn new_unassigned_big( + &self, + e: big_uint, + ) -> UnassignedInteger { + let big = Integer::from_big(e, self.rns_scalar()); + Value::known(big).into() + } + /// Return `IntegerChip` for the base field of the EC pub fn base_field_chip( &self, @@ -171,7 +182,6 @@ impl< point: AssignedPoint, offset: usize, ) -> Result<(), Error> { - use integer::maingate::MainGateInstructions; let main_gate = self.main_gate(); let mut offset = offset; @@ -209,19 +219,34 @@ impl< ctx: &mut RegionCtx<'_, N>, point: Value, ) -> Result, Error> { - let integer_chip = self.base_field_chip(); + let maingate = self.main_gate(); let point = point.map(|point| self.to_rns_point(point)); let (x, y) = point .map(|point| (point.x().clone(), point.y().clone())) .unzip(); - let x = integer_chip.assign_integer(ctx, x.into(), Range::Remainder)?; - let y = integer_chip.assign_integer(ctx, y.into(), Range::Remainder)?; + let (point, is_on_curve) = self.assign_x_y(ctx, x.into(), y.into())?; + maingate.assert_not_zero(ctx, &is_on_curve)?; + Ok(point) + } + + /// Takes `Point.x` and `Point.y` of the EC and returns it as `AssignedPoint` + pub fn assign_x_y( + &self, + ctx: &mut RegionCtx<'_, N>, + x: UnassignedInteger<::Base, N, NUMBER_OF_LIMBS, BIT_LEN_LIMB>, + y: UnassignedInteger<::Base, N, NUMBER_OF_LIMBS, BIT_LEN_LIMB>, + ) -> Result<(AssignedPoint, AssignedCondition), Error> { + let integer_chip = self.base_field_chip(); + let (x, is_x_valid) = integer_chip.try_assign_integer(ctx, x.into(), Range::Remainder)?; + let (y, is_y_valid) = integer_chip.try_assign_integer(ctx, y.into(), Range::Remainder)?; + let is_valid = integer_chip.and(ctx, &is_x_valid, &is_y_valid)?; let point = AssignedPoint::new(x, y); - self.assert_is_on_curve(ctx, &point)?; - Ok(point) + let is_on_curve = self.is_on_curve(ctx, &point)?; + let is_valid = integer_chip.and(ctx, &is_valid, &is_on_curve)?; + Ok((point, is_valid)) } /// Assigns the auxiliary generator point @@ -272,6 +297,21 @@ impl< Ok(()) } + /// Constraints to check if `AssignedPoint` is on curve + pub fn is_on_curve( + &self, + ctx: &mut RegionCtx<'_, N>, + point: &AssignedPoint, + ) -> Result, Error> { + let integer_chip = self.base_field_chip(); + + let y_square = &integer_chip.square(ctx, point.y())?; + let x_square = &integer_chip.square(ctx, point.x())?; + let x_cube = &integer_chip.mul(ctx, point.x(), x_square)?; + let x_cube_b = &integer_chip.add_constant(ctx, x_cube, &self.parameter_b())?; + integer_chip.is_strict_equal(ctx, x_cube_b, y_square) + } + /// Constraints assert two `AssignedPoint`s are equal pub fn assert_equal( &self, @@ -323,7 +363,7 @@ impl< let integer_chip = self.base_field_chip(); let x = integer_chip.reduce(ctx, point.x())?; let y = integer_chip.reduce(ctx, point.y())?; - Ok(AssignedPoint::new(x, y)) + Ok(AssignedPoint::new(x.0, y.0)) } /// Adds 2 distinct `AssignedPoints` diff --git a/ecdsa/src/ecdsa.rs b/ecdsa/src/ecdsa.rs index eac14d62..9e29a634 100644 --- a/ecdsa/src/ecdsa.rs +++ b/ecdsa/src/ecdsa.rs @@ -9,7 +9,7 @@ use halo2::halo2curves::ff::PrimeField; use halo2::{circuit::Value, plonk::Error}; use integer::rns::Integer; use integer::{AssignedInteger, IntegerInstructions}; -use maingate::{MainGateConfig, RangeConfig}; +use maingate::{AssignedCondition, MainGateConfig, RangeConfig}; #[derive(Clone, Debug)] pub struct EcdsaConfig { @@ -98,17 +98,19 @@ impl, pk: &AssignedPublicKey, msg_hash: &AssignedInteger, - ) -> Result<(), Error> { + enable_skipping_invalid_signature: bool, + ) -> Result, Error> { let ecc_chip = self.ecc_chip(); let scalar_chip = ecc_chip.scalar_field_chip(); let base_chip = ecc_chip.base_field_chip(); - // 1. check 0 < r, s < n + // 1. check 0 < r, s < n, if r == 0 or s == 0 the signature is marked as invalid // since `assert_not_zero` already includes a in-field check, we can just // call `assert_not_zero` - scalar_chip.assert_not_zero(ctx, &sig.r)?; - scalar_chip.assert_not_zero(ctx, &sig.s)?; + let is_r_valid = scalar_chip.is_not_zero(ctx, &sig.r)?; + let is_s_valid = scalar_chip.is_not_zero(ctx, &sig.s)?; + let is_r_s_valid = scalar_chip.and(ctx, &is_r_valid, &is_s_valid)?; // 2. w = s^(-1) (mod n) let (s_inv, _) = scalar_chip.invert(ctx, &sig.s)?; @@ -127,13 +129,21 @@ impl, } @@ -262,27 +278,49 @@ mod tests { let offset = 0; let ctx = &mut RegionCtx::new(region, offset); + let is_valid = scalar_chip.assign_constant(ctx, (true as u64).into())?; + let is_valid = scalar_chip.is_not_zero(ctx, &is_valid)?; + let r = self.signature.map(|signature| signature.0); let s = self.signature.map(|signature| signature.1); - let integer_r = ecc_chip.new_unassigned_scalar(r); + + let integer_r = if self.valid_input { + ecc_chip.new_unassigned_scalar(r.clone()) + } else { + let max_reminder = scalar_chip.rns().max_remainder.clone(); + ecc_chip.new_unassigned_big(max_reminder) + }; let integer_s = ecc_chip.new_unassigned_scalar(s); let msg_hash = ecc_chip.new_unassigned_scalar(self.msg_hash); - let r_assigned = - scalar_chip.assign_integer(ctx, integer_r, Range::Remainder)?; - let s_assigned = - scalar_chip.assign_integer(ctx, integer_s, Range::Remainder)?; + let (r_assigned, is_assigned_integer_succeeded ) = + scalar_chip.try_assign_integer(ctx, integer_r, Range::Remainder)?; + let is_valid = scalar_chip.and(ctx, &is_valid, &is_assigned_integer_succeeded)?; + let (s_assigned, is_assigned_integer_succeeded) = + scalar_chip.try_assign_integer(ctx, integer_s, Range::Remainder)?; + let is_valid = scalar_chip.and(ctx, &is_valid, &is_assigned_integer_succeeded)?; let sig = AssignedEcdsaSig { r: r_assigned, s: s_assigned, }; - let pk_in_circuit = ecc_chip.assign_point(ctx, self.public_key)?; + let point = self.public_key.map(|point| ecc_chip.to_rns_point(point)); + let (x, y) = point + .map(|point| (point.x().clone(), point.y().clone())) + .unzip(); + let (pk_in_circuit, is_pk_on_curve) = ecc_chip.assign_x_y(ctx, x.into(), y.into())?; + let is_valid = scalar_chip.and(ctx, &is_valid, &is_pk_on_curve)?; + + let enable_skipping_invalid_signature = scalar_chip.assign_constant(ctx, (self.enable_skipping_invalid_signature as u64).into())?; + let enable_skipping_invalid_signature = scalar_chip.is_not_zero(ctx, &enable_skipping_invalid_signature)?; + scalar_chip.one_or_one(ctx, &enable_skipping_invalid_signature, &is_valid)?; + let pk_assigned = AssignedPublicKey { point: pk_in_circuit, }; let msg_hash = scalar_chip.assign_integer(ctx, msg_hash, Range::Remainder)?; - ecdsa_chip.verify(ctx, &sig, &pk_assigned, &msg_hash) + let response = ecdsa_chip.verify(ctx, &sig, &pk_assigned, &msg_hash, self.enable_skipping_invalid_signature); + return response; }, )?; @@ -299,7 +337,7 @@ mod tests { big_to_fe(x_big) } - fn run + Ord>() { + fn generate_valid_inputs + Ord>() -> (C, C::Scalar, C::Scalar, C::Scalar) { let g = C::generator(); // Generate a key pair @@ -335,25 +373,60 @@ mod tests { let r_candidate = mod_n::(*x_candidate); assert_eq!(r, r_candidate); } + (public_key, r, s, msg_hash) + } + + fn generate_invalid_inputs + Ord>() -> (C, C::Scalar, C::Scalar, C::Scalar) { + let (public_key, r, _, msg_hash) = generate_valid_inputs::(); + (public_key, r, r, msg_hash) + } + + fn run + Ord>(valid_input: bool, enable_skipping_invalid_signature: bool) { + let (public_key, r, s, msg_hash) = if valid_input { + generate_valid_inputs::() + } else { + generate_invalid_inputs::() + }; let aux_generator = C::CurveExt::random(OsRng).to_affine(); let circuit = TestCircuitEcdsaVerify:: { public_key: Value::known(public_key), - signature: Value::known((r, s)), + signature: Value::known((r.clone(), s.clone())), msg_hash: Value::known(msg_hash), aux_generator, window_size: 4, + valid_input, + enable_skipping_invalid_signature, ..Default::default() }; let instance = vec![vec![]]; - mock_prover_verify(&circuit, instance); + let result = mock_prover_verify(&circuit, instance); + if valid_input || enable_skipping_invalid_signature { + assert_eq!(result, Ok(())); + } else { + assert!(result.is_err()); + } } use crate::curves::bn256::Fr as BnScalar; use crate::curves::pasta::{Fp as PastaFp, Fq as PastaFq}; use crate::curves::secp256k1::Secp256k1Affine as Secp256k1; - run::(); - run::(); - run::(); + + // Return Errors + run::(false, false); + run::(false, false); + run::(false, false); + + run::(false, true); + run::(false, true); + run::(false, true); + + run::(true, false); + run::(true, false); + run::(true, false); + + run::(true, true); + run::(true, true); + run::(true, true); } } diff --git a/halo2wrong/src/utils.rs b/halo2wrong/src/utils.rs index 41a660dc..111ec942 100644 --- a/halo2wrong/src/utils.rs +++ b/halo2wrong/src/utils.rs @@ -2,7 +2,7 @@ use crate::{ curves::ff::{FromUniformBytes, PrimeField}, halo2::{ circuit::Value, - dev::MockProver, + dev::{MockProver, VerifyFailure}, plonk::{ Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, ConstraintSystem, Error, Fixed, FloorPlanner, Instance, Selector, @@ -67,14 +67,11 @@ pub fn compose(input: Vec, bit_len: usize) -> big_uint { pub fn mock_prover_verify + Ord, C: Circuit>( circuit: &C, instance: Vec>, -) { +) -> Result<(), Vec> { let dimension = DimensionMeasurement::measure(circuit).unwrap(); let prover = MockProver::run(dimension.k(), circuit, instance) .unwrap_or_else(|err| panic!("{:#?}", err)); - assert_eq!( - prover.verify_at_rows_par(dimension.advice_range(), dimension.advice_range()), - Ok(()) - ) + prover.verify_at_rows_par(dimension.advice_range(), dimension.advice_range()) } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/integer/src/chip.rs b/integer/src/chip.rs index dd583d43..0753638b 100644 --- a/integer/src/chip.rs +++ b/integer/src/chip.rs @@ -4,7 +4,7 @@ use super::{AssignedInteger, AssignedLimb, UnassignedInteger}; use crate::instructions::{IntegerInstructions, Range}; use crate::rns::{Common, Integer, Rns}; use halo2::halo2curves::ff::PrimeField; -use halo2::plonk::Error; +use halo2::{circuit::Value, plonk::Error}; use maingate::{halo2, AssignedCondition, AssignedValue, MainGateInstructions, RegionCtx}; use maingate::{MainGate, MainGateConfig}; use maingate::{RangeChip, RangeConfig}; @@ -85,7 +85,7 @@ impl, // TODO: external integer might have different parameter settings a: &AssignedInteger, - ) -> Result, Error> { + ) -> Result<(AssignedInteger, AssignedCondition), Error> { let to_be_reduced = self.new_assigned_integer(a.limbs(), a.native().clone()); self.reduce(ctx, &to_be_reduced) } @@ -96,7 +96,20 @@ impl, range: Range, ) -> Result, Error> { - self.assign_integer_generic(ctx, integer, range) + let main_gate = self.main_gate(); + let (result, succeeded) = self.assign_integer_generic(ctx, integer, range)?; + main_gate.assert_not_zero(ctx, &succeeded)?; + Ok(result) + } + + fn try_assign_integer( + &self, + ctx: &mut RegionCtx<'_, N>, + integer: UnassignedInteger, + range: Range, + ) -> Result<(AssignedInteger, AssignedCondition), Error> { + let (result, succeeded) = self.assign_integer_generic(ctx, integer, range)?; + Ok((result, succeeded)) } fn assign_constant( @@ -348,7 +361,7 @@ impl, a: &AssignedInteger, - ) -> Result, Error> { + ) -> Result<(AssignedInteger, AssignedCondition), Error> { self.reduce_generic(ctx, a) } @@ -376,6 +389,21 @@ impl, + a: &AssignedInteger, + b: &AssignedInteger, + ) -> Result, Error> { + let main_gate = self.main_gate(); + let mut result = main_gate.assign_value(ctx, Value::known(N::ONE))?; + for idx in 0..NUMBER_OF_LIMBS { + let term_1 = main_gate.is_equal(ctx, a.limb(idx), b.limb(idx))?; + result = main_gate.and(ctx, &result, &term_1)?; + } + Ok(result) + } + fn assert_not_equal( &self, ctx: &mut RegionCtx<'_, N>, @@ -398,6 +426,50 @@ impl, + a: &AssignedInteger, + ) -> Result, Error> { + let main_gate = self.main_gate(); + let (a, is_reduce_succeeded) = &self.try_reduce(ctx, a)?; + let zero = self.assign_constant(ctx, W::ZERO)?; + let is_zero = self.is_strict_equal(ctx, &zero, &a)?; + let result = main_gate.not(ctx, &is_zero)?; + main_gate.and(ctx, &result, is_reduce_succeeded) + } + + fn is_not_zero_without_reduce( + &self, + ctx: &mut RegionCtx<'_, N>, + a: &AssignedInteger, + ) -> Result, Error> { + let main_gate = self.main_gate(); + let zero = self.assign_constant(ctx, W::ZERO)?; + let is_zero = self.is_strict_equal(ctx, &zero, &a)?; + main_gate.not(ctx, &is_zero) + } + + fn one_or_one( + &self, + ctx: &mut RegionCtx<'_, N>, + a: &AssignedCondition, + b: &AssignedCondition, + ) -> Result<(), Error> { + let main_gate = self.main_gate(); + main_gate.one_or_one(ctx, a, b) + } + + fn and( + &self, + ctx: &mut RegionCtx<'_, N>, + a: &AssignedCondition, + b: &AssignedCondition, + ) -> Result, Error> { + let main_gate = self.main_gate(); + main_gate.and(ctx, a, b) + } + fn assert_zero( &self, ctx: &mut RegionCtx<'_, N>, @@ -813,9 +885,9 @@ mod tests { Range::Remainder, )?; let reduced_1 = &integer_chip.reduce(ctx, overflows)?; - assert_eq!(reduced_1.max_val(), self.rns.max_remainder); - integer_chip.assert_equal(ctx, reduced_0, reduced_1)?; - integer_chip.assert_strict_equal(ctx, reduced_0, reduced_1)?; + assert_eq!(reduced_1.0.max_val(), self.rns.max_remainder); + integer_chip.assert_equal(ctx, reduced_0, &reduced_1.0)?; + integer_chip.assert_strict_equal(ctx, reduced_0, &reduced_1.0)?; Ok(()) }, )?; @@ -1157,8 +1229,8 @@ mod tests { c_in_field.into(), Range::Remainder, )?; - integer_chip.assert_equal(ctx, &c_0, &c_1)?; - integer_chip.assert_strict_equal(ctx, &c_0, &c_1)?; + integer_chip.assert_equal(ctx, &c_0.0, &c_1)?; + integer_chip.assert_strict_equal(ctx, &c_0.0, &c_1)?; } { @@ -1185,8 +1257,8 @@ mod tests { c_in_field.into(), Range::Remainder, )?; - integer_chip.assert_equal(ctx, &c_0, &c_1)?; - integer_chip.assert_strict_equal(ctx, &c_0, &c_1)?; + integer_chip.assert_equal(ctx, &c_0.0, &c_1)?; + integer_chip.assert_strict_equal(ctx, &c_0.0, &c_1)?; } { @@ -1204,8 +1276,8 @@ mod tests { integer_chip.assign_integer(ctx, c.into(), Range::Remainder)?; let c_0 = integer_chip.reduce(ctx, &a)?; integer_chip.assert_equal(ctx, &a, &c_1)?; - integer_chip.assert_equal(ctx, &c_0, &c_1)?; - integer_chip.assert_strict_equal(ctx, &c_0, &c_1)?; + integer_chip.assert_equal(ctx, &c_0.0, &c_1)?; + integer_chip.assert_strict_equal(ctx, &c_0.0, &c_1)?; } } diff --git a/integer/src/chip/assert_in_field.rs b/integer/src/chip/assert_in_field.rs index f3feded1..8e13d4cd 100644 --- a/integer/src/chip/assert_in_field.rs +++ b/integer/src/chip/assert_in_field.rs @@ -30,6 +30,7 @@ impl r % 2 ^ 64 = 0 /\ r % native_modulus = 0 // r <> 0 <-> r % 2 ^ 64 <> 0 \/ r % native_modulus <> 0 // r <> 0 <-> invert(r.limb(0)) \/ invert(r.native()) - let cond_zero_0 = main_gate.is_zero(ctx, r.limb(0))?; - let cond_zero_1 = main_gate.is_zero(ctx, r.native())?; + let cond_zero_0 = main_gate.is_zero(ctx, r.0.limb(0))?; + let cond_zero_1 = main_gate.is_zero(ctx, r.0.native())?; // one of them might be succeeded, i.e. cond_zero_0 * cond_zero_1 = 0 main_gate.nand(ctx, &cond_zero_0, &cond_zero_1)?; @@ -46,12 +46,12 @@ impl, integer: UnassignedInteger, range: Range, - ) -> Result, Error> { + ) -> Result<(AssignedInteger, AssignedCondition), Error> { let range_chip = self.range_chip(); let main_gate = self.main_gate(); + let mut is_valid = main_gate.assign_value(ctx, Value::known(N::ONE))?; + let bit_len_limb_msb = match range { Range::Operand => self.rns.max_most_significant_operand_limb.bits(), Range::Remainder => self.rns.max_most_significant_reduced_limb.bits(), @@ -85,9 +88,13 @@ impl IntegerChip @@ -34,12 +34,69 @@ impl, + a: &AssignedInteger, + ) -> Result<(AssignedInteger, AssignedCondition), Error> { + let main_gate = self.main_gate(); + + let (a, is_reduce_if_limb_values_succeeded) = self.try_reduce_if_limb_values_exceeds_reduced(ctx, a)?; + let (a, is_reduce_if_max_operand_value_succeeded) = self.try_reduce_if_max_operand_value_exceeds(ctx, &a)?; + let is_reduce_succeeded = main_gate.and(ctx, &is_reduce_if_limb_values_succeeded, &is_reduce_if_max_operand_value_succeeded)?; + + Ok((a, is_reduce_succeeded)) + } + + /// Try to reduces an [`AssignedInteger`] if any of its limbs values is greater + /// than the [`Rns`] `max_unreduced_limb`. + /// + /// Panics if the value of the integer is greater than [`Rns`] + /// `max_reducible_value`. + pub(super) fn try_reduce_if_limb_values_exceeds_reduced( + &self, + ctx: &mut RegionCtx<'_, N>, + a: &AssignedInteger, + ) -> Result<(AssignedInteger, AssignedCondition), Error> { + let exceeds_max_limb_value = a + .limbs + .iter() + .any(|limb| limb.max_val() > self.rns.max_reduced_limb); + + // Soft sanity check for completeness + // Reduction quotient is limited upto a dense single limb. It is quite possible + // to make it more than a single limb. However even single limb will + // support quite amount of lazy additions and make reduction process + // much easier. + let max_reduction_quotient = self.rns.max_reduced_limb.clone(); + let max_reducible_value = + max_reduction_quotient * &self.rns.wrong_modulus + &self.rns.max_remainder; + let is_valid = self.assign_constant(ctx, ((a.max_val() < max_reducible_value) as u64).into())?; + let is_valid = self.is_not_zero_without_reduce(ctx, &is_valid)?; + + if exceeds_max_limb_value { + let (result, is_reduce_succeeded) = self.reduce(ctx, a)?; + let is_valid = self.and(ctx, &is_valid, &is_reduce_succeeded)?; + Ok((result, is_valid)) + } else { + let zero = self.assign_constant(ctx, W::ZERO)?; + let zero = self.is_strict_equal(ctx, &zero.clone(), &zero)?; + Ok((self.new_assigned_integer(a.limbs(), a.native().clone()), zero)) + } + } + /// Reduces an [`AssignedInteger`] if any of its limbs values is greater /// than the [`Rns`] `max_reduced_limb` pub(super) fn reduce_if_limb_values_exceeds_reduced( @@ -52,7 +109,8 @@ impl self.rns.max_reduced_limb); if exceeds_max_limb_value { - self.reduce(ctx, a) + let result = self.reduce(ctx, a)?; + Ok(result.0) } else { Ok(self.new_assigned_integer(a.limbs(), a.native().clone())) } @@ -67,17 +125,49 @@ impl Result, Error> { let exceeds_max_value = a.max_val() > self.rns.max_operand; if exceeds_max_value { - self.reduce(ctx, a) + let result = self.reduce(ctx, a)?; + Ok(result.0) } else { Ok(self.new_assigned_integer(a.limbs(), a.native().clone())) } } + /// Try to reduces an [`AssignedInteger`] if any of its max value is greater + /// than the [`Rns`] `max_operand`. + pub(super) fn try_reduce_if_max_operand_value_exceeds( + &self, + ctx: &mut RegionCtx<'_, N>, + a: &AssignedInteger, + ) -> Result<(AssignedInteger, AssignedCondition), Error> { + let exceeds_max_value = a.max_val() > self.rns.max_operand; + + // Soft sanity check for completeness + // Reduction quotient is limited upto a dense single limb. It is quite possible + // to make it more than a single limb. However even single limb will + // support quite amount of lazy additions and make reduction process + // much easier. + let max_reduction_quotient = self.rns.max_reduced_limb.clone(); + let max_reducible_value = + max_reduction_quotient * &self.rns.wrong_modulus + &self.rns.max_remainder; + let is_valid = self.assign_constant(ctx, ((a.max_val() < max_reducible_value) as u64).into())?; + let is_valid = self.is_not_zero_without_reduce(ctx, &is_valid)?; + + if exceeds_max_value { + let (result, is_reduce_succeeded) = self.reduce(ctx, a)?; + let is_valid = self.and(ctx, &is_valid, &is_reduce_succeeded)?; + Ok((result, is_valid)) + } else { + let zero = self.assign_constant(ctx, W::ZERO)?; + let zero = self.is_strict_equal(ctx, &zero.clone(), &zero)?; + Ok((self.new_assigned_integer(a.limbs(), a.native().clone()), zero)) + } + } + pub(super) fn reduce_generic( &self, ctx: &mut RegionCtx<'_, N>, a: &AssignedInteger, - ) -> Result, Error> { + ) -> Result<(AssignedInteger, AssignedCondition), Error> { let main_gate = self.main_gate(); let (zero, one) = (N::ZERO, N::ONE); @@ -86,9 +176,13 @@ impl AssignedValue conversion"), - &result, + &result.0, residues, )?; @@ -128,7 +222,7 @@ impl Result, Error>; + /// Try to assigns an [`Integer`] to a cell in the circuit without range check for the + /// appropriate [`Range`]. + fn try_assign_integer( + &self, + ctx: &mut RegionCtx<'_, N>, + integer: UnassignedInteger, + range: Range, + ) -> Result<(AssignedInteger, AssignedCondition), Error>; + /// Assigns an [`Integer`] constant to a cell in the circuit returning an /// [`AssignedInteger`]. fn assign_constant( @@ -197,7 +206,7 @@ pub trait IntegerInstructions< &self, ctx: &mut RegionCtx<'_, N>, a: &AssignedInteger, - ) -> Result, Error>; + ) -> Result<(AssignedInteger, AssignedCondition), Error>; /// Constraints that two [`AssignedInteger`] are equal. fn assert_equal( @@ -215,6 +224,14 @@ pub trait IntegerInstructions< b: &AssignedInteger, ) -> Result<(), Error>; + /// Constraints that limbs of two [`AssignedInteger`] are equal. + fn is_strict_equal( + &self, + ctx: &mut RegionCtx<'_, N>, + a: &AssignedInteger, + b: &AssignedInteger, + ) -> Result, Error>; + /// Constraints that two [`AssignedInteger`] are not equal. fn assert_not_equal( &self, @@ -230,6 +247,37 @@ pub trait IntegerInstructions< a: &AssignedInteger, ) -> Result<(), Error>; + /// Check constraints that an [`AssignedInteger`] is not equal to zero + fn is_not_zero( + &self, + ctx: &mut RegionCtx<'_, N>, + a: &AssignedInteger, + ) -> Result, Error>; + + /// Check constraints that an [`AssignedInteger`] is not equal to zero + fn is_not_zero_without_reduce( + &self, + ctx: &mut RegionCtx<'_, N>, + a: &AssignedInteger, + ) -> Result, Error>; + + /// Enforces one of given two values is `1` + /// `(a-1) * (b-1) = 0` + fn one_or_one( + &self, + ctx: &mut RegionCtx<'_, N>, + a: &AssignedCondition, + b: &AssignedCondition, + ) -> Result<(), Error>; + + /// Constraints for AND + fn and( + &self, + ctx: &mut RegionCtx<'_, N>, + a: &AssignedCondition, + b: &AssignedCondition, + ) -> Result, Error>; + /// Constraints that an [`AssignedInteger`] is equal to zero fn assert_zero( &self, @@ -293,7 +341,7 @@ pub trait IntegerInstructions< &self, ctx: &mut RegionCtx<'_, N>, a: &AssignedInteger, - ) -> Result, Error>; + ) -> Result<(AssignedInteger, AssignedCondition), Error>; /// Applies % 2 to the given input fn sign(