Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ pub struct BlobAccumulator {
pub blob_commitments_hash_acc: Field,
// Challenge at which the batched blob polynomial is evaluated (BN254Fr)
pub z_acc: Field,
// Current state of y's linear combination (sum_i {gamma^i * y_i}) where y_i is blob_i's evaluation y.
// Current state of y's linear combination (sum_i {gamma_pow_acc_i * y_i}) where y_i is blob_i's evaluation y.
pub y_acc: BLS12_381_Fr,
// Current state of C's linear combination (sum_i {gamma^i * C_i}) where C_i is blob_i's commitment C (BLS12 point: { x: BLS12Fq, y: BLS12Fq })
// Current state of C's linear combination (sum_i {gamma_pow_acc_i * C_i}) where C_i is blob_i's commitment C (BLS12 point: { x: BLS12Fq, y: BLS12Fq })
pub c_acc: BLSPoint,
// Challenge for linear combination of each blob's y and C (BLS12Fr but represented here as BN254Fr, since it is hashed natively)
pub gamma_acc: Field,
// gamma^i for current blob, used above.
pub gamma_pow_acc: BLS12_381_Fr,
// Instead of gamma^i (from the plonk paper) we do cheaper nested hashing of gamma.
pub gamma_pow_acc: Field,
}

impl BlobAccumulator {
Expand All @@ -69,13 +69,12 @@ impl BlobAccumulator {
* - y_acc := gamma^0 * y_0 = y_0
* - c_acc := gamma^0 * c_0 = c_0
* - gamma_acc := poseidon2(y_0.limbs)
* - gamma^(i + 1) = gamma^1 = gamma // denoted gamma_pow_acc
* - gamma_pow_acc := final_gamma
*
* For all blobs i > 0 accumulated, see the below documentation for accumulate().
*
*/
pub fn init(first_output: BlobAccumulationInputs, final_gamma: BLS12_381_Fr) -> Self {
// TODO(#13608): use a BLS12 based hash? Is using BN based safe - since the output is smaller is there a skew?
pub fn init(first_output: BlobAccumulationInputs, final_gamma: Field) -> Self {
let hashed_y_0 = poseidon2_hash(first_output.y_i.get_limbs().map(|l| l as Field));
Self {
blob_commitments_hash_acc: sha256_to_field(first_output.c_i.compressed),
Expand All @@ -91,15 +90,13 @@ impl BlobAccumulator {
* LHS Accumulator: Current state of param accumulation from blob 0 to i-1
* RHS Accumulator: Outputs from evaluation of blob i
*
* NB: blob_commitments_hash is written as v below
*
* Each accumulation:
* - blob_commitments_hash_acc := sha256(blob_commitments_hash_acc, C_i)
* - z_acc := poseidon2(z_acc, z_i)
* - y_acc := y_acc + (gamma^i * y_i)
* - c_acc := c_acc + (gamma^i * c_i)
* - y_acc := y_acc + (gamma_pow_acc * y_i)
* - c_acc := c_acc + (gamma_pow_acc * c_i)
* - gamma_acc := poseidon2(gamma_acc, poseidon2(y_i.limbs))
* - gamma^(i + 1) = gamma^i * gamma // denoted gamma_pow_acc
* - gamma_pow_acc = h(gamma_pow_acc, gamma) (i lots of nested hashing)
*
* Final accumulated values (from last blob of last checkpoint of epoch):
* - blob_commitments_hash := blob_commitments_hash_acc (hash of all commitments (C_i s) to be checked on L1)
Expand All @@ -114,14 +111,15 @@ impl BlobAccumulator {
* - Checking final gamma_acc == gamma in root circuit
*
*/
pub fn accumulate(self, other: BlobAccumulationInputs, final_gamma: BLS12_381_Fr) -> Self {
// TODO(#13608): use a BLS12 based hash? Is using BN based safe - since the output is smaller is there a skew?
pub fn accumulate(self, other: BlobAccumulationInputs, final_gamma: Field) -> Self {
let hashed_y_i = poseidon2_hash(other.y_i.get_limbs().map(|l| l as Field));

// Equivalent to self.c_acc.add(other.c_i.point.mul(BLS12_381Scalar::from_bignum(self.gamma_pow_acc)))
let gamma_pow_acc_fr = BLS12_381_Fr::from(self.gamma_pow_acc);

// Equivalent to self.c_acc.add(other.c_i.point.mul(BLS12_381Scalar::from_bignum(gamma_pow_acc_fr)))
let c_acc = BLS12_381::evaluate_linear_expression(
[other.c_i.point],
[BLS12_381Scalar::from_bignum(self.gamma_pow_acc)],
[BLS12_381Scalar::from_bignum(gamma_pow_acc_fr)],
[self.c_acc],
);

Expand All @@ -131,10 +129,10 @@ impl BlobAccumulator {
.to_be_bytes::<32>()
.concat(other.c_i.compressed)),
z_acc: poseidon2_hash([self.z_acc, other.z_i]),
y_acc: self.y_acc.add(other.y_i.mul(self.gamma_pow_acc)),
y_acc: self.y_acc.add(other.y_i.mul(gamma_pow_acc_fr)),
c_acc,
gamma_acc: poseidon2_hash([self.gamma_acc, hashed_y_i]),
gamma_pow_acc: self.gamma_pow_acc.mul(final_gamma),
gamma_pow_acc: poseidon2_hash([self.gamma_pow_acc, final_gamma]),
}
}

Expand All @@ -159,7 +157,7 @@ impl Empty for BlobAccumulator {
y_acc: BLS12_381_Fr::zero(),
c_acc: BLSPoint::point_at_infinity(),
gamma_acc: 0,
gamma_pow_acc: BLS12_381_Fr::zero(),
gamma_pow_acc: 0,
}
}
}
Expand Down Expand Up @@ -195,7 +193,7 @@ impl Serialize for BlobAccumulator {
writer.write_array(self.c_acc.y.get_limbs().map(|l| l as Field));
writer.write(self.c_acc.is_infinity as Field);
writer.write(self.gamma_acc);
writer.write_array(self.gamma_pow_acc.get_limbs().map(|l| l as Field));
writer.write(self.gamma_pow_acc);
}
}

Expand Down Expand Up @@ -223,7 +221,7 @@ impl Deserialize for BlobAccumulator {
is_infinity: reader.read_bool(),
},
gamma_acc: reader.read(),
gamma_pow_acc: BLS12_381_Fr::from_limbs(reader.read_array().map(|e| e as u128)),
gamma_pow_acc: reader.read(),
}
}
}
Expand All @@ -240,7 +238,7 @@ fn serde_test() {
is_infinity: false,
},
gamma_acc: 42,
gamma_pow_acc: BLS12_381_Fr::from_limbs([14, 15, 16]),
gamma_pow_acc: 14,
};

