-
Notifications
You must be signed in to change notification settings - Fork 11
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: add from field method #87
base: main
Are you sure you want to change the base?
Changes from 8 commits
bc2ad16
0939d85
b259a3d
27c6139
7cb70cf
4fd4b0c
fad1d8d
29c753d
976360e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,12 @@ | ||
use crate::params::BigNumParams as P; | ||
|
||
use crate::fns::{ | ||
expressions::evaluate_quadratic_expression, | ||
unconstrained_helpers::{ | ||
__add_with_flags, __neg_with_flags, __sub_with_flags, __validate_gt_remainder, | ||
__add_with_flags, __from_field, __neg_with_flags, __sub_with_flags, __validate_gt_remainder, | ||
__validate_in_field_compute_borrow_flags, | ||
}, | ||
unconstrained_ops::{__div, __mul, __udiv_mod}, | ||
}; | ||
use crate::params::BigNumParams as P; | ||
|
||
/** | ||
* In this file: | ||
|
@@ -36,6 +35,40 @@ use crate::fns::{ | |
* We use a hash function that can be modelled as a random oracle | ||
* This function *should* produce an output that is a uniformly randomly distributed value modulo BigNum::modulus() | ||
**/ | ||
pub(crate) fn from_field<let N: u32, let MOD_BITS: u32>( | ||
params: P<N, MOD_BITS>, | ||
field: Field, | ||
) -> [Field; N] { | ||
// safty: we check that the resulting limbs represent the intended field element | ||
// we check the bit length, the limbs being max 120 bits, and the value in total is less than the field modulus | ||
let result = unsafe { __from_field::<N>(field) }; | ||
// validate the limbs are in range and the value in total is less than 2^254 | ||
let shift = 0x1000000000000000000000000000000; | ||
// validate that the last limb is less than the modulus | ||
if N > 2 { | ||
// validate that the result is less than the modulus | ||
let mut grumpkin_modulus = [0; N]; | ||
grumpkin_modulus[0] = 0x33e84879b9709143e1f593f0000001; | ||
grumpkin_modulus[1] = 0x4e72e131a029b85045b68181585d28; | ||
grumpkin_modulus[2] = 0x3064; | ||
validate_gt::<N, 254>(grumpkin_modulus, result); | ||
// validate that the limbs are in range | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we have to check the At first, I wanted to use the |
||
validate_in_range::<N, 254>(result); | ||
} | ||
// validate the limbs sum up to the field value | ||
let field_val = if N < 2 { | ||
result[0] | ||
} else if N == 2 { | ||
validate_in_range::<N, 254>(result); | ||
result[0] + result[1] * shift | ||
} else { | ||
validate_in_range::<N, 254>(result); | ||
result[0] + result[1] * shift + result[2] * shift * shift | ||
}; | ||
assert(field_val == field); | ||
result | ||
} | ||
|
||
pub(crate) fn derive_from_seed<let N: u32, let MOD_BITS: u32, let SeedBytes: u32>( | ||
params: P<N, MOD_BITS>, | ||
seed: [u8; SeedBytes], | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -790,3 +790,29 @@ fn test_expressions() { | |
assert(wx_constrained.limbs == wx.limbs); | ||
} | ||
|
||
#[test] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess missing a test case where Field is large enough so it will produce 2 limbs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added tests for 2 and 3 digits. |
||
fn test_from_field_1_digit() { | ||
let field: Field = 1; | ||
let result = Fq::from_field(field); | ||
assert(result == Fq::one()); | ||
} | ||
|
||
#[test] | ||
fn test_from_field_2_digits() { | ||
let field: Field = 762576765071760201410184025311678064293966151975347778787092903729041075; | ||
let result = Fq::from_field(field); | ||
let expected: Fq = | ||
BigNum { limbs: [0xe88ed97f8f707abd3fa65763c80eb3, 0x6e7d8b5586595aa1fb2ee04d5cb4f5, 0x0] }; | ||
assert(result == expected); | ||
} | ||
|
||
#[test] | ||
fn test_from_field_3_digits() { | ||
let field: Field = -1; | ||
let result = Fq::from_field(field); | ||
let expected: Fq = BigNum { | ||
limbs: [0x33e84879b9709143e1f593f0000000, 0x4e72e131a029b85045b68181585d28, 0x3064], | ||
}; | ||
assert(result == expected); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
use crate::utils::msb::get_msb64; | ||
use crate::utils::split_bits; | ||
use crate::utils::split_bits::field_to_u60rep; | ||
|
||
/** | ||
* @brief U60Repr represents a BigNum element as a sequence of 60-bit unsigned integers. | ||
|
@@ -57,6 +58,29 @@ impl<let N: u32, let NumSegments: u32> std::convert::From<[Field; N]> for U60Rep | |
} | ||
} | ||
|
||
// impl<let N: u32, let NumSegments: u32> std::convert::From<Field> for U60Repr<N, NumSegments> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this would be my preferred way of doing the casting for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @TomAFrench, I'm just pinging you to see if you have an idea if we can fix this. |
||
// fn from(input: Field) -> Self { | ||
// let (low, mid, high) = unsafe { field_to_u60rep(input) } ; | ||
// let mut result: Self = U60Repr { limbs: [0; N * NumSegments] }; | ||
// let N_u60: u32 = N * NumSegments; | ||
// assert(N_u60 >=1, "N must be at least 1"); | ||
// if N_u60 == 1 { | ||
// assert((mid ==0) & (high == 0), "input field is too large to fit in a single limb"); | ||
// result.limbs[0] = low; | ||
// } | ||
// else if N_u60 == 2{ | ||
// assert(high == 0, "input field is too large to fit in two limbs"); | ||
// result.limbs[0] = low; | ||
// result.limbs[1] = mid; | ||
// }else{ | ||
// result.limbs[0] = low; | ||
// result.limbs[1] = mid; | ||
// result.limbs[2] = high; | ||
// } | ||
// result | ||
// } | ||
// } | ||
|
||
impl<let N: u32, let NumSegments: u32> std::convert::Into<[Field; N]> for U60Repr<N, NumSegments> { | ||
fn into(x: U60Repr<N, NumSegments>) -> [Field; N] { | ||
let mut result: [Field; N] = [0; N]; | ||
|
@@ -94,6 +118,45 @@ impl<let N: u32, let NumSegments: u32> U60Repr<N, NumSegments> { | |
result | ||
} | ||
|
||
pub(crate) fn from_field(input: Field) -> Self { | ||
let (first, second, third, fourth, fifth) = unsafe { field_to_u60rep(input) }; | ||
let mut result: Self = U60Repr { limbs: [0; N * NumSegments] }; | ||
let N_u60: u32 = N * NumSegments; | ||
assert(N_u60 >= 1, "N must be at least 1"); | ||
if N_u60 == 1 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. very ugly, very ugly. but I'm not sure if there's a workaround. |
||
assert( | ||
(second == 0) & (third == 0) & (fourth == 0) & (fifth == 0), | ||
"input field is too large to fit in a single limb", | ||
); | ||
result.limbs[0] = first; | ||
} else if N_u60 == 2 { | ||
assert( | ||
(third == 0) & (fourth == 0) & (fifth == 0), | ||
"input field is too large to fit in two limbs", | ||
); | ||
result.limbs[0] = first; | ||
result.limbs[1] = second; | ||
} else if N_u60 == 3 { | ||
assert((fourth == 0) & (fifth == 0), "input field is too large to fit in three limbs"); | ||
result.limbs[0] = first; | ||
result.limbs[1] = second; | ||
result.limbs[2] = third; | ||
} else if N_u60 == 4 { | ||
assert((fifth == 0), "input field is too large to fit in four limbs"); | ||
result.limbs[0] = first; | ||
result.limbs[1] = second; | ||
result.limbs[2] = third; | ||
result.limbs[3] = fourth; | ||
} else { | ||
result.limbs[0] = first; | ||
result.limbs[1] = second; | ||
result.limbs[2] = third; | ||
result.limbs[3] = fourth; | ||
result.limbs[4] = fifth; | ||
} | ||
result | ||
} | ||
|
||
pub(crate) unconstrained fn into_field_array( | ||
x: U60Repr<N, NumSegments>, | ||
) -> [Field; N * NumSegments / 2] { | ||
|
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.
ideally I would like to use this method, but there's an issue with a similar impl in
U60Repr