Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Install Nargo
uses: noir-lang/noirup@v0.1.4
with:
toolchain: 1.0.0-beta.3
toolchain: 1.0.0-beta.4

- name: Install bb
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:

env:
CARGO_TERM_COLOR: always
MINIMUM_NOIR_VERSION: v1.0.0-beta.3
MINIMUM_NOIR_VERSION: v1.0.0-beta.4
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kashbrti should we update this repo's README:

This library is tested with all stable releases since 1.0.0-beta.0 as well as nightly.


jobs:
noir-version-list:
Expand Down
38 changes: 35 additions & 3 deletions src/benchmarks/bignum_benchmarks.nr
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::bignum::BigNum;
use crate::bignum::{BigNum, BigNumTrait};
use crate::fields::bls12_381Fq::BLS12_381_Fq;
use crate::fields::bn254Fq::BN254_Fq;
use crate::fields::U2048::U2048;
use crate::fields::U256::U256;

comptime fn make_bench(_m: Module, N: u32, MOD_BITS: u32, params: Quoted) -> Quoted {
let module_name = _m.name();
Expand All @@ -14,7 +18,11 @@ comptime fn make_bench(_m: Module, N: u32, MOD_BITS: u32, params: Quoted) -> Quo
f"evaluate_quadratic_expression_3_elements_{module_name}".quoted_contents();
let evaluate_quadratic_expression_12_elements_bench_name =
f"evaluate_quadratic_expression_12_elements_{module_name}".quoted_contents();

let to_be_bytes_bench_name = f"to_be_bytes_{module_name}".quoted_contents();
let from_be_bytes_bench_name = f"from_be_bytes_{module_name}".quoted_contents();
let to_le_bytes_bench_name = f"to_le_bytes_{module_name}".quoted_contents();
let from_le_bytes_bench_name = f"from_le_bytes_{module_name}".quoted_contents();
let BigNumTrait = quote { crate::bignum::BigNumTrait };
let BigNum = quote { crate::bignum::BigNum };
let BigNumTrait = quote { crate::bignum::BigNumTrait };

Expand Down Expand Up @@ -82,7 +90,27 @@ comptime fn make_bench(_m: Module, N: u32, MOD_BITS: u32, params: Quoted) -> Quo
) {
$BigNumTrait::evaluate_quadratic_expression(lhs, lhs_flags, rhs, rhs_flags, add, add_flags)
}


#[export]
fn $to_be_bytes_bench_name(a: $BigNum<$N, $MOD_BITS, $params>) -> [u8; ($MOD_BITS+7) / 8] {
$BigNum::<$N, $MOD_BITS, $params> ::to_be_bytes(a)
}

#[export]
fn $from_be_bytes_bench_name(a: [u8; ($MOD_BITS+7) / 8]) -> $BigNum<$N, $MOD_BITS, $params> {
$BigNum::<$N, $MOD_BITS, $params> ::from_be_bytes(a)
}

#[export]
fn $to_le_bytes_bench_name(a: $BigNum<$N, $MOD_BITS, $params>) -> [u8; ($MOD_BITS+7) / 8] {
$BigNum::<$N, $MOD_BITS, $params> ::to_le_bytes(a)
}

#[export]
fn $from_le_bytes_bench_name(a: [u8; ($MOD_BITS+7) / 8]) -> $BigNum<$N, $MOD_BITS, $params> {
$BigNum::<$N, $MOD_BITS, $params> ::from_le_bytes(a)
}

}
}

Expand All @@ -93,21 +121,25 @@ comptime fn make_bench(_m: Module, N: u32, MOD_BITS: u32, params: Quoted) -> Quo
// type U2048
#[make_bench(3, 254, quote { BN254_Fq_Params })]
pub mod BN254_Fq_Bench {
use crate::bignum::BigNumTrait;
use crate::fields::bn254Fq::BN254_Fq_Params;
}

#[make_bench(3, 257, quote { U256Params })]
pub mod U256_Bench {
use crate::bignum::BigNumTrait;
use crate::fields::U256::U256Params;
}

#[make_bench(4, 381, quote { BLS12_381_Fq_Params })]
pub mod BLS12_381Fq_Bench {

use crate::bignum::BigNumTrait;
use crate::fields::bls12_381Fq::BLS12_381_Fq_Params;
}

