@@ -7,8 +7,8 @@ use crate::{
77};
88use bigint:: {BigNum , BLS12_381_Fr as F };
99use types:: {
10- abis::sponge_blob::SpongeBlob , constants::FIELDS_PER_BLOB , hash:: poseidon2_hash_subarray ,
11- traits::Empty , utils::arrays:: array_splice ,
10+ constants::FIELDS_PER_BLOB , hash:: poseidon2_hash_subarray , traits::Empty ,
11+ utils::arrays:: array_splice ,
1212};
1313
1414// Evaluates a single blob:
@@ -40,23 +40,24 @@ fn compute_blob_challenge(hashed_blobs_fields: Field, kzg_commitment: [Field; 2]
4040
4141// Evaluates each blob required for a block:
4242// - Hashes all fields in the block's blobs (to use for the challenges z_i)
43+ // - Checks that any fields above num_fields are empty (inside poseidon2_hash_subarray()).
4344// - Compresses each of the blob's injected commitments (")
4445// - Evaluates each blob individually to find its challenge z_i & evaluation y_i
4546// - Updates the batched blob accumulator
4647pub fn evaluate_blobs_and_batch <let NumBlobs : u32 >(
4748 blobs_as_fields : [Field ; FIELDS_PER_BLOB * NumBlobs ],
49+ num_fields : u32 ,
4850 kzg_commitments_points : [BLSPoint ; NumBlobs ],
49- mut sponge_blob : SpongeBlob ,
5051 final_blob_challenges : FinalBlobBatchingChallenges ,
5152 start_accumulator : BlobAccumulatorPublicInputs ,
52- ) -> BlobAccumulatorPublicInputs {
53+ ) -> ( BlobAccumulatorPublicInputs , Field ) {
5354 // See components.nr out_sponge definition as to why we copy here:
5455 let mut end_accumulator = start_accumulator ;
5556 // Note that with multiple blobs per block, each blob uses the same hashed_blobs_fields in:
5657 // z_i = H(hashed_blobs_fields, kzg_commitment[0], kzg_commitment[1])
5758 // This is ok, because each commitment is unique to the blob, and we need hashed_blobs_fields to encompass
5859 // all fields in the blob, which it does.
59- let hashed_blobs_fields = check_block_blob_sponge (blobs_as_fields , sponge_blob );
60+ let hashed_blobs_fields = poseidon2_hash_subarray (blobs_as_fields , num_fields );
6061 for i in 0 ..NumBlobs {
6162 let single_blob_fields = array_splice (blobs_as_fields , i * FIELDS_PER_BLOB );
6263 let c_i = compress_to_blob_commitment (kzg_commitments_points [i ]);
@@ -69,7 +70,7 @@ pub fn evaluate_blobs_and_batch<let NumBlobs: u32>(
6970 // TODO(#14646): If the evaluation being zero is sufficient to say the blob i is empty, remove the range check.
7071 // 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
7172 // 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).
72- let is_empty_blob = sponge_blob . fields <= i * FIELDS_PER_BLOB ;
73+ let is_empty_blob = num_fields <= i * FIELDS_PER_BLOB ;
7374 if (!y_i .is_zero ()) & (!is_empty_blob ) {
7475 // Only accumulate if the blob is non empty
7576 if (end_accumulator .is_empty ()) & (i == 0 ) {
@@ -90,38 +91,15 @@ pub fn evaluate_blobs_and_batch<let NumBlobs: u32>(
9091 }
9192 }
9293 }
93- end_accumulator
94- }
95-
96- // Validates this block's injected blob fields against the validated sponge propagated from the previous circuits:
97- // - Checks that we haven't accumulated too many fields over the rollup
98- // - Checks that we have absorbed the exact number of fields we claim to have accumulated
99- // - Checks that the injected fields match the tx effects absorbed over the rollup
100- // - Checks that any fields above expected_fields are empty (inside poseidon2_hash_subarray())
101- fn check_block_blob_sponge <let N : u32 >(
102- blobs_as_fields : [Field ; N ],
103- mut sponge_blob : SpongeBlob ,
104- ) -> Field {
105- // Check that we haven't overfilled the blobs
106- assert (sponge_blob .expected_fields <= N , "Attempted to overfill blobs" );
107- // Check that the blob is full
108- assert (
109- sponge_blob .expected_fields == sponge_blob .fields ,
110- "Incorrect number of tx effects added to blob" ,
111- );
112- let sponge_hash = sponge_blob .squeeze ();
113- let hash = poseidon2_hash_subarray (blobs_as_fields , sponge_blob .fields );
114- assert (hash == sponge_hash , "Mismatched hashed tx effects" );
115-
116- sponge_hash
94+ (end_accumulator , hashed_blobs_fields )
11795}
11896
11997mod tests {
12098 use crate::blob_batching_public_inputs:: {
12199 BatchingBlobCommitment , BlobAccumulatorPublicInputs , compress_to_blob_commitment ,
122100 FinalBlobBatchingChallenges ,
123101 };
124- use super:: { check_block_blob_sponge , evaluate_blobs_and_batch } ;
102+ use super::evaluate_blobs_and_batch ;
125103 use bigcurve:: {BigCurve , curves::bls12_381::BLS12_381 as BLSPoint };
126104 use bigint:: {BigNum , BLS12_381_Fr as F };
127105 use types:: {
@@ -134,7 +112,7 @@ mod tests {
134112
135113 // 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'
136114 #[test]
137- unconstrained fn test_400_batched () {
115+ unconstrained fn test_1_blob_batched () {
138116 // We evaluate 1 blob of 400 items using the batch methods.
139117 // This ensures a block with a single blob will work:
140118 let mut blob : [Field ; FIELDS_PER_BLOB ] = [0 ; FIELDS_PER_BLOB ];
@@ -143,6 +121,7 @@ mod tests {
143121 }
144122 let mut sponge_blob = SpongeBlob ::new (400 );
145123 sponge_blob .absorb (blob , 400 );
124+
146125 let kzg_commitment_in = BatchingBlobCommitment ::from_limbs (
147126 [
148127 0xa971c7e8d8292be943d05bccebcfea ,
@@ -172,13 +151,16 @@ mod tests {
172151 ]),
173152 };
174153 // Evaluation
175- let res = evaluate_blobs_and_batch (
176- pad_end (blob , 0 ),
154+ let (res , hashed_blobs_fields ) = evaluate_blobs_and_batch (
155+ pad_end (blob ),
156+ 400 ,
177157 kzg_commitments_in ,
178- sponge_blob ,
179158 final_challenges ,
180159 BlobAccumulatorPublicInputs ::empty (),
181160 );
161+
162+ assert_eq (hashed_blobs_fields , sponge_blob .squeeze ());
163+
182164 let final_acc = res .finalize_and_validate (final_challenges );
183165
184166 assert_eq (final_acc .z , final_challenges .z );
@@ -207,16 +189,17 @@ mod tests {
207189 #[test]
208190 unconstrained fn test_full_blobs_batched () {
209191 // Fill three blobs completely with different values (to avoid a constant polynomial)
210- let mut blob : [Field ; FIELDS_PER_BLOB * BLOBS_PER_BLOCK ] =
192+ let mut blob_fields : [Field ; FIELDS_PER_BLOB * BLOBS_PER_BLOCK ] =
211193 [0 ; FIELDS_PER_BLOB * BLOBS_PER_BLOCK ];
212194 for j in 0 ..BLOBS_PER_BLOCK {
213195 for i in 0 ..FIELDS_PER_BLOB {
214- blob [j * FIELDS_PER_BLOB + i ] = ((j + 3 ) * (i + 1 )) as Field ;
196+ blob_fields [j * FIELDS_PER_BLOB + i ] = ((j + 3 ) * (i + 1 )) as Field ;
215197 }
216198 }
217199 // Absorb the values into a sponge
218200 let mut sponge_blob = SpongeBlob ::new (FIELDS_PER_BLOB * BLOBS_PER_BLOCK );
219- sponge_blob .absorb (blob , FIELDS_PER_BLOB * BLOBS_PER_BLOCK );
201+ sponge_blob .absorb (blob_fields , FIELDS_PER_BLOB * BLOBS_PER_BLOCK );
202+
220203 // Init. injected values:
221204 // - Commitments are injected and checked for correctness on L1 via acc.v
222205 let kzg_commitments_in = [
@@ -280,13 +263,16 @@ mod tests {
280263 // Init. the accumulator
281264 let start_acc = BlobAccumulatorPublicInputs ::empty ();
282265 // Evaluate all three blobs and iteratively accumulate the results
283- let output = evaluate_blobs_and_batch (
284- blob ,
266+ let (output , hashed_blobs_fields ) = evaluate_blobs_and_batch (
267+ blob_fields ,
268+ blob_fields .len (),
285269 kzg_commitments_in ,
286- sponge_blob ,
287270 final_challenges ,
288271 start_acc ,
289272 );
273+
274+ assert_eq (hashed_blobs_fields , sponge_blob .squeeze ());
275+
290276 // Finalize the output (actually done in the root circuit)
291277 let final_acc = output .finalize_and_validate (final_challenges );
292278
@@ -338,33 +324,28 @@ mod tests {
338324 blob [i ] = 3 ;
339325 }
340326 // ...but the rollup's sponge is only expecting 45...
341- let mut sponge_blob = SpongeBlob ::new (45 );
342- sponge_blob .absorb (blob , 45 );
327+ let num_fields = 45 ;
343328
344329 // ...so the below should fail as it detects we are adding effects which did not come from the rollup.
345- let _ = super:: check_block_blob_sponge (blob , sponge_blob );
346- }
347-
348- #[test(should_fail_with = "Incorrect number of tx effects added to blob")]
349- unconstrained fn test_absorbed_too_few_blob_fields () {
350- let mut blob = [0 ; FIELDS_PER_BLOB ];
351- // Fill fields with 50 inputs...
352- for i in 0 ..50 {
353- blob [i ] = 3 ;
354- }
355- // ...but the rollup's sponge is expecting 100...
356- let mut sponge_blob = SpongeBlob ::new (100 );
357- sponge_blob .absorb (blob , 50 );
358-
359- // ...so the below should fail as it detects we have not added all the tx effects.
360- let _ = check_block_blob_sponge (blob , sponge_blob );
330+ let _ = evaluate_blobs_and_batch (
331+ blob ,
332+ num_fields ,
333+ [BLSPoint ::point_at_infinity ()],
334+ FinalBlobBatchingChallenges ::empty (),
335+ BlobAccumulatorPublicInputs ::empty (),
336+ );
361337 }
362338
363339 #[test]
364340 unconstrained fn test_empty_blob () {
365- let mut blob = [0 ; FIELDS_PER_BLOB ];
366- let mut sponge_blob = SpongeBlob ::new (0 );
341+ let blob = [0 ; FIELDS_PER_BLOB ];
367342 // The below should not throw
368- let _ = check_block_blob_sponge (blob , sponge_blob );
343+ let _ = evaluate_blobs_and_batch (
344+ blob ,
345+ 0 ,
346+ [BLSPoint ::point_at_infinity ()],
347+ FinalBlobBatchingChallenges ::empty (),
348+ BlobAccumulatorPublicInputs ::empty (),
349+ );
369350 }
370351}
0 commit comments