mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 10:22:38 +00:00
Add individual by_range sync requests (#6497)
Part of - https://github.com/sigp/lighthouse/issues/6258 To address PeerDAS sync issues we need to make individual by_range requests within a batch retriable. We should adopt the same pattern for lookup sync where each request (block/blobs/columns) is tracked individually within a "meta" request that group them all and handles retry logic. - Building on https://github.com/sigp/lighthouse/pull/6398 second step is to add individual request accumulators for `blocks_by_range`, `blobs_by_range`, and `data_columns_by_range`. This will allow each request to progress independently and be retried separately. Most of the logic is just piping, excuse the large diff. This PR does not change the logic of how requests are handled or retried. This will be done in a future PR changing the logic of `RangeBlockComponentsRequest`. ### Before - Sync manager receives block with `SyncRequestId::RangeBlockAndBlobs` - Insert block into `SyncNetworkContext::range_block_components_requests` - (If received stream terminators of all requests) - Return `Vec<RpcBlock>`, and insert into `range_sync` ### Now - Sync manager receives block with `SyncRequestId::RangeBlockAndBlobs` - Insert block into `SyncNetworkContext:: blocks_by_range_requests` - (If received stream terminator of this request) - Return `Vec<SignedBlock>`, and insert into `SyncNetworkContext::components_by_range_requests ` - (If received a result for all requests) - Return `Vec<RpcBlock>`, and insert into `range_sync`
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
use beacon_chain::{
|
||||
block_verification_types::RpcBlock, data_column_verification::CustodyDataColumn, get_block_root,
|
||||
};
|
||||
use lighthouse_network::PeerId;
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
sync::Arc,
|
||||
@@ -29,9 +28,6 @@ pub struct RangeBlockComponentsRequest<E: EthSpec> {
|
||||
/// Used to determine if the number of data columns stream termination this accumulator should
|
||||
/// wait for. This may be less than the number of `expects_custody_columns` due to request batching.
|
||||
num_custody_column_requests: Option<usize>,
|
||||
/// The peers the request was made to.
|
||||
pub(crate) peer_ids: Vec<PeerId>,
|
||||
max_blobs_per_block: usize,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> RangeBlockComponentsRequest<E> {
|
||||
@@ -39,8 +35,6 @@ impl<E: EthSpec> RangeBlockComponentsRequest<E> {
|
||||
expects_blobs: bool,
|
||||
expects_custody_columns: Option<Vec<ColumnIndex>>,
|
||||
num_custody_column_requests: Option<usize>,
|
||||
peer_ids: Vec<PeerId>,
|
||||
max_blobs_per_block: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
blocks: <_>::default(),
|
||||
@@ -52,50 +46,42 @@ impl<E: EthSpec> RangeBlockComponentsRequest<E> {
|
||||
expects_blobs,
|
||||
expects_custody_columns,
|
||||
num_custody_column_requests,
|
||||
peer_ids,
|
||||
max_blobs_per_block,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This function should be deprecated when simplying the retry mechanism of this range
|
||||
// requests.
|
||||
pub fn get_requirements(&self) -> (bool, Option<Vec<ColumnIndex>>) {
|
||||
(self.expects_blobs, self.expects_custody_columns.clone())
|
||||
pub fn add_blocks(&mut self, blocks: Vec<Arc<SignedBeaconBlock<E>>>) {
|
||||
for block in blocks {
|
||||
self.blocks.push_back(block);
|
||||
}
|
||||
self.is_blocks_stream_terminated = true;
|
||||
}
|
||||
|
||||
pub fn add_block_response(&mut self, block_opt: Option<Arc<SignedBeaconBlock<E>>>) {
|
||||
match block_opt {
|
||||
Some(block) => self.blocks.push_back(block),
|
||||
None => self.is_blocks_stream_terminated = true,
|
||||
pub fn add_blobs(&mut self, blobs: Vec<Arc<BlobSidecar<E>>>) {
|
||||
for blob in blobs {
|
||||
self.blobs.push_back(blob);
|
||||
}
|
||||
self.is_sidecars_stream_terminated = true;
|
||||
}
|
||||
|
||||
pub fn add_sidecar_response(&mut self, sidecar_opt: Option<Arc<BlobSidecar<E>>>) {
|
||||
match sidecar_opt {
|
||||
Some(sidecar) => self.blobs.push_back(sidecar),
|
||||
None => self.is_sidecars_stream_terminated = true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_data_column(&mut self, column_opt: Option<Arc<DataColumnSidecar<E>>>) {
|
||||
match column_opt {
|
||||
Some(column) => self.data_columns.push_back(column),
|
||||
// TODO(das): this mechanism is dangerous, if somehow there are two requests for the
|
||||
// same column index it can terminate early. This struct should track that all requests
|
||||
// for all custody columns terminate.
|
||||
None => self.custody_columns_streams_terminated += 1,
|
||||
pub fn add_custody_columns(&mut self, columns: Vec<Arc<DataColumnSidecar<E>>>) {
|
||||
for column in columns {
|
||||
self.data_columns.push_back(column);
|
||||
}
|
||||
// TODO(das): this mechanism is dangerous, if somehow there are two requests for the
|
||||
// same column index it can terminate early. This struct should track that all requests
|
||||
// for all custody columns terminate.
|
||||
self.custody_columns_streams_terminated += 1;
|
||||
}
|
||||
|
||||
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)
|
||||
} else {
|
||||
self.into_responses_with_blobs()
|
||||
self.into_responses_with_blobs(spec)
|
||||
}
|
||||
}
|
||||
|
||||
fn into_responses_with_blobs(self) -> Result<Vec<RpcBlock<E>>, String> {
|
||||
fn into_responses_with_blobs(self, spec: &ChainSpec) -> Result<Vec<RpcBlock<E>>, String> {
|
||||
let RangeBlockComponentsRequest { blocks, blobs, .. } = self;
|
||||
|
||||
// There can't be more more blobs than blocks. i.e. sending any blob (empty
|
||||
@@ -103,7 +89,8 @@ impl<E: EthSpec> RangeBlockComponentsRequest<E> {
|
||||
let mut responses = Vec::with_capacity(blocks.len());
|
||||
let mut blob_iter = blobs.into_iter().peekable();
|
||||
for block in blocks.into_iter() {
|
||||
let mut blob_list = Vec::with_capacity(self.max_blobs_per_block);
|
||||
let max_blobs_per_block = spec.max_blobs_per_block(block.epoch()) as usize;
|
||||
let mut blob_list = Vec::with_capacity(max_blobs_per_block);
|
||||
while {
|
||||
let pair_next_blob = blob_iter
|
||||
.peek()
|
||||
@@ -114,7 +101,7 @@ impl<E: EthSpec> RangeBlockComponentsRequest<E> {
|
||||
blob_list.push(blob_iter.next().ok_or("Missing next blob".to_string())?);
|
||||
}
|
||||
|
||||
let mut blobs_buffer = vec![None; self.max_blobs_per_block];
|
||||
let mut blobs_buffer = vec![None; max_blobs_per_block];
|
||||
for blob in blob_list {
|
||||
let blob_index = blob.index as usize;
|
||||
let Some(blob_opt) = blobs_buffer.get_mut(blob_index) else {
|
||||
@@ -128,7 +115,7 @@ impl<E: EthSpec> RangeBlockComponentsRequest<E> {
|
||||
}
|
||||
let blobs = RuntimeVariableList::new(
|
||||
blobs_buffer.into_iter().flatten().collect::<Vec<_>>(),
|
||||
self.max_blobs_per_block,
|
||||
max_blobs_per_block,
|
||||
)
|
||||
.map_err(|_| "Blobs returned exceeds max length".to_string())?;
|
||||
responses.push(RpcBlock::new(None, block, Some(blobs)).map_err(|e| format!("{e:?}"))?)
|
||||
@@ -246,30 +233,25 @@ mod tests {
|
||||
use beacon_chain::test_utils::{
|
||||
generate_rand_block_and_blobs, generate_rand_block_and_data_columns, test_spec, NumBlobs,
|
||||
};
|
||||
use lighthouse_network::PeerId;
|
||||
use rand::SeedableRng;
|
||||
use types::{test_utils::XorShiftRng, ForkName, MinimalEthSpec as E};
|
||||
use std::sync::Arc;
|
||||
use types::{test_utils::XorShiftRng, ForkName, MinimalEthSpec as E, SignedBeaconBlock};
|
||||
|
||||
#[test]
|
||||
fn no_blobs_into_responses() {
|
||||
let spec = test_spec::<E>();
|
||||
let peer_id = PeerId::random();
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
let blocks = (0..4)
|
||||
.map(|_| {
|
||||
generate_rand_block_and_blobs::<E>(ForkName::Base, NumBlobs::None, &mut rng, &spec)
|
||||
.0
|
||||
.into()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let max_len = spec.max_blobs_per_block(blocks.first().unwrap().epoch()) as usize;
|
||||
let mut info =
|
||||
RangeBlockComponentsRequest::<E>::new(false, None, None, vec![peer_id], max_len);
|
||||
.collect::<Vec<Arc<SignedBeaconBlock<E>>>>();
|
||||
let mut info = RangeBlockComponentsRequest::<E>::new(false, None, None);
|
||||
|
||||
// Send blocks and complete terminate response
|
||||
for block in blocks {
|
||||
info.add_block_response(Some(block.into()));
|
||||
}
|
||||
info.add_block_response(None);
|
||||
info.add_blocks(blocks);
|
||||
|
||||
// Assert response is finished and RpcBlocks can be constructed
|
||||
assert!(info.is_finished());
|
||||
@@ -279,7 +261,6 @@ mod tests {
|
||||
#[test]
|
||||
fn empty_blobs_into_responses() {
|
||||
let spec = test_spec::<E>();
|
||||
let peer_id = PeerId::random();
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
let blocks = (0..4)
|
||||
.map(|_| {
|
||||
@@ -291,19 +272,15 @@ mod tests {
|
||||
&spec,
|
||||
)
|
||||
.0
|
||||
.into()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let max_len = spec.max_blobs_per_block(blocks.first().unwrap().epoch()) as usize;
|
||||
let mut info =
|
||||
RangeBlockComponentsRequest::<E>::new(true, None, None, vec![peer_id], max_len);
|
||||
.collect::<Vec<Arc<SignedBeaconBlock<E>>>>();
|
||||
let mut info = RangeBlockComponentsRequest::<E>::new(true, None, None);
|
||||
|
||||
// Send blocks and complete terminate response
|
||||
for block in blocks {
|
||||
info.add_block_response(Some(block.into()));
|
||||
}
|
||||
info.add_block_response(None);
|
||||
info.add_blocks(blocks);
|
||||
// Expect no blobs returned
|
||||
info.add_sidecar_response(None);
|
||||
info.add_blobs(vec![]);
|
||||
|
||||
// Assert response is finished and RpcBlocks can be constructed, even if blobs weren't returned.
|
||||
// This makes sure we don't expect blobs here when they have expired. Checking this logic should
|
||||
@@ -316,7 +293,6 @@ mod tests {
|
||||
fn rpc_block_with_custody_columns() {
|
||||
let spec = test_spec::<E>();
|
||||
let expects_custody_columns = vec![1, 2, 3, 4];
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
let blocks = (0..4)
|
||||
.map(|_| {
|
||||
@@ -328,34 +304,24 @@ mod tests {
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let max_len = spec.max_blobs_per_block(blocks.first().unwrap().0.epoch()) as usize;
|
||||
let mut info = RangeBlockComponentsRequest::<E>::new(
|
||||
false,
|
||||
Some(expects_custody_columns.clone()),
|
||||
Some(expects_custody_columns.len()),
|
||||
vec![PeerId::random()],
|
||||
max_len,
|
||||
);
|
||||
// Send blocks and complete terminate response
|
||||
for block in &blocks {
|
||||
info.add_block_response(Some(block.0.clone().into()));
|
||||
}
|
||||
info.add_block_response(None);
|
||||
info.add_blocks(blocks.iter().map(|b| b.0.clone().into()).collect());
|
||||
// Assert response is not finished
|
||||
assert!(!info.is_finished());
|
||||
|
||||
// Send data columns interleaved
|
||||
for block in &blocks {
|
||||
for column in &block.1 {
|
||||
if expects_custody_columns.contains(&column.index) {
|
||||
info.add_data_column(Some(column.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Terminate the requests
|
||||
for (i, _column_index) in expects_custody_columns.iter().enumerate() {
|
||||
info.add_data_column(None);
|
||||
// Send data columns
|
||||
for (i, &column_index) in expects_custody_columns.iter().enumerate() {
|
||||
info.add_custody_columns(
|
||||
blocks
|
||||
.iter()
|
||||
.flat_map(|b| b.1.iter().filter(|d| d.index == column_index).cloned())
|
||||
.collect(),
|
||||
);
|
||||
|
||||
if i < expects_custody_columns.len() - 1 {
|
||||
assert!(
|
||||
@@ -377,8 +343,21 @@ mod tests {
|
||||
#[test]
|
||||
fn rpc_block_with_custody_columns_batched() {
|
||||
let spec = test_spec::<E>();
|
||||
let expects_custody_columns = vec![1, 2, 3, 4];
|
||||
let num_of_data_column_requests = 2;
|
||||
let batched_column_requests = [vec![1_u64, 2], vec![3, 4]];
|
||||
let expects_custody_columns = batched_column_requests
|
||||
.iter()
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let custody_column_request_ids =
|
||||
(0..batched_column_requests.len() as u32).collect::<Vec<_>>();
|
||||
let num_of_data_column_requests = custody_column_request_ids.len();
|
||||
|
||||
let mut info = RangeBlockComponentsRequest::<E>::new(
|
||||
false,
|
||||
Some(expects_custody_columns.clone()),
|
||||
Some(num_of_data_column_requests),
|
||||
);
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
let blocks = (0..4)
|
||||
@@ -391,34 +370,25 @@ mod tests {
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let max_len = spec.max_blobs_per_block(blocks.first().unwrap().0.epoch()) as usize;
|
||||
let mut info = RangeBlockComponentsRequest::<E>::new(
|
||||
false,
|
||||
Some(expects_custody_columns.clone()),
|
||||
Some(num_of_data_column_requests),
|
||||
vec![PeerId::random()],
|
||||
max_len,
|
||||
);
|
||||
|
||||
// Send blocks and complete terminate response
|
||||
for block in &blocks {
|
||||
info.add_block_response(Some(block.0.clone().into()));
|
||||
}
|
||||
info.add_block_response(None);
|
||||
info.add_blocks(blocks.iter().map(|b| b.0.clone().into()).collect());
|
||||
// Assert response is not finished
|
||||
assert!(!info.is_finished());
|
||||
|
||||
// Send data columns interleaved
|
||||
for block in &blocks {
|
||||
for column in &block.1 {
|
||||
if expects_custody_columns.contains(&column.index) {
|
||||
info.add_data_column(Some(column.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i, column_indices) in batched_column_requests.iter().enumerate() {
|
||||
// Send the set of columns in the same batch request
|
||||
info.add_custody_columns(
|
||||
blocks
|
||||
.iter()
|
||||
.flat_map(|b| {
|
||||
b.1.iter()
|
||||
.filter(|d| column_indices.contains(&d.index))
|
||||
.cloned()
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
// Terminate the requests
|
||||
for i in 0..num_of_data_column_requests {
|
||||
info.add_data_column(None);
|
||||
if i < num_of_data_column_requests - 1 {
|
||||
assert!(
|
||||
!info.is_finished(),
|
||||
|
||||
Reference in New Issue
Block a user