@@ -7,8 +7,9 @@ use std::sync::Arc;
7
7
use types:: beacon_block_body:: KzgCommitments ;
8
8
use types:: data_column_sidecar:: { Cell , DataColumn , DataColumnSidecarError } ;
9
9
use types:: {
10
- Blob , ChainSpec , ColumnIndex , DataColumnSidecar , DataColumnSidecarList , EthSpec , Hash256 ,
11
- KzgCommitment , KzgProof , KzgProofs , SignedBeaconBlock , SignedBeaconBlockHeader ,
10
+ Blob , BlobSidecar , BlobSidecarList , ChainSpec , ColumnIndex , DataColumnSidecar ,
11
+ DataColumnSidecarList , EthSpec , Hash256 , KzgCommitment , KzgProof , KzgProofs , SignedBeaconBlock ,
12
+ SignedBeaconBlockHeader , SignedBlindedBeaconBlock ,
12
13
} ;
13
14
14
15
/// Converts a blob ssz List object to an array to be used with the kzg
@@ -243,6 +244,80 @@ fn build_data_column_sidecars<E: EthSpec>(
243
244
Ok ( sidecars)
244
245
}
245
246
247
+ /// Reconstruct blobs from a subset of data column sidecars (requires at least 50%).
248
+ ///
249
+ /// If `blob_indices_opt` is `None`, this function attempts to reconstruct all blobs.
250
+ pub fn reconstruct_blobs < E : EthSpec > (
251
+ kzg : & Kzg ,
252
+ data_columns : & [ Arc < DataColumnSidecar < E > > ] ,
253
+ blob_indices_opt : Option < Vec < u64 > > ,
254
+ signed_block : & SignedBlindedBeaconBlock < E > ,
255
+ ) -> Result < BlobSidecarList < E > , String > {
256
+ let first_data_column = data_columns
257
+ . first ( )
258
+ . ok_or ( "data_columns should have at least one element" . to_string ( ) ) ?;
259
+
260
+ let blob_indices: Vec < usize > = match blob_indices_opt {
261
+ Some ( indices) => indices. into_iter ( ) . map ( |i| i as usize ) . collect ( ) ,
262
+ None => {
263
+ let num_of_blobs = first_data_column. kzg_commitments . len ( ) ;
264
+ ( 0 ..num_of_blobs) . collect ( )
265
+ }
266
+ } ;
267
+
268
+ let blob_sidecars = blob_indices
269
+ . into_par_iter ( )
270
+ . map ( |row_index| {
271
+ let mut cells: Vec < KzgCellRef > = vec ! [ ] ;
272
+ let mut cell_ids: Vec < u64 > = vec ! [ ] ;
273
+ for data_column in data_columns {
274
+ let cell = data_column
275
+ . column
276
+ . get ( row_index)
277
+ . ok_or ( format ! ( "Missing data column at index {row_index}" ) )
278
+ . and_then ( |cell| {
279
+ ssz_cell_to_crypto_cell :: < E > ( cell) . map_err ( |e| format ! ( "{e:?}" ) )
280
+ } ) ?;
281
+
282
+ cells. push ( cell) ;
283
+ cell_ids. push ( data_column. index ) ;
284
+ }
285
+
286
+ kzg. recover_cells_and_compute_kzg_proofs ( & cell_ids, & cells)
287
+ . map_err ( |e| format ! ( "Failed to recover cells and compute KZG proofs: {e:?}" ) )
288
+ . and_then ( |( cells, _kzg_proofs) | {
289
+ let num_cells_original_blob = cells. len ( ) / 2 ;
290
+ let blob_bytes = cells
291
+ . into_iter ( )
292
+ . take ( num_cells_original_blob)
293
+ . flat_map ( |cell| cell. into_iter ( ) )
294
+ . collect ( ) ;
295
+ let blob = Blob :: < E > :: new ( blob_bytes) . map_err ( |e| format ! ( "{e:?}" ) ) ?;
296
+ let kzg_commitment = first_data_column
297
+ . kzg_commitments
298
+ . get ( row_index)
299
+ . ok_or ( format ! ( "Missing KZG commitment for blob {row_index}" ) ) ?;
300
+ let kzg_proof = compute_blob_kzg_proof :: < E > ( kzg, & blob, * kzg_commitment)
301
+ . map_err ( |e| format ! ( "{e:?}" ) ) ?;
302
+
303
+ BlobSidecar :: < E > :: new_with_existing_proof (
304
+ row_index,
305
+ blob,
306
+ signed_block,
307
+ first_data_column. signed_block_header . clone ( ) ,
308
+ & first_data_column. kzg_commitments_inclusion_proof ,
309
+ kzg_proof,
310
+ )
311
+ . map ( Arc :: new)
312
+ . map_err ( |e| format ! ( "{e:?}" ) )
313
+ } )
314
+ } )
315
+ . collect :: < Result < Vec < _ > , _ > > ( ) ?
316
+ . into ( ) ;
317
+
318
+ Ok ( blob_sidecars)
319
+ }
320
+
246
321
/// Reconstruct all data columns from a subset of data column sidecars (requires at least 50%).
247
322
pub fn reconstruct_data_columns < E : EthSpec > (
248
323
kzg : & Kzg ,
@@ -289,12 +364,16 @@ pub fn reconstruct_data_columns<E: EthSpec>(
289
364
290
365
#[ cfg( test) ]
291
366
mod test {
292
- use crate :: kzg_utils:: { blobs_to_data_column_sidecars, reconstruct_data_columns} ;
367
+ use crate :: kzg_utils:: {
368
+ blobs_to_data_column_sidecars, reconstruct_blobs, reconstruct_data_columns,
369
+ } ;
293
370
use bls:: Signature ;
371
+ use eth2:: types:: BlobsBundle ;
372
+ use execution_layer:: test_utils:: generate_blobs;
294
373
use kzg:: { trusted_setup:: get_trusted_setup, Kzg , KzgCommitment , TrustedSetup } ;
295
374
use types:: {
296
- beacon_block_body:: KzgCommitments , BeaconBlock , BeaconBlockDeneb , Blob , BlobsList ,
297
- ChainSpec , EmptyBlock , EthSpec , MainnetEthSpec , SignedBeaconBlock ,
375
+ beacon_block_body:: KzgCommitments , BeaconBlock , BeaconBlockDeneb , BlobsList , ChainSpec ,
376
+ EmptyBlock , EthSpec , MainnetEthSpec , SignedBeaconBlock ,
298
377
} ;
299
378
300
379
type E = MainnetEthSpec ;
@@ -308,6 +387,7 @@ mod test {
308
387
test_build_data_columns_empty ( & kzg, & spec) ;
309
388
test_build_data_columns ( & kzg, & spec) ;
310
389
test_reconstruct_data_columns ( & kzg, & spec) ;
390
+ test_reconstruct_blobs_from_data_columns ( & kzg, & spec) ;
311
391
}
312
392
313
393
#[ track_caller]
@@ -379,6 +459,36 @@ mod test {
379
459
}
380
460
}
381
461
462
+ #[ track_caller]
463
+ fn test_reconstruct_blobs_from_data_columns ( kzg : & Kzg , spec : & ChainSpec ) {
464
+ let num_of_blobs = 6 ;
465
+ let ( signed_block, blobs) = create_test_block_and_blobs :: < E > ( num_of_blobs, spec) ;
466
+ let blob_refs = blobs. iter ( ) . collect :: < Vec < _ > > ( ) ;
467
+ let column_sidecars =
468
+ blobs_to_data_column_sidecars ( & blob_refs, & signed_block, kzg, spec) . unwrap ( ) ;
469
+
470
+ // Now reconstruct
471
+ let signed_blinded_block = signed_block. into ( ) ;
472
+ let blob_indices = vec ! [ 3 , 4 , 5 ] ;
473
+ let reconstructed_blobs = reconstruct_blobs (
474
+ kzg,
475
+ & column_sidecars. iter ( ) . as_slice ( ) [ 0 ..column_sidecars. len ( ) / 2 ] ,
476
+ Some ( blob_indices. clone ( ) ) ,
477
+ & signed_blinded_block,
478
+ )
479
+ . unwrap ( ) ;
480
+
481
+ for i in blob_indices {
482
+ let reconstructed_blob = & reconstructed_blobs
483
+ . iter ( )
484
+ . find ( |sidecar| sidecar. index == i)
485
+ . map ( |sidecar| sidecar. blob . clone ( ) )
486
+ . expect ( "reconstructed blob should exist" ) ;
487
+ let original_blob = blobs. get ( i as usize ) . unwrap ( ) ;
488
+ assert_eq ! ( reconstructed_blob, original_blob, "{i}" ) ;
489
+ }
490
+ }
491
+
382
492
fn get_kzg ( ) -> Kzg {
383
493
let trusted_setup: TrustedSetup = serde_json:: from_reader ( get_trusted_setup ( ) . as_slice ( ) )
384
494
. map_err ( |e| format ! ( "Unable to read trusted setup file: {}" , e) )
@@ -397,12 +507,20 @@ mod test {
397
507
KzgCommitments :: < E > :: new ( vec ! [ KzgCommitment :: empty_for_testing( ) ; num_of_blobs] )
398
508
. unwrap ( ) ;
399
509
400
- let signed_block = SignedBeaconBlock :: from_block ( block, Signature :: empty ( ) ) ;
510
+ let mut signed_block = SignedBeaconBlock :: from_block ( block, Signature :: empty ( ) ) ;
511
+
512
+ let ( blobs_bundle, _) = generate_blobs :: < E > ( num_of_blobs) . unwrap ( ) ;
513
+ let BlobsBundle {
514
+ blobs,
515
+ commitments,
516
+ proofs : _,
517
+ } = blobs_bundle;
401
518
402
- let blobs = ( 0 ..num_of_blobs)
403
- . map ( |_| Blob :: < E > :: default ( ) )
404
- . collect :: < Vec < _ > > ( )
405
- . into ( ) ;
519
+ * signed_block
520
+ . message_mut ( )
521
+ . body_mut ( )
522
+ . blob_kzg_commitments_mut ( )
523
+ . unwrap ( ) = commitments;
406
524
407
525
( signed_block, blobs)
408
526
}
0 commit comments