Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
fbab829
Remove request tracking inside syncing chains
dapplion Jan 24, 2025
2faf5f1
Prioritize by range peers in network context
dapplion Jan 24, 2025
0e13a8d
Prioritize custody peers for columns by range
dapplion Jan 25, 2025
70e6066
Explicit error handling of the no peers error case
dapplion Jan 25, 2025
8cf4e8c
Remove good_peers_on_sampling_subnets
dapplion Jan 25, 2025
4da322f
Count AwaitingDownload towards the buffer limit
dapplion Jan 25, 2025
9cd238d
Retry syncing chains in AwaitingDownload state
dapplion Jan 25, 2025
891c8fc
Use same peer priorization for lookups
dapplion Feb 5, 2025
29a5aff
Review PR
dapplion Feb 6, 2025
cd6c5d6
Address TODOs
dapplion Feb 6, 2025
2c6a7cc
Revert changes to peer erroring in range sync
dapplion Feb 6, 2025
f77bc24
Revert metrics changes
dapplion Feb 6, 2025
4792275
Update comment
dapplion Feb 6, 2025
45f5528
Pass peers_to_deprioritize to select_columns_by_range_peers_to_request
dapplion Mar 17, 2025
7ec350b
more idiomatic
dapplion Mar 17, 2025
6f7a524
Merge remote-tracking branch 'sigp/unstable' into peer-loadbalancing
dapplion Mar 17, 2025
8962808
Idiomatic while
dapplion Mar 17, 2025
e9247d2
Add note about infinite loop
dapplion Mar 17, 2025
97e75ef
Use while let
dapplion Mar 23, 2025
0e7284f
Merge remote-tracking branch 'sigp/unstable' into peer-loadbalancing
dapplion Mar 25, 2025
0af1bd6
Fix wrong custody column count for lookup blocks
dapplion Apr 8, 2025
d6a68a6
Remove impl
dapplion Apr 8, 2025
3131892
Remove stale comment
jimmygchen Apr 8, 2025
017c6ab
Fix build errors.
jimmygchen Apr 8, 2025
cac7f3e
Or default
dapplion Apr 8, 2025
5c87e37
Review PR
dapplion Apr 8, 2025
53536c7
Merge remote-tracking branch 'sigp/unstable' into peer-loadbalancing
dapplion Apr 8, 2025
001e70d
Merge branch 'fix-custody-column-count-lookup' into peerdas-sync
dapplion Apr 11, 2025
2df888a
Merge branch 'peer-loadbalancing' into peerdas-sync
dapplion Apr 11, 2025
034186f
BatchPeerGroup
dapplion Mar 21, 2025
ecc894a
Match block and blob signatures
dapplion Apr 12, 2025
bb6175c
Explicit match statement to BlockError in range sync
dapplion Apr 12, 2025
dbd23a4
Remove todo in BatchPeerGroup
dapplion Apr 12, 2025
ea6cdb7
Remove participating peers from backfill sync
dapplion Apr 12, 2025
9db66f0
Remove MissingAllCustodyColumns error
dapplion Apr 12, 2025
a3d26b6
Merge remote-tracking branch 'sigp/peerdas-devnet-7' into peerdas-sync
dapplion May 14, 2025
4b2bbe3
Merge fixes
dapplion May 14, 2025
e0b3650
Clean up PR
dapplion May 14, 2025
6d56323
Consistent naming of batch_peers
dapplion May 14, 2025
1b8a8a2
Address multiple review comments
dapplion May 14, 2025
6ba7f7c
Better errors for das
dapplion May 14, 2025
675ae07
Penalize column peers once
dapplion May 14, 2025
56f7c36
Restore fn
dapplion May 14, 2025
f12d210
Fix error enum
dapplion May 14, 2025
f0c7750
Removed MismatchedPublicKeyLen
dapplion May 14, 2025
fad3a0a
Revert testing changes
dapplion May 14, 2025
f637cbb
Change BlockAndCustodyColumns enum variant
dapplion May 14, 2025
9292f82
Revert type change in import_historical_block_batch
dapplion May 15, 2025
edc7da0
Drop pubkey cache
dapplion May 15, 2025
859f380
Don't collect Vec
dapplion May 15, 2025
74f137e
Classify errors
dapplion May 15, 2025
f48b586
Remove ReconstructColumnsError
dapplion May 15, 2025
4649211
More detailed UnrequestedSlot error
dapplion May 15, 2025
10882fd
Lint test
dapplion May 15, 2025
50032a4
Fix slot conversion
dapplion May 15, 2025
06d4076
Reduce penalty for missing blobs
dapplion May 15, 2025
91663f4
Revert changes in peer selection
dapplion May 15, 2025
31f46a1
Lint tests
dapplion May 15, 2025
b11f8a3
Rename block matching functions
dapplion May 16, 2025
d82bf26
Reorder block matching in historical blocks
dapplion May 16, 2025
9434046
Fix order of block matching
dapplion May 16, 2025
40772ca
Add store tests
dapplion May 19, 2025
e575e10
Merge branch 'peerdas-devnet-7' into peerdas-sync
jimmygchen May 20, 2025
0058471
Filter blockchain in assert_correct_historical_block_chain
dapplion May 20, 2025
d4046ac
Also filter before KZG checks
dapplion May 20, 2025
fe0a15d
Lint tests
dapplion May 20, 2025
0659420
Fix lint
pawanjay176 May 20, 2025
7a8555c
Merge branch 'peerdas-devnet-7' into peerdas-sync
pawanjay176 May 20, 2025
fb8e6fe
Fix fulu err assertion
dapplion May 20, 2025
eb400f0
Check point is not at infinity
dapplion May 20, 2025
aabadb5
Fix ws sync test
dapplion May 20, 2025
6a663d8
Revert dropping filter fn
dapplion May 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 33 additions & 14 deletions beacon_node/beacon_chain/src/block_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ use store::{Error as DBError, HotStateSummary, KeyValueStore, StoreOp};
use strum::AsRefStr;
use task_executor::JoinHandle;
use tracing::{debug, error};
use types::ColumnIndex;
use types::{
data_column_sidecar::DataColumnSidecarError, BeaconBlockRef, BeaconState, BeaconStateError,
BlobsList, ChainSpec, DataColumnSidecarList, Epoch, EthSpec, ExecutionBlockHash, FullPayload,
Expand Down Expand Up @@ -220,6 +221,10 @@ pub enum BlockError {
///
/// The block is invalid and the peer is faulty.
InvalidSignature(InvalidSignature),
/// One or more signatures in a BlobSidecar of an RpcBlock are invalid
InvalidBlobsSignature(Vec<u64>),
/// One or more signatures in a DataColumnSidecar of an RpcBlock are invalid
InvalidDataColumnsSignature(Vec<ColumnIndex>),
/// The provided block is not from a later slot than its parent.
///
/// ## Peer scoring
Expand Down Expand Up @@ -634,6 +639,34 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
&chain.spec,
)?;

// Verify signatures before matching blocks and data. Otherwise we may penalize blob or column
// peers for valid signatures if the block peer sends us an invalid signature.
let pubkey_cache = get_validator_pubkey_cache(chain)?;
let mut signature_verifier = get_signature_verifier(&state, &pubkey_cache, &chain.spec);
for (block_root, block) in &chain_segment {
let mut consensus_context =
ConsensusContext::new(block.slot()).set_current_block_root(*block_root);
signature_verifier.include_all_signatures(block.as_block(), &mut consensus_context)?;
}
if signature_verifier.verify().is_err() {
return Err(BlockError::InvalidSignature(InvalidSignature::Unknown));
}
drop(pubkey_cache);

// Verify that blobs or data columns signatures match
//
// TODO(das): Should check correct proposer cheap for added protection if blocks and columns
// don't match. This code attributes fault to the blobs / data columns if they don't match the
// block
for (_, block) in &chain_segment {
if let Err(indices) = block.match_block_and_blobs() {
return Err(BlockError::InvalidBlobsSignature(indices));
}
if let Err(indices) = block.match_block_and_data_columns() {
return Err(BlockError::InvalidDataColumnsSignature(indices));
}
}

// unzip chain segment and verify kzg in bulk
let (roots, blocks): (Vec<_>, Vec<_>) = chain_segment.into_iter().unzip();
let maybe_available_blocks = chain
Expand All @@ -655,20 +688,6 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
})
.collect::<Vec<_>>();

