Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2368fb1
feat: skip calculation of partial sums when simulating blobs
TomAFrench Jan 16, 2025
632a209
Merge branch 'master' into tf/optimize-blob-simulation
TomAFrench Jan 16, 2025
445b852
Update noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr
TomAFrench Jan 16, 2025
102f30e
Merge branch 'master' into tf/optimize-blob-simulation
TomAFrench Jan 17, 2025
63083d8
Update noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr
TomAFrench Jan 17, 2025
60b8c4e
chore: add test that `__compute_sum` agrees with result from `__compu…
TomAFrench Jan 20, 2025
c553596
Merge branch 'master' into tf/optimize-blob-simulation
TomAFrench Jan 20, 2025
c4eb68c
Merge branch 'master' into tf/optimize-blob-simulation
TomAFrench Jan 22, 2025
cd8debd
.
TomAFrench Jan 22, 2025
ded7827
Merge branch 'master' into tf/optimize-blob-simulation
TomAFrench Jan 22, 2025
27e72f6
Merge branch 'master' into tf/optimize-blob-simulation
TomAFrench Jan 22, 2025
fe87b3b
Update noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr
TomAFrench Jan 23, 2025
cdcf97c
Update noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr
TomAFrench Jan 23, 2025
42311e3
Merge branch 'master' into tf/optimize-blob-simulation
TomAFrench Jan 30, 2025
b408f0f
Merge branch 'master' into tf/optimize-blob-simulation
TomAFrench Jan 31, 2025
a5e1869
Update noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr
TomAFrench Jan 31, 2025
dd705f2
Update noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr
TomAFrench Jan 31, 2025
d5a39ec
.
TomAFrench Jan 31, 2025
67282fd
Apply suggestions from code review
TomAFrench Jan 31, 2025
89fe4dd
.
TomAFrench Feb 3, 2025
04040ca
.
TomAFrench Feb 3, 2025
5177434
.
TomAFrench Feb 3, 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
99 changes: 74 additions & 25 deletions noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ unconstrained fn __batch_invert_impl<let N: u32>(mut x: [F; N]) -> [F; N] {
}

accumulator = accumulator.__invmod();
let mut T0: F = BigNum::new();
let mut T0: F = BigNum::zero();
for i in 0..N {
let idx = N - 1 - i;
if (x[idx].__is_zero() == false) {
Expand Down Expand Up @@ -83,7 +83,7 @@ fn field_to_bignum(x: Field) -> F {
}

fn convert_blob_fields(blob_as_fields: [Field; FIELDS_PER_BLOB]) -> [F; FIELDS_PER_BLOB] {
let mut blob: [F; FIELDS_PER_BLOB] = [BigNum::new(); FIELDS_PER_BLOB];
let mut blob: [F; FIELDS_PER_BLOB] = [BigNum::zero(); FIELDS_PER_BLOB];
for i in 0..FIELDS_PER_BLOB {
blob[i] = field_to_bignum(blob_as_fields[i]);
}
Expand Down Expand Up @@ -203,25 +203,27 @@ fn barycentric_evaluate_blob_at_z(z: F, ys: [F; FIELDS_PER_BLOB]) -> F {
// perhaps because all the omega^i terms are constant witnesses?
let fracs = compute_fracs(z, ys);

// OK so...we can add multiple product terms into a sum...but I am not sure how many!
// we are computing 254 * 254 bit products and we need to ensure each product limb doesn't overflow
// each limb is 120 bits => 120 * 120 = 240 bits.
// however when computing a mul we add up to 5 product terms into a single field element => 243 bits (ish)
// when we do a modular reduction we validate that a field element >> 120 bits is less than 2^{126} which implies we have 246 bits to play with
// which implies...we can accommodate up to EIGHT additions of product terms before we risk overflowing
// (this is really messy! I never considered the case of giant linear sequences of products)
let sum = if !std::runtime::is_unconstrained() {
// OK so...we can add multiple product terms into a sum...but I am not sure how many!
// we are computing 254 * 254 bit products and we need to ensure each product limb doesn't overflow
// each limb is 120 bits => 120 * 120 = 240 bits.
// however when computing a mul we add up to 5 product terms into a single field element => 243 bits (ish)
// when we do a modular reduction we validate that a field element >> 120 bits is less than 2^{126} which implies we have 246 bits to play with
// which implies...we can accommodate up to EIGHT additions of product terms before we risk overflowing
// (this is really messy! I never considered the case of giant linear sequences of products)

// Seeking:
// ___d-1
// \ omega^i
// sum = / y_i . ---------
// /____ z - omega^i
// i=0
let NUM_PARTIAL_SUMS = FIELDS_PER_BLOB / 8;
/// Safety: This sum is checked by the following `BigNum::evaluate_quadratic_expression` calls.
let partial_sums: [F; FIELDS_PER_BLOB / 8] = unsafe { __compute_partial_sums(fracs, ROOTS) };
// Seeking:
// ___d-1
// \ omega^i
// sum = / y_i . ---------
// /____ z - omega^i
// i=0

let NUM_PARTIAL_SUMS = FIELDS_PER_BLOB / 8;
/// Safety: This sum is checked by the following `BigNum::evaluate_quadratic_expression` calls.
let partial_sums: [F; FIELDS_PER_BLOB / 8] =
unsafe { __compute_partial_sums(fracs, ROOTS) };

if !std::runtime::is_unconstrained() {
// We split off the first term to check the initial sum

// partial_sums[0] <- (lhs[0] * rhs[0] + ... + lhs[7] * rhs[7])
Expand Down Expand Up @@ -295,10 +297,20 @@ fn barycentric_evaluate_blob_at_z(z: F, ys: [F; FIELDS_PER_BLOB]) -> F {
[false, true],
);
}
}

// The final term in `partial_sums` is then the full sum.
partial_sums[FIELDS_PER_BLOB / 8 - 1]
} else {
// There's no need to jump through hoops to check partial sums if we don't need to constrain the result.
// We can just calculate the final sum.

/// Safety: We're running under the condition that `std::runtime::is_unconstrained()` is true.
unsafe {
__compute_sum(fracs, ROOTS)
Comment thread
TomAFrench marked this conversation as resolved.
}
};

let factor = compute_factor(z);
let sum = partial_sums[FIELDS_PER_BLOB / 8 - 1];
factor.mul(sum)
}

Expand Down Expand Up @@ -352,7 +364,7 @@ unconstrained fn __compute_fracs(
ys: [F; FIELDS_PER_BLOB],
unconstrained_roots: [F; FIELDS_PER_BLOB],
) -> [F; FIELDS_PER_BLOB] {
let mut denoms = [BigNum::new(); FIELDS_PER_BLOB];
let mut denoms = [BigNum::zero(); FIELDS_PER_BLOB];
for i in 0..FIELDS_PER_BLOB {
denoms[i] = z.__sub(unconstrained_roots[i]); // (z - omega^i)
}
Expand Down Expand Up @@ -407,7 +419,7 @@ unconstrained fn __compute_partial_sums(
// k=i*8 + 0

// Need to split off the first iteration.
let mut partial_sum: F = BigNum::new();
let mut partial_sum: F = BigNum::zero();
for i in 0..8 {
// y_k * ( omega^k / (z - omega^k) )
let summand = unconstrained_roots[i].__mul(fracs[i]);
Expand Down Expand Up @@ -439,6 +451,28 @@ unconstrained fn __compute_partial_sums(
partial_sums
}

unconstrained fn __compute_sum(
fracs: [F; FIELDS_PER_BLOB],
unconstrained_roots: [F; FIELDS_PER_BLOB],
) -> F {
// Seeking:
// ___d-1
// \ omega^i
// sum = / y_i . ---------
// /____ z - omega^i
// i=0

let mut sum: F = BigNum::zero();
for i in 0..FIELDS_PER_BLOB {
// y_k * ( omega^k / (z - omega^k) )
Comment thread
TomAFrench marked this conversation as resolved.
let summand = unconstrained_roots[i].__mul(fracs[i]);

// partial_sum + ( y_k * ( omega^k / (z - omega^k) ) -> partial_sum
Comment thread
TomAFrench marked this conversation as resolved.
sum = sum.__add(summand);
}
sum
}

mod tests {
// TODO(#9982): Replace unconstrained_config with config and import ROOTS - calculating ROOTS in unconstrained is insecure.
use crate::{
Expand All @@ -447,8 +481,9 @@ mod tests {
field_to_bignum,
},
blob_public_inputs::BlobCommitment,
config::{D, D_INV, F},
config::{D, D_INV, F, ROOTS},
};
use super::{__compute_partial_sums, __compute_sum};
use bigint::{BigNum, fields::bls12_381Fr::BLS12_381_Fr_Params};
use bigint::bignum::BigNumTrait;
use types::{
Expand Down Expand Up @@ -632,7 +667,7 @@ mod tests {
let z: F = BigNum { limbs: [2, 0, 0] };

// many y's form a blob:
let mut ys: [F; FIELDS_PER_BLOB] = [BigNum::new(); FIELDS_PER_BLOB];
let mut ys: [F; FIELDS_PER_BLOB] = [BigNum::zero(); FIELDS_PER_BLOB];

ys[0] = BigNum { limbs: [0x1234, 0, 0] };
ys[1] = BigNum { limbs: [0xabcd, 0, 0] };
Expand Down Expand Up @@ -683,5 +718,19 @@ mod tests {
let d_inversed = D.__invmod();
assert_eq(d_inversed, D_INV);
}

#[test]
fn compute_sum_and_compute_partial_sums_agree() {
let mut fields = [BigNum::zero(); FIELDS_PER_BLOB];
for i in 0..FIELDS_PER_BLOB {
fields[i] = field_to_bignum(i as Field);
}
unsafe {
let partial_sums = __compute_partial_sums(fields, ROOTS);
let sum = __compute_sum(fields, ROOTS);

assert_eq(partial_sums[FIELDS_PER_BLOB / 8 - 1], sum);
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl BlobPublicInputs {

impl Empty for BlobPublicInputs {
fn empty() -> Self {
Self { z: 0, y: BigNum::new(), kzg_commitment: BlobCommitment::empty() }
Self { z: 0, y: BigNum::zero(), kzg_commitment: BlobCommitment::empty() }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ unconstrained fn compute_big_minus_arr(
}

unconstrained fn bit_reversal_permutation(arr: [F; FIELDS_PER_BLOB]) -> [F; FIELDS_PER_BLOB] {
let mut arr_brp: [F; FIELDS_PER_BLOB] = [BigNum::new(); FIELDS_PER_BLOB];
let mut arr_brp: [F; FIELDS_PER_BLOB] = [BigNum::zero(); FIELDS_PER_BLOB];
let PLUS = FIELDS_PER_BLOB >> 1;
let MINUS = PLUS >> 1;
let mut I = 0;
Expand Down Expand Up @@ -81,7 +81,7 @@ unconstrained fn bit_reversal_permutation(arr: [F; FIELDS_PER_BLOB]) -> [F; FIEL

// x ^ i for i in 0..4096
unconstrained fn compute_powers(x: F) -> [F; FIELDS_PER_BLOB] {
let mut powers: [F; FIELDS_PER_BLOB] = [BigNum::new(); FIELDS_PER_BLOB];
let mut powers: [F; FIELDS_PER_BLOB] = [BigNum::zero(); FIELDS_PER_BLOB];
let mut current_power: F = BigNum::one();
for i in 0..FIELDS_PER_BLOB {
powers[i] = current_power;
Expand Down