Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7bae1d6
macros for trait impls
Mar 20, 2025
dd1d6c9
Merge branch 'main' into kb/trait_impls
kashbrti Mar 20, 2025
b1253d1
removed all types but BN254fq
Mar 20, 2025
ca793d6
chore: working example
TomAFrench Mar 20, 2025
daf05aa
made the BigNumTrait impl a macro
Mar 25, 2025
2b2b915
working example
Mar 25, 2025
ccda19a
bug reproduction
Mar 25, 2025
424c8c3
removed limbedobject
Mar 25, 2025
65e95b9
the metaprogramming issue
Mar 25, 2025
cc1c975
chore: removed direct accesses to private struct members
Mar 26, 2025
3fc8a1e
changed nargo version in workflow
Apr 8, 2025
19752a0
Merge branch 'main' into kb/trait_impls
kashbrti Apr 8, 2025
e2837ca
fixed the benchmark macros
Apr 8, 2025
85fdc62
Merge branch 'kb/trait_impls' of github.com:noir-lang/noir-bignum int…
Apr 8, 2025
cbbaa34
fixed benchmarks
Apr 8, 2025
6e34fb9
chore: fix indexing warning
TomAFrench Apr 8, 2025
a1b47c1
chore: fix a bunch of unused variable issues
TomAFrench Apr 8, 2025
01cf152
chore: rename macro
TomAFrench Apr 8, 2025
65a6efc
.
TomAFrench Apr 8, 2025
ec1fd19
added back the from_field benchmark
Apr 8, 2025
5d9ca99
removed From::<[u128;N]>
Apr 8, 2025
3c6797d
chore: remove insecure `From` implementation
TomAFrench Apr 8, 2025
4c03ace
chore: move logic out of macro and into trait definition where possible
TomAFrench Apr 8, 2025
6f1354f
chore: remove `BigNumParamsGetter` trait
TomAFrench Apr 8, 2025
dee66f6
chore: remove `BigNum` struct
TomAFrench Apr 8, 2025
e299e3b
chore: remove redundant `Trait` from `BigNumTrait` name
TomAFrench Apr 8, 2025
28005a6
readme changes
Apr 8, 2025
9ceb12d
Merge branch 'main' into kb/trait_impls
TomAFrench Apr 14, 2025
f0b0ef6
.
TomAFrench Apr 14, 2025
fc56153
.
TomAFrench Apr 14, 2025
ce18d6e
.
TomAFrench Apr 14, 2025
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
70 changes: 47 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ use dep::bignum::BigNum;