// verify signatures
let pubkey_cache = get_validator_pubkey_cache(chain)?;
let mut signature_verifier = get_signature_verifier(&state, &pubkey_cache, &chain.spec);
for svb in &mut signature_verified_blocks {
signature_verifier
.include_all_signatures(svb.block.as_block(), &mut svb.consensus_context)?;
}

if signature_verifier.verify().is_err() {
return Err(BlockError::InvalidSignature(InvalidSignature::Unknown));
}

drop(pubkey_cache);

if let Some(signature_verified_block) = signature_verified_blocks.first_mut() {
signature_verified_block.parent = Some(parent);
}
Expand Down
141 changes: 109 additions & 32 deletions beacon_node/beacon_chain/src/block_verification_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use types::blob_sidecar::BlobIdentifier;
use types::{
BeaconBlockRef, BeaconState, BlindedPayload, BlobSidecarList, ChainSpec, Epoch, EthSpec,
Hash256, RuntimeVariableList, SignedBeaconBlock, SignedBeaconBlockHeader, Slot,
BeaconBlockRef, BeaconState, BlindedPayload, BlobSidecarList, ChainSpec, ColumnIndex,
DataColumnSidecar, Epoch, EthSpec, Hash256, RuntimeVariableList, SignedBeaconBlock,
SignedBeaconBlockHeader, Slot,
};

