Skip to content
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
97e2a01
feat: blob batching methods - nr only
MirandaWood Apr 15, 2025
52801e6
chore: fmt, more tests, rearranging
MirandaWood Apr 16, 2025
35e8be2
feat: BLS12 field, curve methods, blob batching methods, ts only
MirandaWood Apr 16, 2025
db03339
chore: lint, cleanup
MirandaWood Apr 16, 2025
68be71e
chore: remove trusted setup file + test using it (size issues)
MirandaWood Apr 16, 2025
1fa5d49
Revert "chore: remove trusted setup file + test using it (size issues)"
MirandaWood Apr 17, 2025
33d62a7
chore: cleanup packages + increase playground size
MirandaWood Apr 17, 2025
46b8866
feat: address some comments, cleanup
MirandaWood Apr 17, 2025
346ca9a
chore: update some comments
MirandaWood Apr 21, 2025
f655bf5
Merge remote-tracking branch 'origin/mw/blob-batching' into mw/blob-b…
MirandaWood Apr 21, 2025
8d57216
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood Apr 21, 2025
9490422
chore: renaming, cleanup
MirandaWood Apr 22, 2025
c300c77
chore: renaming, cleanup
MirandaWood Apr 22, 2025
63ffe96
chore: add issue nums (hopefully force ci cache reset)
MirandaWood Apr 22, 2025
606a942
feat: as isNegative to F, rename proof -> Q
MirandaWood Apr 22, 2025
75d6d35
Merge remote-tracking branch 'origin/mw/blob-batching' into mw/blob-b…
MirandaWood Apr 22, 2025
40ffd8b
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood Apr 22, 2025
951d329
chore: bumped vite kb limit 1700 -> 1720
MirandaWood Apr 22, 2025
307cb09
chore: bumped vite kb limit 1700 -> 1750
MirandaWood Apr 22, 2025
5251cd1
feat: adding helpers, constants, docs, etc. for integration
MirandaWood Apr 24, 2025
de8ec1e
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood Apr 24, 2025
ca0da9d
Merge remote-tracking branch 'origin/mw/blob-batching' into mw/blob-b…
MirandaWood May 7, 2025
d5f11d4
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood May 7, 2025
d3ac058
chore: rename v -> blob_commitments_hash, move noir ref further up stack
MirandaWood May 13, 2025
6750476
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood May 13, 2025
8e968ac
chore: rename v to blobCommitmentsHash
MirandaWood May 13, 2025
1fc2c0d
Merge remote-tracking branch 'origin/mw/blob-batching' into mw/blob-b…
MirandaWood May 15, 2025
cda163a
chore: use updated methods from bignum, remove warnings
MirandaWood May 15, 2025
4806435
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood May 15, 2025
49c7be3
chore: switch bigcurve branch to remove visibility warnings
MirandaWood May 16, 2025
ee900fe
Merge remote-tracking branch 'origin/mw/blob-batching' into mw/blob-b…
MirandaWood May 16, 2025
2216519
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood May 16, 2025
55bc974
Merge remote-tracking branch 'origin/mw/blob-batching' into mw/blob-b…
MirandaWood May 20, 2025
df48994
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood May 20, 2025
dc1bd18
chore: fmt
MirandaWood May 20, 2025
cb146ae
fix: include is_inf in all serialization so recursion works
MirandaWood May 20, 2025
18db30a
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood May 20, 2025
3398526
chore: update import
MirandaWood May 20, 2025
e0f687a
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood May 20, 2025
5f02ae1
feat: add point compression unit test
MirandaWood May 20, 2025
480b8de
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood May 20, 2025
51899bd
chore: add fixture test for point compression, bring down new bls met…
MirandaWood May 21, 2025
4c5c437
Merge remote-tracking branch 'origin/mw/blob-batching' into mw/blob-b…
MirandaWood May 22, 2025
1b7fbf0
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood May 22, 2025
8667c5c
Merge remote-tracking branch 'origin/mw/blob-batching' into mw/blob-b…
MirandaWood May 28, 2025
ff52662
chore: bump bignum
MirandaWood May 28, 2025
0c91085
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood May 28, 2025
3064028
feat: address some comments
MirandaWood May 28, 2025
b358b3e
chore: test using toEqual in jest
MirandaWood May 29, 2025
fb8e45a
feat: init bigint and buffer, remove static compress
MirandaWood May 29, 2025
c75232b
feat: replace empty blob assumption
MirandaWood May 30, 2025
9e80ff2
feat: address some comments
MirandaWood May 30, 2025
2d1f35e
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood May 30, 2025
ea19acd
chore: add extra check before blob acc init
MirandaWood Jun 2, 2025
e89fd4a
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood Jun 2, 2025
92f6e5f
chore: renaming, bring down changes from integration branch, cleanup
MirandaWood Jun 2, 2025
88b4b28
Merge remote-tracking branch 'origin/mw/blob-batching-bls-utils' into…
MirandaWood Jun 2, 2025
0ee11fd
chore: cleanup, bring down changes from other PRs
MirandaWood Jun 2, 2025
f8ea4d2
Merge remote-tracking branch 'origin/mw/blob-batching' into mw/blob-b…
MirandaWood Jun 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
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ compiler_version = ">=0.30.0"

