-
Notifications
You must be signed in to change notification settings - Fork 303
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
Improve isDataAvailable
checks
#7575
Improve isDataAvailable
checks
#7575
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
"Blob sidecar slot %s does not match block slot %s", | ||
blobSidecar.getSlot(), | ||
slot); | ||
checkArgument( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also add parent root check (but it will require changes in interface)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started thinking that these checks could leave in the BlockBlobSidecarsTracker
. If the tracker guarantees that block and sidecars field are consistent, we can then have this method only dedicated to KZG check, which is the original intention. The good thing on discarding inconsistent blobSidecars at BlockBlobSidecarsTracker
level is that we could try to refetch them early.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thinking more about it, an early refetch might be undesirable here. If the proposer is doing something strange, is better to do nothing, skip the block import, vote on previous head, and then lookup only if next block builds on top of this (which might be not the case most times, unless next proposer is under control of the same malicious\buggy entity)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so ForkChoiceBlobSidecarsAvailabilityChecker::validateBatch
might be a better place to have this checks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to move the validation back to miscHelper but on a dedicated method. This simplified the tests in ForkChoiceBlobSidecarsAvailabilityCheckerTest
return kzg.verifyBlobKzgProofBatch(blobs, kzgCommitments, proofs); | ||
checkArgument( | ||
blobSidecars.size() == kzgCommitmentsFromBlock.size(), | ||
"Blob sidecars count %s does not match KZG commitments count %s", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe match block KZG commitments
to be more clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would this be more helpful to report if we had the actual block slot or something associating it to the block in the message?
It'll be very hard for anyone to understand what this relates to because there's nothing tying it into data we can understand, so from the perspective of a node op, this will be a 'useless message' as it stands.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right!
checkArgument( | ||
kzgCommitmentsFromBlock.get(index).equals(blobSidecar.getKZGCommitment()), | ||
"Blob sidecar KZG commitment %s does not match block KZG commitment %s", | ||
blobSidecar.getKZGCommitment(), | ||
kzgCommitmentsFromBlock.get(index)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar to above, adding slot at a minimum would be helpful
move all blobSidecar-block sanity checks in the availability checker. Fun with tests
…into improve-isDataAvailable-checks
.../src/test/java/tech/pegasys/teku/spec/logic/versions/deneb/helpers/MiscHelpersDenebTest.java
Fixed
Show fixed
Hide fixed
final Bytes32 beaconBlockRoot, | ||
final List<KZGCommitment> kzgCommitments, | ||
final List<BlobSidecar> blobSidecars) { | ||
public boolean isDataAvailable(final List<BlobSidecar> blobSidecars) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I don't like here, both methods, this and validateBlobSidecarsAgainstBlock
could be called now with part of blobs, which is not clear. Next, isDataAvailable
is a spec thing, so I expect it will verify it completely. Plus the last piece of puzzle, that we call this method with blobSidecars with indexes matching all indexes of kzgCommitments
from block is somewhere else in the 3rd place.
I'm not sure what's the best solution to make this better. My suggestion (not the best one, need to think more on this) is:
- rename this method, so it's actually 2nd round of verification
- add comments to both methods that they could be used for partial verification
- add actual isDataAvailable(kzgCommitmentsFromBlock, verifiedBlobKzgCommitments) here, which will finalize the action by just equal matching 2 lists
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or better isDataAvailable(block, verifiedBlobIdentifiers)
return IntRange.of(0, block.getKzgCommitments.size()).map(index -> verifiedBlobIdentifiers.contains(new BlobIdentifier(block.getRoot, index))).filter(false).findFirst().isEmpty()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it isn't clear that those methods can be called with partial blobs. I'm thinking if it is better to go back to single method.
Consider that ForkChoiceBlobSidecarsAvailabilityChecker::computeFinalValidationResult
is the method performing the checks on the final set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, yeah, I understand it's happened there but we'd better have it here it and by my understanding it should be called isDataAvailable
(for block, all data)
|
||
public List<BlobSidecar> randomBlobSidecarsForBlock( | ||
final SignedBeaconBlock block, | ||
BiFunction<Integer, RandomBlobSidecarBuilder, RandomBlobSidecarBuilder> modifier) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
boring final
final int numberOfKzgCommitments = | ||
public List<SignedBlobSidecar> randomSignedBlobSidecarsForBlock( | ||
final SignedBeaconBlock block, | ||
BiFunction<Integer, RandomBlobSidecarBuilder, RandomBlobSidecarBuilder> modifier) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another one
* @return | ||
*/ | ||
private BlobSidecarsAndValidationResult performCompletenessValidation( | ||
final BlobSidecarsAndValidationResult additionalBlobSidecarsAndValidationResult) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's probably not additional, all instead, especially if it could pass completeness check
.verifyBlobSidecarCompleteness( | ||
additionalBlobSidecarsAndValidationResult.getBlobSidecars(), | ||
kzgCommitmentsFromBlockSupplier.get()); | ||
} catch (final IllegalArgumentException ex) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear by verifyBlobSidecarCompleteness
signature that we should catch IllegalArgumentException
from it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe worth adding a test especially on completeness?
|
||
if (!blobSidecars.isEmpty()) { | ||
return validateBatch(blobSidecars, kzgCommitments); | ||
return validateBatch(blobSidecars); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not doing completeness check in this case or I missed it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, good catch, I wasn't clearly in a good shape!
return validateBatch(blobSidecars, kzgCommitments); | ||
final BlobSidecarsAndValidationResult blobSidecarsAndValidationResult = | ||
validateBatch(blobSidecars); | ||
performCompletenessValidation(blobSidecarsAndValidationResult); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You ignore the result of completeness check, there should be probably return on this line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
performCompletenessValidation
is designed to throw only
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has the line return BlobSidecarsAndValidationResult.NOT_REQUIRED;
Unless it's not void
return I assume we care on return
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're right.
blobSidecarsAndValidationResult.getBlobSidecars(), | ||
kzgCommitmentsFromBlockSupplier.get()); | ||
} catch (final IllegalArgumentException ex) { | ||
checkArgument( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some concerns on checkArgument vs checkState here, anyway we are already not super consistent with it in code. The more important is that we throw it all the way up, while we have BlobSidecarsAndValidationResult.invalidResult(blobSidecars, throwable) which looks better in this case. Or we could at least add a comment that this could throw. Though I like .invalidResult more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This PR is mainly focused on making sure that commitments coming from block match the commitments contained in theBlobSidecar
sUpdated strategy by moving away extra checks from
isDataAvailable
method to a dedicated method in miscHelper.Since we check that blobsSidecar content are consistent with the block before calling
isDataAvailable
, we can now pass them only to perform data availability.Documentation
doc-change-required
label to this PR if updates are required.Changelog