@@ -156,93 +156,6 @@ impl From<BeaconStateError> for GossipBlobError {
156156 }
157157}
158158
159- /// A wrapper around a `BlobSidecar` that indicates it has been approved for re-gossiping on
160- /// the p2p network.
161- #[ derive( Debug ) ]
162- pub struct GossipVerifiedBlob < T : BeaconChainTypes , O : ObservationStrategy = Observe > {
163- block_root : Hash256 ,
164- blob : KzgVerifiedBlob < T :: EthSpec > ,
165- _phantom : PhantomData < O > ,
166- }
167-
168- impl < T : BeaconChainTypes , O : ObservationStrategy > Clone for GossipVerifiedBlob < T , O > {
169- fn clone ( & self ) -> Self {
170- Self {
171- block_root : self . block_root ,
172- blob : self . blob . clone ( ) ,
173- _phantom : PhantomData ,
174- }
175- }
176- }
177-
178- impl < T : BeaconChainTypes , O : ObservationStrategy > GossipVerifiedBlob < T , O > {
179- pub fn new (
180- blob : Arc < BlobSidecar < T :: EthSpec > > ,
181- subnet_id : u64 ,
182- chain : & BeaconChain < T > ,
183- ) -> Result < Self , GossipBlobError > {
184- let header = blob. signed_block_header . clone ( ) ;
185- // We only process slashing info if the gossip verification failed
186- // since we do not process the blob any further in that case.
187- validate_blob_sidecar_for_gossip :: < T , O > ( blob, subnet_id, chain) . map_err ( |e| {
188- process_block_slash_info :: < _ , GossipBlobError > (
189- chain,
190- BlockSlashInfo :: from_early_error_blob ( header, e) ,
191- )
192- } )
193- }
194- /// Construct a `GossipVerifiedBlob` that is assumed to be valid.
195- ///
196- /// This should ONLY be used for testing.
197- pub fn __assumed_valid ( blob : Arc < BlobSidecar < T :: EthSpec > > ) -> Self {
198- Self {
199- block_root : blob. block_root ( ) ,
200- blob : KzgVerifiedBlob {
201- blob,
202- seen_timestamp : Duration :: from_secs ( 0 ) ,
203- } ,
204- _phantom : PhantomData ,
205- }
206- }
207- pub fn id ( & self ) -> BlobIdentifier {
208- BlobIdentifier {
209- block_root : self . block_root ,
210- index : self . blob . blob_index ( ) ,
211- }
212- }
213- pub fn block_root ( & self ) -> Hash256 {
214- self . block_root
215- }
216- pub fn slot ( & self ) -> Slot {
217- self . blob . blob . slot ( )
218- }
219- pub fn epoch ( & self ) -> Epoch {
220- self . blob . blob . epoch ( )
221- }
222- pub fn index ( & self ) -> u64 {
223- self . blob . blob . index
224- }
225- pub fn kzg_commitment ( & self ) -> KzgCommitment {
226- self . blob . blob . kzg_commitment
227- }
228- pub fn signed_block_header ( & self ) -> SignedBeaconBlockHeader {
229- self . blob . blob . signed_block_header . clone ( )
230- }
231- pub fn block_proposer_index ( & self ) -> u64 {
232- self . blob . blob . block_proposer_index ( )
233- }
234- pub fn into_inner ( self ) -> KzgVerifiedBlob < T :: EthSpec > {
235- self . blob
236- }
237- pub fn as_blob ( & self ) -> & BlobSidecar < T :: EthSpec > {
238- self . blob . as_blob ( )
239- }
240- /// This is cheap as we're calling clone on an Arc
241- pub fn clone_blob ( & self ) -> Arc < BlobSidecar < T :: EthSpec > > {
242- self . blob . clone_blob ( )
243- }
244- }
245-
246159/// Wrapper over a `BlobSidecar` for which we have completed kzg verification.
247160/// i.e. `verify_blob_kzg_proof(blob, commitment, proof) == true`.
248161#[ derive( Debug , Educe , Clone , Encode , Decode ) ]
@@ -281,14 +194,14 @@ impl<E: EthSpec> KzgVerifiedBlob<E> {
281194 & self . blob
282195 }
283196 pub fn get_commitment ( & self ) -> & KzgCommitment {
284- & self . blob . kzg_commitment
197+ & self . blob . kzg_commitment ( )
285198 }
286199 /// This is cheap as we're calling clone on an Arc
287200 pub fn clone_blob ( & self ) -> Arc < BlobSidecar < E > > {
288201 self . blob . clone ( )
289202 }
290203 pub fn blob_index ( & self ) -> u64 {
291- self . blob . index
204+ * self . blob . index ( )
292205 }
293206 pub fn seen_timestamp ( & self ) -> Duration {
294207 self . seen_timestamp
@@ -321,7 +234,7 @@ pub fn verify_kzg_for_blob<E: EthSpec>(
321234 kzg : & Kzg ,
322235 seen_timestamp : Duration ,
323236) -> Result < KzgVerifiedBlob < E > , KzgError > {
324- validate_blob :: < E > ( kzg, & blob. blob , blob. kzg_commitment , blob. kzg_proof ) ?;
237+ validate_blob :: < E > ( kzg, blob. blob ( ) , * blob. kzg_commitment ( ) , * blob. kzg_proof ( ) ) ?;
325238 Ok ( KzgVerifiedBlob {
326239 blob,
327240 seen_timestamp,
@@ -382,228 +295,11 @@ where
382295 I : Iterator < Item = & ' a Arc < BlobSidecar < E > > > ,
383296{
384297 let ( blobs, ( commitments, proofs) ) : ( Vec < _ > , ( Vec < _ > , Vec < _ > ) ) = blob_iter
385- . map ( |blob| ( & blob. blob , ( blob. kzg_commitment , blob. kzg_proof ) ) )
298+ . map ( |blob| ( blob. blob ( ) , ( * blob. kzg_commitment ( ) , * blob. kzg_proof ( ) ) ) )
386299 . unzip ( ) ;
387300 validate_blobs :: < E > ( kzg, commitments. as_slice ( ) , blobs, proofs. as_slice ( ) )
388301}
389302
390- pub fn validate_blob_sidecar_for_gossip < T : BeaconChainTypes , O : ObservationStrategy > (
391- blob_sidecar : Arc < BlobSidecar < T :: EthSpec > > ,
392- subnet : u64 ,
393- chain : & BeaconChain < T > ,
394- ) -> Result < GossipVerifiedBlob < T , O > , GossipBlobError > {
395- let blob_slot = blob_sidecar. slot ( ) ;
396- let blob_index = blob_sidecar. index ;
397- let block_parent_root = blob_sidecar. block_parent_root ( ) ;
398- let blob_proposer_index = blob_sidecar. block_proposer_index ( ) ;
399- let block_root = blob_sidecar. block_root ( ) ;
400- let blob_epoch = blob_slot. epoch ( T :: EthSpec :: slots_per_epoch ( ) ) ;
401- let signed_block_header = & blob_sidecar. signed_block_header ;
402-
403- let seen_timestamp = chain. slot_clock . now_duration ( ) . unwrap_or_default ( ) ;
404-
405- // This condition is not possible if we have received the blob from the network
406- // since we only subscribe to `MaxBlobsPerBlock` subnets over gossip network.
407- // We include this check only for completeness.
408- // Getting this error would imply something very wrong with our networking decoding logic.
409- if blob_index >= chain. spec . max_blobs_per_block ( blob_epoch) {
410- return Err ( GossipBlobError :: InvalidSubnet {
411- expected : subnet,
412- received : blob_index,
413- } ) ;
414- }
415-
416- // Verify that the blob_sidecar was received on the correct subnet.
417- if blob_index != subnet {
418- return Err ( GossipBlobError :: InvalidSubnet {
419- expected : subnet,
420- received : blob_index,
421- } ) ;
422- }
423-
424- // Verify that the sidecar is not from a future slot.
425- let latest_permissible_slot = chain
426- . slot_clock
427- . now_with_future_tolerance ( chain. spec . maximum_gossip_clock_disparity ( ) )
428- . ok_or ( BeaconChainError :: UnableToReadSlot ) ?;
429- if blob_slot > latest_permissible_slot {
430- return Err ( GossipBlobError :: FutureSlot {
431- message_slot : blob_slot,
432- latest_permissible_slot,
433- } ) ;
434- }
435-
436- // Verify that the sidecar slot is greater than the latest finalized slot
437- let latest_finalized_slot = chain
438- . head ( )
439- . finalized_checkpoint ( )
440- . epoch
441- . start_slot ( T :: EthSpec :: slots_per_epoch ( ) ) ;
442- if blob_slot <= latest_finalized_slot {
443- return Err ( GossipBlobError :: PastFinalizedSlot {
444- blob_slot,
445- finalized_slot : latest_finalized_slot,
446- } ) ;
447- }
448-
449- // Verify that this is the first blob sidecar received for the tuple:
450- // (block_header.slot, block_header.proposer_index, blob_sidecar.index)
451- if chain
452- . observed_blob_sidecars
453- . read ( )
454- . proposer_is_known ( & blob_sidecar)
455- . map_err ( |e| GossipBlobError :: BeaconChainError ( Box :: new ( e. into ( ) ) ) ) ?
456- {
457- return Err ( GossipBlobError :: RepeatBlob {
458- proposer : blob_proposer_index,
459- slot : blob_slot,
460- index : blob_index,
461- } ) ;
462- }
463-
464- // Verify the inclusion proof in the sidecar
465- let _timer = metrics:: start_timer ( & metrics:: BLOB_SIDECAR_INCLUSION_PROOF_VERIFICATION ) ;
466- if !blob_sidecar. verify_blob_sidecar_inclusion_proof ( ) {
467- return Err ( GossipBlobError :: InvalidInclusionProof ) ;
468- }
469- drop ( _timer) ;
470-
471- let fork_choice = chain. canonical_head . fork_choice_read_lock ( ) ;
472-
473- // We have already verified that the blob is past finalization, so we can
474- // just check fork choice for the block's parent.
475- let Some ( parent_block) = fork_choice. get_block ( & block_parent_root) else {
476- return Err ( GossipBlobError :: ParentUnknown {
477- parent_root : block_parent_root,
478- } ) ;
479- } ;
480-
481- // Do not process a blob that does not descend from the finalized root.
482- // We just loaded the parent_block, so we can be sure that it exists in fork choice.
483- if !fork_choice. is_finalized_checkpoint_or_descendant ( block_parent_root) {
484- return Err ( GossipBlobError :: NotFinalizedDescendant { block_parent_root } ) ;
485- }
486- drop ( fork_choice) ;
487-
488- if parent_block. slot >= blob_slot {
489- return Err ( GossipBlobError :: BlobIsNotLaterThanParent {
490- blob_slot,
491- parent_slot : parent_block. slot ,
492- } ) ;
493- }
494-
495- let proposer_shuffling_root =
496- parent_block. proposer_shuffling_root_for_child_block ( blob_epoch, & chain. spec ) ;
497-
498- let proposer = chain. with_proposer_cache (
499- proposer_shuffling_root,
500- blob_epoch,
501- |proposers| proposers. get_slot :: < T :: EthSpec > ( blob_slot) ,
502- || {
503- debug ! (
504- %block_root,
505- index = %blob_index,
506- "Proposer shuffling cache miss for blob verification"
507- ) ;
508- chain
509- . store
510- . get_advanced_hot_state ( block_parent_root, blob_slot, parent_block. state_root )
511- . map_err ( |e| GossipBlobError :: BeaconChainError ( Box :: new ( e. into ( ) ) ) ) ?
512- . ok_or_else ( || {
513- GossipBlobError :: BeaconChainError ( Box :: new ( BeaconChainError :: DBInconsistent (
514- format ! ( "Missing state for parent block {block_parent_root:?}" , ) ,
515- ) ) )
516- } )
517- } ,
518- ) ?;
519- let proposer_index = proposer. index ;
520- let fork = proposer. fork ;
521-
522- // Signature verify the signed block header.
523- let signature_is_valid = {
524- let pubkey_cache =
525- get_validator_pubkey_cache ( chain) . map_err ( |_| GossipBlobError :: PubkeyCacheTimeout ) ?;
526-
527- let pubkey = pubkey_cache
528- . get ( proposer_index)
529- . ok_or_else ( || GossipBlobError :: UnknownValidator ( proposer_index as u64 ) ) ?;
530- signed_block_header. verify_signature :: < T :: EthSpec > (
531- pubkey,
532- & fork,
533- chain. genesis_validators_root ,
534- & chain. spec ,
535- )
536- } ;
537-
538- if !signature_is_valid {
539- return Err ( GossipBlobError :: ProposalSignatureInvalid ) ;
540- }
541-
542- if proposer_index != blob_proposer_index as usize {
543- return Err ( GossipBlobError :: ProposerIndexMismatch {
544- sidecar : blob_proposer_index as usize ,
545- local : proposer_index,
546- } ) ;
547- }
548-
549- // Kzg verification for gossip blob sidecar
550- let kzg = chain. kzg . as_ref ( ) ;
551-
552- let kzg_verified_blob = KzgVerifiedBlob :: new ( blob_sidecar. clone ( ) , kzg, seen_timestamp)
553- . map_err ( GossipBlobError :: KzgError ) ?;
554- let blob_sidecar = & kzg_verified_blob. blob ;
555-
556- chain
557- . observed_slashable
558- . write ( )
559- . observe_slashable (
560- blob_sidecar. slot ( ) ,
561- blob_sidecar. block_proposer_index ( ) ,
562- block_root,
563- )
564- . map_err ( |e| GossipBlobError :: BeaconChainError ( Box :: new ( e. into ( ) ) ) ) ?;
565-
566- if O :: observe ( ) {
567- observe_gossip_blob ( & kzg_verified_blob. blob , chain) ?;
568- }
569-
570- Ok ( GossipVerifiedBlob {
571- block_root,
572- blob : kzg_verified_blob,
573- _phantom : PhantomData ,
574- } )
575- }
576-
577- pub fn observe_gossip_blob < T : BeaconChainTypes > (
578- blob_sidecar : & BlobSidecar < T :: EthSpec > ,
579- chain : & BeaconChain < T > ,
580- ) -> Result < ( ) , GossipBlobError > {
581- // Now the signature is valid, store the proposal so we don't accept another blob sidecar
582- // with the same `BlobIdentifier`. It's important to double-check that the proposer still
583- // hasn't been observed so we don't have a race-condition when verifying two blocks
584- // simultaneously.
585- //
586- // Note: If this BlobSidecar goes on to fail full verification, we do not evict it from the
587- // seen_cache as alternate blob_sidecars for the same identifier can still be retrieved over
588- // rpc. Evicting them from this cache would allow faster propagation over gossip. So we
589- // allow retrieval of potentially valid blocks over rpc, but try to punish the proposer for
590- // signing invalid messages. Issue for more background
591- // https://github.com/ethereum/consensus-specs/issues/3261
592- if chain
593- . observed_blob_sidecars
594- . write ( )
595- . observe_sidecar ( blob_sidecar)
596- . map_err ( |e| GossipBlobError :: BeaconChainError ( Box :: new ( e. into ( ) ) ) ) ?
597- {
598- return Err ( GossipBlobError :: RepeatBlob {
599- proposer : blob_sidecar. block_proposer_index ( ) ,
600- slot : blob_sidecar. slot ( ) ,
601- index : blob_sidecar. index ,
602- } ) ;
603- }
604- Ok ( ( ) )
605- }
606-
607303/// Returns the canonical root of the given `blob`.
608304///
609305/// Use this function to ensure that we report the blob hashing time Prometheus metric.
0 commit comments