Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add BlobSidecar inclusion proof verification in req/resp #8775

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Blob> randomBlobs(final int count, final UInt64 slot) {
final List<Blob> blobs = new ArrayList<>();
final BlobSchema blobSchema = getDenebSchemaDefinitions(slot).getBlobSchema();
Expand Down Expand Up @@ -2483,6 +2501,12 @@ public List<Bytes32> randomKzgCommitmentInclusionProof() {
return IntStream.range(0, depth).mapToObj(__ -> randomBytes32()).toList();
}

public List<Bytes32> validKzgCommitmentInclusionProof(
final UInt64 blobIndex, final BeaconBlockBody beaconBlockBody) {
return MiscHelpersDeneb.required(spec.forMilestone(SpecMilestone.DENEB).miscHelpers())
.computeKzgCommitmentInclusionProof(blobIndex, beaconBlockBody);
}

public SszList<SszKZGCommitment> randomBlobKzgCommitments() {
// use MAX_BLOBS_PER_BLOCK as a limit
return randomBlobKzgCommitments(randomNumberOfBlobsPerBlock());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {

Expand All @@ -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);
Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public void validate(final BlobSidecar blobSidecar) {
peer, InvalidResponseType.BLOB_SIDECAR_UNEXPECTED_IDENTIFIER);
}

verifyInclusionProof(blobSidecar);
verifyKzg(blobSidecar);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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 org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -64,7 +65,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);
Expand All @@ -78,6 +79,30 @@ 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 = 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());
}

@Test
void blobSidecarSlotSmallerThanFromSlot() {
final UInt64 startSlot = UInt64.valueOf(1);
Expand All @@ -87,7 +112,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);
Expand All @@ -110,16 +135,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());
Expand All @@ -139,19 +166,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));
Expand Down Expand Up @@ -179,10 +206,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));
Expand All @@ -207,14 +234,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));
Expand Down Expand Up @@ -243,9 +278,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));

Expand All @@ -270,10 +308,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));
Expand All @@ -290,7 +330,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);
Expand All @@ -313,10 +353,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());
Expand All @@ -341,10 +381,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));
Expand Down
Loading