Skip to content
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: added to_field method #99

Merged
merged 18 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ jobs:
uses: noir-lang/[email protected]
with:
toolchain: ${{env.MINIMUM_NOIR_VERSION}}

- name: Run formatter
run: nargo fmt --check

Expand Down
17 changes: 15 additions & 2 deletions src/bignum.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use crate::params::BigNumParamsGetter;

use crate::fns::{
constrained_ops::{
add, assert_is_not_equal, conditional_select, derive_from_seed, div, eq, from_field, mul,
neg, sub, udiv, udiv_mod, umod, validate_in_field, validate_in_range,
add, assert_is_not_equal, conditional_select, derive_from_seed, div, eq, from_field,
limbs_to_field, mul, neg, sub, udiv, udiv_mod, umod, validate_in_field, validate_in_range,
},
expressions::{__compute_quadratic_expression, evaluate_quadratic_expression},
serialization::{from_be_bytes, to_le_bytes},
Expand Down Expand Up @@ -374,3 +374,16 @@ where
}
}

/**
* @brief to_field converts a BigNum to a Field, conditioned on the bignum fitting in a field element
* we have opted to not add this to the BigNumTrait as it might lead to bad usage of it
* i.e. using this function with modulus larger than the Grumpkin modulus would lead to runtime errors, if the bignum is not deliberately picked to be in range, e.g. the bignum is the output of a hash function.
* for such use cases we advise developers to use comptime assertions to ensure the modulus is not larger than the Grumpkin modulus
**/
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
pub fn to_field<let N: u32, let MOD_BITS: u32, Params>(input: BigNum<N, MOD_BITS, Params>) -> Field
where
Params: BigNumParamsGetter<N, MOD_BITS>,
{
limbs_to_field(Params::get_params(), input.limbs)
}

37 changes: 33 additions & 4 deletions src/fns/constrained_ops.nr
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,35 @@ use crate::params::BigNumParams as P;
* umod
*/

pub(crate) fn limbs_to_field<let N: u32, let MOD_BITS: u32>(
params: P<N, MOD_BITS>,
limbs: [Field; N],
) -> Field {
let TWO_POW_120 = 0x1000000000000000000000000000000;
// let mut field: Field = 0;
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
if N > 2 {
// validate that the limbs is less than the modulus the grumpkin modulus
let mut grumpkin_modulus = [0; N];
grumpkin_modulus[0] = 0x33e84879b9709143e1f593f0000001;
grumpkin_modulus[1] = 0x4e72e131a029b85045b68181585d28;
grumpkin_modulus[2] = 0x3064;
validate_gt::<N, MOD_BITS>(grumpkin_modulus, limbs);
// validate that the limbs are in range
validate_in_range::<N, MOD_BITS>(limbs);
}
// validate the limbs sum up to the field value
let field_val = if N < 2 {
limbs[0]
} else if N == 2 {
validate_in_range::<N, MOD_BITS>(limbs);
limbs[0] + limbs[1] * TWO_POW_120
} else {
// validate_in_range::<N, 254>(limbs);
limbs[0] + limbs[1] * TWO_POW_120 + limbs[2] * TWO_POW_120 * TWO_POW_120
};
field_val
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
}

