Skip to content

Commit

Permalink
Add BlobSidecar inclusion proof verification in req/resp
Browse files Browse the repository at this point in the history
  • Loading branch information
zilm13 committed Oct 23, 2024
1 parent 7751981 commit c560084
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 37 deletions.
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 @@ -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;
Expand All @@ -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")
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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());
Expand All @@ -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));
Expand Down Expand Up @@ -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));
Expand All @@ -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));
Expand Down Expand Up @@ -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));

Expand All @@ -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));
Expand All @@ -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);
Expand All @@ -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());
Expand All @@ -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));
Expand Down
Loading

0 comments on commit c560084

Please sign in to comment.