/// A block that has been received over RPC. It has 2 internal variants:
Expand Down Expand Up @@ -53,31 +54,60 @@ impl<E: EthSpec> RpcBlock<E> {
match &self.block {
RpcBlockInner::Block(block) => block,
RpcBlockInner::BlockAndBlobs(block, _) => block,
RpcBlockInner::BlockAndCustodyColumns(block, _) => block,
RpcBlockInner::BlockAndCustodyColumns { block, .. } => block,
}
}

pub fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
match &self.block {
RpcBlockInner::Block(block) => block.clone(),
RpcBlockInner::BlockAndBlobs(block, _) => block.clone(),
RpcBlockInner::BlockAndCustodyColumns(block, _) => block.clone(),
RpcBlockInner::BlockAndCustodyColumns { block, .. } => block.clone(),
}
}

pub fn blobs(&self) -> Option<&BlobSidecarList<E>> {
match &self.block {
RpcBlockInner::Block(_) => None,
RpcBlockInner::BlockAndBlobs(_, blobs) => Some(blobs),
RpcBlockInner::BlockAndCustodyColumns(_, _) => None,
RpcBlockInner::BlockAndCustodyColumns { .. } => None,
}
}

pub fn custody_columns(&self) -> Option<&CustodyDataColumnList<E>> {
match &self.block {
RpcBlockInner::Block(_) => None,
RpcBlockInner::BlockAndBlobs(_, _) => None,
RpcBlockInner::BlockAndCustodyColumns(_, data_columns) => Some(data_columns),
RpcBlockInner::BlockAndCustodyColumns { data_columns, .. } => Some(data_columns),
}
}

/// Returns Err if any of its inner BlobSidecar's signed_block_header does not match the inner
/// block
pub fn match_block_and_blobs(&self) -> Result<(), Vec<u64>> {
match &self.block {
RpcBlockInner::Block(_) => Ok(()),
RpcBlockInner::BlockAndBlobs(block, blobs) => match_block_and_blobs(block, blobs),
RpcBlockInner::BlockAndCustodyColumns { .. } => Ok(()),
}
}

