diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactory.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactory.java index d381a31a98d..2b23505b679 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactory.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactory.java @@ -13,13 +13,16 @@ package tech.pegasys.teku.validator.coordinator; +import java.util.List; import java.util.Optional; import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.BlockContainer; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockContainer; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; public interface BlockFactory { @@ -39,4 +42,6 @@ SafeFuture createUnsignedBlock( Optional optionalGraffiti); SafeFuture unblindSignedBlockIfBlinded(SignedBeaconBlock maybeBlindedBlock); + + List createBlobSidecars(SignedBlockContainer blockContainer); } diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDeneb.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDeneb.java index 750964b4941..e9a82623c11 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDeneb.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDeneb.java @@ -24,8 +24,10 @@ import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.Blob; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.BlockContainer; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockContainer; import tech.pegasys.teku.spec.datastructures.blocks.versions.deneb.BlockContents; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; @@ -56,10 +58,10 @@ public SafeFuture createUnsignedBlock( if (blockContainer.isBlinded()) { return SafeFuture.completedFuture(blockContainer); } - // TODO: add blobs and proofs + // TODO: add blobs and proofs from the execution BlobsBundle final BeaconBlock block = blockContainer.getBlock(); return operationSelector - .createBlobSidecarsSelector() + .createBlobSidecarsSelectorOld() .apply(block) .thenApply( blobSidecars -> @@ -80,10 +82,10 @@ public SafeFuture createUnsignedBlock( if (blockContainer.isBlinded()) { return SafeFuture.completedFuture(blockContainer); } - // TODO: add blobs and proofs + // TODO: add blobs and proofs from the execution BlobsBundle final BeaconBlock block = blockContainer.getBlock(); return operationSelector - .createBlobSidecarsSelector() + .createBlobSidecarsSelectorOld() .apply(block) .thenApply( blobSidecars -> @@ -92,6 +94,11 @@ public SafeFuture createUnsignedBlock( }); } + @Override + public List createBlobSidecars(final SignedBlockContainer blockContainer) { + return operationSelector.createBlobSidecarsSelector().apply(blockContainer); + } + private BlockContents createBlockContents( final BeaconBlock block, final List blobs, final List kzgProofs) { return schemaDefinitionsDeneb.getBlockContentsSchema().create(block, kzgProofs, blobs); diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0.java index 7821e4a596a..fd0647175d2 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0.java @@ -15,15 +15,19 @@ import static com.google.common.base.Preconditions.checkArgument; +import java.util.Collections; +import java.util.List; import java.util.Optional; import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.infrastructure.async.SafeFuture; 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.BeaconBlockAndState; import tech.pegasys.teku.spec.datastructures.blocks.BlockContainer; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockContainer; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; public class BlockFactoryPhase0 implements BlockFactory { @@ -103,4 +107,9 @@ public SafeFuture unblindSignedBlockIfBlinded( } return SafeFuture.completedFuture(maybeBlindedBlock); } + + @Override + public List createBlobSidecars(final SignedBlockContainer blockContainer) { + return Collections.emptyList(); + } } diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java index bf657c6789e..8b395fcc6ac 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.validator.coordinator; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -27,13 +28,16 @@ import tech.pegasys.teku.infrastructure.ssz.SszList; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.Blob; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecarOld; -import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecarSchemaOld; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.Eth1Data; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlockUnblinder; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockContainer; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBodyBuilder; +import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.deneb.BeaconBlockBodyDeneb; import tech.pegasys.teku.spec.datastructures.builder.BuilderPayload; import tech.pegasys.teku.spec.datastructures.execution.BlobsBundle; import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload; @@ -48,7 +52,9 @@ import tech.pegasys.teku.spec.datastructures.operations.SignedVoluntaryExit; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment; +import tech.pegasys.teku.spec.datastructures.type.SszKZGProof; import tech.pegasys.teku.spec.executionlayer.ExecutionLayerBlockProductionManager; +import tech.pegasys.teku.spec.logic.versions.deneb.helpers.MiscHelpersDeneb; import tech.pegasys.teku.spec.schemas.SchemaDefinitions; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsBellatrix; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; @@ -289,7 +295,7 @@ private void builderSetKzgCommitments( if (bodyBuilder.isBlinded()) { return getBlobKzgCommitments(executionPayloadResult); } else { - return getBlobsBundle(executionPayloadResult) + return getExecutionBlobsBundle(executionPayloadResult) .thenApply( blobsBundle -> schemaDefinitionsDeneb @@ -329,38 +335,68 @@ public Consumer createBlockUnblinderSelector() { }; } - // TODO: create blob sidecars with inclusion proofs - public Function>> createBlobSidecarsSelector() { - return block -> { - final BlobSidecarSchemaOld blobSidecarSchema = - SchemaDefinitionsDeneb.required(spec.atSlot(block.getSlot()).getSchemaDefinitions()) - .getBlobSidecarOldSchema(); - return getCachedBlobsBundle(block.getSlot()) - .thenApply( - blobsBundle -> - IntStream.range(0, blobsBundle.getNumberOfBlobs()) - .mapToObj( - index -> - blobSidecarSchema.create( - block.getRoot(), - UInt64.valueOf(index), - block.getSlot(), - block.getParentRoot(), - block.getProposerIndex(), - blobsBundle.getBlobs().get(index), - blobsBundle.getCommitments().get(index), - blobsBundle.getProofs().get(index))) - .toList()); + @Deprecated + public Function>> createBlobSidecarsSelectorOld() { + return block -> SafeFuture.completedFuture(Collections.emptyList()); + } + + public Function> createBlobSidecarsSelector() { + return blockContainer -> { + final UInt64 slot = blockContainer.getSlot(); + final MiscHelpersDeneb miscHelpersDeneb = + MiscHelpersDeneb.required(spec.atSlot(slot).miscHelpers()); + + final SszList blobs; + final SszList commitments; + final SszList proofs; + + if (blockContainer.isBlinded()) { + // need to use the already available builder blobs bundle for the blinded flow, because the + // blobs and the proofs wouldn't be part of the BlockContainer + final tech.pegasys.teku.spec.datastructures.builder.BlobsBundle blobsBundle = + getCachedBuilderBlobsBundle(slot); + blobs = blobsBundle.getBlobs(); + commitments = blobsBundle.getCommitments(); + proofs = blobsBundle.getProofs(); + } else { + blobs = + blockContainer + .getBlobs() + .orElseThrow( + () -> + new IllegalStateException("Blobs are not available in " + blockContainer)); + commitments = + BeaconBlockBodyDeneb.required(blockContainer.getSignedBlock().getMessage().getBody()) + .getBlobKzgCommitments(); + proofs = + blockContainer + .getKzgProofs() + .orElseThrow( + () -> + new IllegalStateException("Proofs are not available in " + blockContainer)); + } + + return IntStream.range(0, blobs.size()) + .mapToObj( + index -> + miscHelpersDeneb.computeBlobSidecar( + blockContainer.getSignedBlock(), + UInt64.valueOf(index), + blobs.get(index), + commitments.get(index), + proofs.get(index))) + .toList(); }; } - private SafeFuture getBlobsBundle( + private SafeFuture getExecutionBlobsBundle( final ExecutionPayloadResult executionPayloadResult) { return executionPayloadResult .getBlobsBundleFuture() - .orElseThrow(this::blobsBundleIsNotAvailableException) + .orElseThrow(this::executionBlobsBundleIsNotAvailableException) .thenApply( - blobsBundle -> blobsBundle.orElseThrow(this::blobsBundleIsNotAvailableException)); + blobsBundle -> + blobsBundle.orElseThrow(this::executionBlobsBundleIsNotAvailableException)); } private SafeFuture> getBlobKzgCommitments( @@ -376,18 +412,19 @@ private SafeFuture> getBlobKzgCommitments( () -> new IllegalStateException("BlobKzgCommitments are not available"))); } - private SafeFuture getCachedBlobsBundle(final UInt64 slot) { - final ExecutionPayloadResult executionPayloadResult = getCachedPayloadResult(slot); - return getBlobsBundle(executionPayloadResult); - } - - private ExecutionPayloadResult getCachedPayloadResult(final UInt64 slot) { + private tech.pegasys.teku.spec.datastructures.builder.BlobsBundle getCachedBuilderBlobsBundle( + final UInt64 slot) { return executionLayerBlockProductionManager - .getCachedPayloadResult(slot) - .orElseThrow(() -> new IllegalStateException("ExecutionPayloadResult is not available")); + .getCachedUnblindedPayload(slot) + .orElseThrow( + () -> new IllegalStateException("BuilderPayload hasn't been cached for slot " + slot)) + .getOptionalBlobsBundle() + .orElseThrow( + () -> + new IllegalStateException("builder BlobsBundle is not available for slot " + slot)); } - private IllegalStateException blobsBundleIsNotAvailableException() { - return new IllegalStateException("BlobsBundle is not available"); + private IllegalStateException executionBlobsBundleIsNotAvailableException() { + return new IllegalStateException("execution BlobsBundle is not available"); } } diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/MilestoneBasedBlockFactory.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/MilestoneBasedBlockFactory.java index 3fee772f75d..0045ba40418 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/MilestoneBasedBlockFactory.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/MilestoneBasedBlockFactory.java @@ -15,6 +15,7 @@ import com.google.common.base.Suppliers; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Supplier; @@ -24,8 +25,10 @@ import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.BlockContainer; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockContainer; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; public class MilestoneBasedBlockFactory implements BlockFactory { @@ -89,6 +92,12 @@ public SafeFuture unblindSignedBlockIfBlinded( return registeredFactories.get(milestone).unblindSignedBlockIfBlinded(maybeBlindedBlock); } + @Override + public List createBlobSidecars(final SignedBlockContainer blockContainer) { + final SpecMilestone milestone = getMilestone(blockContainer.getSlot()); + return registeredFactories.get(milestone).createBlobSidecars(blockContainer); + } + private SpecMilestone getMilestone(final UInt64 slot) { return spec.atSlot(slot).getMilestone(); } diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisher.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisher.java index 05ad63740af..631a3b5632c 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisher.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisher.java @@ -60,10 +60,14 @@ public SafeFuture sendSignedBlock( .unblindSignedBlockIfBlinded(blockContainer.getSignedBlock()) .thenPeek(performanceTracker::saveProducedBlock) .thenCompose( - signedBlock -> - // TODO: produce blob sidecars for Deneb (using BlockFactory) - gossipAndImportUnblindedSignedBlockAndBlobSidecars( - signedBlock, List.of(), broadcastValidationLevel)) + signedBlock -> { + // creating blob sidecars after unblinding the block to ensure in the blinded flow we + // already have cached the builder payload + final List blobSidecars = + blockFactory.createBlobSidecars(blockContainer); + return gossipAndImportUnblindedSignedBlockAndBlobSidecars( + signedBlock, blobSidecars, broadcastValidationLevel); + }) .thenCompose(result -> calculateResult(blockContainer, result)); } diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java index c7c0b18a309..82a32960b52 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java @@ -38,10 +38,12 @@ import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.BlockContainer; import tech.pegasys.teku.spec.datastructures.blocks.Eth1Data; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockContainer; import tech.pegasys.teku.spec.datastructures.blocks.SlotAndBlockRoot; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.altair.BeaconBlockBodyAltair; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.altair.BeaconBlockBodySchemaAltair; @@ -291,6 +293,50 @@ protected SignedBeaconBlock blindSignedBeaconBlockIfUnblinded( return spec.blindSignedBeaconBlock(unblindedSignedBeaconBlock); } + protected List assertBlobSidecarsCreated(final boolean blinded, final Spec spec) { + final BlockFactory blockFactory = createBlockFactory(spec); + final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); + + final SignedBlockContainer signedBlockContainer; + + if (spec.isMilestoneSupported(SpecMilestone.DENEB)) { + final SszList commitments = + blobKzgCommitments.orElseGet(dataStructureUtil::randomBlobKzgCommitments); + if (blinded) { + // simulate caching of the builder payload + when(executionLayer.getCachedUnblindedPayload(any())) + .thenReturn(Optional.of(getBuilderPayload(spec))); + signedBlockContainer = + dataStructureUtil.randomSignedBlindedBeaconBlockWithCommitments(commitments); + } else { + signedBlockContainer = + dataStructureUtil.randomSignedBlockContents( + blobsBundle.orElseGet(dataStructureUtil::randomBlobsBundle)); + } + } else { + if (blinded) { + signedBlockContainer = dataStructureUtil.randomSignedBlindedBeaconBlock(); + } else { + signedBlockContainer = dataStructureUtil.randomSignedBeaconBlock(); + } + } + + final List blobSidecars = blockFactory.createBlobSidecars(signedBlockContainer); + + if (spec.isMilestoneSupported(SpecMilestone.DENEB)) { + blobKzgCommitments + .map(SszList::size) + .ifPresentOrElse( + numberOfCommitments -> assertThat(blobSidecars).hasSize(numberOfCommitments), + () -> assertThat(blobSidecars).isNotEmpty()); + + } else { + assertThat(blobSidecars).isEmpty(); + } + + return blobSidecars; + } + protected void prepareDefaultPayload(final Spec spec) { executionPayload = SchemaDefinitionsBellatrix.required(spec.getGenesisSpec().getSchemaDefinitions()) @@ -342,6 +388,23 @@ protected SszList prepareBlobKzgCommitments(final Spec spec, f return blobKzgCommitments; } + protected BuilderPayload getBuilderPayload(final Spec spec) { + // pre Deneb + if (blobsBundle.isEmpty()) { + return executionPayload; + } + // post Deneb + final SchemaDefinitionsDeneb schemaDefinitionsDeneb = + SchemaDefinitionsDeneb.required(spec.getGenesisSchemaDefinitions()); + final tech.pegasys.teku.spec.datastructures.builder.BlobsBundle builderBlobsBundle = + schemaDefinitionsDeneb + .getBlobsBundleSchema() + .createFromExecutionBlobsBundle(blobsBundle.orElseThrow()); + return schemaDefinitionsDeneb + .getExecutionPayloadAndBlobsBundleSchema() + .create(executionPayload, builderBlobsBundle); + } + private void setupExecutionLayerBlockAndBlobsProduction() { // pre Deneb when(executionLayer.initiateBlockProduction(any(), any(), eq(false))) @@ -420,21 +483,4 @@ private List getCommitmentsFromBlobsBundle() { new IllegalStateException( "Neither BlobsBundle or BlobKzgCommitments were prepared")); } - - private BuilderPayload getBuilderPayload(final Spec spec) { - // pre Deneb - if (blobsBundle.isEmpty()) { - return executionPayload; - } - // post Deneb - final SchemaDefinitionsDeneb schemaDefinitionsDeneb = - SchemaDefinitionsDeneb.required(spec.getGenesisSchemaDefinitions()); - final tech.pegasys.teku.spec.datastructures.builder.BlobsBundle builderBlobsBundle = - schemaDefinitionsDeneb - .getBlobsBundleSchema() - .createFromExecutionBlobsBundle(blobsBundle.orElseThrow()); - return schemaDefinitionsDeneb - .getExecutionPayloadAndBlobsBundleSchema() - .create(executionPayload, builderBlobsBundle); - } } diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDenebTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDenebTest.java index 078b05547e5..0a8bfd3fad5 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDenebTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryDenebTest.java @@ -16,15 +16,19 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; +import java.util.List; +import java.util.stream.IntStream; import org.apache.tuweni.bytes.Bytes32; import org.junit.jupiter.api.Test; import tech.pegasys.teku.infrastructure.ssz.SszList; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.TestSpecFactory; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.BlockContainer; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.versions.deneb.BlockContents; +import tech.pegasys.teku.spec.datastructures.execution.BlobsBundle; import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment; import tech.pegasys.teku.spec.util.DataStructureUtil; @@ -89,6 +93,51 @@ void unblindSignedBlock_shouldUnblindBeaconBlock() { assertThat(unblindedBlock).isEqualTo(expectedUnblindedBlock); } + @Test + void shouldCreateValidBlobSidecarsForBlockContents() { + final Spec spec = TestSpecFactory.createMinimalDeneb(); + final BlobsBundle blobsBundle = prepareBlobsBundle(spec, 3); + + final List blobSidecars = assertBlobSidecarsCreated(false, spec); + + IntStream.range(0, blobSidecars.size()) + .forEach( + index -> { + final BlobSidecar blobSidecar = blobSidecars.get(index); + // check sidecar is created using the prepared BlobsBundle + assertThat(blobSidecar.getKZGProof()).isEqualTo(blobsBundle.getProofs().get(index)); + assertThat(blobSidecar.getBlob()).isEqualTo(blobsBundle.getBlobs().get(index)); + assertThat(blobSidecar.getKZGCommitment()) + .isEqualTo(blobsBundle.getCommitments().get(index)); + }); + } + + @Test + void shouldCreateValidBlobSidecarsForBlindedBlock() { + final Spec spec = TestSpecFactory.createMinimalDeneb(); + final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); + + // random payload required to construct a valid BuilderPayload + executionPayload = dataStructureUtil.randomExecutionPayload(); + + final int blobsCount = 3; + final BlobsBundle blobsBundle = prepareBlobsBundle(spec, blobsCount); + + final List blobSidecars = assertBlobSidecarsCreated(true, spec); + + IntStream.range(0, blobSidecars.size()) + .forEach( + index -> { + final BlobSidecar blobSidecar = blobSidecars.get(index); + // check sidecar is created using the cached BuilderPayload and block commitments + // (based on the execution blobs bundle) + assertThat(blobSidecar.getKZGProof()).isEqualTo(blobsBundle.getProofs().get(index)); + assertThat(blobSidecar.getBlob()).isEqualTo(blobsBundle.getBlobs().get(index)); + assertThat(blobSidecar.getKZGCommitment()) + .isEqualTo(blobsBundle.getCommitments().get(index)); + }); + } + @Override public BlockFactory createBlockFactory(final Spec spec) { final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0Test.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0Test.java index 97e85b6dc09..f10e6b4a8ea 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0Test.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockFactoryPhase0Test.java @@ -163,6 +163,16 @@ void unblindSignedBlock_shouldUnblindBlockWhenBellatrixIsActive() { assertBlockUnblinded(originalBlindedSignedBlock, spec); } + @Test + void shouldCreateEmptyBlobSidecarsForBlock() { + assertBlobSidecarsCreated(false, TestSpecFactory.createMinimalPhase0()); + } + + @Test + void shouldCreateEmptyBlobSidecarsForBlindedBlock() { + assertBlobSidecarsCreated(true, TestSpecFactory.createMinimalPhase0()); + } + @Override public BlockFactory createBlockFactory(final Spec spec) { final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java index 866346bac05..ceab2878b97 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java @@ -497,24 +497,9 @@ void shouldCreateBlobSidecarsForBlockFromCachedPayloadResult() { blobsBundle); final List blobSidecars = - safeJoin(factory.createBlobSidecarsSelector().apply(block)); - - assertThat(blobSidecars) - .hasSize(blobsBundle.getNumberOfBlobs()) - .first() - .satisfies( - // assert on one of the sidecars - blobSidecar -> { - assertThat(blobSidecar.getBlockRoot()).isEqualTo(block.getRoot()); - assertThat(blobSidecar.getBlockParentRoot()).isEqualTo(block.getParentRoot()); - assertThat(blobSidecar.getIndex()).isEqualTo(UInt64.ZERO); - assertThat(blobSidecar.getSlot()).isEqualTo(block.getSlot()); - assertThat(blobSidecar.getProposerIndex()).isEqualTo(block.getProposerIndex()); - assertThat(blobSidecar.getBlob()).isEqualTo(blobsBundle.getBlobs().get(0)); - assertThat(blobSidecar.getKZGCommitment()) - .isEqualTo(blobsBundle.getCommitments().get(0)); - assertThat(blobSidecar.getKZGProof()).isEqualTo(blobsBundle.getProofs().get(0)); - }); + safeJoin(factory.createBlobSidecarsSelectorOld().apply(block)); + + assertThat(blobSidecars).isEmpty(); } private void prepareBlockProductionWithPayload( diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisherTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisherTest.java index 475ce46bea3..9ffde68cde2 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisherTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/publisher/AbstractBlockPublisherTest.java @@ -40,7 +40,6 @@ import tech.pegasys.teku.validator.coordinator.DutyMetrics; import tech.pegasys.teku.validator.coordinator.performance.PerformanceTracker; -// TODO: test blob sidecars passing when implemented public class AbstractBlockPublisherTest { private final Spec spec = TestSpecFactory.createMinimalDeneb(); private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); @@ -56,11 +55,13 @@ public class AbstractBlockPublisherTest { final SignedBlockContents signedBlockContents = dataStructureUtil.randomSignedBlockContents(); final SignedBeaconBlock signedBlock = signedBlockContents.getSignedBlock(); + final List blobSidecars = dataStructureUtil.randomBlobSidecarsForBlock(signedBlock); @BeforeEach public void setUp() { when(blockFactory.unblindSignedBlockIfBlinded(signedBlock)) .thenReturn(SafeFuture.completedFuture(signedBlock)); + when(blockFactory.createBlobSidecars(signedBlockContents)).thenReturn(blobSidecars); } @Test @@ -68,33 +69,32 @@ public void setUp() { sendSignedBlock_shouldPublishImmediatelyAndImportWhenBroadcastValidationIsNotRequired() { when(blockPublisher.importBlockAndBlobSidecars( - signedBlock, List.of(), BroadcastValidationLevel.NOT_REQUIRED)) + signedBlock, blobSidecars, BroadcastValidationLevel.NOT_REQUIRED)) .thenReturn( SafeFuture.completedFuture( new BlockImportAndBroadcastValidationResults( - SafeFuture.completedFuture( - BlockImportResult.successful(signedBlockContents.getSignedBlock()))))); + SafeFuture.completedFuture(BlockImportResult.successful(signedBlock))))); assertThatSafeFuture( blockPublisher.sendSignedBlock( signedBlockContents, BroadcastValidationLevel.NOT_REQUIRED)) .isCompletedWithValue(SendSignedBlockResult.success(signedBlockContents.getRoot())); - verify(blockPublisher).publishBlockAndBlobSidecars(signedBlock, List.of()); + verify(blockPublisher).publishBlockAndBlobSidecars(signedBlock, blobSidecars); verify(blockPublisher) - .importBlockAndBlobSidecars(signedBlock, List.of(), BroadcastValidationLevel.NOT_REQUIRED); + .importBlockAndBlobSidecars( + signedBlock, blobSidecars, BroadcastValidationLevel.NOT_REQUIRED); } @Test public void sendSignedBlock_shouldWaitToPublishWhenBroadcastValidationIsSpecified() { final SafeFuture validationResult = new SafeFuture<>(); when(blockPublisher.importBlockAndBlobSidecars( - signedBlock, List.of(), BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION)) + signedBlock, blobSidecars, BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION)) .thenReturn( SafeFuture.completedFuture( new BlockImportAndBroadcastValidationResults( - SafeFuture.completedFuture( - BlockImportResult.successful(signedBlockContents.getSignedBlock())), + SafeFuture.completedFuture(BlockImportResult.successful(signedBlock)), validationResult))); final SafeFuture sendSignedBlockResult = @@ -105,13 +105,13 @@ public void sendSignedBlock_shouldWaitToPublishWhenBroadcastValidationIsSpecifie verify(blockPublisher) .importBlockAndBlobSidecars( - signedBlock, List.of(), BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION); + signedBlock, blobSidecars, BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION); - verify(blockPublisher, never()).publishBlockAndBlobSidecars(signedBlock, List.of()); + verify(blockPublisher, never()).publishBlockAndBlobSidecars(signedBlock, blobSidecars); validationResult.complete(BroadcastValidationResult.SUCCESS); - verify(blockPublisher).publishBlockAndBlobSidecars(signedBlock, List.of()); + verify(blockPublisher).publishBlockAndBlobSidecars(signedBlock, blobSidecars); assertThatSafeFuture(sendSignedBlockResult) .isCompletedWithValue(SendSignedBlockResult.success(signedBlockContents.getRoot())); } @@ -120,12 +120,11 @@ public void sendSignedBlock_shouldWaitToPublishWhenBroadcastValidationIsSpecifie public void sendSignedBlock_shouldNotPublishWhenBroadcastValidationFails() { final SafeFuture validationResult = new SafeFuture<>(); when(blockPublisher.importBlockAndBlobSidecars( - signedBlock, List.of(), BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION)) + signedBlock, blobSidecars, BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION)) .thenReturn( SafeFuture.completedFuture( new BlockImportAndBroadcastValidationResults( - SafeFuture.completedFuture( - BlockImportResult.successful(signedBlockContents.getSignedBlock())), + SafeFuture.completedFuture(BlockImportResult.successful(signedBlock)), validationResult))); final SafeFuture sendSignedBlockResult = @@ -136,13 +135,13 @@ public void sendSignedBlock_shouldNotPublishWhenBroadcastValidationFails() { verify(blockPublisher) .importBlockAndBlobSidecars( - signedBlock, List.of(), BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION); + signedBlock, blobSidecars, BroadcastValidationLevel.CONSENSUS_AND_EQUIVOCATION); - verify(blockPublisher, never()).publishBlockAndBlobSidecars(signedBlock, List.of()); + verify(blockPublisher, never()).publishBlockAndBlobSidecars(signedBlock, blobSidecars); validationResult.complete(BroadcastValidationResult.CONSENSUS_FAILURE); - verify(blockPublisher, never()).publishBlockAndBlobSidecars(signedBlock, List.of()); + verify(blockPublisher, never()).publishBlockAndBlobSidecars(signedBlock, blobSidecars); assertThatSafeFuture(sendSignedBlockResult) .isCompletedWithValue( SendSignedBlockResult.rejected("FAILED_BROADCAST_VALIDATION: CONSENSUS_FAILURE")); 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 e3572f3d771..50fd73c8bf1 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 @@ -47,22 +47,40 @@ public BlobSidecar( final BlobSidecarSchema schema, final UInt64 index, final Blob blob, - final KZGCommitment kzgCommitment, - final KZGProof kzgProof, + final SszKZGCommitment sszKzgCommitment, + final SszKZGProof sszKzgProof, final SignedBeaconBlockHeader signedBeaconBlockHeader, final List kzgCommitmentInclusionProof) { super( schema, SszUInt64.of(index), schema.getBlobSchema().create(blob.getBytes()), - new SszKZGCommitment(kzgCommitment), - new SszKZGProof(kzgProof), + sszKzgCommitment, + sszKzgProof, signedBeaconBlockHeader, schema .getKzgCommitmentInclusionProofSchema() .createFromElements(kzgCommitmentInclusionProof.stream().map(SszBytes32::of).toList())); } + public BlobSidecar( + final BlobSidecarSchema schema, + final UInt64 index, + final Blob blob, + final KZGCommitment kzgCommitment, + final KZGProof kzgProof, + final SignedBeaconBlockHeader signedBeaconBlockHeader, + final List kzgCommitmentInclusionProof) { + this( + schema, + index, + blob, + new SszKZGCommitment(kzgCommitment), + new SszKZGProof(kzgProof), + signedBeaconBlockHeader, + kzgCommitmentInclusionProof); + } + public UInt64 getIndex() { return getField0().get(); } @@ -79,6 +97,10 @@ public KZGCommitment getKZGCommitment() { return getField2().getKZGCommitment(); } + public SszKZGProof getSszKZGProof() { + return getField3(); + } + public KZGProof getKZGProof() { return getField3().getKZGProof(); } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blobs/versions/deneb/BlobSidecarSchema.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blobs/versions/deneb/BlobSidecarSchema.java index 3cbfe26deae..c75d7b1a6a0 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blobs/versions/deneb/BlobSidecarSchema.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blobs/versions/deneb/BlobSidecarSchema.java @@ -84,6 +84,23 @@ public SszBytes32VectorSchema getKzgCommitmentInclusionProofSchema() { getChildSchema(getFieldIndex(FIELD_KZG_COMMITMENT_INCLUSION_PROOF)); } + public BlobSidecar create( + final UInt64 index, + final Blob blob, + final SszKZGCommitment sszKzgCommitment, + final SszKZGProof sszKzgProof, + final SignedBeaconBlockHeader signedBeaconBlockHeader, + final List kzgCommitmentInclusionProof) { + return new BlobSidecar( + this, + index, + blob, + sszKzgCommitment, + sszKzgProof, + signedBeaconBlockHeader, + kzgCommitmentInclusionProof); + } + public BlobSidecar create( final UInt64 index, final Bytes blob, diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDeneb.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDeneb.java index 5d5a3f3a3c5..e5a3bf71b66 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDeneb.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDeneb.java @@ -21,20 +21,26 @@ import java.util.Optional; import java.util.stream.IntStream; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.infrastructure.crypto.Hash; import tech.pegasys.teku.infrastructure.ssz.SszList; import tech.pegasys.teku.infrastructure.ssz.tree.GIndexUtil; +import tech.pegasys.teku.infrastructure.ssz.tree.MerkleUtil; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.kzg.KZG; import tech.pegasys.teku.kzg.KZGCommitment; import tech.pegasys.teku.kzg.KZGProof; import tech.pegasys.teku.spec.config.SpecConfigDeneb; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.Blob; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecarOld; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecarSchema; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.deneb.BeaconBlockBodyDeneb; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.deneb.BeaconBlockBodySchemaDeneb; +import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment; +import tech.pegasys.teku.spec.datastructures.type.SszKZGProof; import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; import tech.pegasys.teku.spec.logic.common.helpers.Predicates; import tech.pegasys.teku.spec.logic.versions.capella.helpers.MiscHelpersCapella; @@ -44,6 +50,7 @@ public class MiscHelpersDeneb extends MiscHelpersCapella { private final Predicates predicates; private final BeaconBlockBodySchemaDeneb beaconBlockBodySchema; + private final BlobSidecarSchema blobSidecarSchema; public static MiscHelpersDeneb required(final MiscHelpers miscHelpers) { return miscHelpers @@ -63,6 +70,7 @@ public MiscHelpersDeneb( this.predicates = predicates; this.beaconBlockBodySchema = (BeaconBlockBodySchemaDeneb) schemaDefinitions.getBeaconBlockBodySchema(); + this.blobSidecarSchema = schemaDefinitions.getBlobSidecarSchema(); } /** @@ -241,6 +249,20 @@ public int getBlobSidecarKzgCommitmentGeneralizedIndex(final UInt64 blobSidecarI GIndexUtil.gIdxCompose(blobKzgCommitmentsGeneralizedIndex, commitmentGeneralizedIndex); } + public BlobSidecar computeBlobSidecar( + final SignedBeaconBlock signedBeaconBlock, + final UInt64 index, + final Blob blob, + final SszKZGCommitment commitment, + final SszKZGProof proof) { + final List kzgCommitmentInclusionProof = + MerkleUtil.constructMerkleProof( + signedBeaconBlock.getMessage().getBody().getBackingNode(), + getBlobSidecarKzgCommitmentGeneralizedIndex(index)); + return blobSidecarSchema.create( + index, blob, commitment, proof, signedBeaconBlock.asHeader(), kzgCommitmentInclusionProof); + } + public boolean verifyBlobSidecarMerkleProof(final BlobSidecar blobSidecar) { return predicates.isValidMerkleBranch( blobSidecar.getSszKZGCommitment().hashTreeRoot(), diff --git a/ethereum/spec/src/property-test/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDenebPropertyTest.java b/ethereum/spec/src/property-test/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDenebPropertyTest.java index 5c78fdc8fb3..76cd40fd3b2 100644 --- a/ethereum/spec/src/property-test/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDenebPropertyTest.java +++ b/ethereum/spec/src/property-test/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDenebPropertyTest.java @@ -14,7 +14,6 @@ package tech.pegasys.teku.spec.logic.versions.deneb.helpers; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; import java.util.List; import java.util.Objects; @@ -22,30 +21,40 @@ import net.jqwik.api.From; import net.jqwik.api.Property; import net.jqwik.api.lifecycle.AddLifecycleHook; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.kzg.KZG; import tech.pegasys.teku.kzg.KZGCommitment; import tech.pegasys.teku.kzg.KZGException; +import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.config.SpecConfigDeneb; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.Blob; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; +import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; +import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment; +import tech.pegasys.teku.spec.datastructures.type.SszKZGProof; import tech.pegasys.teku.spec.logic.common.helpers.Predicates; import tech.pegasys.teku.spec.propertytest.suppliers.SpecSupplier; import tech.pegasys.teku.spec.propertytest.suppliers.blobs.versions.deneb.BlobSidecarSupplier; +import tech.pegasys.teku.spec.propertytest.suppliers.blobs.versions.deneb.BlobSupplier; +import tech.pegasys.teku.spec.propertytest.suppliers.blocks.SignedBeaconBlockSupplier; import tech.pegasys.teku.spec.propertytest.suppliers.blocks.versions.deneb.BeaconBlockSupplier; import tech.pegasys.teku.spec.propertytest.suppliers.type.KZGCommitmentSupplier; +import tech.pegasys.teku.spec.propertytest.suppliers.type.SszKZGCommitmentSupplier; +import tech.pegasys.teku.spec.propertytest.suppliers.type.SszKZGProofSupplier; +import tech.pegasys.teku.spec.propertytest.suppliers.type.UInt64Supplier; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; public class MiscHelpersDenebPropertyTest { + private final Spec spec = + Objects.requireNonNull(new SpecSupplier(SpecMilestone.DENEB).get()).sample(); private final SpecConfigDeneb specConfig = - Objects.requireNonNull(new SpecSupplier(SpecMilestone.DENEB).get()) - .sample() - .getGenesisSpecConfig() - .toVersionDeneb() - .orElseThrow(); - private final Predicates predicates = mock(Predicates.class); - private final SchemaDefinitionsDeneb schemaDefinitionsDeneb = mock(SchemaDefinitionsDeneb.class); + spec.getGenesisSpecConfig().toVersionDeneb().orElseThrow(); + private final Predicates predicates = spec.getGenesisSpec().predicates(); + private final SchemaDefinitionsDeneb schemaDefinitionsDeneb = + spec.getGenesisSchemaDefinitions().toVersionDeneb().orElseThrow(); private final MiscHelpersDeneb miscHelpers = new MiscHelpersDeneb(specConfig, predicates, schemaDefinitionsDeneb); @@ -102,4 +111,20 @@ void fuzzVerifyBlobSidecarCompleteness( assertThat(e).isInstanceOf(IllegalArgumentException.class); } } + + @Property(tries = 100) + void fuzzComputeBlobSidecar( + @ForAll(supplier = SignedBeaconBlockSupplier.class) final SignedBeaconBlock signedBlock, + @ForAll(supplier = UInt64Supplier.class) final UInt64 index, + @ForAll(supplier = BlobSupplier.class) final Blob blob, + @ForAll(supplier = SszKZGCommitmentSupplier.class) final SszKZGCommitment commitment, + @ForAll(supplier = SszKZGProofSupplier.class) final SszKZGProof proof) { + miscHelpers.computeBlobSidecar(signedBlock, index, blob, commitment, proof); + } + + @Property(tries = 100) + void fuzzVerifyBlobSidecarMerkleProof( + @ForAll(supplier = BlobSidecarSupplier.class) final BlobSidecar blobSidecar) { + miscHelpers.verifyBlobSidecarMerkleProof(blobSidecar); + } } diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDenebTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDenebTest.java index f2df823d3d8..afb770b19eb 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDenebTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDenebTest.java @@ -28,6 +28,7 @@ import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.TestSpecFactory; import tech.pegasys.teku.spec.config.SpecConfigDeneb; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.Blob; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlockHeader; @@ -35,6 +36,7 @@ import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlockHeader; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.deneb.BeaconBlockBodyDeneb; import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment; +import tech.pegasys.teku.spec.datastructures.type.SszKZGProof; import tech.pegasys.teku.spec.logic.common.helpers.Predicates; import tech.pegasys.teku.spec.logic.versions.deneb.types.VersionedHash; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; @@ -186,6 +188,30 @@ void verifyBlobSidecarCompleteness_shouldThrowWhenBlobSidecarIndexIsWrong() { "Blob sidecar index mismatch, expected 0, got %s", blobSidecars.get(0).getIndex()); } + @Test + void shouldComputeValidBlobSidecar() { + final SignedBeaconBlock signedBeaconBlock = + dataStructureUtil.randomSignedBeaconBlockWithCommitments(1); + final Blob blob = dataStructureUtil.randomBlob(); + final SszKZGCommitment commitment = + BeaconBlockBodyDeneb.required(signedBeaconBlock.getMessage().getBody()) + .getBlobKzgCommitments() + .get(0); + final SszKZGProof proof = dataStructureUtil.randomSszKZGProof(); + + final BlobSidecar blobSidecar = + miscHelpersDeneb.computeBlobSidecar( + signedBeaconBlock, UInt64.ZERO, blob, commitment, proof); + + assertThat(blobSidecar.getIndex()).isEqualTo(UInt64.ZERO); + assertThat(blobSidecar.getBlob()).isEqualTo(blob); + assertThat(blobSidecar.getSszKZGProof()).isEqualTo(proof); + assertThat(blobSidecar.getSszKZGCommitment()).isEqualTo(commitment); + assertThat(blobSidecar.getSignedBeaconBlockHeader()).isEqualTo(signedBeaconBlock.asHeader()); + // verify the merkle proof + assertThat(miscHelpersDeneb.verifyBlobSidecarMerkleProof(blobSidecar)).isTrue(); + } + @Test void verifyBlobSidecarMerkleProofShouldValidate() { final int numberOfCommitments = 4; diff --git a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java index 0fe56657903..71dbef4bbcf 100644 --- a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java +++ b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java @@ -903,6 +903,27 @@ public SignedBeaconBlock randomSignedBlindedBeaconBlock(UInt64 slotNum) { return signedBlock(beaconBlock); } + public SignedBeaconBlock randomSignedBlindedBeaconBlockWithCommitments( + final SszList commitments) { + final UInt64 proposerIndex = randomUInt64(); + final UInt64 slot = randomUInt64(); + final Bytes32 stateRoot = randomBytes32(); + final Bytes32 parentRoot = randomBytes32(); + + final BeaconBlockBody body = randomBlindedBeaconBlockBodyWithCommitments(slot, commitments); + + final BeaconBlock beaconBlock = + new BeaconBlock( + spec.atSlot(slot).getSchemaDefinitions().getBlindedBeaconBlockSchema(), + slot, + proposerIndex, + parentRoot, + stateRoot, + body); + + return signedBlock(beaconBlock); + } + public SignedBeaconBlock randomSignedBeaconBlock() { return randomSignedBeaconBlock(randomUInt64()); } @@ -945,12 +966,17 @@ public SignedBeaconBlock randomSignedBeaconBlockWithEmptyCommitments() { } public SignedBeaconBlock randomSignedBeaconBlockWithCommitments(final int count) { + return randomSignedBeaconBlockWithCommitments(randomBlobKzgCommitments(count)); + } + + public SignedBeaconBlock randomSignedBeaconBlockWithCommitments( + final SszList commitments) { final UInt64 proposerIndex = randomUInt64(); final UInt64 slot = randomUInt64(); final Bytes32 stateRoot = randomBytes32(); final Bytes32 parentRoot = randomBytes32(); - final BeaconBlockBody body = randomBeaconBlockBodyWithCommitments(count); + final BeaconBlockBody body = randomBeaconBlockBodyWithCommitments(commitments); final BeaconBlock beaconBlock = new BeaconBlock( @@ -1181,6 +1207,49 @@ public BeaconBlockBody randomBlindedBeaconBlockBody() { return randomBlindedBeaconBlockBody(randomUInt64()); } + public BeaconBlockBody randomBlindedBeaconBlockBodyWithCommitments( + final UInt64 slot, final SszList commitments) { + BeaconBlockBodySchema schema = + spec.atSlot(slot).getSchemaDefinitions().getBlindedBeaconBlockBodySchema(); + + return schema + .createBlockBody( + builder -> { + builder + .randaoReveal(randomSignature()) + .eth1Data(randomEth1Data()) + .graffiti(Bytes32.ZERO) + .proposerSlashings( + randomSszList( + schema.getProposerSlashingsSchema(), this::randomProposerSlashing, 1)) + .attesterSlashings( + randomSszList( + schema.getAttesterSlashingsSchema(), this::randomAttesterSlashing, 1)) + .attestations( + randomSszList(schema.getAttestationsSchema(), this::randomAttestation, 3)) + .deposits( + randomSszList(schema.getDepositsSchema(), this::randomDepositWithoutIndex, 1)) + .voluntaryExits( + randomSszList( + schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)) + .syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); + if (builder.supportsSyncAggregate()) { + builder.syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); + } + if (builder.supportsExecutionPayload()) { + builder.executionPayloadHeader( + SafeFuture.completedFuture(randomExecutionPayloadHeader(spec.atSlot(slot)))); + } + if (builder.supportsBlsToExecutionChanges()) { + builder.blsToExecutionChanges(randomSignedBlsToExecutionChangesList()); + } + if (builder.supportsKzgCommitments()) { + builder.blobKzgCommitments(SafeFuture.completedFuture(commitments)); + } + }) + .join(); + } + public BeaconBlockBody randomBlindedBeaconBlockBody(UInt64 slotNum) { BeaconBlockBodySchema schema = spec.atSlot(slotNum).getSchemaDefinitions().getBlindedBeaconBlockBodySchema(); @@ -1206,7 +1275,7 @@ public BeaconBlockBody randomBlindedBeaconBlockBody(UInt64 slotNum) { randomSszList( schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)); if (builder.supportsSyncAggregate()) { - builder.syncAggregate(this.randomSyncAggregateIfRequiredBySchema(schema)); + builder.syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); } if (builder.supportsExecutionPayload()) { builder.executionPayloadHeader( @@ -1247,7 +1316,7 @@ public BeaconBlockBody randomBeaconBlockBody(final UInt64 slotNum) { randomSszList( schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)); if (builder.supportsSyncAggregate()) { - builder.syncAggregate(this.randomSyncAggregateIfRequiredBySchema(schema)); + builder.syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); } if (builder.supportsExecutionPayload()) { builder.executionPayload( @@ -1287,7 +1356,7 @@ public BeaconBlockBody randomBeaconBlockBody() { randomSszList( schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)); if (builder.supportsSyncAggregate()) { - builder.syncAggregate(this.randomSyncAggregateIfRequiredBySchema(schema)); + builder.syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); } if (builder.supportsExecutionPayload()) { builder.executionPayload(SafeFuture.completedFuture(randomExecutionPayload())); @@ -1336,7 +1405,7 @@ public BeaconBlockBody randomBeaconBlockBody( randomSszList( schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)); if (builder.supportsSyncAggregate()) { - builder.syncAggregate(this.randomSyncAggregateIfRequiredBySchema(schema)); + builder.syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); } if (builder.supportsExecutionPayload()) { builder.executionPayload( @@ -1376,14 +1445,19 @@ public BeaconBlockBody randomBeaconBlockBodyWithEmptyCommitments() { .voluntaryExits( randomSszList( schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)) - .syncAggregate(this.randomSyncAggregateIfRequiredBySchema(schema)) + .syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)) .executionPayload(SafeFuture.completedFuture(randomExecutionPayload())) - .blsToExecutionChanges(this.randomSignedBlsToExecutionChangesList()) - .blobKzgCommitments(SafeFuture.completedFuture(this.emptyBlobKzgCommitments()))) + .blsToExecutionChanges(randomSignedBlsToExecutionChangesList()) + .blobKzgCommitments(SafeFuture.completedFuture(emptyBlobKzgCommitments()))) .join(); } public BeaconBlockBody randomBeaconBlockBodyWithCommitments(final int count) { + return randomBeaconBlockBodyWithCommitments(randomBlobKzgCommitments(count)); + } + + public BeaconBlockBody randomBeaconBlockBodyWithCommitments( + final SszList commitments) { BeaconBlockBodySchema schema = spec.getGenesisSpec().getSchemaDefinitions().getBeaconBlockBodySchema(); return schema @@ -1410,8 +1484,7 @@ public BeaconBlockBody randomBeaconBlockBodyWithCommitments(final int count) { .syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)) .executionPayload(SafeFuture.completedFuture(randomExecutionPayload())) .blsToExecutionChanges(randomSignedBlsToExecutionChangesList()) - .blobKzgCommitments( - SafeFuture.completedFuture(randomBlobKzgCommitments(count)))) + .blobKzgCommitments(SafeFuture.completedFuture(commitments))) .join(); } @@ -2305,6 +2378,19 @@ public SignedBlockContents randomSignedBlockContents(final UInt64 slot) { .create(signedBeaconBlock, kzgProofs, blobs); } + public SignedBlockContents randomSignedBlockContents(final BlobsBundle blobsBundle) { + final UInt64 slot = randomUInt64(); + final BlobKzgCommitmentsSchema blobKzgCommitmentsSchema = + SchemaDefinitionsDeneb.required(spec.atSlot(slot).getSchemaDefinitions()) + .getBlobKzgCommitmentsSchema(); + final SignedBeaconBlock signedBeaconBlock = + randomSignedBeaconBlockWithCommitments( + blobKzgCommitmentsSchema.createFromBlobsBundle(blobsBundle)); + return getDenebSchemaDefinitions(slot) + .getSignedBlockContentsSchema() + .create(signedBeaconBlock, blobsBundle.getProofs(), blobsBundle.getBlobs()); + } + public BlockContents randomBlockContents() { return randomBlockContents(randomSlot()); }