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: implement From<Field> on BigNum #87

Merged
merged 16 commits into from
Jan 16, 2025
1 change: 1 addition & 0 deletions export/test_add_BN.json

Large diffs are not rendered by default.

22 changes: 19 additions & 3 deletions src/bignum.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ use crate::params::BigNumParamsGetter;

use crate::fns::{
constrained_ops::{
add, assert_is_not_equal, conditional_select, derive_from_seed, div, eq, mul, neg, sub,
udiv, udiv_mod, umod, validate_in_field, validate_in_range,
}, expressions::{__compute_quadratic_expression, evaluate_quadratic_expression},
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,
},
expressions::{__compute_quadratic_expression, evaluate_quadratic_expression},
serialization::{from_be_bytes, to_le_bytes},
unconstrained_ops::{
__add, __batch_invert, __batch_invert_slice, __derive_from_seed, __div, __eq, __invmod,
Expand All @@ -24,6 +25,7 @@ pub trait BigNumTrait {
// fn default() -> Self { std::default::Default::default () }
pub fn new() -> Self;
pub fn one() -> Self;
pub fn from_field(field: Field) -> Self;
pub fn derive_from_seed<let SeedBytes: u32>(seed: [u8; SeedBytes]) -> Self;
pub unconstrained fn __derive_from_seed<let SeedBytes: u32>(seed: [u8; SeedBytes]) -> Self;
pub fn from_slice(limbs: [Field]) -> Self;
Expand Down Expand Up @@ -99,6 +101,15 @@ pub trait BigNumTrait {
pub fn conditional_select(lhs: Self, rhs: Self, predicate: bool) -> Self;
}

// impl<let N: u32, let MOD_BITS: u32, Params> std::convert::From<Field> for BigNum<N, MOD_BITS, Params>
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
// where
// Params: BigNumParamsGetter<N, MOD_BITS>,
// {
// fn from(input: Field) -> Self {
// let params = Params::get_params();
// Self { limbs: from_field::<N, MOD_BITS>(params, input) }
// }
// }
impl<let N: u32, let MOD_BITS: u32, Params> BigNumTrait for BigNum<N, MOD_BITS, Params>
where
Params: BigNumParamsGetter<N, MOD_BITS>,
Expand All @@ -114,6 +125,11 @@ where
result
}

fn from_field(field: Field) -> Self {
let params = Params::get_params();
Self { limbs: from_field::<N, MOD_BITS>(params, field) }
}

fn derive_from_seed<let SeedBytes: u32>(seed: [u8; SeedBytes]) -> Self {
let params = Params::get_params();
Self { limbs: derive_from_seed::<_, MOD_BITS, _>(params, seed) }
Expand Down
23 changes: 21 additions & 2 deletions src/fns/constrained_ops.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ 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},
},
unconstrained_ops::{__div, __mul, __udiv_mod},
};

/**
Expand Down Expand Up @@ -35,6 +36,24 @@ 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] {
let result = __from_field::<N>(field);
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
// validate the limbs are in range and the value in total is less than 2^254
validate_in_range::<N, 254>(result);
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
let shift = 0x1000000000000000000000000000000;
kashbrti marked this conversation as resolved.
Show resolved Hide resolved
// validate the limbs sum up to the field value
let field_val = if N < 2 {
result[0]
} else {
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],
Expand Down
17 changes: 12 additions & 5 deletions src/fns/unconstrained_helpers.nr
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ global TWO_POW_60: u64 = 0x1000000000000000;
* __tonelli_shanks_sqrt
*/

pub(crate) unconstrained fn __from_field<let N: u32>(field: Field) -> [Field; N] {
// cast the field to a u60 representation
let res_u60: U60Repr<N, 2> = U60Repr::from_field(field);
let result: [Field; N] = U60Repr::into(res_u60);
kashbrti marked this conversation as resolved.
Show resolved Hide resolved
result
}

