Skip to content

Commit b832b91

Browse files
authored
Simplify blob sidecar availability checker (#8840)
1 parent b5dd2ae commit b832b91

File tree

26 files changed

+592
-1051
lines changed

26 files changed

+592
-1051
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### Breaking Changes
1010

1111
### Additions and Improvements
12+
- Optimized blobs validation pipeline
1213

1314
### Bug Fixes
1415
- Updated the gas change check for block building so that warnings only get raised if the change is off spec.

beacon/sync/src/main/java/tech/pegasys/teku/beacon/sync/historical/HistoricalBatchFetcher.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ private void validateBlobSidecars(final SignedBeaconBlock block) {
402402
final List<BlobSidecar> blobSidecars =
403403
blobSidecarsBySlotToImport.getOrDefault(
404404
block.getSlotAndBlockRoot(), Collections.emptyList());
405+
405406
LOG.trace("Validating {} blob sidecars for block {}", blobSidecars.size(), block.getRoot());
406407
final BlobSidecarsAndValidationResult validationResult =
407408
blobSidecarManager.createAvailabilityCheckerAndValidateImmediately(block, blobSidecars);

beacon/sync/src/test/java/tech/pegasys/teku/beacon/sync/historical/HistoricalBatchFetcherTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public void setup() {
117117
.map(SignedBlockAndState::getBlock)
118118
.collect(Collectors.toList());
119119
lastBlockInBatch = chainBuilder.getLatestBlockAndState().getBlock();
120-
firstBlockInBatch = blockBatch.get(0);
120+
firstBlockInBatch = blockBatch.getFirst();
121121
blobSidecarsBatch =
122122
chainBuilder
123123
.streamBlobSidecars(10, 20)
@@ -202,7 +202,7 @@ public void run_returnAllBlocksAndBlobSidecarsOnFirstRequest() {
202202
earliestBlobSidecarSlotCaptor.capture());
203203
assertThat(blockCaptor.getValue()).containsExactlyElementsOf(blockBatch);
204204
assertThat(blobSidecarCaptor.getValue()).isEqualTo(blobSidecarsBatch);
205-
assertThat(earliestBlobSidecarSlotCaptor.getValue()).contains(blockBatch.get(0).getSlot());
205+
assertThat(earliestBlobSidecarSlotCaptor.getValue()).contains(blockBatch.getFirst().getSlot());
206206
}
207207

208208
@Test

beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,8 @@ void shouldCreateBlobSidecarsForBlockContents() {
801801
assertThat(blobSidecar.getSszKZGCommitment())
802802
.isEqualTo(expectedCommitments.get(index));
803803
// verify the merkle proof
804-
assertThat(miscHelpersDeneb.verifyBlobSidecarMerkleProof(blobSidecar)).isTrue();
804+
assertThat(miscHelpersDeneb.verifyBlobKzgCommitmentInclusionProof(blobSidecar))
805+
.isTrue();
805806
});
806807
}
807808

@@ -921,7 +922,8 @@ void shouldCreateBlobSidecarsForBlindedBlock(final boolean useLocalFallback) {
921922
assertThat(blobSidecar.getSszKZGCommitment())
922923
.isEqualTo(expectedCommitments.get(index));
923924
// verify the merkle proof
924-
assertThat(miscHelpersDeneb.verifyBlobSidecarMerkleProof(blobSidecar)).isTrue();
925+
assertThat(miscHelpersDeneb.verifyBlobKzgCommitmentInclusionProof(blobSidecar))
926+
.isTrue();
925927
});
926928
}
927929