pub(crate) fn from_field<let N: u32, let MOD_BITS: u32>(
params: P<N, MOD_BITS>,
field: Field,
Expand All @@ -45,18 +74,18 @@ pub(crate) fn from_field<let N: u32, let MOD_BITS: u32>(
grumpkin_modulus[0] = 0x33e84879b9709143e1f593f0000001;
grumpkin_modulus[1] = 0x4e72e131a029b85045b68181585d28;
grumpkin_modulus[2] = 0x3064;
validate_gt::<N, 254>(grumpkin_modulus, result);
validate_gt::<N, MOD_BITS>(grumpkin_modulus, result);
// validate that the limbs are in range
validate_in_range::<N, 254>(result);
validate_in_range::<N, MOD_BITS>(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);
validate_in_range::<N, MOD_BITS>(result);
result[0] + result[1] * TWO_POW_120
} else {
validate_in_range::<N, 254>(result);
validate_in_range::<N, MOD_BITS>(result);
result[0] + result[1] * TWO_POW_120 + result[2] * TWO_POW_120 * TWO_POW_120
};
assert(field_val == field);
Expand Down
101 changes: 99 additions & 2 deletions src/tests/bignum_test.nr
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use crate::utils::u60_representation::U60Repr;

use crate::bignum::BigNum;
use crate::bignum::BigNumTrait;
use crate::bignum::to_field;
use crate::utils::u60_representation::U60Repr;

use crate::params::BigNumParams;
use crate::params::BigNumParamsGetter;

use crate::fields::bls12_381Fq::BLS12_381_Fq_Params;
use crate::fields::bn254Fq::BN254_Fq_Params;
use crate::fields::U256::U256Params;
use std::bigint::Bn254Fq;

struct Test2048Params {}

Expand Down Expand Up @@ -815,3 +816,99 @@ fn test_from_field_3_digits() {
};
assert(result == expected);
}

#[test]
fn test_from_field_3_digits_BLS381() {
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
let field: Field = -1;
let result = BN381::from(field);
let expected: BN381 = BigNum {
limbs: [0x33e84879b9709143e1f593f0000000, 0x4e72e131a029b85045b68181585d28, 0x3064, 0x0],
};
assert(result == expected);
}

#[test]
fn test_to_field_one() {
let field: Field = 1;
let bn = Fq::one();
let result = to_field(bn);
assert(result == field);
}

#[test]
fn test_to_field_one_digit() {
let field: Field = 1066513542066841864585910935480267774;
let bn = Fq { limbs: [0xcd672d695ef3129e4c40867a7173fe, 0x0, 0x0] };
let result = to_field(bn);
assert(result == field);
}

#[test]
fn test_to_field_two_digits() {
let field: Field = 697955470585821007263499235110798476786097877002667034107578965871052378;
let bn =
Fq { limbs: [0x5a10b956d41840745e0a9f6e34465a, 0x65209b74583b912262843211905e41, 0x0] };
let result = to_field(bn);
assert(result == field);
}

#[test]
fn test_to_field_three_digits() {
let field: Field = 2330301921655783950764183713945533646391233209687308929386184468126823563744;
let bn =
Fq { limbs: [0x862cf8ea69d6c70c9cc8d8871b41e0, 0xe7763528201566c2fc8d93973cf1b4, 0x526] };
let result = to_field(bn);
assert(result == field);
}

#[test(should_fail_with = "BigNum::validate_gt check fails")]
fn test_to_field_three_digits_overflow() {
let bn: Fq = BigNum {
limbs: [0x4e6405505a33bb9b9c0563df2bd59a, 0x48dbe03a9bb4865ba961e41ef9dded, 0x3a36],
};
let result = to_field(bn);
}

#[test(should_fail_with = "BigNum::validate_gt check fails")]
fn test_to_field_too_many_digits() {
let bn: BN381 = BN381 {
limbs: [0xea1742447ee9d92f9f18e1c80a481e, 0x3d89ad3d3ae85f3f482a08435c93ec, 0x1e9f, 0x1],
};
let result = to_field(bn);
}

#[test]
fn test_from_to_field_1() {
let a = 20192735083400333763152317277081729935089452774154199134677444560763605803197;
let b = Fq::from(a);
let c = to_field(b);
assert(c == a);
}

#[test]
fn test_from_to_field_fuzz(a: Field) {
let b = BN381::from(a);
let c = to_field(b);
assert(c == a);
}

#[test]
fn test_to_from_field_1() {
let a: Fq = BigNum {
limbs: [0x3c768db7732ea1b536c06ae66bce70, 0xb9936c1401d91e7e9e1138375650b4, 0x8c8],
};
let b = to_field(a);
let c = Fq::from(b);
assert(a == c);
}

#[test]
fn test_to_from_field_2() {
let a: BN381 = BigNum {
limbs: [0xd7562bf2b1fe13d458685c96a46d28, 0x2079950acd45bb43a9beeba69d5dc9, 0x18ca, 0x0],
};
let b = to_field(a);
let c = BN381::from(b);
assert(a == c);
}

Loading