Skip to content

Commit

Permalink
Improve isDataAvailable checks (#7575)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbenr authored Oct 17, 2023
1 parent b1de819 commit 55b2d87
Show file tree
Hide file tree
Showing 12 changed files with 624 additions and 246 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -343,18 +343,28 @@ public boolean isExecutionEnabled(final BeaconState genericState, final BeaconBl
return false;
}

/**
* Performs data availability by validating the blobs against the kzg commitments from the block.
* It will also check the slot and blockRoot consistency.
*/
public boolean isDataAvailable(
final UInt64 slot,
final Bytes32 beaconBlockRoot,
final List<KZGCommitment> kzgCommitments,
final List<BlobSidecar> blobSidecars) {
public boolean isDataAvailable(final List<BlobSidecar> blobSidecars, final BeaconBlock block) {
return false;
}

public boolean verifyBlobKzgProofBatch(final List<BlobSidecar> blobSidecars) {
return false;
}

public void validateBlobSidecarsBatchAgainstBlock(
final List<BlobSidecar> blobSidecars,
final BeaconBlock block,
final List<KZGCommitment> kzgCommitmentsFromBlock) {
throw new UnsupportedOperationException("Blob Sidecars before Deneb");
}

public void verifyBlobSidecarCompleteness(
final List<BlobSidecar> verifiedBlobSidecars,
final List<KZGCommitment> kzgCommitmentsFromBlock)
throws IllegalArgumentException {
throw new UnsupportedOperationException("No KZGCommitments before Deneb");
}

public VersionedHash kzgCommitmentToVersionedHash(final KZGCommitment kzgCommitment) {
throw new UnsupportedOperationException("No KZGCommitments before Deneb");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
import static com.google.common.base.Preconditions.checkArgument;
import static tech.pegasys.teku.spec.config.SpecConfigDeneb.VERSIONED_HASH_VERSION_KZG;

import java.util.ArrayList;
import java.util.List;
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.unsigned.UInt64;
Expand All @@ -31,8 +32,10 @@
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.blocks.blockbody.versions.deneb.BeaconBlockBodyDeneb;
import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment;
import tech.pegasys.teku.spec.logic.versions.capella.helpers.MiscHelpersCapella;
import tech.pegasys.teku.spec.logic.versions.deneb.types.VersionedHash;

Expand All @@ -58,33 +61,142 @@ private static KZG initKZG(final SpecConfigDeneb config) {
}

/**
* <a
* Performs complete data availability check <a
* href="https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/fork-choice.md#is_data_available">is_data_available</a>
*
* <p>In Deneb we don't need to retrieve data, everything is already available via blob sidecars.
*/
@Override
public boolean isDataAvailable(final List<BlobSidecar> blobSidecars, final BeaconBlock block) {

final List<KZGCommitment> kzgCommitmentsFromBlock =
BeaconBlockBodyDeneb.required(block.getBody()).getBlobKzgCommitments().stream()
.map(SszKZGCommitment::getKZGCommitment)
.toList();

validateBlobSidecarsBatchAgainstBlock(blobSidecars, block, kzgCommitmentsFromBlock);

if (!verifyBlobKzgProofBatch(blobSidecars)) {
return false;
}

verifyBlobSidecarCompleteness(blobSidecars, kzgCommitmentsFromBlock);

return true;
}

/**
* Performs a verifyBlobKzgProofBatch on the given blob sidecars
*
* @param blobSidecars blob sidecars to verify, can be a partial set
* @return true if all blob sidecars are valid
*/
@Override
public boolean isDataAvailable(
final UInt64 slot,
final Bytes32 beaconBlockRoot,
final List<KZGCommitment> kzgCommitments,
final List<BlobSidecar> blobSidecars) {
public boolean verifyBlobKzgProofBatch(final List<BlobSidecar> blobSidecars) {
final List<Bytes> blobs = new ArrayList<>();
final List<KZGProof> kzgProofs = new ArrayList<>();
final List<KZGCommitment> kzgCommitments = new ArrayList<>();

blobSidecars.forEach(
blobSidecar -> {
blobs.add(blobSidecar.getBlob().getBytes());
kzgProofs.add(blobSidecar.getKZGProof());
kzgCommitments.add(blobSidecar.getKZGCommitment());
});

return kzg.verifyBlobKzgProofBatch(blobs, kzgCommitments, kzgProofs);
}

/**
* Validates blob sidecars against block by matching all fields they have in common
*
* @param blobSidecars blob sidecars to validate
* @param block block to validate blob sidecar against
* @param kzgCommitmentsFromBlock kzg commitments from block. They could be extracted from block
* but since we already have them we can avoid extracting them again.
*/
@Override
public void validateBlobSidecarsBatchAgainstBlock(
final List<BlobSidecar> blobSidecars,
final BeaconBlock block,
final List<KZGCommitment> kzgCommitmentsFromBlock) {

final String slotAndBlockRoot = block.getSlotAndBlockRoot().toLogString();

blobSidecars.forEach(
blobSidecar -> {
final UInt64 blobIndex = blobSidecar.getIndex();

checkArgument(
slot.equals(blobSidecar.getSlot()),
"Blob sidecar slot %s does not match block slot %s",
blobSidecar.getSlot(),
slot);
blobSidecar.getBlockRoot().equals(block.getRoot()),
"Block and blob sidecar slot mismatch for %s, blob index %s",
slotAndBlockRoot,
blobIndex);

checkArgument(
beaconBlockRoot.equals(blobSidecar.getBlockRoot()),
"Blob sidecar block root %s does not match block root %s",
blobSidecar.getBlockRoot(),
beaconBlockRoot);
blobSidecar.getSlot().equals(block.getSlot()),
"Block and blob sidecar slot mismatch for %s, blob index %s",
slotAndBlockRoot,
blobIndex);

checkArgument(
blobSidecar.getProposerIndex().equals(block.getProposerIndex()),
"Block and blob sidecar proposer index mismatch for %s, blob index %s",
slotAndBlockRoot,
blobIndex);
checkArgument(
blobSidecar.getBlockParentRoot().equals(block.getParentRoot()),
"Block and blob sidecar parent block mismatch for %s, blob index %s",
slotAndBlockRoot,
blobIndex);

final KZGCommitment kzgCommitmentFromBlock;

try {
kzgCommitmentFromBlock = kzgCommitmentsFromBlock.get(blobIndex.intValue());
} catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException(
String.format(
"Blob sidecar index out of bound with respect to block %s, blob index %s",
slotAndBlockRoot, blobIndex));
}

checkArgument(
blobSidecar.getKZGCommitment().equals(kzgCommitmentFromBlock),
"Block and blob sidecar kzg commitments mismatch for %s, blob index %s",
slotAndBlockRoot,
blobIndex);
});
final List<Bytes> blobs =
blobSidecars.stream().map(BlobSidecar::getBlob).map(Blob::getBytes).toList();
final List<KZGProof> proofs = blobSidecars.stream().map(BlobSidecar::getKZGProof).toList();
}

return kzg.verifyBlobKzgProofBatch(blobs, kzgCommitments, proofs);
/**
* Verifies that blob sidecars are complete and with expected indexes
*
* @param completeVerifiedBlobSidecars blob sidecars to verify, It is assumed that it is an
* ordered list based on BlobSidecar index
* @param kzgCommitmentsFromBlock kzg commitments from block.
*/
@Override
public void verifyBlobSidecarCompleteness(
final List<BlobSidecar> completeVerifiedBlobSidecars,
final List<KZGCommitment> kzgCommitmentsFromBlock)
throws IllegalArgumentException {
checkArgument(
completeVerifiedBlobSidecars.size() == kzgCommitmentsFromBlock.size(),
"Blob sidecars are not complete");

IntStream.range(0, completeVerifiedBlobSidecars.size())
.forEach(
index -> {
final BlobSidecar blobSidecar = completeVerifiedBlobSidecars.get(index);
final UInt64 blobIndex = blobSidecar.getIndex();

checkArgument(
blobIndex.longValue() == index,
"Blob sidecar index mismatch, expected %s, got %s",
index,
blobIndex);
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@
import net.jqwik.api.ForAll;
import net.jqwik.api.From;
import net.jqwik.api.Property;
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.kzg.KZGCommitment;
import tech.pegasys.teku.spec.config.SpecConfigDeneb;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock;
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.type.Bytes32Supplier;
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.UInt64Supplier;

public class MiscHelpersDenebPropertyTest {
private final SpecConfigDeneb specConfig =
Expand All @@ -40,22 +38,40 @@ public class MiscHelpersDenebPropertyTest {
.orElseThrow();
private final MiscHelpersDeneb miscHelpers = new MiscHelpersDeneb(specConfig);

@Property
void fuzzKzgCommitmentToVersionedHash(
@ForAll(supplier = KZGCommitmentSupplier.class) final KZGCommitment commitment) {
miscHelpers.kzgCommitmentToVersionedHash(commitment);
}

@Property(tries = 100)
void fuzzIsDataAvailable(
@ForAll(supplier = UInt64Supplier.class) final UInt64 slot,
@ForAll(supplier = Bytes32Supplier.class) final Bytes32 beaconBlockRoot,
@ForAll final List<@From(supplier = KZGCommitmentSupplier.class) KZGCommitment> commitments,
void fuzzVerifyBlobKzgProofBatch(
@ForAll final List<@From(supplier = BlobSidecarSupplier.class) BlobSidecar> blobSidecars) {
miscHelpers.verifyBlobKzgProofBatch(blobSidecars);
}

@Property(tries = 100)
void fuzzValidateBlobSidecarsBatchAgainstBlock(
@ForAll final List<@From(supplier = BlobSidecarSupplier.class) BlobSidecar> blobSidecars,
@ForAll(supplier = BeaconBlockSupplier.class) final BeaconBlock block,
@ForAll
final List<@From(supplier = KZGCommitmentSupplier.class) KZGCommitment> kzgCommitments) {
try {
miscHelpers.isDataAvailable(slot, beaconBlockRoot, commitments, blobSidecars);
miscHelpers.validateBlobSidecarsBatchAgainstBlock(blobSidecars, block, kzgCommitments);
} catch (Exception e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class);
}
}

@Property
void fuzzKzgCommitmentToVersionedHash(
@ForAll(supplier = KZGCommitmentSupplier.class) final KZGCommitment commitment) {
miscHelpers.kzgCommitmentToVersionedHash(commitment);
@Property(tries = 100)
void fuzzVerifyBlobSidecarCompleteness(
@ForAll final List<@From(supplier = BlobSidecarSupplier.class) BlobSidecar> blobSidecars,
@ForAll
final List<@From(supplier = KZGCommitmentSupplier.class) KZGCommitment> kzgCommitments) {
try {
miscHelpers.verifyBlobSidecarCompleteness(blobSidecars, kzgCommitments);
} catch (Exception e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class);
}
}
}
Loading

0 comments on commit 55b2d87

Please sign in to comment.