data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/events/EventSubscriber.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public class EventSubscriber {
4545
private final AtomicBoolean processingQueue;
4646
private final AsyncRunner asyncRunner;
4747
private final AtomicLong excessiveQueueingDisconnectionTime = new AtomicLong(Long.MAX_VALUE);
48-
private volatile AtomicInteger successiveFailureCounter = new AtomicInteger(0);
48+
private final AtomicInteger successiveFailureCounter = new AtomicInteger(0);
4949

5050
public EventSubscriber(
5151
final List<String> eventTypes,

eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/forkchoice/StubBlobSidecarManager.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,6 @@ public SafeFuture<BlobSidecarsAndValidationResult> getAvailabilityCheckResult()
9898
return SafeFuture.completedFuture(validateImmediately(block, blobsAndProofs));
9999
}
100100

101-
@Override
102-
public BlobSidecarsAndValidationResult validateImmediately(
103-
final List<BlobSidecar> blobSidecars) {
104-
throw new UnsupportedOperationException("Not available in fork choice reference tests");
105-
}
106-
107101
private BlobSidecarsAndValidationResult validateImmediately(
108102
final SignedBeaconBlock block, final BlobsAndProofs blobsAndProofs) {
109103
final List<KZGCommitment> kzgCommitments =

ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blobs/versions/deneb/BlobSidecar.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ public class BlobSidecar
3939
SignedBeaconBlockHeader,
4040
SszBytes32Vector> {
4141

42+
private volatile boolean kzgValidated = false;
43+
private volatile boolean kzgCommitmentInclusionProofValidated = false;
44+
private volatile boolean signatureValidated = false;
45+
4246
BlobSidecar(final BlobSidecarSchema blobSidecarSchema, final TreeNode backingTreeNode) {
4347
super(blobSidecarSchema, backingTreeNode);
4448
}
@@ -139,6 +143,30 @@ public String toLogString() {
139143
getKZGProof().toAbbreviatedString());
140144
}
141145

146+
public boolean isKzgValidated() {
147+
return kzgValidated;
148+
}
149+
150+
public boolean isKzgCommitmentInclusionProofValidated() {
151+
return kzgCommitmentInclusionProofValidated;
152+
}
153+
154+
public boolean isSignatureValidated() {
155+
return signatureValidated;
156+
}
157+
158+
public void markKzgAsValidated() {
159+
kzgValidated = true;
160+
}
161+
162+
public void markKzgCommitmentInclusionProofAsValidated() {
163+
kzgCommitmentInclusionProofValidated = true;
164+
}
165+
166+
public void markSignatureAsValidated() {
167+
signatureValidated = true;
168+
}
169+
142170
@Override
143171
public BlobSidecarSchema getSchema() {
144172
return (BlobSidecarSchema) super.getSchema();

ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/helpers/MiscHelpers.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -380,16 +380,22 @@ public boolean verifyBlobKzgProofBatch(final KZG kzg, final List<BlobSidecar> bl
380380
return false;
381381
}
382382

383-
public void validateBlobSidecarsBatchAgainstBlock(
384-
final List<BlobSidecar> blobSidecars,
385-
final BeaconBlock block,
386-
final List<KZGCommitment> kzgCommitmentsFromBlock) {
383+
public boolean verifyBlobSidecarBlockHeaderSignatureViaValidatedSignedBlock(
384+
final List<BlobSidecar> blobSidecars, final SignedBeaconBlock signedBeaconBlock) {
385+
return blobSidecars.stream()
386+
.allMatch(
387+
blobSidecar ->
388+
verifyBlobSidecarBlockHeaderSignatureViaValidatedSignedBlock(
389+
blobSidecar, signedBeaconBlock));
390+
}
391+
392+
public boolean verifyBlobSidecarBlockHeaderSignatureViaValidatedSignedBlock(
393+
final BlobSidecar blobSidecar, final SignedBeaconBlock signedBeaconBlock) {
387394
throw new UnsupportedOperationException("No Blob Sidecars before Deneb");
388395
}
389396

390397
public void verifyBlobSidecarCompleteness(
391-
final List<BlobSidecar> verifiedBlobSidecars,
392-
final List<KZGCommitment> kzgCommitmentsFromBlock)
398+
final List<BlobSidecar> verifiedBlobSidecars, final SignedBeaconBlock signedBeaconBlock)
393399
throws IllegalArgumentException {
394400
throw new UnsupportedOperationException("No Blob Sidecars before Deneb");
395401
}

ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/deneb/blobs/BlobSidecarsAvailabilityChecker.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515

1616
import static tech.pegasys.teku.spec.logic.versions.deneb.blobs.BlobSidecarsAndValidationResult.NOT_REQUIRED_RESULT_FUTURE;
1717

18-
import java.util.List;
1918
import tech.pegasys.teku.infrastructure.async.SafeFuture;
20-
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
2119
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadHeader;
2220
import tech.pegasys.teku.spec.datastructures.execution.NewPayloadRequest;
2321
import tech.pegasys.teku.spec.logic.versions.bellatrix.block.OptimisticExecutionPayloadExecutor;
@@ -35,12 +33,6 @@ public boolean initiateDataAvailabilityCheck() {
3533
public SafeFuture<BlobSidecarsAndValidationResult> getAvailabilityCheckResult() {
3634
return NOT_REQUIRED_RESULT_FUTURE;
3735
}
38-
39-
@Override
40-
public BlobSidecarsAndValidationResult validateImmediately(
41-
final List<BlobSidecar> blobSidecars) {
42-
return BlobSidecarsAndValidationResult.NOT_REQUIRED;
43-
}
4436
};
4537

4638
BlobSidecarsAvailabilityChecker NOT_REQUIRED = NOOP;
@@ -55,7 +47,4 @@ public BlobSidecarsAndValidationResult validateImmediately(
5547
boolean initiateDataAvailabilityCheck();
5648

5749
SafeFuture<BlobSidecarsAndValidationResult> getAvailabilityCheckResult();
58-
59-
/** Perform the data availability check immediately on the provided blob sidecars */
60-
BlobSidecarsAndValidationResult validateImmediately(List<BlobSidecar> blobSidecars);
6150
}

ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDeneb.java

Lines changed: 79 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,20 @@ public MiscHelpersDeneb(
8484
*/
8585
@Override
8686
public boolean verifyBlobKzgProof(final KZG kzg, final BlobSidecar blobSidecar) {
87-
return kzg.verifyBlobKzgProof(
88-
blobSidecar.getBlob().getBytes(),
89-
blobSidecar.getKZGCommitment(),
90-
blobSidecar.getKZGProof());
87+
if (blobSidecar.isKzgValidated()) {
88+
return true;
89+
}
90+
final boolean result =
91+
kzg.verifyBlobKzgProof(
92+
blobSidecar.getBlob().getBytes(),
93+
blobSidecar.getKZGCommitment(),
94+
blobSidecar.getKZGProof());
95+
96+
if (result) {
97+
blobSidecar.markKzgAsValidated();
98+
}
99+
100+
return result;
91101
}
92102

93103
/**
@@ -105,77 +115,71 @@ public boolean verifyBlobKzgProofBatch(final KZG kzg, final List<BlobSidecar> bl
105115
final List<KZGCommitment> kzgCommitments = new ArrayList<>();
106116
final List<KZGProof> kzgProofs = new ArrayList<>();
107117

108-
blobSidecars.forEach(
109-
blobSidecar -> {
110-
blobs.add(blobSidecar.getBlob().getBytes());
111-
kzgCommitments.add(blobSidecar.getKZGCommitment());
112-
kzgProofs.add(blobSidecar.getKZGProof());
113-
});
118+
blobSidecars.stream()
119+
.filter(blobSidecar -> !blobSidecar.isKzgValidated())
120+
.forEach(
121+
blobSidecar -> {
122+
blobs.add(blobSidecar.getBlob().getBytes());
123+
kzgCommitments.add(blobSidecar.getKZGCommitment());
124+
kzgProofs.add(blobSidecar.getKZGProof());
125+
});
126+
127+
if (blobs.isEmpty()) {
128+
return true;
129+
}
130+
131+
final boolean result = kzg.verifyBlobKzgProofBatch(blobs, kzgCommitments, kzgProofs);
114132

115-
return kzg.verifyBlobKzgProofBatch(blobs, kzgCommitments, kzgProofs);
133+
if (result) {
134+
blobSidecars.stream()
135+
.filter(blobSidecar -> !blobSidecar.isKzgValidated())
136+
.forEach(BlobSidecar::markKzgAsValidated);
137+
}
138+
139+
return result;
116140
}
117141

118-
/**
119-
* Validates blob sidecars against block. We need to check block root and kzg commitment, it's
120-
* enough to guarantee BlobSidecars belong to block
121-
*
122-
* @param blobSidecars blob sidecars to validate
123-
* @param block block to validate blob sidecar against
124-
* @param kzgCommitmentsFromBlock kzg commitments from block. They could be extracted from block
125-
* but since we already have them we can avoid extracting them again.
126-
*/
127142
@Override
128-
public void validateBlobSidecarsBatchAgainstBlock(
129-
final List<BlobSidecar> blobSidecars,
130-
final BeaconBlock block,
131-
final List<KZGCommitment> kzgCommitmentsFromBlock) {
132-
133-
final String slotAndBlockRoot = block.getSlotAndBlockRoot().toLogString();
134-
135-
blobSidecars.forEach(
136-
blobSidecar -> {
137-
final UInt64 blobIndex = blobSidecar.getIndex();
138-
139-
checkArgument(
140-
blobSidecar.getBlockRoot().equals(block.getRoot()),
141-
"Block and blob sidecar root mismatch for %s, blob index %s",
142-
slotAndBlockRoot,
143-
blobIndex);
144-
145-
final KZGCommitment kzgCommitmentFromBlock;
146-
147-
try {
148-
kzgCommitmentFromBlock = kzgCommitmentsFromBlock.get(blobIndex.intValue());
149-
} catch (IndexOutOfBoundsException e) {
150-
throw new IllegalArgumentException(
151-
String.format(
152-
"Blob sidecar index out of bound with respect to block %s, blob index %s",
153-
slotAndBlockRoot, blobIndex));
154-
}
155-
156-
checkArgument(
157-
blobSidecar.getKZGCommitment().equals(kzgCommitmentFromBlock),
158-
"Block and blob sidecar kzg commitments mismatch for %s, blob index %s",
159-
slotAndBlockRoot,
160-
blobIndex);
161-
});
143+
public boolean verifyBlobSidecarBlockHeaderSignatureViaValidatedSignedBlock(
144+
final BlobSidecar blobSidecar, final SignedBeaconBlock signedBeaconBlock) {
145+
if (blobSidecar.isSignatureValidated()) {
146+
return true;
147+
}
148+
149+
final boolean result =
150+
blobSidecar
151+
.getSignedBeaconBlockHeader()
152+
.hashTreeRoot()
153+
.equals(signedBeaconBlock.hashTreeRoot());
154+
155+
if (result) {
156+
blobSidecar.markSignatureAsValidated();
157+
}
158+
159+
return result;
162160
}
163161

164162
/**
165163
* Verifies that blob sidecars are complete and with expected indexes
166164
*
167165
* @param completeVerifiedBlobSidecars blob sidecars to verify, It is assumed that it is an
168166
* ordered list based on BlobSidecar index
169-
* @param kzgCommitmentsFromBlock kzg commitments from block.
167+
* @param signedBeaconBlock block with commitments
170168
*/
171169
@Override
172170
public void verifyBlobSidecarCompleteness(
173171
final List<BlobSidecar> completeVerifiedBlobSidecars,
174-
final List<KZGCommitment> kzgCommitmentsFromBlock)
172+
final SignedBeaconBlock signedBeaconBlock)
175173
throws IllegalArgumentException {
174+
int commitmentCount =
175+
signedBeaconBlock
176+
.getBeaconBlock()
177+
.map(BeaconBlock::getBody)
178+
.flatMap(BeaconBlockBody::getOptionalBlobKzgCommitments)
179+
.orElseThrow()
180+
.size();
176181
checkArgument(
177-
completeVerifiedBlobSidecars.size() == kzgCommitmentsFromBlock.size(),
178-
"Blob sidecars are not complete");
182+
completeVerifiedBlobSidecars.size() == commitmentCount, "Blob sidecars are not complete");
179183

180184
IntStream.range(0, completeVerifiedBlobSidecars.size())
181185
.forEach(
@@ -262,12 +266,22 @@ public BlobSidecar constructBlobSidecar(
262266
index, blob, commitment, proof, signedBeaconBlock.asHeader(), kzgCommitmentInclusionProof);
263267
}
264268

265-
public boolean verifyBlobSidecarMerkleProof(final BlobSidecar blobSidecar) {
266-
return predicates.isValidMerkleBranch(
267-
blobSidecar.getSszKZGCommitment().hashTreeRoot(),
268-
blobSidecar.getKzgCommitmentInclusionProof(),
269-
SpecConfigDeneb.required(specConfig).getKzgCommitmentInclusionProofDepth(),
270-
getBlobSidecarKzgCommitmentGeneralizedIndex(blobSidecar.getIndex()),
271-
blobSidecar.getBlockBodyRoot());
269+
public boolean verifyBlobKzgCommitmentInclusionProof(final BlobSidecar blobSidecar) {
270+
if (blobSidecar.isKzgCommitmentInclusionProofValidated()) {
271+
return true;
272+
}
273+
final boolean result =
274+
predicates.isValidMerkleBranch(
275+
blobSidecar.getSszKZGCommitment().hashTreeRoot(),
276+
blobSidecar.getKzgCommitmentInclusionProof(),
277+
SpecConfigDeneb.required(specConfig).getKzgCommitmentInclusionProofDepth(),
278+
getBlobSidecarKzgCommitmentGeneralizedIndex(blobSidecar.getIndex()),
279+
blobSidecar.getBlockBodyRoot());
280+
281+
if (result) {
282+
blobSidecar.markKzgCommitmentInclusionProofAsValidated();
283+
}
284+
285+
return result;
272286
}
273287
}

0 commit comments

Comments
 (0)