-
Notifications
You must be signed in to change notification settings - Fork 294
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: modexp precompile 384 bits #1513
base: main
Are you sure you want to change the base?
Conversation
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 are some tests commented?
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.
Those tests won't pass as the scope of the issue is to impl modexp for up to 384 bits.
I kept the tests code for reference, let me know how best to handle it.
@@ -88,18 +87,158 @@ pub impl ModExp of Precompile { | |||
let gas = calc_gas(base_len.into(), exp_len.into(), mod_len.into(), exp_highp); | |||
|
|||
// Padding is needed if the input does not contain all 3 values. | |||
let base = input.slice_right_padded(0, base_len); | |||
let exponent = input.slice_right_padded(base_len, exp_len); | |||
|
|||
let (mod_start_idx, _) = base_len.overflowing_add(exp_len); |
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.
note (for us): this should probably not be overflowing_add here. if base_len + exp_len > u32 there's something wrong in the input (which will lead to an OOG prior to that anyway)
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.
great :)
let mut s_limb0: u128 = Into::<_, felt252>::into(s.limb0).try_into().unwrap(); | ||
while s_limb0 != 0 { | ||
let (q, r) = core::traits::DivRem::div_rem(s_limb0, 2); | ||
bits.append(r.into()); | ||
s_limb0 = q; | ||
}; | ||
let mut s_limb1: u128 = Into::<_, felt252>::into(s.limb1).try_into().unwrap(); | ||
if s_limb1 != 0 { | ||
while bits.len() != 96 { | ||
bits.append(0); | ||
} | ||
} | ||
while s_limb1 != 0 { | ||
let (q, r) = core::traits::DivRem::div_rem(s_limb1, 2); | ||
bits.append(r.into()); | ||
s_limb1 = q; | ||
}; | ||
let mut s_limb2: u128 = Into::<_, felt252>::into(s.limb2).try_into().unwrap(); | ||
if s_limb2 != 0 { | ||
while bits.len() != 192 { | ||
bits.append(0); | ||
} | ||
} | ||
while s_limb2 != 0 { | ||
let (q, r) = core::traits::DivRem::div_rem(s_limb2, 2); | ||
bits.append(r.into()); | ||
s_limb2 = q; | ||
}; | ||
let mut s_limb3: u128 = Into::<_, felt252>::into(s.limb3).try_into().unwrap(); | ||
if s_limb3 != 0 { | ||
while bits.len() != 288 { | ||
bits.append(0); | ||
} | ||
} | ||
while s_limb3 != 0 { | ||
let (q, r) = core::traits::DivRem::div_rem(s_limb3, 2); | ||
bits.append(r.into()); | ||
s_limb3 = q; | ||
}; |
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's an generic impl BitsUsedImpl. Perhaps it can be used directly; otherwise, we can implement the missing traits for the limb type (u96)
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 can try again, the main issue is that only TryInto felt252 is impl'd for u96.
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.
Tried again without success, libfuncs are missing for u96
impl U8SpanTryIntoU384 of TryInto<Span<u8>, u384> { | ||
fn try_into(self: Span<u8>) -> Option<u384> { | ||
if self.len() != 48 { | ||
return Option::None; | ||
} | ||
let limb3_128: u128 = self.slice(0, 12).pad_left_with_zeroes(16).from_be_bytes().unwrap(); | ||
let limb2_128: u128 = self.slice(12, 12).pad_left_with_zeroes(16).from_be_bytes().unwrap(); | ||
let limb1_128: u128 = self.slice(24, 12).pad_left_with_zeroes(16).from_be_bytes().unwrap(); | ||
let limb0_128: u128 = self.slice(36, 12).pad_left_with_zeroes(16).from_be_bytes().unwrap(); | ||
let limb0: u96 = Into::<_, felt252>::into(limb0_128).try_into().unwrap(); | ||
let limb1: u96 = Into::<_, felt252>::into(limb1_128).try_into().unwrap(); | ||
let limb2: u96 = Into::<_, felt252>::into(limb2_128).try_into().unwrap(); | ||
let limb3: u96 = Into::<_, felt252>::into(limb3_128).try_into().unwrap(); | ||
Option::Some(u384 { limb0, limb1, limb2, limb3 }) | ||
} | ||
} | ||
|
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.
Result::Ok((gas.into(), return_data)) | ||
// Compute the modular exponentiation | ||
// x^y mod n | ||
pub fn modexp_circuit(x: u384, y: u384, n: u384) -> u384 { |
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.
Please explain the algorithm in high level + add resources to get up to speed in docstring.
For example (and from my understanding of your implementation) this one: Repeated square-and-multiply algorithm for exponentiation
from Handbook of Applied Cryptography - Alfred J. Menezes, Paul C. van Oorschot and Scott A. Vanstone (Algo 2.143)
Feel free to put the resource you used.
Explain the implementation details where necessary for example the exponent is taken in little endian to process bits from LSB to MSB
This algo/implementation is not trivial and other devs (or future auditors) need context.
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.
Let me know if the docstring is correct and sufficiently explicit
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.
Doesn't it miss the check Assert :: (modulus - 1) * (modulus - 1) does not overflow base
in the implementation ?
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.
It's not needed in our case since the circuit already does all the operations with modular arithmetic.
Time spent on this PR:
Pull request type
Please check the type of change your PR introduces:
What is the current behavior?
Resolves #1496
What is the new behavior?
This change is