// Define (compile-time) BigNum type
// number of limbs, number of bits of modulus, parameter set
type U256 = BigNum<3, 257, U256Params>;
// implement the BigNum trait for the custom bignum type
#[derive_bignum_impl(3, 257, quote {U256_PARAMS})]
pub struct U256 {
limbs: [u128; 3],
}
```

### Quick example: Addition in U256
Expand All @@ -102,17 +106,37 @@ fn main() {

### `BigNum` / `RuntimeBigNum` definition

A BigNum is a number modulo `modulus` and is represented as an array of 120-bit limbs in little endian format. When the `modulus` is known at compile-time, use this type:
A BigNum is a number modulo `modulus` and is represented as an array of 120-bit limbs in little endian format. When the `modulus` is known at compile-time, use `BigNum` type. For instance, to build a 5-limb bignum, you can use the following definition:

```rust
pub struct BigNum<let N: u32, let MOD_BITS: u32, Params> {
pub limbs: [Field; N],
pub struct MyBignum {
pub limbs: [Field; 5],
}
```
The `BigNum` trait can be implemented for this type by using the `derive_bignum_impl` macro.

```rust
pub(crate) comptime fn derive_bignum_impl(
strukt: TypeDefinition,
N: u32,
MOD_BITS: u32,
params: Quoted,
) -> Quoted
```

- `N` is the number of limbs needed to represent the number. Each limb is a `Field`, which contains max 120 bits. The field type has 254 bits of space and by only using 120 bits, there is space for multiplications and additions without overflowing.
- `MOD_BITS` is the number of bits needed to represent the modulus.
- `Params` is a parameter set (`BigNumParams`) associated with the big number. More information below.

To implement the trait for your bignum type, you need to provide the number of limbs and the number of bits of the modulus and the parameters.

```rust
#[derive_bignum_impl(4, 377, quote {BLS12_377_Fq_PARAMS})]
pub struct MyBignum {
pub limbs: [Field; 4],
}
```

The actual value of a BigNum can be calculated by multiplying each limb by an increasing power of $2^{120}$. For example `[1,20,300]` represents $1 \cdot 2^{120\cdot0} + 20 \cdot 2^{120 \cdot 1} + 300 \cdot 2^{120 \cdot 2}$. We say that the BigNum is represented in radix- $2^{120}$.

When `modulus` is known at runtime, the type is slightly different, but the representation of the actual number in limbs works the same way:
Expand All @@ -137,7 +161,7 @@ To define a `BigNum` or `RuntimeBigNum`, you need to provide a `BigNumParams`. F

- `double_modulus` is derived via the method `compute_double_modulus` in `runtime_bignum.nr`. If you want to provide this value as a compile-time constant (see `fields/bn254Fq.nr` for an example), follow the algorithm `compute_double_modulus` as this parameter is _not_ structly 2 \* modulus. Each limb except the most significant limb borrows 2^120 from the next most significant limb. This ensure that when performing limb subtractions `double_modulus.limbs[i] - x.limbs[i]`, we know that the result will not underflow

- `has_multiplicative_inverse`, a boolean indicating whether the elements have a multiplicative inverse or not
- `has_multiplicative_inverse`, a boolean indicating whether all elements have a multiplicative inverse, i.e. whether modulus is a prime.


## `BigNum` / `RuntimeBigNum` methods
Expand Down Expand Up @@ -192,24 +216,35 @@ Constrained arithmetic operations. These perform the expected arithmetic operati

These methods can be used using operators (`+`, `-`, `*`, `/`).

`derive_bignum_impl` will also implement conversions from a native `Field` type. For example, if you have a `Field` type `Fq`, you can convert it to your `BigNum` type `MyBigNum` by using the following syntax:

```rust
let a: Field = 10;
let my_bignum: Fq = Fq::from(a);
```
We also support comparison operators (`==`, `!=`, `>`, `>=`, `<`, `<=`) for `BigNum` types.


> **Note:** `div`, `udiv` and `umod` are expensive due to requiring modular exponentiations during witness computation. It is worth modifying witness generation algorithms to minimize the number of modular exponentiations required. (for example, using batch inverses)

Other constrained functions:
- `new`, returns a bignum with value 0
- `zero`, returns a bignum with value 0
- `one`, returns a bignum with value 1
- `modulus`, returns `modulus` from the parameters
- `from_limbs`, returns a bignum from an array of limbs
- `from_slice`, returns a bignum from a slice of limbs
- `get_limbs`, returns the limbs of the bignum
- `set_limb(idx, value)`, set limbs `idx` to new value
- `modulus_bits`, returns nr of bits from the parameters
- `num_limbs`, returns N, the number of limbs needed to represent the bignum for the current parameters
- `evaluate_quadratic_expression`, more explanation below
- `validate_in_range`, validates the bignum doesn't have more bits than `modulus_bits`
- `validate_in_field(val)`, validates `val` < `modulus`
- `assert_is_not_equal`, assert 2 bignums are distinct
- `eq`, also available with operator `==`
- `get(idx)`, return value of limbs `idx` (this is a field)
- `set_limb(idx, value)`, set limbs `idx` to new value
- `conditional_select(lhs, rhs, predicate)`, if `predicate` is 0 returns `lhs`, else if `predicate` is 1 returns `rhs`
- `to_le_bytes`, returns the little-endian byte representation of a bignum

- `from_be_bytes`, returns a bignum from a big-endian byte array
### `evaluate_quadratic_expression`

For a lower gatecount and thus better performance perform multiple unconstrained arithmetic operations and then constrain them at once using `evaluate_quadratic_expression`.
Expand Down Expand Up @@ -278,10 +313,7 @@ In the base field of Ed25519, which is the integers mod $2^{255}-19$, perform si

```rust
use dep::bignum::BigNum;
use dep::bignum::fields::ed25519Fq::ED25519_Fq_Params;

// Prime field mod 2^255-19
type Fq = BigNum<3, 255, ED25519_Fq_Params>;
use dep::bignum::fields::ed25519Fq::ED25519_Fq as Fq;

// Check that (x1 * x2) + x3 equals `expected`
fn main(x1: Fq, x2: Fq, x3: Fq, expected: Fq) {
Expand Down Expand Up @@ -325,11 +357,9 @@ BigNum supports operations over unsigned integers, with predefined types for 256
All arithmetic operations are supported including integer div and mod functions (make sure to use udiv, umod). Bit shifts and comparison operators are not yet implemented.

```rust
use dep::bignum::fields::U256::U256Params;
use dep::bignum::fields::U256;
use dep::bignum::BigNum;

type U256 = BigNum<3, 257, U256Params>;

fn foo(x: U256, y: U256) -> U256 {
x.udiv(y)
}
Expand Down Expand Up @@ -357,15 +387,9 @@ Feature requests and/or pull requests welcome for missing fields you need.

### Create a new parameter set for custom modulus

TODO: the paramgen tool is not up to date for `BigNum` version >= `0.4`, an issue has been created for this [here](https://github.com/noir-lang/noir-bignum-paramgen/issues/4). This section should be adjusted after this fix.

The easiest way to generate everything you need for a parameter set is to use [this tool](https://github.com/noir-lang/noir-bignum-paramgen).

For example, after cloning and building the tool, for a `modulus` of 1024 bits for RSA run `./target/release/paramgen instance <modulus> RSA1024_example > out.txt`. This prints the parameter set to `out.txt`. Since this is not a field, also add:
```rust
fn has_multiplicative_inverse() -> bool { false }
```
to the traits implementations for the parameter set.
For example, after cloning and building the tool, for a `modulus` of 1024 bits for RSA run `./target/release/paramgen instance <modulus> RSA1024_example > out.txt`. This prints the parameter set to `out.txt`.

#### Provide parameters for runtime known modulus

Expand Down
127 changes: 54 additions & 73 deletions src/benchmarks/bignum_benchmarks.nr
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
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();
use crate::fields::{bls12_381Fq::BLS12_381_Fq, bn254Fq::BN254_Fq, U2048::U2048, U256::U256};

comptime fn make_bench(m: Module, params: Quoted) -> Quoted {
let module_name = m.name();
let add_bench_name = f"add_{module_name}".quoted_contents();
let sub_bench_name = f"sub_{module_name}".quoted_contents();
let mul_bench_name = f"mul_{module_name}".quoted_contents();
let div_bench_name = f"div_{module_name}".quoted_contents();
let udiv_mod_bench_name = f"udiv_mod_{module_name}".quoted_contents();
let udiv_bench_name = f"udiv_{module_name}".quoted_contents();
let from_field_bench_name = f"from_field_{module_name}".quoted_contents();
let validate_in_field_bench_name = f"validate_in_field_{module_name}".quoted_contents();
let evaluate_quadratic_expression_3_elements_bench_name =
f"evaluate_quadratic_expression_3_elements_{module_name}".quoted_contents();
Expand All @@ -22,95 +17,94 @@ comptime fn make_bench(_m: Module, N: u32, MOD_BITS: u32, params: Quoted) -> Quo
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 };
let from_field_bench_name = f"from_field_{module_name}".quoted_contents();
let typ = params.as_type();

quote {
#[export]
fn $add_bench_name(a: $BigNum<$N, $MOD_BITS, $params>, b: $BigNum<$N, $MOD_BITS, $params>) -> $BigNum<$N, $MOD_BITS, $params> {
fn $add_bench_name(a: $typ, b: $typ) -> $typ {
a + b
}

#[export]
fn $sub_bench_name(a: $BigNum<$N, $MOD_BITS, $params>, b: $BigNum<$N, $MOD_BITS, $params>) -> $BigNum<$N, $MOD_BITS, $params> {
fn $sub_bench_name(a: $typ, b: $typ) -> $typ {
a - b
}

#[export]
fn $mul_bench_name(a: $BigNum<$N, $MOD_BITS, $params>, b: $BigNum<$N, $MOD_BITS, $params>) -> $BigNum<$N, $MOD_BITS, $params> {
fn $mul_bench_name(a: $typ, b: $typ) -> $typ {
a * b
}

#[export]
fn $div_bench_name(a: $BigNum<$N, $MOD_BITS, $params>, b: $BigNum<$N, $MOD_BITS, $params>) -> $BigNum<$N, $MOD_BITS, $params> {
fn $div_bench_name(a: $typ, b: $typ) -> $typ {
a / b
}

#[export]
fn $udiv_mod_bench_name(a: $BigNum<$N, $MOD_BITS, $params>, b: $BigNum<$N, $MOD_BITS, $params>) -> ($BigNum<$N, $MOD_BITS, $params>, $BigNum<$N, $MOD_BITS, $params>) {
$BigNumTrait::udiv_mod(a, b)
fn $udiv_mod_bench_name(a: $typ, b: $typ) -> ($typ, $typ) {
$BigNum::udiv_mod(a, b)
}

#[export]
fn $udiv_bench_name(a: $BigNum<$N, $MOD_BITS, $params>, b: $BigNum<$N, $MOD_BITS, $params>) -> $BigNum<$N, $MOD_BITS, $params> {
$BigNumTrait::udiv(a, b)
}

#[export]
fn $from_field_bench_name(a: Field) -> $BigNum<$N, $MOD_BITS, $params> {
$BigNum::from(a)
fn $udiv_bench_name(a: $typ, b: $typ) -> $typ {
$BigNum::udiv(a, b)
}

#[export]
fn $validate_in_field_bench_name(a: $BigNum<$N, $MOD_BITS, $params>) {
$BigNumTrait::validate_in_field(a)
fn $validate_in_field_bench_name(a: $typ) {
$BigNum::validate_in_field(a)
}

#[export]
fn $evaluate_quadratic_expression_3_elements_bench_name(
lhs: [[$BigNum<$N, $MOD_BITS, $params>; 3]; 3],
lhs: [[$typ; 3]; 3],
lhs_flags: [[bool; 3]; 3],
rhs: [[$BigNum<$N, $MOD_BITS, $params>; 3]; 3],
rhs: [[$typ; 3]; 3],
rhs_flags: [[bool; 3]; 3],
add: [$BigNum<$N, $MOD_BITS, $params>; 3],
add: [$typ; 3],
add_flags: [bool; 3],
) {
$BigNumTrait::evaluate_quadratic_expression(lhs, lhs_flags, rhs, rhs_flags, add, add_flags)
$BigNum::evaluate_quadratic_expression(lhs, lhs_flags, rhs, rhs_flags, add, add_flags)
}

#[export]
fn $evaluate_quadratic_expression_12_elements_bench_name(
lhs: [[$BigNum<$N, $MOD_BITS, $params>; 3]; 12],
lhs: [[$typ; 3]; 12],
lhs_flags: [[bool; 3]; 12],
rhs: [[$BigNum<$N, $MOD_BITS, $params>; 3]; 12],
rhs: [[$typ; 3]; 12],
rhs_flags: [[bool; 3]; 12],
add: [$BigNum<$N, $MOD_BITS, $params>; 3],
add: [$typ; 3],
add_flags: [bool; 3],
) {
$BigNumTrait::evaluate_quadratic_expression(lhs, lhs_flags, rhs, rhs_flags, add, add_flags)
$BigNum::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)
fn $from_field_bench_name(a: Field) -> $typ {
$typ::from(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)
}

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

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

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

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

Expand All @@ -119,27 +113,14 @@ comptime fn make_bench(_m: Module, N: u32, MOD_BITS: u32, params: Quoted) -> Quo
// type U256
// type BLS12_381Fq
// 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(quote { BN254_Fq })]
mod BN254_Fq_Bench {}

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

#[make_bench(4, 381, quote { BLS12_381_Fq_Params })]
pub mod BLS12_381Fq_Bench {
#[make_bench(quote { BLS12_381_Fq })]
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;
}
#[make_bench(quote { U2048 })]
mod U2048_Bench {}
Loading