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 all 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
16 changes: 14 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,15 @@ where
}
}

/// `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
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)
}

38 changes: 33 additions & 5 deletions src/fns/constrained_ops.nr
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,38 @@ 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;
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
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
}
}

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
// Safety: 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) };

Expand All @@ -47,22 +74,23 @@ 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);
}

result
}

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