pub(crate) unconstrained fn __validate_in_field_compute_borrow_flags<let N: u32, let MOD_BITS: u32>(
params: P<N, MOD_BITS>,
val: [Field; N],
Expand All @@ -35,8 +42,8 @@ pub(crate) unconstrained fn __validate_gt_remainder<let N: u32>(
lhs: [Field; N],
rhs: [Field; N],
) -> ([Field; N], [bool; N], [bool; N]) {
let a_u60: U60Repr<N, 2> = U60Repr::from(lhs);
let mut b_u60: U60Repr<N, 2> = U60Repr::from(rhs);
let a_u60: U60Repr<N, 2> = From::from(lhs);
let mut b_u60: U60Repr<N, 2> = From::from(rhs);

let underflow = b_u60.gte(a_u60);
b_u60 += U60Repr::one();
Expand Down Expand Up @@ -76,7 +83,7 @@ pub(crate) unconstrained fn __neg_with_flags<let N: u32, let MOD_BITS: u32>(
params: P<N, MOD_BITS>,
val: [Field; N],
) -> ([Field; N], [bool; N]) {
let x_u60: U60Repr<N, 2> = U60Repr::from(val);
let x_u60: U60Repr<N, 2> = From::from(val);
let mut result_u60: U60Repr<N, 2> = U60Repr { limbs: [0; 2 * N] };

let mut borrow_in: u64 = 0;
Expand All @@ -101,8 +108,8 @@ pub(crate) unconstrained fn __add_with_flags<let N: u32, let MOD_BITS: u32>(
lhs: [Field; N],
rhs: [Field; N],
) -> ([Field; N], [bool; N], [bool; N], bool) {
let a_u60: U60Repr<N, 2> = U60Repr::from(lhs);
let b_u60: U60Repr<N, 2> = U60Repr::from(rhs);
let a_u60: U60Repr<N, 2> = From::from(lhs);
let b_u60: U60Repr<N, 2> = From::from(rhs);
let add_u60 = a_u60 + b_u60;

let overflow = add_u60.gte(params.modulus_u60);
Expand Down
3 changes: 2 additions & 1 deletion src/runtime_bignum.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::fns::{
constrained_ops::{
add, assert_is_not_equal, conditional_select, derive_from_seed, div, eq, mul, neg, sub,
udiv, udiv_mod, umod, validate_in_field, validate_in_range,
}, expressions::{__compute_quadratic_expression, evaluate_quadratic_expression},
},
expressions::{__compute_quadratic_expression, evaluate_quadratic_expression},
serialization::{from_be_bytes, to_le_bytes},
unconstrained_ops::{
__add, __batch_invert, __batch_invert_slice, __derive_from_seed, __div, __eq, __invmod,
Expand Down
7 changes: 7 additions & 0 deletions src/tests/bignum_test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -790,3 +790,10 @@ fn test_expressions() {
assert(wx_constrained.limbs == wx.limbs);
}

#[test]
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
fn test_from_field() {
let field: Field = 1;
let result = Fq::from_field(field);
assert(result == Fq::one());
}

16 changes: 16 additions & 0 deletions src/utils/split_bits.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ global TWO_POW_56: u64 = 0x100000000000000;
global TWO_POW_60: u64 = 0x1000000000000000;
global TWO_POW_64: Field = 0x10000000000000000;

//fields to u60rep conversion
pub unconstrained fn field_to_u60rep(mut x: Field) -> (u64, u64, u64) {
// get the first 60 bits by casting to u64 and then taking the lower 60 bits
// we use the fact that this casting drops everything above 64 bits
let x_low_u64 = (x as u64);
let low = x_low_u64 % TWO_POW_60;
// this becomes the same as a integer division because we're removing the remainder
x = (x - (low as Field)) / (TWO_POW_60 as Field);
let x_mid_u64 = (x as u64);
let mid = x_mid_u64 % TWO_POW_60;
x = x - (mid as Field);
let x_high_u64 = (x as u64);
let high = x_high_u64 % TWO_POW_60;
(low, mid, high)
}

// Decomposes a single field into two 120 bit fields
pub unconstrained fn split_120_bits(mut x: Field) -> (Field, Field) {
// Here we're taking advantage of truncating 64 bit limbs from the input field
Expand Down
44 changes: 44 additions & 0 deletions src/utils/u60_representation.nr
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.
Expand Down Expand Up @@ -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> {
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
// 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];
Expand Down Expand Up @@ -94,6 +118,26 @@ impl<let N: u32, let NumSegments: u32> U60Repr<N, NumSegments> {
result
}

pub(crate) fn from_field(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 {
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
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
}

pub(crate) unconstrained fn into_field_array(
x: U60Repr<N, NumSegments>,
) -> [Field; N * NumSegments / 2] {
Expand Down
Loading