@@ -15,10 +15,11 @@ import
1515 stew/ byteutils,
1616 # Internals
1717 ../ spec/ [
18- beaconstate, state_transition_block, forks, helpers, network, signatures],
18+ beaconstate, state_transition_block, forks,
19+ helpers, network, signatures, eip7594_helpers],
1920 ../ consensus_object_pools/ [
2021 attestation_pool, blockchain_dag, blob_quarantine, block_quarantine,
21- spec_cache, light_client_pool, sync_committee_msg_pool,
22+ data_column_quarantine, spec_cache, light_client_pool, sync_committee_msg_pool,
2223 validator_change_pool],
2324 " .." / [beacon_clock],
2425 ./ batch_validation
@@ -209,6 +210,22 @@ func check_blob_sidecar_inclusion_proof(
209210
210211 ok ()
211212
213+ func check_data_column_sidecar_inclusion_proof (
214+ data_column_sidecar: DataColumnSidecar ): Result [void , ValidationError ] =
215+ let res = data_column_sidecar.verify_data_column_sidecar_inclusion_proof ()
216+ if res.isErr:
217+ return errReject (res.error)
218+
219+ ok ()
220+
221+ proc check_data_column_sidecar_kzg_proofs (
222+ data_column_sidecar: DataColumnSidecar ): Result [void , ValidationError ] =
223+ let res = data_column_sidecar.verify_data_column_sidecar_kzg_proofs ()
224+ if res.isErr:
225+ return errReject (res.error)
226+
227+ ok ()
228+
212229# Gossip Validation
213230# ----------------------------------------------------------------
214231
@@ -475,6 +492,134 @@ proc validateBlobSidecar*(
475492
476493 ok ()
477494
495+ # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/_features/eip7594/p2p-interface.md#data_column_sidecar_subnet_id
496+ proc validateDataColumnSidecar * (
497+ dag: ChainDAGRef , quarantine: ref Quarantine ,
498+ dataColumnQuarantine: ref DataColumnQuarantine ,
499+ data_column_sidecar: DataColumnSidecar ,
500+ wallTime: BeaconTime , subnet_id: uint64 ):
501+ Result [void , ValidationError ] =
502+
503+ template block_header : untyped = data_column_sidecar.signed_block_header.message
504+
505+ # [REJECT] The sidecar's index is consistent with `NUMBER_OF_COLUMNS`
506+ # -- i.e. `data_column_sidecar.index < NUMBER_OF_COLUMNS`
507+ if not (data_column_sidecar.index < NUMBER_OF_COLUMNS ):
508+ return dag.checkedReject (" DataColumnSidecar: The sidecar's index should be consistent with NUMBER_OF_COLUMNS" )
509+
510+ # [REJECT] The sidecar is for the correct subnet
511+ # -- i.e. `compute_subnet_for_data_column_sidecar(blob_sidecar.index) == subnet_id`.
512+ if not (compute_subnet_for_data_column_sidecar (data_column_sidecar.index) == subnet_id):
513+ return dag.checkedReject (" DataColumnSidecar: The sidecar is not for the correct subnet" )
514+
515+ # [IGNORE] The sidecar is not from a future slot
516+ # (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that
517+ # `block_header.slot <= current_slot`(a client MAY queue future sidecars for
518+ # processing at the appropriate slot).
519+ if not (block_header.slot <=
520+ (wallTime + MAXIMUM_GOSSIP_CLOCK_DISPARITY ).slotOrZero):
521+ return errIgnore (" DataColumnSidecar: slot too high" )
522+
523+ # [IGNORE] The sidecar is from a slot greater than the latest
524+ # finalized slot -- i.e. validate that `block_header.slot >
525+ # compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)`
526+ if not (block_header.slot > dag.finalizedHead.slot):
527+ return errIgnore (" DataColumnSidecar: slot already finalized" )
528+
529+ # [IGNORE] The sidecar is the first sidecar for the tuple
530+ # (block_header.slot, block_header.proposer_index, data_column_sidecar.index)
531+ # with valid header signature, sidecar inclusion proof, and kzg proof.
532+ let block_root = hash_tree_root (block_header)
533+ if dag.getBlockRef (block_root).isSome ():
534+ return errIgnore (" DataColumnSidecar: already have block" )
535+ if dataColumnQuarantine[].hasDataColumn (
536+ block_header.slot, block_header.proposer_index, data_column_sidecar.index):
537+ return errIgnore (" DataColumnSidecar: already have valid data column from same proposer" )
538+
539+ # [REJECT] The sidecar's `kzg_commitments` inclusion proof is valid as verified by
540+ # `verify_data_column_sidecar_inclusion_proof(sidecar)`.
541+ block :
542+ let v = check_data_column_sidecar_inclusion_proof (data_column_sidecar)
543+ if v.isErr:
544+ return dag.checkedReject (v.error)
545+
546+ # [IGNORE] The sidecar's block's parent (defined by
547+ # `block_header.parent_root`) has been seen (via both gossip and
548+ # non-gossip sources) (a client MAY queue sidecars for processing
549+ # once the parent block is retrieved).
550+ #
551+ # [REJECT] The sidecar's block's parent (defined by
552+ # `block_header.parent_root`) passes validation.
553+ let parent = dag.getBlockRef (block_header.parent_root).valueOr:
554+ if block_header.parent_root in quarantine[].unviable:
555+ quarantine[].addUnviable (block_root)
556+ return dag.checkedReject (" DataColumnSidecar: parent not validated" )
557+ else :
558+ quarantine[].addMissing (block_header.parent_root)
559+ return errIgnore (" DataColumnSidecar: parent not found" )
560+
561+ # [REJECT] The sidecar is from a higher slot than the sidecar's
562+ # block's parent (defined by `block_header.parent_root`).
563+ if not (block_header.slot > parent.bid.slot):
564+ return dag.checkedReject (" DataColumnSidecar: slot lower than parents'" )
565+
566+ # [REJECT] The current finalized_checkpoint is an ancestor of the sidecar's
567+ # block -- i.e. `get_checkpoint_block(store, block_header.parent_root,
568+ # store.finalized_checkpoint.epoch) == store.finalized_checkpoint.root`.
569+ let
570+ finalized_checkpoint = getStateField (dag.headState, finalized_checkpoint)
571+ ancestor = get_ancestor (parent, finalized_checkpoint.epoch.start_slot)
572+
573+ if ancestor.isNil:
574+ # This shouldn't happen: we should always be able to trace the parent back
575+ # to the finalized checkpoint (else it wouldn't be in the DAG)
576+ return errIgnore (" DataColumnSidecar: Can't find ancestor" )
577+
578+ if not (
579+ finalized_checkpoint.root == ancestor.root or
580+ finalized_checkpoint.root.isZero):
581+ quarantine[].addUnviable (block_root)
582+ return dag.checkedReject (
583+ " DataColumnSidecar: Finalized checkpoint not an ancestor" )
584+
585+ # [REJECT] The sidecar is proposed by the expected `proposer_index`
586+ # for the block's slot in the context of the current shuffling
587+ # (defined by `block_header.parent_root`/`block_header.slot`).
588+ # If the proposer_index cannot immediately be verified against the expected
589+ # shuffling, the sidecar MAY be queued for later processing while proposers
590+ # for the block's branch are calculated -- in such a case do not
591+ # REJECT, instead IGNORE this message.
592+ let proposer = getProposer (dag, parent, block_header.slot).valueOr:
593+ warn " cannot compute proposer for data column"
594+ return errIgnore (" DataColumnSidecar: Cannot compute proposer" ) # internal issue
595+
596+ if uint64 (proposer) != block_header.proposer_index:
597+ return dag.checkedReject (" DataColumnSidecar: Unexpected proposer" )
598+
599+ # [REJECT] The proposer signature of `data_column_sidecar.signed_block_header`,
600+ # is valid with respect to the `block_header.proposer_index` pubkey.
601+ if not verify_block_signature (
602+ dag.forkAtEpoch (block_header.slot.epoch),
603+ getStateField (dag.headState, genesis_validators_root),
604+ block_header.slot,
605+ block_root,
606+ dag.validatorKey (proposer).get (),
607+ data_column_sidecar.signed_block_header.signature):
608+ return dag.checkedReject (" DataColumnSidecar: Invalid proposer signature" )
609+
610+ # [REJECT] The sidecar's column data is valid as
611+ # verified by `verify_data_column_kzg_proofs(sidecar)`
612+ block :
613+ let r = check_data_column_sidecar_kzg_proofs (data_column_sidecar)
614+ if r.isErr:
615+ return dag.checkedReject (r.error)
616+
617+ # Send notification about new data column sidecar via callback
618+ if not (isNil (dataColumnQuarantine.onDataColumnSidecarCallback)):
619+ dataColumnQuarantine.onDataColumnSidecarCallback (data_column_sidecar)
620+
621+ ok ()
622+
478623# https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/phase0/p2p-interface.md#beacon_block
479624# https://github.com/ethereum/consensus-specs/blob/v1.3.0/specs/bellatrix/p2p-interface.md#beacon_block
480625proc validateBeaconBlock * (
0 commit comments