-
Notifications
You must be signed in to change notification settings - Fork 762
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
Match block and blobs after validating execution #6469
base: unstable
Are you sure you want to change the base?
Conversation
@@ -85,60 +86,41 @@ impl<E: EthSpec> RangeBlockComponentsRequest<E> { | |||
} | |||
|
|||
pub fn into_responses(self, spec: &ChainSpec) -> Result<Vec<RpcBlock<E>>, String> { | |||
if let Some(expects_custody_columns) = self.expects_custody_columns.clone() { | |||
self.into_responses_with_custody_columns(expects_custody_columns, spec) | |||
if let Some(expected_column_indices) = self.expects_custody_columns.as_ref() { |
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.
This field should probably be renamed to expected_column_indices
to better reflect its type. Doesn't need to be part of this PR though
)); | ||
} | ||
// Safe to convert to `CustodyDataColumn`: we have asserted that the index of | ||
// this column is in the set of `expects_custody_columns` and with the expected |
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.
Where have we done this assertion?
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.
In sync we only request custody columns
} | ||
let blobs = VariableList::from(blobs_buffer.into_iter().flatten().collect::<Vec<_>>()); | ||
responses.push(RpcBlock::new(None, block, Some(blobs)).map_err(|e| format!("{e:?}"))?) | ||
for blob in blobs { |
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.
Neat simplificaiton in the logic 👍
@@ -311,8 +311,13 @@ impl<T: BeaconChainTypes> DataAvailabilityChecker<T> { | |||
let (block_root, block, blobs, data_columns) = block.deconstruct(); | |||
if self.blobs_required_for_block(&block) { | |||
return if let Some(blob_list) = blobs.as_ref() { | |||
if block.num_expected_blobs() != blob_list.len() { |
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.
Should we also check if the block and blob commitments match here?
Same check in RpcBlock::new
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 could also do this in RpcBlock::deconstruct
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.
if we check the inclusion proof, we check that the commitments match the block. I've added a check in the da_checker. It's unnecessary in most paths but would be best to skip it only as an optimization by adding a property to RpcBlock::valid_inclusion_proof
, and only if true, skip the check
for block in blocks { | ||
let block_root = get_block_root(&block); | ||
let blobs = blobs_by_block.remove(&block_root).map(VariableList::from); | ||
rpc_blocks.push(RpcBlock::new_unchecked(block_root, block, blobs, None)); |
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 think using the unchecked variant here might result in an inconsistent state.
Consider the example you used, the peer/peers have sent us a valid block for a block_root x
which contains 1 kzg_commitment
. The peer sends us 2 blobs for this block root which do not match with the kzg_commitment
in the block. The malicious blobs pass the kzg verification in isolation, they just doesn't match with the block commitment.
Now the unchecked RpcBlock
is sent for processing. The block goes through the validation pipeline
I couldn't find anywhere we check that the block and blob commitments match up. Correct me if i'm wrong here though.
We should potentially do this check when we doRpcBlock::deconstruct
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 couldn't find anywhere we check that the block and blob commitments match up
In lookup sync when doing a blobs request we check the inclusion proof, here
lighthouse/beacon_node/network/src/sync/network_context/requests/blobs_by_root.rs
Line 53 in 40d3423
if !blob.verify_blob_sidecar_inclusion_proof() { |
However we don't for range sync. I've added the inclusion proof check in the da_checker. We can remove it after #6497
@@ -193,34 +170,60 @@ impl<E: EthSpec> RpcBlock<E> { | |||
Self::new(Some(block_root), block, blobs) | |||
} | |||
|
|||
/// Create an RpcBlock from an untrusted block, without checking that the blobs or columns are |
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 think the docs for RpcBlock and RpcBlockInner need to be updated to reflect the fact that they could be constructed without checking for consistency
130c04c
to
328f4d4
Compare
Issue Addressed
After PeerDAS we request block and data to different peers. Consider the following scenario when doing a by range sync:
Current unstable will attempt to match block and columns and consider the entire RpcBlock invalid. A similar scenario can occur where the block peer is honest and the column peer is dishonest.
We need to make the code more aware of who is at fault, and specifically do not attempt to match columns before validating the block.
Part of
Note: this PR touches code on the mainnet path, not only PeerDAS
Proposed Changes
Relax RpcBlock construction with
new_unchecked
and perform matching inverify_kzg_for_rpc_block
after the block is execution validated.This PR does not include a fix for peer scoring. This will be tackled latter as part of #6258