let serialized = blob_accumulator.serialize();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use bignum::{BigNum, BLS12_381_Fr};
use types::{
constants::BLS12_FR_LIMBS,
constants::FINAL_BLOB_BATCHING_CHALLENGES_LENGTH,
traits::{Deserialize, Empty, Serialize},
utils::{reader::Reader, writer::Writer},
};
Expand All @@ -15,19 +14,19 @@ use types::{
* - where z_i = H(H(fields of blob_i), C_i),
* - used such that p_i(z) = y_i = Blob.evaluationY for all n blob polynomials p_i().
* - gamma = H(H(...H(H(y_0, y_1) y_2)..y_n), z)
* - used such that y = sum_i { gamma^i * y_i }, and C = sum_i { gamma^i * C_i }
* for all blob evaluations y_i (see above) and commitments C_i.
* - used as a basis for nested hashing to derive multipliers gamma_pow_acc_i for
* y = sum_i { gamma_pow_acc_i * y_i } and C = sum_i { gamma_pow_acc_i * C_i }.
*
* Iteratively calculated by BlobAccumulator.accumulate() above. See also precomputeBatchedBlobChallenges() in ts.
*/
pub struct FinalBlobBatchingChallenges {
pub z: Field,
pub gamma: BLS12_381_Fr,
pub gamma: Field,
}

impl Empty for FinalBlobBatchingChallenges {
fn empty() -> Self {
Self { z: 0, gamma: BLS12_381_Fr::zero() }
Self { z: 0, gamma: 0 }
}
}

Expand All @@ -38,7 +37,7 @@ impl Eq for FinalBlobBatchingChallenges {
}

impl Serialize for FinalBlobBatchingChallenges {
let N: u32 = BLS12_FR_LIMBS + 1;
let N: u32 = FINAL_BLOB_BATCHING_CHALLENGES_LENGTH;

fn serialize(self) -> [Field; Self::N] {
let mut writer: Writer<Self::N> = Writer::new();
Expand All @@ -49,12 +48,12 @@ impl Serialize for FinalBlobBatchingChallenges {
#[inline_always]
fn stream_serialize<let K: u32>(self, writer: &mut Writer<K>) {
writer.write(self.z);
writer.write_array(self.gamma.get_limbs().map(|l| l as Field));
writer.write(self.gamma);
}
}

impl Deserialize for FinalBlobBatchingChallenges {
let N: u32 = BLS12_FR_LIMBS + 1;
let N: u32 = FINAL_BLOB_BATCHING_CHALLENGES_LENGTH;

fn deserialize(fields: [Field; Self::N]) -> Self {
let mut reader = Reader::new(fields);
Expand All @@ -65,17 +64,13 @@ impl Deserialize for FinalBlobBatchingChallenges {

#[inline_always]
fn stream_deserialize<let K: u32>(reader: &mut Reader<K>) -> Self {
Self {
z: reader.read(),
gamma: BLS12_381_Fr::from_limbs(reader.read_array().map(|e| e as u128)),
}
Self { z: reader.read(), gamma: reader.read() }
}
}

#[test]
fn serde_test() {
let final_blob_batching_challenges =
FinalBlobBatchingChallenges { z: 1, gamma: BLS12_381_Fr::from_limbs([2, 3, 4]) };
let final_blob_batching_challenges = FinalBlobBatchingChallenges { z: 1, gamma: 2 };
let serialized = final_blob_batching_challenges.serialize();
let deserialized = FinalBlobBatchingChallenges::deserialize(serialized);
assert_eq(final_blob_batching_challenges, deserialized);
Expand Down
22 changes: 1 addition & 21 deletions noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ unconstrained fn __compute_fracs(z: BLS12_381_Fr) -> [BLS12_381_Fr; FIELDS_PER_B
denoms[i] = z.__sub(ROOTS[i]); // (z - w^i)
}
let inv_denoms: [BLS12_381_Fr; FIELDS_PER_BLOB] = bignum::bignum::batch_invert(denoms); // 1 / (z - w^i), for all i
// We're now done with `denoms` so we can reuse the allocated array to build `fracs`.

let mut fracs = [BLS12_381_Fr::zero(); FIELDS_PER_BLOB]; // aiming for: w^i / (z - w^i), for all i
for i in 0..FIELDS_PER_BLOB {
let inv_denom = inv_denoms[i]; // 1 / (z - w^i)
Expand Down Expand Up @@ -797,24 +797,4 @@ mod tests {
// This should fail because the constraints check each position independently
validate_fracs(z, fracs);
}

#[test(should_fail)]
fn test_validate_fracs_rejects_nonzero_frac_for_zero_y() {
// Test that when y[i] = 0, fracs[i] must also satisfy the constraint
// (which means fracs[i] * (z - w^i) = 0)
let z = BLS12_381_Fr::from_limbs([42, 0, 0]);
let ys = [BLS12_381_Fr::zero(); FIELDS_PER_BLOB]; // All zeros

// Compute correct fracs (should all be zero)
// Safety: computing correct values first
let mut fracs = unsafe { __compute_fracs(z) };

// Set fracs[0] to a non-zero value
// Since y[0] = 0, the constraint is fracs[0] * (z - w^0) = 0
// For this to hold with non-zero fracs[0], we'd need z = w^0 = 1
// But z = 42, so this should fail
fracs[0] = BLS12_381_Fr::from_limbs([999, 0, 0]);

validate_fracs(z, fracs);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,16 @@ mod tests {
};
use super::evaluate_blobs_and_batch;
use bigcurve::{BigCurve, curves::bls12_381::BLS12_381 as BLSPoint};
use bignum::{BigNum, BLS12_381_Fr};
use bignum::bignum::BigNum;
use bignum::BLS12_381_Fr;
use bignum::fields::bls12_381Fq::BLS12_381_Fq;
use types::{
constants::FIELDS_PER_BLOB,
hash::sha256_to_field,
tests::utils::{pad_end, pad_end_with},
traits::Empty,
};
use types::hash::poseidon2_hash;

#[test]
unconstrained fn test_1_blob_batched() {
Expand Down Expand Up @@ -172,8 +174,8 @@ mod tests {
0x1962ca,
];
let z_blob_400_from_ts = 0x2386cdc1cd34e082e14fa1410694c58b99c7c9b93b8effd9ae3a6b2d819854f7;
let gamma_limbs_blob_400_from_ts =
[0x0f8e4bdfc88b8bad985c43ef399030, 0xa6111cf46305c115d0c5d3ac38c51a, 0x262d];
let gamma_blob_400_from_ts =
0x262da6111cf46305c115d0c5d3ac38c51a0f8e4bdfc88b8bad985c43ef399030;
let y_limbs_blob_400_from_ts =
[0xfc33eee32e4cfe0cb39ef7578a85c6, 0x2c004486b1796de6f1403c71acdba4, 0x2784];

Expand All @@ -187,7 +189,7 @@ mod tests {
// = z_0
z: z_blob_400_from_ts,
// = H(y_0, z_0)
gamma: BLS12_381_Fr::from_limbs(gamma_limbs_blob_400_from_ts),
gamma: gamma_blob_400_from_ts,
};

let challenge_z = BLS12_381_Fr::from(final_challenges.z);
Expand All @@ -208,7 +210,7 @@ mod tests {
let final_acc = res.finalize();

assert_eq(final_acc.z, final_challenges.z);
// Since i = 1, gamma_pow = gamma^1 = gamma:
// For 1 blob, gamma_pow_acc is initialized to gamma in init():
assert_eq(res.gamma_pow_acc, final_challenges.gamma);

assert_eq(final_acc.y, BLS12_381_Fr::from_limbs(y_limbs_blob_400_from_ts));
Expand Down Expand Up @@ -274,21 +276,21 @@ mod tests {
0x0c6b4d,
];
let z_3_blobs_from_ts = 0x1d6f81c9ee5724f7e9d8eea03ba0c9e29e53913151a395a301e17b0901bfa68e;
let gamma_limbs_3_blobs_from_ts =
[0xcaac06f034aff18046f72a833b5e56, 0x14d28092a1b7b9b3a7f541251733c2, 0x1863];
let gamma_3_blobs_from_ts =
0x186314d28092a1b7b9b3a7f541251733c2caac06f034aff18046f72a833b5e56;
let y_limbs_3_blobs_from_ts =
[0xdd96a38e51f8550be2a4c5ad587664, 0xa65e19e250e80342e1b1c9ea3a5a7e, 0x0338];
[0x2e2740491a2f2b9dd4644499a3c7df, 0x3ab792aba713eaca455f94c1b3aa34, 0x33a9];
let batched_c_x_limbs_3_blobs_from_ts = [
0xba3b9220d2b505a37963a53b388fbb,
0x105b5d6a410c8e44a923538967bcf8,
0x415f4e2efbad9d9a9f461bddcc5863,
0x11c03e,
0x2e9f0e41fb49cbeeb6bc4b90bb4826,
0x91d6934a906903c9cac3a40007ef3a,
0x020813642b40773233357fde3f6f5c,
0x10f120,
];
let batched_c_y_limbs_3_blobs_from_ts = [
0x2f053c58fd82b21f0203ed94078699,
0x8c9d5eeaf252eed94085bd7897c3c2,
0x91605fcb7bb8f16d6f01a33c0fc9d4,
0x0c1b2f,
0xf2494ac2be499c87e3976e6827f18d,
0x3fe6ea2c73611ade32da7be910ec19,
0x9b8b30364e8214635803364195e052,
0x0aa8ef,
];
let blob_commitments_hash_3_blobs_from_ts =
0x00993920f366febbb5cd05c6436eda28a447447f58a1b7d713c21423790c395b;
Expand Down Expand Up @@ -317,7 +319,7 @@ mod tests {
// - The final z value is injected and checked for correctness in root (see below final_acc)
z: z_3_blobs_from_ts,
// - The final gamma value is injected and checked for correctness in root (see below final_acc)
gamma: BLS12_381_Fr::from_limbs(gamma_limbs_3_blobs_from_ts),
gamma: gamma_3_blobs_from_ts,
};

let challenge_z = BLS12_381_Fr::from(final_challenges.z);
Expand All @@ -341,10 +343,12 @@ mod tests {
let final_acc = output.finalize();

assert_eq(final_acc.z, final_challenges.z);
assert_eq(
output.gamma_pow_acc,
final_challenges.gamma.__pow(BLS12_381_Fr::from(num_blobs as Field)),
);
// gamma_pow_acc after 3 blobs = h(h(gamma, gamma), gamma)
let mut expected_gamma_pow = final_challenges.gamma;
for _ in 1..num_blobs {
expected_gamma_pow = poseidon2_hash([expected_gamma_pow, final_challenges.gamma]);
}
assert_eq(output.gamma_pow_acc, expected_gamma_pow);

let expected_y = BLS12_381_Fr::from_limbs(y_limbs_3_blobs_from_ts);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::abis::{
blob_accumulator::BlobAccumulator, final_blob_batching_challenges::FinalBlobBatchingChallenges,
};
use bignum::bignum;
use types::hash::poseidon2_hash;

/// Validate the challenges used throughout the blob accumulation against the final state of the accumulator.
Expand All @@ -12,5 +11,5 @@ pub fn validate_final_blob_batching_challenges(
assert_eq(accumulator.z_acc, challenges.z, "Final blob challenge z mismatch.");

let gamma = poseidon2_hash([accumulator.gamma_acc, accumulator.z_acc]);
assert_eq(gamma, bignum::to_field(challenges.gamma), "Final blob challenge gamma mismatch.");
assert_eq(gamma, challenges.gamma, "Final blob challenge gamma mismatch.");
}
Loading
Loading