/// Returns Err if any of its inner DataColumnSidecar's signed_block_header does not match the
/// inner block
pub fn match_block_and_data_columns(&self) -> Result<(), Vec<ColumnIndex>> {
match &self.block {
RpcBlockInner::Block(_) => Ok(()),
RpcBlockInner::BlockAndBlobs(..) => Ok(()),
RpcBlockInner::BlockAndCustodyColumns {
block,
data_columns,
..
} => match_block_and_data_columns(
block,
data_columns
.iter()
.map(|data_column| data_column.as_data_column()),
),
}
}
}
Expand All @@ -88,14 +118,20 @@ impl<E: EthSpec> RpcBlock<E> {
#[derive(Debug, Clone, Derivative)]
#[derivative(Hash(bound = "E: EthSpec"))]
enum RpcBlockInner<E: EthSpec> {
/// Single block lookup response. This should potentially hit the data availability cache.
/// **Range sync**: Variant for all pre-Deneb blocks
/// **Lookup sync**: Variant used for all blocks of all forks, regardless if the have data or
/// not
Block(Arc<SignedBeaconBlock<E>>),
/// This variant is used with parent lookups and by-range responses. It should have all blobs
/// ordered, all block roots matching, and the correct number of blobs for this block.
/// **Range sync**: Variant for all post-Deneb blocks regardless if they have data or not
/// **Lookup sync**: Not used
BlockAndBlobs(Arc<SignedBeaconBlock<E>>, BlobSidecarList<E>),
/// This variant is used with parent lookups and by-range responses. It should have all
/// requested data columns, all block roots matching for this block.
BlockAndCustodyColumns(Arc<SignedBeaconBlock<E>>, CustodyDataColumnList<E>),
/// **Range sync**: Variant for all post-Fulu blocks regardless if they have data or not
/// **Lookup sync**: Not used
BlockAndCustodyColumns {
block: Arc<SignedBeaconBlock<E>>,
data_columns: CustodyDataColumnList<E>,
expected_custody_indices: Vec<ColumnIndex>,
},
}

impl<E: EthSpec> RpcBlock<E> {
Expand Down Expand Up @@ -161,23 +197,24 @@ impl<E: EthSpec> RpcBlock<E> {
block_root: Option<Hash256>,
block: Arc<SignedBeaconBlock<E>>,
custody_columns: Vec<CustodyDataColumn<E>>,
custody_columns_count: usize,
expected_custody_indices: Vec<ColumnIndex>,
spec: &ChainSpec,
) -> Result<Self, AvailabilityCheckError> {
let block_root = block_root.unwrap_or_else(|| get_block_root(&block));

if block.num_expected_blobs() > 0 && custody_columns.is_empty() {
// The number of required custody columns is out of scope here.
return Err(AvailabilityCheckError::MissingCustodyColumns);
}
// Treat empty data column lists as if they are missing.
let inner = if !custody_columns.is_empty() {
RpcBlockInner::BlockAndCustodyColumns(
block,
RuntimeVariableList::new(custody_columns, spec.number_of_columns as usize)?,
let custody_columns_count = expected_custody_indices.len();
let inner = RpcBlockInner::BlockAndCustodyColumns {
block,
data_columns: RuntimeVariableList::new(
custody_columns,
spec.number_of_columns as usize,
)
} else {
RpcBlockInner::Block(block)
.map_err(|e| {
AvailabilityCheckError::Unexpected(format!(
"custody_columns len exceeds number_of_columns: {e:?}"
))
})?,
expected_custody_indices,
};
Ok(Self {
block_root,
Expand All @@ -193,27 +230,34 @@ impl<E: EthSpec> RpcBlock<E> {
Hash256,
Arc<SignedBeaconBlock<E>>,
Option<BlobSidecarList<E>>,
Option<CustodyDataColumnList<E>>,
Option<(CustodyDataColumnList<E>, Vec<ColumnIndex>)>,
) {
let block_root = self.block_root();
match self.block {
RpcBlockInner::Block(block) => (block_root, block, None, None),
RpcBlockInner::BlockAndBlobs(block, blobs) => (block_root, block, Some(blobs), None),
RpcBlockInner::BlockAndCustodyColumns(block, data_columns) => {
(block_root, block, None, Some(data_columns))
}
RpcBlockInner::BlockAndCustodyColumns {
block,
data_columns,
expected_custody_indices,
} => (
block_root,
block,
None,
Some((data_columns, expected_custody_indices)),
),
}
}
pub fn n_blobs(&self) -> usize {
match &self.block {
RpcBlockInner::Block(_) | RpcBlockInner::BlockAndCustodyColumns(_, _) => 0,
RpcBlockInner::Block(_) | RpcBlockInner::BlockAndCustodyColumns { .. } => 0,
RpcBlockInner::BlockAndBlobs(_, blobs) => blobs.len(),
}
}
pub fn n_data_columns(&self) -> usize {
match &self.block {
RpcBlockInner::Block(_) | RpcBlockInner::BlockAndBlobs(_, _) => 0,
RpcBlockInner::BlockAndCustodyColumns(_, data_columns) => data_columns.len(),
RpcBlockInner::BlockAndCustodyColumns { data_columns, .. } => data_columns.len(),
}
}
}
Expand Down Expand Up @@ -528,17 +572,50 @@ impl<E: EthSpec> AsBlock<E> for RpcBlock<E> {
match &self.block {
RpcBlockInner::Block(block) => block,
RpcBlockInner::BlockAndBlobs(block, _) => block,
RpcBlockInner::BlockAndCustodyColumns(block, _) => block,
RpcBlockInner::BlockAndCustodyColumns { block, .. } => block,
}
}
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
match &self.block {
RpcBlockInner::Block(block) => block.clone(),
RpcBlockInner::BlockAndBlobs(block, _) => block.clone(),
RpcBlockInner::BlockAndCustodyColumns(block, _) => block.clone(),
RpcBlockInner::BlockAndCustodyColumns { block, .. } => block.clone(),
}
}
fn canonical_root(&self) -> Hash256 {
self.as_block().canonical_root()
}
}

/// Returns Err if any of `blobs` BlobSidecar's signed_block_header does not match
/// block
pub fn match_block_and_blobs<E: EthSpec>(
block: &SignedBeaconBlock<E>,
blobs: &BlobSidecarList<E>,
) -> Result<(), Vec<u64>> {
let indices = blobs
.iter()
.filter(|blob| &blob.signed_block_header.signature != block.signature())
.map(|blob| blob.index)
.collect::<Vec<_>>();
if indices.is_empty() {
Ok(())
} else {
Err(indices)
}
}

pub fn match_block_and_data_columns<'a, E: EthSpec>(
block: &SignedBeaconBlock<E>,
data_columns: impl Iterator<Item = &'a Arc<DataColumnSidecar<E>>>,
) -> Result<(), Vec<ColumnIndex>> {
let indices = data_columns
.filter(|column| &column.signed_block_header.signature != block.signature())
.map(|column| column.index)
.collect::<Vec<_>>();
if indices.is_empty() {
Ok(())
} else {
Err(indices)
}
}
Loading
Loading