#[make_bench(18, 2049, quote { U2048Params })]
pub mod U2048_Bench {
use crate::bignum::BigNumTrait;
use crate::fields::U2048::U2048Params;
}
27 changes: 19 additions & 8 deletions src/bignum.nr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::fns::{
validate_in_range,
},
expressions::{__compute_quadratic_expression, evaluate_quadratic_expression},
serialization::{from_be_bytes, to_le_bytes},
serialization::{from_be_bytes, from_le_bytes, to_be_bytes, to_le_bytes},
unconstrained_ops::{
__add, __batch_invert, __batch_invert_slice, __derive_from_seed, __div, __eq, __invmod,
__is_zero, __mul, __neg, __pow, __sub, __tonelli_shanks_sqrt, __udiv_mod,
Expand All @@ -24,6 +24,7 @@ pub struct BigNum<let N: u32, let MOD_BITS: u32, Params> {
// We aim to avoid needing to add a generic parameter to this trait, for this reason we do not allow
// accessing the limbs of the bignum except through slices.
pub trait BigNumTrait: Neg + Add + Sub + Mul + Div + Eq {
let MOD_BITS: u32;
// TODO: this crashes the compiler? v0.32
// fn default() -> Self { std::default::Default::default () }
fn new() -> Self;
Expand All @@ -32,8 +33,10 @@ pub trait BigNumTrait: Neg + Add + Sub + Mul + Div + Eq {
fn derive_from_seed<let SeedBytes: u32>(seed: [u8; SeedBytes]) -> Self;
unconstrained fn __derive_from_seed<let SeedBytes: u32>(seed: [u8; SeedBytes]) -> Self;
fn from_slice(limbs: [u128]) -> Self;
fn from_be_bytes<let NBytes: u32>(x: [u8; NBytes]) -> Self;
fn to_le_bytes<let NBytes: u32>(self) -> [u8; NBytes];
fn from_be_bytes(x: [u8; (MOD_BITS + 7) / 8]) -> Self;
fn to_be_bytes(self) -> [u8; (MOD_BITS + 7) / 8];
fn from_le_bytes(x: [u8; (MOD_BITS + 7) / 8]) -> Self;
fn to_le_bytes(self) -> [u8; (MOD_BITS + 7) / 8];

fn modulus() -> Self;
fn modulus_bits(self) -> u32;
Expand Down Expand Up @@ -112,7 +115,7 @@ impl<let N: u32, let MOD_BITS: u32, Params> BigNumTrait for BigNum<N, MOD_BITS,
where
Params: BigNumParamsGetter<N, MOD_BITS>,
{

let MOD_BITS: u32 = MOD_BITS;
#[deprecated("`BigNum::zero()` is preferred")]
fn new() -> Self {
Self::zero()
Expand Down Expand Up @@ -142,12 +145,20 @@ where
Self { limbs: limbs.as_array() }
}

fn from_be_bytes<let NBytes: u32>(x: [u8; NBytes]) -> Self {
Self { limbs: from_be_bytes::<_, MOD_BITS, _>(x) }
fn from_be_bytes(x: [u8; (MOD_BITS + 7) / 8]) -> Self {
Self { limbs: from_be_bytes::<_, MOD_BITS>(x) }
}

fn to_be_bytes(self) -> [u8; (MOD_BITS + 7) / 8] {
to_be_bytes::<_, MOD_BITS>(self.limbs)
}

fn from_le_bytes(x: [u8; (MOD_BITS + 7) / 8]) -> Self {
Self { limbs: from_le_bytes::<_, MOD_BITS>(x) }
}

fn to_le_bytes<let NBytes: u32>(self) -> [u8; NBytes] {
to_le_bytes::<_, MOD_BITS, _>(self.limbs)
fn to_le_bytes(self) -> [u8; (MOD_BITS + 7) / 8] {
to_le_bytes::<_, MOD_BITS>(self.limbs)
}

fn modulus() -> Self {
Expand Down
2 changes: 1 addition & 1 deletion src/fns/constrained_ops.nr
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ pub(crate) fn derive_from_seed<let N: u32, let MOD_BITS: u32, let SeedBytes: u32
for i in 0..num_bigfield_chunks {
let bigfield_lhs_limbs = bigfield_chunks[i];

// result = mul(params, result, bigfield_rhs_limbs);
result = mul(params, result, bigfield_rhs_limbs);
result = add(params, result, bigfield_lhs_limbs);
}

Expand Down
76 changes: 47 additions & 29 deletions src/fns/serialization.nr
Original file line number Diff line number Diff line change
@@ -1,61 +1,79 @@
/**
* @brief construct a BigNum instance out of an array of bytes in BIG ENDIAN format
* @description: each 120-bit limb represents 15 bytes, we require that the size of the byte array
* is precisely large enough to cover MOD_BITS
* @param x: input byte array
**/
pub(crate) fn from_be_bytes<let N: u32, let MOD_BITS: u32, let NBytes: u32>(
x: [u8; NBytes],
use crate::utils::map::invert_array;
/// conversions between big endian and little endian byte arrays and BigNum instances
/// the byte serialization should have `(MOD_BITS + 7) / 8` bytes.
/// each 120-bit limb is represented by 15 bytes, and there are fewer bytes for covering the most significant limb
pub(crate) fn from_be_bytes<let N: u32, let MOD_BITS: u32>(
x: [u8; (MOD_BITS + 7) / 8],
) -> [u128; N] {
let num_bits = NBytes * 8;
let num_bits = (MOD_BITS + 7) / 8 * 8;
assert(num_bits >= MOD_BITS);
assert(num_bits - MOD_BITS < 8);
let mut result: [u128; N] = [0; N];

let excess_bytes = N * 15 - NBytes;
let excess_bytes = N * 15 - (MOD_BITS + 7) / 8;
let final_limb_bytes = 15 - excess_bytes;
let mut limb: u128 = 0;
let mut limb: Field = 0;
let mut k = 0;
for _j in 0..final_limb_bytes {
limb *= 256;
limb += x[k] as u128;
limb += x[k] as Field;
k += 1;
}
result[N - 1] = limb;
limb.assert_max_bit_size::<128>();
result[N - 1] = limb as u128;

for i in 1..N {
let mut limb: u128 = 0;
let mut limb: Field = 0;
for _j in 0..15 {
limb *= 256;
limb += x[k] as u128;
limb += x[k] as Field;
k += 1;
}
result[N - i - 1] = limb;
limb.assert_max_bit_size::<128>();
result[N - i - 1] = limb as u128;
}

let most_significant_byte: Field = x[0] as Field;

most_significant_byte.assert_max_bit_size::<8 - (NBytes * 8 - MOD_BITS)>();
most_significant_byte.assert_max_bit_size::<8 - ((MOD_BITS + 7) / 8 * 8 - MOD_BITS)>();
result
}

pub(crate) fn to_le_bytes<let N: u32, let MOD_BITS: u32, let NBytes: u32>(
pub(crate) fn to_be_bytes<let N: u32, let MOD_BITS: u32>(
val: [u128; N],
) -> [u8; NBytes] {
let nbytes = (MOD_BITS / 8) + (MOD_BITS % 8 != 0) as u32;
assert(nbytes <= NBytes);

let mut result: [u8; NBytes] = [0; NBytes];
) -> [u8; (MOD_BITS + 7) / 8] {
let mut result: [u8; (MOD_BITS + 7) / 8] = [0; (MOD_BITS + 7) / 8];
// the last limb will not have all the 15 bytes so we deal with the full limbs first
for i in 0..N - 1 {
let limb_bytes: [u8; 15] = (val[i] as Field).to_le_bytes();
let index = N - i - 2;
let limb_bytes: [u8; 15] = (val[index] as Field).to_be_bytes();
for j in 0..15 {
result[i * 15 + j] = limb_bytes[j];
// we leave the space for the first byte empty, which would take (MOD_BITS+7)/8 - MOD_BITS/8 bytes
result[i * 15 + j + (MOD_BITS + 7) / 8 - (N - 1) * 15] = limb_bytes[j];
}
}
let last_limb_bytes: [u8; 15] = (val[N - 1] as Field).to_le_bytes();
let num_last_bytes = (NBytes - (N - 1) * 15);
for i in 0..num_last_bytes {
result[(N - 1) * 15 + i] = last_limb_bytes[i];
// now we deal with the last limb
let last_limb_bytes: [u8; ((MOD_BITS + 7) / 8 - (N - 1) * 15)] =
(val[N - 1] as Field).to_be_bytes();

for i in 0..((MOD_BITS + 7) / 8 - (N - 1) * 15) {
result[i] = last_limb_bytes[i];
}
result
}

pub(crate) fn to_le_bytes<let N: u32, let MOD_BITS: u32>(
val: [u128; N],
) -> [u8; (MOD_BITS + 7) / 8] {
let result_be: [u8; (MOD_BITS + 7) / 8] = to_be_bytes(val);
let result = invert_array(result_be);
result
}

pub(crate) fn from_le_bytes<let N: u32, let MOD_BITS: u32>(
x: [u8; (MOD_BITS + 7) / 8],
) -> [u128; N] {
// make the bytes big endian
let be_x = invert_array(x);
from_be_bytes(be_x)
}
21 changes: 13 additions & 8 deletions src/runtime_bignum.nr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::fns::{
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},
serialization::{from_be_bytes, from_le_bytes, to_be_bytes, to_le_bytes},
unconstrained_ops::{
__add, __batch_invert, __batch_invert_slice, __derive_from_seed, __div, __eq, __invmod,
__is_zero, __mul, __neg, __pow, __sub, __tonelli_shanks_sqrt, __udiv_mod,
Expand Down Expand Up @@ -60,15 +60,20 @@ impl<let N: u32, let MOD_BITS: u32> RuntimeBigNum<N, MOD_BITS> {
Self { limbs, params }
}

pub fn from_be_bytes<let NBytes: u32>(
params: BigNumParams<N, MOD_BITS>,
x: [u8; NBytes],
) -> Self {
Self { limbs: from_be_bytes::<_, MOD_BITS, _>(x), params }
pub fn from_be_bytes(params: BigNumParams<N, MOD_BITS>, x: [u8; (MOD_BITS + 7) / 8]) -> Self {
Self { limbs: from_be_bytes::<_, MOD_BITS>(x), params }
}

pub fn from_le_bytes(params: BigNumParams<N, MOD_BITS>, x: [u8; (MOD_BITS + 7) / 8]) -> Self {
Self { limbs: from_le_bytes::<_, MOD_BITS>(x), params }
}

pub fn to_be_bytes(self) -> [u8; (MOD_BITS + 7) / 8] {
to_be_bytes::<_, MOD_BITS>(self.limbs)
}

pub fn to_le_bytes<let NBytes: u32>(self) -> [u8; NBytes] {
to_le_bytes::<_, MOD_BITS, _>(self.limbs)
pub fn to_le_bytes(self) -> [u8; (MOD_BITS + 7) / 8] {
to_le_bytes::<_, MOD_BITS>(self.limbs)
}

pub fn modulus(self) -> Self {
Expand Down
16 changes: 16 additions & 0 deletions src/tests/bignum_test.nr
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,22 @@ fn test_cmp_BN_fuzz(seed: [u8; 5]) {
assert(a < modulus_sub_1_div_2);
}

#[test]
fn fuzz_from_le_bytes(seed: [u8; 5]) {
let a = BN254_Fq::derive_from_seed(seed);
let bytes = a.to_le_bytes();
let b = BN254_Fq::from_le_bytes(bytes);
assert(a == b);
}

#[test]
fn fuzz_to_be_bytes(seed: [u8; 5]) {
let a = BN254_Fq::derive_from_seed(seed);
let bytes = a.to_be_bytes();
let b = BN254_Fq::from_be_bytes(bytes);
assert(a == b);
}

struct SecP224r1_Params {}
type SecP224r1 = BigNum<2, 224, SecP224r1_Params>;

Expand Down
9 changes: 9 additions & 0 deletions src/utils/map.nr
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@ pub(crate) fn map<T, let N: u32, U, Env>(arr: [T; N], f: fn[Env](T) -> U) -> [U;

ret
}

pub(crate) fn invert_array<T, let M: u32>(array: [T; M]) -> [T; M] {
let mut ret: [T; M] = std::mem::zeroed();

for i in 0..M {
ret[i] = array[M - i - 1];
}
ret
}