From c560084143ac88655537c200a5a0c4a1a6d98ee3 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Wed, 23 Oct 2024 21:47:44 +0200 Subject: [PATCH 1/2] Add BlobSidecar inclusion proof verification in req/resp --- .../teku/spec/util/DataStructureUtil.java | 24 ++++ .../AbstractBlobSidecarsValidator.java | 22 +++ ...idecarsByRangeListenerValidatingProxy.java | 1 + .../methods/BlobSidecarsByRootValidator.java | 1 + ...ecarsResponseInvalidResponseException.java | 2 + ...arsByRangeListenerValidatingProxyTest.java | 129 +++++++++++++----- .../BlobSidecarsByRootValidatorTest.java | 53 ++++++- 7 files changed, 195 insertions(+), 37 deletions(-) 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 f864a53f41f..b18347760c4 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 @@ -192,6 +192,7 @@ import tech.pegasys.teku.spec.datastructures.validator.BeaconPreparableProposer; import tech.pegasys.teku.spec.executionlayer.ForkChoiceState; import tech.pegasys.teku.spec.executionlayer.PayloadBuildingAttributes; +import tech.pegasys.teku.spec.logic.versions.deneb.helpers.MiscHelpersDeneb; import tech.pegasys.teku.spec.logic.versions.deneb.types.VersionedHash; import tech.pegasys.teku.spec.schemas.SchemaDefinitions; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsAltair; @@ -2251,6 +2252,23 @@ public BlobSidecar randomBlobSidecarForBlock( .build(); } + public BlobSidecar randomBlobSidecarWithValidInclusionProofForBlock( + final SignedBeaconBlock signedBeaconBlock, final int index) { + return new RandomBlobSidecarBuilder() + .signedBeaconBlockHeader(signedBeaconBlock.asHeader()) + .index(UInt64.valueOf(index)) + .kzgCommitment( + BeaconBlockBodyDeneb.required(signedBeaconBlock.getMessage().getBody()) + .getBlobKzgCommitments() + .get(index) + .getKZGCommitment() + .getBytesCompressed()) + .kzgCommitmentInclusionProof( + validKzgCommitmentInclusionProof( + UInt64.valueOf(index), signedBeaconBlock.getBeaconBlock().orElseThrow().getBody())) + .build(); + } + public List randomBlobs(final int count, final UInt64 slot) { final List blobs = new ArrayList<>(); final BlobSchema blobSchema = getDenebSchemaDefinitions(slot).getBlobSchema(); @@ -2483,6 +2501,12 @@ public List randomKzgCommitmentInclusionProof() { return IntStream.range(0, depth).mapToObj(__ -> randomBytes32()).toList(); } + public List validKzgCommitmentInclusionProof( + final UInt64 blobIndex, final BeaconBlockBody beaconBlockBody) { + return MiscHelpersDeneb.required(spec.forMilestone(SpecMilestone.DENEB).miscHelpers()) + .computeKzgCommitmentInclusionProof(blobIndex, beaconBlockBody); + } + public SszList randomBlobKzgCommitments() { // use MAX_BLOBS_PER_BLOCK as a limit return randomBlobKzgCommitments(randomNumberOfBlobsPerBlock()); diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/AbstractBlobSidecarsValidator.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/AbstractBlobSidecarsValidator.java index 51a4748c941..368f50d5e32 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/AbstractBlobSidecarsValidator.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/AbstractBlobSidecarsValidator.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods; +import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_INCLUSION_PROOF_VERIFICATION_FAILED; import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_KZG_VERIFICATION_FAILED; import org.apache.logging.log4j.LogManager; @@ -21,6 +22,7 @@ import tech.pegasys.teku.networking.p2p.peer.Peer; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; +import tech.pegasys.teku.spec.logic.versions.deneb.helpers.MiscHelpersDeneb; public class AbstractBlobSidecarsValidator { @@ -43,6 +45,13 @@ protected void verifyKzg(final BlobSidecar blobSidecar) { } } + protected void verifyInclusionProof(final BlobSidecar blobSidecar) { + if (!verifyBlobSidecarInclusionProof(blobSidecar)) { + throw new BlobSidecarsResponseInvalidResponseException( + peer, BLOB_SIDECAR_INCLUSION_PROOF_VERIFICATION_FAILED); + } + } + private boolean verifyBlobKzgProof(final BlobSidecar blobSidecar) { try { return spec.atSlot(blobSidecar.getSlot()).miscHelpers().verifyBlobKzgProof(kzg, blobSidecar); @@ -52,4 +61,17 @@ private boolean verifyBlobKzgProof(final BlobSidecar blobSidecar) { peer, BLOB_SIDECAR_KZG_VERIFICATION_FAILED, ex); } } + + private boolean verifyBlobSidecarInclusionProof(final BlobSidecar blobSidecar) { + try { + return MiscHelpersDeneb.required(spec.atSlot(blobSidecar.getSlot()).miscHelpers()) + .verifyBlobSidecarMerkleProof(blobSidecar); + } catch (final Exception ex) { + LOG.debug( + "Block inclusion proof verification failed for BlobSidecar {}", + blobSidecar.toLogString()); + throw new BlobSidecarsResponseInvalidResponseException( + peer, BLOB_SIDECAR_INCLUSION_PROOF_VERIFICATION_FAILED, ex); + } + } } diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxy.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxy.java index 4faad1e3cf6..e697b9564d5 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxy.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxy.java @@ -71,6 +71,7 @@ public SafeFuture onResponse(final BlobSidecar blobSidecar) { final BlobSidecarSummary blobSidecarSummary = BlobSidecarSummary.create(blobSidecar); verifyBlobSidecarIsAfterLast(blobSidecarSummary); + verifyInclusionProof(blobSidecar); verifyKzg(blobSidecar); maybeLastBlobSidecarSummary = Optional.of(blobSidecarSummary); 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 709fe1ac029..ea4e96fe2b0 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 @@ -45,6 +45,7 @@ public void validate(final BlobSidecar blobSidecar) { peer, InvalidResponseType.BLOB_SIDECAR_UNEXPECTED_IDENTIFIER); } + verifyInclusionProof(blobSidecar); verifyKzg(blobSidecar); } } diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsResponseInvalidResponseException.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsResponseInvalidResponseException.java index 52368174378..6644ad36608 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsResponseInvalidResponseException.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsResponseInvalidResponseException.java @@ -35,6 +35,8 @@ public BlobSidecarsResponseInvalidResponseException( public enum InvalidResponseType { BLOB_SIDECAR_KZG_VERIFICATION_FAILED("KZG verification for BlobSidecar has failed"), + BLOB_SIDECAR_INCLUSION_PROOF_VERIFICATION_FAILED( + "Block inclusion proof verification for BlobSidecar has failed"), BLOB_SIDECAR_SLOT_NOT_IN_RANGE("BlobSidecar slot not in requested range"), BLOB_SIDECAR_UNEXPECTED_INDEX("BlobSidecar with unexpected index"), BLOB_SIDECAR_UNKNOWN_PARENT( diff --git a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxyTest.java b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxyTest.java index 384ee3c096c..7ac8775afcd 100644 --- a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxyTest.java +++ b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxyTest.java @@ -23,6 +23,7 @@ import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ONE; import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ZERO; +import java.util.stream.IntStream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import tech.pegasys.teku.infrastructure.async.SafeFuture; @@ -34,6 +35,7 @@ import tech.pegasys.teku.spec.TestSpecFactory; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; import tech.pegasys.teku.spec.util.DataStructureUtil; @SuppressWarnings("JavaCase") @@ -64,7 +66,7 @@ void blobSidecarFailsKzgVerification() { spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); final BlobSidecar blobSidecar1_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(ONE), 0); final SafeFuture result = listenerWrapper.onResponse(blobSidecar1_0); @@ -78,6 +80,52 @@ void blobSidecarFailsKzgVerification() { .describe()); } + @Test + void blobSidecarFailsInclusionProofVerification() { + final UInt64 startSlot = UInt64.valueOf(1); + final UInt64 count = UInt64.valueOf(4); + listenerWrapper = + new BlobSidecarsByRangeListenerValidatingProxy( + spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); + + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( + dataStructureUtil.randomSignedBeaconBlock(ONE), 0); + final BlobSidecar blobSidecar1_0_modified = + SchemaDefinitionsDeneb.required(spec.getGenesisSchemaDefinitions()) + .getBlobSidecarSchema() + .create( + blobSidecar1_0.getIndex(), + blobSidecar1_0.getBlob(), + blobSidecar1_0.getKZGCommitment(), + blobSidecar1_0.getKZGProof(), + blobSidecar1_0.getSignedBeaconBlockHeader(), + IntStream.range(0, blobSidecar1_0.getKzgCommitmentInclusionProof().size()) + .mapToObj( + index -> { + if (index == 0) { + return blobSidecar1_0 + .getKzgCommitmentInclusionProof() + .get(index) + .get() + .not(); + } else { + return blobSidecar1_0.getKzgCommitmentInclusionProof().get(index).get(); + } + }) + .toList()); + + final SafeFuture result = listenerWrapper.onResponse(blobSidecar1_0_modified); + assertThat(result).isCompletedExceptionally(); + assertThatThrownBy(result::get) + .hasCauseExactlyInstanceOf(BlobSidecarsResponseInvalidResponseException.class); + assertThatThrownBy(result::get) + .hasMessageContaining( + BlobSidecarsResponseInvalidResponseException.InvalidResponseType + .BLOB_SIDECAR_INCLUSION_PROOF_VERIFICATION_FAILED + .describe()); + } + @Test void blobSidecarSlotSmallerThanFromSlot() { final UInt64 startSlot = UInt64.valueOf(1); @@ -87,7 +135,7 @@ void blobSidecarSlotSmallerThanFromSlot() { spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); final BlobSidecar blobSidecar0_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(ZERO), 0); final SafeFuture result = listenerWrapper.onResponse(blobSidecar0_0); @@ -110,16 +158,18 @@ void blobSidecarsSlotsAreCorrect() { spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(ONE); - final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarForBlock(block1, 0); - final BlobSidecar blobSidecar1_1 = dataStructureUtil.randomBlobSidecarForBlock(block1, 1); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); + final BlobSidecar blobSidecar1_1 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 1); final BlobSidecar blobSidecar2_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(2, block1.getRoot()), 0); final BlobSidecar blobSidecar3_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(3, blobSidecar2_0.getBlockRoot()), 0); final BlobSidecar blobSidecar4_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(4, blobSidecar3_0.getBlockRoot()), 0); assertDoesNotThrow(() -> listenerWrapper.onResponse(blobSidecar1_0).join()); @@ -139,19 +189,19 @@ void blobSidecarSlotGreaterThanToSlot() { spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); final BlobSidecar blobSidecar1_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(1), 0); final BlobSidecar blobSidecar3_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(3), 0); final BlobSidecar blobSidecar5_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(5), 0); final BlobSidecar blobSidecar8_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(8), 0); final BlobSidecar blobSidecar9_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(9, blobSidecar8_0.getBlockRoot()), 0); safeJoin(listenerWrapper.onResponse(blobSidecar1_0)); @@ -179,10 +229,10 @@ void blobSidecarParentRootDoesNotMatch() { spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); final BlobSidecar blobSidecar1_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(1), 0); final BlobSidecar blobSidecar2_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(2), 0); safeJoin(listenerWrapper.onResponse(blobSidecar1_0)); @@ -207,14 +257,22 @@ void blobSidecarIndexIsGreaterOrEqualThanMaxBlobs() { new BlobSidecarsByRangeListenerValidatingProxy( spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); - final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(ONE); - final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarForBlock(block1, 0); - final BlobSidecar blobSidecar1_1 = dataStructureUtil.randomBlobSidecarForBlock(block1, 1); - final BlobSidecar blobSidecar1_2 = dataStructureUtil.randomBlobSidecarForBlock(block1, 2); - final BlobSidecar blobSidecar1_3 = dataStructureUtil.randomBlobSidecarForBlock(block1, 3); - final BlobSidecar blobSidecar1_4 = dataStructureUtil.randomBlobSidecarForBlock(block1, 4); - final BlobSidecar blobSidecar1_5 = dataStructureUtil.randomBlobSidecarForBlock(block1, 5); - final BlobSidecar blobSidecar1_6 = dataStructureUtil.randomBlobSidecarForBlock(block1, 6); + final SignedBeaconBlock block1 = + dataStructureUtil.randomSignedBeaconBlockWithCommitments(ONE, 7); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); + final BlobSidecar blobSidecar1_1 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 1); + final BlobSidecar blobSidecar1_2 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 2); + final BlobSidecar blobSidecar1_3 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 3); + final BlobSidecar blobSidecar1_4 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 4); + final BlobSidecar blobSidecar1_5 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 5); + final BlobSidecar blobSidecar1_6 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 6); safeJoin(listenerWrapper.onResponse(blobSidecar1_0)); safeJoin(listenerWrapper.onResponse(blobSidecar1_1)); @@ -243,9 +301,12 @@ void blobSidecarIndexIsInTheSameBlockButNotNext() { new BlobSidecarsByRangeListenerValidatingProxy( spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); - final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(ONE); - final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarForBlock(block1, 0); - final BlobSidecar blobSidecar1_2 = dataStructureUtil.randomBlobSidecarForBlock(block1, 2); + final SignedBeaconBlock block1 = + dataStructureUtil.randomSignedBeaconBlockWithCommitments(ONE, 3); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); + final BlobSidecar blobSidecar1_2 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 2); safeJoin(listenerWrapper.onResponse(blobSidecar1_0)); @@ -270,10 +331,12 @@ void isFirstBlobSidecarAfterAnEmptyBlobsBlock() { spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(ONE); - final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarForBlock(block1, 0); - final BlobSidecar blobSidecar1_1 = dataStructureUtil.randomBlobSidecarForBlock(block1, 1); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); + final BlobSidecar blobSidecar1_1 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 1); final BlobSidecar blobSidecar3_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(3), 0); safeJoin(listenerWrapper.onResponse(blobSidecar1_0)); @@ -290,7 +353,7 @@ void firstBlobSidecarIndexIsINotZero() { spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); final BlobSidecar blobSidecar1_1 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(2), 1); final SafeFuture result = listenerWrapper.onResponse(blobSidecar1_1); @@ -313,10 +376,10 @@ void firstBlobSidecarIndexInNextBlockIsNotZero() { spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); final BlobSidecar blobSidecar1_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(1), 0); final BlobSidecar blobSidecar2_1 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(2, blobSidecar1_0.getBlockRoot()), 1); assertDoesNotThrow(() -> listenerWrapper.onResponse(blobSidecar1_0).join()); @@ -341,10 +404,10 @@ void blobSidecarUnexpectedSlot() { spec, peer, listener, maxBlobsPerBlock, kzg, startSlot, count); final BlobSidecar blobSidecar2_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(2), 0); final BlobSidecar blobSidecar1_0 = - dataStructureUtil.randomBlobSidecarForBlock( + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(1), 0); safeJoin(listenerWrapper.onResponse(blobSidecar2_0)); diff --git a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidatorTest.java b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidatorTest.java index ce012053897..d8621cd1473 100644 --- a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidatorTest.java +++ b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidatorTest.java @@ -20,6 +20,7 @@ import static org.mockito.Mockito.when; import java.util.List; +import java.util.stream.IntStream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import tech.pegasys.teku.infrastructure.unsigned.UInt64; @@ -30,6 +31,7 @@ import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.BlobIdentifier; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; import tech.pegasys.teku.spec.util.DataStructureUtil; @SuppressWarnings("JavaCase") @@ -49,7 +51,8 @@ void setUp() { void blobSidecarIsCorrect() { final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(UInt64.ONE); final BlobIdentifier blobIdentifier1_0 = new BlobIdentifier(block1.getRoot(), UInt64.ZERO); - final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarForBlock(block1, 0); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); validator = new BlobSidecarsByRootValidator(peer, spec, kzg, List.of(blobIdentifier1_0)); assertDoesNotThrow(() -> validator.validate(blobSidecar1_0)); @@ -60,7 +63,8 @@ void blobSidecarIdentifierNotRequested() { final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(UInt64.ONE); final BlobIdentifier blobIdentifier2_0 = new BlobIdentifier(dataStructureUtil.randomBytes32(), UInt64.ZERO); - final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarForBlock(block1, 0); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); validator = new BlobSidecarsByRootValidator(peer, spec, kzg, List.of(blobIdentifier2_0)); assertThatThrownBy(() -> validator.validate(blobSidecar1_0)) @@ -76,7 +80,8 @@ void blobSidecarFailsKzgVerification() { when(kzg.verifyBlobKzgProof(any(), any(), any())).thenReturn(false); final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(UInt64.ONE); final BlobIdentifier blobIdentifier1_0 = new BlobIdentifier(block1.getRoot(), UInt64.ZERO); - final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarForBlock(block1, 0); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); validator = new BlobSidecarsByRootValidator(peer, spec, kzg, List.of(blobIdentifier1_0)); assertThatThrownBy(() -> validator.validate(blobSidecar1_0)) @@ -87,11 +92,51 @@ void blobSidecarFailsKzgVerification() { .describe()); } + @Test + void blobSidecarFailsInclusionProofVerification() { + final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(UInt64.ONE); + final BlobIdentifier blobIdentifier1_0 = new BlobIdentifier(block1.getRoot(), UInt64.ZERO); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); + final BlobSidecar blobSidecar1_0_modified = + SchemaDefinitionsDeneb.required(spec.getGenesisSchemaDefinitions()) + .getBlobSidecarSchema() + .create( + blobSidecar1_0.getIndex(), + blobSidecar1_0.getBlob(), + blobSidecar1_0.getKZGCommitment(), + blobSidecar1_0.getKZGProof(), + blobSidecar1_0.getSignedBeaconBlockHeader(), + IntStream.range(0, blobSidecar1_0.getKzgCommitmentInclusionProof().size()) + .mapToObj( + index -> { + if (index == 0) { + return blobSidecar1_0 + .getKzgCommitmentInclusionProof() + .get(index) + .get() + .not(); + } else { + return blobSidecar1_0.getKzgCommitmentInclusionProof().get(index).get(); + } + }) + .toList()); + + validator = new BlobSidecarsByRootValidator(peer, spec, kzg, List.of(blobIdentifier1_0)); + assertThatThrownBy(() -> validator.validate(blobSidecar1_0_modified)) + .isExactlyInstanceOf(BlobSidecarsResponseInvalidResponseException.class) + .hasMessageContaining( + BlobSidecarsResponseInvalidResponseException.InvalidResponseType + .BLOB_SIDECAR_INCLUSION_PROOF_VERIFICATION_FAILED + .describe()); + } + @Test void blobSidecarResponseWithDuplicateSidecar() { final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(UInt64.ONE); final BlobIdentifier blobIdentifier1_0 = new BlobIdentifier(block1.getRoot(), UInt64.ZERO); - final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarForBlock(block1, 0); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); validator = new BlobSidecarsByRootValidator(peer, spec, kzg, List.of(blobIdentifier1_0)); assertDoesNotThrow(() -> validator.validate(blobSidecar1_0)); From 57e37f28fbe471607aba7173450518a9e6b61289 Mon Sep 17 00:00:00 2001 From: Dmitrii Shmatko Date: Wed, 23 Oct 2024 22:13:46 +0200 Subject: [PATCH 2/2] Fix test, refactor invalid proof blobSidecar creation --- ...arsByRangeListenerValidatingProxyTest.java | 27 +--------- ...carsByRootListenerValidatingProxyTest.java | 51 +++++++++++++++---- .../BlobSidecarsByRootValidatorTest.java | 51 ++++++++++--------- 3 files changed, 72 insertions(+), 57 deletions(-) diff --git a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxyTest.java b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxyTest.java index 7ac8775afcd..bcbe3297723 100644 --- a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxyTest.java +++ b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeListenerValidatingProxyTest.java @@ -22,8 +22,8 @@ import static tech.pegasys.teku.infrastructure.async.SafeFutureAssert.safeJoin; import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ONE; import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ZERO; +import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsByRootValidatorTest.breakInclusionProof; -import java.util.stream.IntStream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import tech.pegasys.teku.infrastructure.async.SafeFuture; @@ -35,7 +35,6 @@ import tech.pegasys.teku.spec.TestSpecFactory; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; -import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; import tech.pegasys.teku.spec.util.DataStructureUtil; @SuppressWarnings("JavaCase") @@ -91,29 +90,7 @@ void blobSidecarFailsInclusionProofVerification() { final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock( dataStructureUtil.randomSignedBeaconBlock(ONE), 0); - final BlobSidecar blobSidecar1_0_modified = - SchemaDefinitionsDeneb.required(spec.getGenesisSchemaDefinitions()) - .getBlobSidecarSchema() - .create( - blobSidecar1_0.getIndex(), - blobSidecar1_0.getBlob(), - blobSidecar1_0.getKZGCommitment(), - blobSidecar1_0.getKZGProof(), - blobSidecar1_0.getSignedBeaconBlockHeader(), - IntStream.range(0, blobSidecar1_0.getKzgCommitmentInclusionProof().size()) - .mapToObj( - index -> { - if (index == 0) { - return blobSidecar1_0 - .getKzgCommitmentInclusionProof() - .get(index) - .get() - .not(); - } else { - return blobSidecar1_0.getKzgCommitmentInclusionProof().get(index).get(); - } - }) - .toList()); + final BlobSidecar blobSidecar1_0_modified = breakInclusionProof(spec, blobSidecar1_0); final SafeFuture result = listenerWrapper.onResponse(blobSidecar1_0_modified); assertThat(result).isCompletedExceptionally(); diff --git a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootListenerValidatingProxyTest.java b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootListenerValidatingProxyTest.java index 679cb76387c..cfabe5c4a3a 100644 --- a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootListenerValidatingProxyTest.java +++ b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootListenerValidatingProxyTest.java @@ -19,6 +19,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsByRootValidatorTest.breakInclusionProof; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -69,11 +70,16 @@ void blobSidecarsAreCorrect() { listenerWrapper = new BlobSidecarsByRootListenerValidatingProxy(peer, spec, listener, kzg, blobIdentifiers); - final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarForBlock(block1, 0); - final BlobSidecar blobSidecar1_1 = dataStructureUtil.randomBlobSidecarForBlock(block1, 1); - final BlobSidecar blobSidecar2_0 = dataStructureUtil.randomBlobSidecarForBlock(block2, 0); - final BlobSidecar blobSidecar3_0 = dataStructureUtil.randomBlobSidecarForBlock(block3, 0); - final BlobSidecar blobSidecar4_0 = dataStructureUtil.randomBlobSidecarForBlock(block4, 0); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); + final BlobSidecar blobSidecar1_1 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 1); + final BlobSidecar blobSidecar2_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block2, 0); + final BlobSidecar blobSidecar3_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block3, 0); + final BlobSidecar blobSidecar4_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block4, 0); assertDoesNotThrow(() -> listenerWrapper.onResponse(blobSidecar1_0).join()); assertDoesNotThrow(() -> listenerWrapper.onResponse(blobSidecar1_1).join()); @@ -93,9 +99,12 @@ void blobSidecarIdentifierNotRequested() { listenerWrapper = new BlobSidecarsByRootListenerValidatingProxy(peer, spec, listener, kzg, blobIdentifiers); - final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarForBlock(block1, 0); - final BlobSidecar blobSidecar1_1 = dataStructureUtil.randomBlobSidecarForBlock(block1, 1); - final BlobSidecar blobSidecar2_0 = dataStructureUtil.randomBlobSidecarForBlock(block2, 0); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); + final BlobSidecar blobSidecar1_1 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 1); + final BlobSidecar blobSidecar2_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block2, 0); assertDoesNotThrow(() -> listenerWrapper.onResponse(blobSidecar1_0).join()); assertDoesNotThrow(() -> listenerWrapper.onResponse(blobSidecar1_1).join()); @@ -119,7 +128,8 @@ void blobSidecarFailsKzgVerification() { new BlobSidecarsByRootListenerValidatingProxy( peer, spec, listener, kzg, List.of(blobIdentifier)); - final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarForBlock(block1, 0); + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); final SafeFuture result = listenerWrapper.onResponse(blobSidecar1_0); assertThat(result).isCompletedExceptionally(); @@ -131,4 +141,27 @@ void blobSidecarFailsKzgVerification() { .BLOB_SIDECAR_KZG_VERIFICATION_FAILED .describe()); } + + @Test + void blobSidecarFailsInclusionProofVerification() { + final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(UInt64.ONE); + final BlobIdentifier blobIdentifier = new BlobIdentifier(block1.getRoot(), UInt64.ZERO); + listenerWrapper = + new BlobSidecarsByRootListenerValidatingProxy( + peer, spec, listener, kzg, List.of(blobIdentifier)); + + final BlobSidecar blobSidecar1_0 = + dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); + final BlobSidecar blobSidecar1_0_modified = breakInclusionProof(spec, blobSidecar1_0); + + final SafeFuture result = listenerWrapper.onResponse(blobSidecar1_0_modified); + assertThat(result).isCompletedExceptionally(); + assertThatThrownBy(result::get) + .hasCauseExactlyInstanceOf(BlobSidecarsResponseInvalidResponseException.class); + assertThatThrownBy(result::get) + .hasMessageContaining( + BlobSidecarsResponseInvalidResponseException.InvalidResponseType + .BLOB_SIDECAR_INCLUSION_PROOF_VERIFICATION_FAILED + .describe()); + } } diff --git a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidatorTest.java b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidatorTest.java index d8621cd1473..01548f8f016 100644 --- a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidatorTest.java +++ b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootValidatorTest.java @@ -47,6 +47,33 @@ void setUp() { when(kzg.verifyBlobKzgProof(any(), any(), any())).thenReturn(true); } + public static BlobSidecar breakInclusionProof(final Spec spec, final BlobSidecar blobSidecar) { + final BlobSidecar blobSidecarModified = + SchemaDefinitionsDeneb.required(spec.getGenesisSchemaDefinitions()) + .getBlobSidecarSchema() + .create( + blobSidecar.getIndex(), + blobSidecar.getBlob(), + blobSidecar.getKZGCommitment(), + blobSidecar.getKZGProof(), + blobSidecar.getSignedBeaconBlockHeader(), + IntStream.range(0, blobSidecar.getKzgCommitmentInclusionProof().size()) + .mapToObj( + index -> { + if (index == 0) { + return blobSidecar + .getKzgCommitmentInclusionProof() + .get(index) + .get() + .not(); + } else { + return blobSidecar.getKzgCommitmentInclusionProof().get(index).get(); + } + }) + .toList()); + return blobSidecarModified; + } + @Test void blobSidecarIsCorrect() { final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(UInt64.ONE); @@ -98,29 +125,7 @@ void blobSidecarFailsInclusionProofVerification() { final BlobIdentifier blobIdentifier1_0 = new BlobIdentifier(block1.getRoot(), UInt64.ZERO); final BlobSidecar blobSidecar1_0 = dataStructureUtil.randomBlobSidecarWithValidInclusionProofForBlock(block1, 0); - final BlobSidecar blobSidecar1_0_modified = - SchemaDefinitionsDeneb.required(spec.getGenesisSchemaDefinitions()) - .getBlobSidecarSchema() - .create( - blobSidecar1_0.getIndex(), - blobSidecar1_0.getBlob(), - blobSidecar1_0.getKZGCommitment(), - blobSidecar1_0.getKZGProof(), - blobSidecar1_0.getSignedBeaconBlockHeader(), - IntStream.range(0, blobSidecar1_0.getKzgCommitmentInclusionProof().size()) - .mapToObj( - index -> { - if (index == 0) { - return blobSidecar1_0 - .getKzgCommitmentInclusionProof() - .get(index) - .get() - .not(); - } else { - return blobSidecar1_0.getKzgCommitmentInclusionProof().get(index).get(); - } - }) - .toList()); + final BlobSidecar blobSidecar1_0_modified = breakInclusionProof(spec, blobSidecar1_0); validator = new BlobSidecarsByRootValidator(peer, spec, kzg, List.of(blobIdentifier1_0)); assertThatThrownBy(() -> validator.validate(blobSidecar1_0_modified))