-
Notifications
You must be signed in to change notification settings - Fork 6
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
feat: end-to-end modexp, ecadd, ecmul and ecpairing #22
base: main
Are you sure you want to change the base?
Conversation
6806fe4
to
31d01c1
Compare
b0d43be
to
88349a3
Compare
ca1c9a0
to
7e89898
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are many places in the code where it can be optimized. I have a question about the tests; they check whether the witnesses are correctly calculated but do not check whether the constraints are satisfied. Please add this check (check_if_satisfied). This function is an important indicator of whether or not the circuits are working
) -> ((NN, NN), Boolean<F>) { | ||
let params = self.x.get_params().clone(); | ||
let is_point_at_infty = NN::is_zero(&mut self.z, cs); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can normalize once
let rhs = rhs.add(cs, &mut gamma); | ||
|
||
let zero = Fq6::zero(cs, params); | ||
let is_zero_sum = sum.is_zero(cs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume you got this code from our old version of franklin-crypto. There are two versions, safe and not. This code was taken from an unsafe version. The point is that we do not check that the sum of g1 and g2 is zero because the constraint guarantees that malicious will not find such g2 = -g1 that won't be the root of gamma. The unsafe version was made on the assumption to reduce constraints ( that means that we are pretty sure that this is not underconstrained)
let params = f.get_params(); | ||
let mut c0 = f.c0.clone(); | ||
let mut c1 = f.c1.clone(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you use two versions here but didn't implement two versions for the other code?
// rhs = g == 0 ? zero : gamma | ||
let zero = Fq6::zero(cs, params); | ||
let gamma = Fq6::gamma(cs, params); | ||
let is_zero_g = self.encoding.is_zero(cs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same is mention here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, it isn't possible that gamma could be zero
pub fn inverse<CS>(&mut self, cs: &mut CS) -> Self | ||
where | ||
CS: ConstraintSystem<F>, | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this version will be cheaper if you compute inv like a witness and prove a*a_inv = 1
Self::from_le_bytes(cs, bytes) | ||
} | ||
|
||
/// Finds the result of multiplying `self` by `other` mod `modulo`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do mul through the ab = qP + rem
@@ -351,6 +358,64 @@ impl<F: SmallField> UInt256<F> { | |||
}); | |||
Self::from_le_bytes(cs, bytes) | |||
} | |||
|
|||
/// Finds the result of multiplying `self` by `other` mod `modulo`. | |||
pub fn modmul<CS: ConstraintSystem<F>>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function has trouble with generating witnesses.
2. You check that the module is zero, which is exceptional; you should do this through the constraint is_zero.
3. You did this wrong first; it creates many inefficiencies because you perform additional range checks on the variables that were already checked.
4. It looks like you didn't wrap properly carry
// let mut carry = (q[0] * p[0] + r[0] - a[0] * b[0]) >> 16;
// carry = (q[1] * p[0] + q[0] * p[1] + r[1] - a[1] * b[0] - a[0] * b[0] + carry) >> 16;
|
||
let rhs = q.widening_mul(cs, &modulo, 8, 8); | ||
let r_u512 = r.to_u512(cs); | ||
let (rhs, overflow) = rhs.overflowing_add(cs, &r_u512); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to check overflow because this m_ge_than_r is doing this
let (rhs, overflow) = rhs.overflowing_add(cs, &r_u512); | ||
Boolean::enforce_equal(cs, &overflow, &bool_false); | ||
|
||
let are_equal = UInt512::equals(cs, &lhs, &rhs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dont do this twice; write enforce_equal for UInt512
|
||
/// Returns `true` if `self >= other`, and `false` otherwise. | ||
/// Here, `self` and `other` are represented as `UInt512<F>` and `UInt256<F>` respectively. | ||
pub fn geq_than_u256<CS>(&self, cs: &mut CS, other: &UInt256<F>) -> Boolean<F> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think here could be optimization for this; we have a special type of gate UIntXAddGate
see u16_long_subtraction_noborrow_must_borrow
…onNativeFieldOverU16
8d65413
to
778b95c
Compare
@@ -394,19 +394,21 @@ impl<F: SmallField> UInt256<F> { | |||
let q = UInt256::allocate(cs, q); | |||
let r = UInt256::allocate(cs, r); | |||
|
|||
let (_, m_greater_than_r) = r.overflowing_sub(cs, &modulo); | |||
let mod_is_zero = Boolean::allocate(cs, m.is_zero()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can't do this. Allocate just m and then check with constraint is_zero this 'm'
What?
Implementation of
modexp
,ecadd
,ecmul
andecpairing
precompiles as circuits.Previous PR
Basically all changes are made to
boojum
crate's gadgets. Previous PR for deprecated repository which has been reviewed:era-boojum
Homogeneity with previous PR
Code has certain changes apart from contained in previous PR such as caused by rebasing and noticing missed problems.