diff --git a/beacon/sync/src/main/java/tech/pegasys/teku/beacon/sync/historical/HistoricalBatchFetcher.java b/beacon/sync/src/main/java/tech/pegasys/teku/beacon/sync/historical/HistoricalBatchFetcher.java index c8118b548d3..1330aa2e97d 100644 --- a/beacon/sync/src/main/java/tech/pegasys/teku/beacon/sync/historical/HistoricalBatchFetcher.java +++ b/beacon/sync/src/main/java/tech/pegasys/teku/beacon/sync/historical/HistoricalBatchFetcher.java @@ -401,16 +401,25 @@ private void validateBlobSidecars(final SignedBeaconBlock block) { blobSidecarsBySlotToImport.getOrDefault( block.getSlotAndBlockRoot(), Collections.emptyList()); - final boolean blobsBlockRootMatchBlockRoot = + final boolean blobsBlockHeaderRootMatchBlockRoot = blobSidecars.stream() .allMatch( - blobSidecar -> - blobSidecar - .getSignedBeaconBlockHeader() - .hashTreeRoot() - .equals(block.hashTreeRoot())); - - if (!blobsBlockRootMatchBlockRoot) { + blobSidecar -> { + final boolean signedHeaderMatchesBlock = + blobSidecar + .getSignedBeaconBlockHeader() + .hashTreeRoot() + .equals(block.hashTreeRoot()); + + if (signedHeaderMatchesBlock) { + // since we already validated block's signature, we can mark the blob sidecar's + // signature as validated + blobSidecar.markSignatureAsValidated(); + } + return signedHeaderMatchesBlock; + }); + + if (!blobsBlockHeaderRootMatchBlockRoot) { throw new IllegalArgumentException( String.format( "Blob sidecars' signed beacon block header don't match signed block for %s", diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blobs/versions/deneb/BlobSidecar.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blobs/versions/deneb/BlobSidecar.java index 812cfeb2c63..0693ce6e016 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blobs/versions/deneb/BlobSidecar.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blobs/versions/deneb/BlobSidecar.java @@ -39,6 +39,9 @@ public class BlobSidecar SignedBeaconBlockHeader, SszBytes32Vector> { + private volatile boolean kzgAndInclusionProofValidated = false; + private volatile boolean signatureValidated = false; + BlobSidecar(final BlobSidecarSchema blobSidecarSchema, final TreeNode backingTreeNode) { super(blobSidecarSchema, backingTreeNode); } @@ -139,6 +142,22 @@ public String toLogString() { getKZGProof().toAbbreviatedString()); } + public boolean isKzgAndInclusionProofValidated() { + return kzgAndInclusionProofValidated; + } + + public void markKzgAndInclusionProofAsValidated() { + kzgAndInclusionProofValidated = true; + } + + public boolean isSignatureValidated() { + return signatureValidated; + } + + public void markSignatureAsValidated() { + signatureValidated = true; + } + @Override public BlobSidecarSchema getSchema() { return (BlobSidecarSchema) super.getSchema(); diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/blobs/BlockBlobSidecarsTracker.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/blobs/BlockBlobSidecarsTracker.java index 721211524f3..a9dc3c9cadc 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/blobs/BlockBlobSidecarsTracker.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/blobs/BlockBlobSidecarsTracker.java @@ -142,6 +142,10 @@ public boolean add(final BlobSidecar blobSidecar) { blobSidecar.getBlockRoot().equals(slotAndBlockRoot.getBlockRoot()), "Wrong blobSidecar block root"); + checkArgument( + blobSidecar.isKzgAndInclusionProofValidated(), + "BlobSidecar must be validated before adding"); + if (blobSidecarsComplete.isDone()) { // already completed return false; diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/forkchoice/ForkChoiceBlobSidecarsAvailabilityChecker.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/forkchoice/ForkChoiceBlobSidecarsAvailabilityChecker.java index 0985b2227ae..ac08c648bed 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/forkchoice/ForkChoiceBlobSidecarsAvailabilityChecker.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/forkchoice/ForkChoiceBlobSidecarsAvailabilityChecker.java @@ -21,6 +21,8 @@ import tech.pegasys.teku.infrastructure.exceptions.ExceptionUtil; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.logic.versions.deneb.blobs.BlobSidecarsAndValidationResult; import tech.pegasys.teku.spec.logic.versions.deneb.blobs.BlobSidecarsAvailabilityChecker; import tech.pegasys.teku.statetransition.blobs.BlockBlobSidecarsTracker; @@ -67,10 +69,7 @@ public boolean initiateDataAvailabilityCheck() { blockBlobSidecarsTracker .getCompletionFuture() .orTimeout(waitForTrackerCompletionTimeout) - .thenApply( - __ -> - BlobSidecarsAndValidationResult.validResult( - List.copyOf(blockBlobSidecarsTracker.getBlobSidecars().values()))) + .thenApply(__ -> validateCompletedBlobSidecars()) .exceptionallyCompose( error -> ExceptionUtil.getCause(error, TimeoutException.class) @@ -88,6 +87,26 @@ public boolean initiateDataAvailabilityCheck() { return true; } + private BlobSidecarsAndValidationResult validateCompletedBlobSidecars() { + final List blobSidecars = + List.copyOf(blockBlobSidecarsTracker.getBlobSidecars().values()); + final SignedBeaconBlock block = blockBlobSidecarsTracker.getBlock().orElseThrow(); + + for (final BlobSidecar blobSidecar : blobSidecars) { + if (!blobSidecar.isKzgAndInclusionProofValidated()) { + return BlobSidecarsAndValidationResult.notAvailable( + new IllegalStateException("Blob sidecars are not validated")); + } + if (!blobSidecar.isSignatureValidated() + && blobSidecar.getSignedBeaconBlockHeader().hashTreeRoot() != block.hashTreeRoot()) { + return BlobSidecarsAndValidationResult.notAvailable( + new IllegalStateException("Blob sidecars block header does not match signed block")); + } + } + + return BlobSidecarsAndValidationResult.validResult(blobSidecars); + } + @Override public SafeFuture getAvailabilityCheckResult() { return validationResult; diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/BlobSidecarGossipValidator.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/BlobSidecarGossipValidator.java index 9baad8da958..b13094e11d1 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/BlobSidecarGossipValidator.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/BlobSidecarGossipValidator.java @@ -159,6 +159,7 @@ public SafeFuture validate(final BlobSidecar blobSidec // Optimization: If we have already completely verified BlobSidecar with the same // SignedBlockHeader, we can skip most steps and jump to shortened validation if (validSignedBlockHeaders.contains(blobSidecar.getSignedBeaconBlockHeader().hashTreeRoot())) { + blobSidecar.markSignatureAsValidated(); return validateBlobSidecarWithKnownValidHeader(blobSidecar, blockHeader); } @@ -225,6 +226,8 @@ public SafeFuture validate(final BlobSidecar blobSidec return completedFuture(reject("BlobSidecar does not pass kzg validation")); } + blobSidecar.markKzgAndInclusionProofAsValidated(); + return gossipValidationHelper .getParentStateInBlockEpoch( parentBlockSlot, blockHeader.getParentRoot(), blockHeader.getSlot()) @@ -260,6 +263,8 @@ public SafeFuture validate(final BlobSidecar blobSidec return reject("BlobSidecar block header signature is invalid."); } + blobSidecar.markSignatureAsValidated(); + /* * Checking it again at the very end because whole method is not synchronized * @@ -317,6 +322,8 @@ private SafeFuture validateBlobSidecarWithKnownValidHe reject("BlobSidecar block header does not descend from finalized checkpoint")); } + blobSidecar.markKzgAndInclusionProofAsValidated(); + /* * [IGNORE] The sidecar is the first sidecar for the tuple (block_header.slot, block_header.proposer_index, blob_sidecar.index) * with valid header signature, sidecar inclusion proof, and kzg proof. diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidator.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidator.java index ea4e96fe2b0..71032550086 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidator.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidator.java @@ -47,5 +47,7 @@ public void validate(final BlobSidecar blobSidecar) { verifyInclusionProof(blobSidecar); verifyKzg(blobSidecar); + + blobSidecar.markKzgAndInclusionProofAsValidated(); } }