[dependencies]
bigint = { tag = "v0.7.2", git = "https://github.com/noir-lang/noir-bignum" }
bigcurve = { tag = "mw/bump", git = "https://github.com/noir-lang/noir_bigcurve" }
types = { path = "../types" }
poseidon = { tag = "v0.1.1", git = "https://github.com/noir-lang/poseidon" }
17 changes: 13 additions & 4 deletions noir-projects/noir-protocol-circuits/crates/blob/src/blob.nr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ use types::{
utils::arrays::array_splice,
};

fn convert_blob_fields(blob_as_fields: [Field; FIELDS_PER_BLOB]) -> [F; FIELDS_PER_BLOB] {
// TODO(MW): remove pub when fully moved to batching
pub(crate) fn convert_blob_fields(
blob_as_fields: [Field; FIELDS_PER_BLOB],
) -> [F; FIELDS_PER_BLOB] {
let mut blob: [F; FIELDS_PER_BLOB] = [F::zero(); FIELDS_PER_BLOB];
for i in 0..FIELDS_PER_BLOB {
blob[i] = F::from(blob_as_fields[i]);
Expand Down Expand Up @@ -42,7 +45,11 @@ pub fn check_block_blob_sponge(
sponge_hash
}

fn compute_challenge(hashed_blobs_fields: Field, kzg_commitment: BlobCommitment) -> Field {
// TODO(MW): remove pub when fully moved to batching
pub(crate) fn compute_challenge(
hashed_blobs_fields: Field,
kzg_commitment: BlobCommitment,
) -> Field {
let preimage = [hashed_blobs_fields, kzg_commitment.inner[0], kzg_commitment.inner[1]];
let challenge = poseidon::poseidon2::Poseidon2::hash(preimage, 3);
challenge
Expand All @@ -52,7 +59,8 @@ fn compute_challenge(hashed_blobs_fields: Field, kzg_commitment: BlobCommitment)
// we don't actually need to operate on it so we've simply encoded it as fitting inside a
// [Field; 2], since two 254-bit fields more-than covers 381+1=382 bits.
// See yarn-project/foundation/src/blob/index.ts -> commitmentToFields() for encoding
fn evaluate_blob(
// TODO(MW): remove pub when fully moved to batching
pub(crate) fn evaluate_blob(
blob_as_fields: [Field; FIELDS_PER_BLOB],
kzg_commitment: BlobCommitment,
hashed_blobs_fields: Field,
Expand Down Expand Up @@ -112,8 +120,9 @@ pub fn evaluate_blobs(
* @param ys - the many y_i's of the blob.
*
* @return y = p(z)
* TODO(MW): remove pub when fully moved to batching
*/
fn barycentric_evaluate_blob_at_z(z: F, ys: [F; FIELDS_PER_BLOB]) -> F {
pub(crate) fn barycentric_evaluate_blob_at_z(z: F, ys: [F; FIELDS_PER_BLOB]) -> F {
// Note: it's more efficient (saving 30k constraints) to compute:
// ___d-1
// \ / y_i \
Expand Down
312 changes: 312 additions & 0 deletions noir-projects/noir-protocol-circuits/crates/blob/src/blob_batching.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
use crate::{
blob::{
barycentric_evaluate_blob_at_z, check_block_blob_sponge, compute_challenge,
convert_blob_fields,
},
blob_batching_public_inputs::{
BatchingBlobCommitment, BlobAccumulationInputs, BlobAccumulatorPublicInputs, BLSPoint,
compress_to_blob_commitment,
},
blob_public_inputs::BlobCommitment,
};
use bigint::{BigNum, BLS12_381_Fr as F};
use types::{
abis::sponge_blob::SpongeBlob,
constants::{BLOBS_PER_BLOCK, FIELDS_PER_BLOB},
traits::is_empty,
utils::arrays::array_splice,
};

// Evaluates a single blob:
// - Evaluates the blob at shared challenge z and returns result y_i
// - Calculates this blob's challenge z_i (= H(H(blob_i), C_i)), where C_i = kzg_commitment, and blob_i = blob_as_fields[i].
fn evaluate_blob_for_batching(
blob_as_fields: [Field; FIELDS_PER_BLOB],
kzg_commitment: BatchingBlobCommitment,
hashed_blobs_fields: Field,
challenge_z: Field,
) -> (Field, F) {
let challenge_z_as_bignum = F::from(challenge_z);
let blob = convert_blob_fields(blob_as_fields);

let y_i: F = barycentric_evaluate_blob_at_z(challenge_z_as_bignum, blob);
let z_i: Field = compute_challenge(
hashed_blobs_fields,
// TODO(MW): At some point BatchingBlobCommitment will replace BlobCommitment and we won't need this silly conversion
BlobCommitment { inner: kzg_commitment.to_compressed_fields() },
);

(z_i, y_i)
}

// Evaluates each blob required for a block:
// - Hashes all fields in the block's blobs (to use for the challenges z_i)
// - Compresses each of the blob's injected commitments (")
// - Evaluates each blob individually to find its challenge z_i & evaluation y_i
// - Updates the batched blob accumulator
pub fn evaluate_blobs_and_batch(
blobs_as_fields: [Field; FIELDS_PER_BLOB * BLOBS_PER_BLOCK],
kzg_commitments_points: [BLSPoint; BLOBS_PER_BLOCK],
mut sponge_blob: SpongeBlob,
challenge_z: Field, // shared over epoch, verified in root
challenge_gamma: F, // shared over epoch, verified in root
start_accumulator: BlobAccumulatorPublicInputs,
) -> BlobAccumulatorPublicInputs {
// See components.nr out_sponge definition as to why we copy here:
let mut end_accumulator = start_accumulator;
// Note that with multiple blobs per block, each blob uses the same hashed_blobs_fields in:
// z_i = H(hashed_blobs_fields, kzg_commitment[0], kzg_commitment[1])
// This is ok, because each commitment is unique to the blob, and we need hashed_blobs_fields to encompass
// all fields in the blob, which it does.
let hashed_blobs_fields = check_block_blob_sponge(blobs_as_fields, sponge_blob);
for i in 0..BLOBS_PER_BLOCK {
let single_blob_fields = array_splice(blobs_as_fields, i * FIELDS_PER_BLOB);
let c_i = compress_to_blob_commitment(kzg_commitments_points[i]);
let (z_i, y_i) =
evaluate_blob_for_batching(single_blob_fields, c_i, hashed_blobs_fields, challenge_z);
// TODO(MW): If the evaluation being zero is sufficient to say the blob i is empty, remove the range check.
// The range check exists because we cannot use c_i (it's injected and L1 relies on this circuit to check whether the blob is
// empty for c_i = O) or z_i (z_i relies on the hashed_blobs_fields, which is the hash of the items in ALL block blobs, not just i).
let is_empty_blob = sponge_blob.fields <= i * FIELDS_PER_BLOB;
if (!y_i.is_zero()) & (!is_empty_blob) {
// Only accumulate if the blob is non empty
if is_empty(end_accumulator) {
// Init only if accumulator is empty:
// - This will be checked in root, where the left input's start acc will be constrained to be zero
// - No other accs can be zero since each block_merge checks left's end acc == right's start acc
end_accumulator = BlobAccumulatorPublicInputs::init(
BlobAccumulationInputs { z_i, y_i, c_i },
challenge_gamma,
);
} else {
end_accumulator = end_accumulator.accumulate(
BlobAccumulationInputs { z_i, y_i, c_i },
challenge_gamma,
);
}
}
}
end_accumulator
}

mod tests {
use crate::{
blob_batching::evaluate_blobs_and_batch,
blob_batching_public_inputs::{
BatchingBlobCommitment, BlobAccumulatorPublicInputs, compress_to_blob_commitment,
},
};
use crate::blob_batching_public_inputs::FinalBlobBatchingChallenges;
use bigcurve::{BigCurveTrait, curves::bls12_381::BLS12_381 as BLSPoint};
use bigint::{BigNum, BLS12_381_Fq as Q, BLS12_381_Fr as F};
use types::{
abis::sponge_blob::SpongeBlob,
constants::{BLOBS_PER_BLOCK, FIELDS_PER_BLOB},
hash::sha256_to_field,
tests::utils::pad_end,
traits::Empty,
};

// All hardcoded values in this test are taken from yarn-project/foundation/src/blob/blob_batching.test.ts -> 'should construct and verify a batched blob of 400 items'
#[test]
unconstrained fn test_400_batched() {
// We evaluate 1 blob of 400 items using the batch methods.
// This ensures a block with a single blob will work:
let mut blob: [Field; FIELDS_PER_BLOB] = [0; FIELDS_PER_BLOB];
for i in 0..400 {
blob[i] = 3;
}
let mut sponge_blob = SpongeBlob::new(400);
sponge_blob.absorb(blob, 400);
let kzg_commitment_in = BatchingBlobCommitment::from_limbs(
[
0xa971c7e8d8292be943d05bccebcfea,
0xcddefc3721a54895a7a45e77504dd1,
0x5fe972914ba3616033e2748bbaa6db,
0x12803d,
],
[
0x71bde5210b6cae1530202c8a928127,
0x5e7d987fb4afc5bcee960c6fc0628c,
0x64801e9aff2901eb6916e65c51f280,
0x1996ca,
],
)
.point;
let kzg_commitments_in =
[kzg_commitment_in, BLSPoint::point_at_infinity(), BLSPoint::point_at_infinity()];

let final_challenges = FinalBlobBatchingChallenges {
// = z_0
z: 0x135d767e8b86b949d264be7a6b71d257c538893f3cef60c95d76ba420df18c3c,
// = H(y_0, z_0)
gamma: F::from_limbs([
0xda0ebb0c577c62d5954852cf7a8863,
0xaac05db8dabf148f011d29f2d308e4,
0x0b28,
]),
};
// Evaluation
let res = evaluate_blobs_and_batch(
pad_end(blob, 0),
kzg_commitments_in,
sponge_blob,
final_challenges.z,
final_challenges.gamma,
BlobAccumulatorPublicInputs::empty(),
);
let final_acc = res.finalize_and_validate(final_challenges);

assert_eq(final_acc.z, final_challenges.z);
assert_eq(F::from(final_acc.gamma), final_challenges.gamma);
// Since i = 1, gamma_pow = gamma^1 = gamma:
assert_eq(final_acc.gamma_pow, final_challenges.gamma);

// y is a BLS field with value 0x212c4f0c0ee5e7dd037110686a4639d191dde7b57ab99b51e4b06e7d827b6c4c
let expected_y: F = F::from_limbs([
0xdde7b57ab99b51e4b06e7d827b6c4c,
0x4f0c0ee5e7dd037110686a4639d191,
0x212c,
]);
assert_eq(final_acc.y, expected_y);

// Since i = 1, blob_commitments_hash is just the sha256 hash of the single (compressed) commitment
let expected_blob_commitments_hash = sha256_to_field(
compress_to_blob_commitment(kzg_commitments_in[0]).compressed,
);
assert_eq(final_acc.blob_commitments_hash, expected_blob_commitments_hash);

// Since i = 1, C = gamma^0 * C_0 = C_0
assert_eq(final_acc.c, kzg_commitments_in[0]);
}

// All hardcoded values in this test are taken from yarn-project/foundation/src/blob/blob_batching.test.ts -> 'should construct and verify a batch of 3 full blobs'
#[test]
unconstrained fn test_full_blobs_batched() {
// Fill three blobs completely with different values (to avoid a constant polynomial)
let mut blob: [Field; FIELDS_PER_BLOB * BLOBS_PER_BLOCK] =
[0; FIELDS_PER_BLOB * BLOBS_PER_BLOCK];
for j in 0..BLOBS_PER_BLOCK {
for i in 0..FIELDS_PER_BLOB {
blob[j * FIELDS_PER_BLOB + i] = ((j + 3) * (i + 1)) as Field;
}
}
// Absorb the values into a sponge
let mut sponge_blob = SpongeBlob::new(FIELDS_PER_BLOB * BLOBS_PER_BLOCK);
sponge_blob.absorb(blob, FIELDS_PER_BLOB * BLOBS_PER_BLOCK);
// Init. injected values:
// - Commitments are injected and checked for correctness on L1 via acc.v
let kzg_commitments_in = [
BatchingBlobCommitment::from_limbs(
[
0x2627fc88755984d7f002e5ef0e6b3e,
0x0ea98f6a26672e17f919eb020b00ee,
0xea6e5173f2ef1bedbb07bfa9ac6ed8,
0x01c6e6,
],
[
0xaa96f04ba1d419683f218cc4f15a3f,
0x8887c5e719583b765309e4b3d18752,
0x9ff512de37b8582f7167fdfbb29539,
0x18f531,
],
)
.point,
BatchingBlobCommitment::from_limbs(
[
0x71556bb7217816fbb3f822fc873740,
0x9c57d93d7fd33a388e13e95cfdba95,
0x4f0ddbdc9d6a3653cd825ebd9f5730,
0x12324e,
],
[
0xabf9f60fc773ef1802a706d6b170a4,
0x788f9000166d54151ac05df44e63be,
0x0a8b45ead129885bb12837fb59033b,
0x12aadd,
],
)
.point,
BatchingBlobCommitment::from_limbs(
[
0x69afb94a09e713e7fb94e26f33c3ed,
0x8161293f65480c3b7bad57aaef1984,
0xc34d68dc32d1ecd46f46ec4c969bb1,
0x0d97ef,
],
[
0x9837a79d9fa4d0370198419b273360,
0x9e7340f07732e2cb3d51db22b1dcb3,
0x8285e8cad42f634bb51ad7d2c68a12,
0x07db3c,
],
)
.point,
];

let final_challenges = FinalBlobBatchingChallenges {
// - The final z value is injected and checked for correctness in root (see below final_acc)
z: 0x02d6a54e591ada73e5eea35188a02ac87779f4293ea3e7d675fa50ae7ff332ce,
// - The final gamma value is injected and checked for correctness in root (see below final_acc)
gamma: F::from_limbs([
0x281287a8d44071d216177e06a02327,
0x16571aa3dcfef75c2447c705c6c68a,
0x16f2,
]),
};
// Init. the accumulator
let start_acc = BlobAccumulatorPublicInputs::empty();
// Evaluate all three blobs and iteratively accumulate the results
let output = evaluate_blobs_and_batch(
blob,
kzg_commitments_in,
sponge_blob,
final_challenges.z,
final_challenges.gamma,
start_acc,
);
// Finalize the output (actually done in the root circuit)
let final_acc = output.finalize_and_validate(final_challenges);

assert_eq(final_acc.z, final_challenges.z);
assert_eq(F::from(final_acc.gamma), final_challenges.gamma);
assert_eq(
final_acc.gamma_pow,
final_challenges.gamma.__pow(F::from(BLOBS_PER_BLOCK as Field)),
);

// y is a BLS Fr field with value 0x0cd2fd9a46ba70fd7f212d08ec7283024b0b1ff9446b1f78a482fb7443e49b57
let expected_y = F::from_limbs([
0x0b1ff9446b1f78a482fb7443e49b57,
0xfd9a46ba70fd7f212d08ec7283024b,
0x0cd2,
]);

// C is a BLS point with value:
// x: 0x0f2f5f62cc6c3ab4c1ac1abcb9da9677e12796a76064f68c0d4f659f25a046a6d42616100269935afcb1b98c85d5e93e,
// y: 0x0af1e4abfa449daf65201c2b24507b1058d8ea9bf82ff948a1d01912615c4a8e507160da282e6c41bab917c868923254,
let expected_c = BLSPoint {
x: Q::from_limbs([
0x2616100269935afcb1b98c85d5e93e,
0x96a76064f68c0d4f659f25a046a6d4,
0x62cc6c3ab4c1ac1abcb9da9677e127,
0x0f2f5f,
]),
y: Q::from_limbs([
0x7160da282e6c41bab917c868923254,
0xea9bf82ff948a1d01912615c4a8e50,
0xabfa449daf65201c2b24507b1058d8,
0x0af1e4,
]),
is_infinity: false,
};

// blob_commitments_hash is a BN Fr field with value 0x00d2f7bffbc5a9008207a188e348e753087f54557a686efd7f74c90cac52a9a1
let expected_blob_commitments_hash =
0xd2f7bffbc5a9008207a188e348e753087f54557a686efd7f74c90cac52a9a1;

assert_eq(final_acc.y, expected_y);
assert_eq(final_acc.c, expected_c);
assert_eq(final_acc.blob_commitments_hash, expected_blob_commitments_hash);
}
}
Loading
Loading