mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-19 21:04:41 +00:00
Implement PeerDAS Fulu fork activation (#6795)
Addresses #6706 This PR activates PeerDAS at the Fulu fork epoch instead of `EIP_7594_FORK_EPOCH`. This means we no longer support testing PeerDAS with Deneb / Electrs, as it's now part of a hard fork.
This commit is contained in:
@@ -613,6 +613,11 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
||||
blocks: Vec<RpcBlock<T::EthSpec>>,
|
||||
) -> Result<(), Error<T::EthSpec>> {
|
||||
let is_backfill = matches!(&process_id, ChainSegmentProcessId::BackSyncBatchId { .. });
|
||||
debug!(self.log, "Batch sending for process";
|
||||
"blocks" => blocks.len(),
|
||||
"id" => ?process_id,
|
||||
);
|
||||
|
||||
let processor = self.clone();
|
||||
let process_fn = async move {
|
||||
let notify_execution_layer = if processor
|
||||
|
||||
@@ -15,7 +15,7 @@ use beacon_chain::test_utils::{
|
||||
use beacon_chain::{BeaconChain, WhenSlotSkipped};
|
||||
use beacon_processor::{work_reprocessing_queue::*, *};
|
||||
use lighthouse_network::discovery::ConnectionId;
|
||||
use lighthouse_network::rpc::methods::BlobsByRangeRequest;
|
||||
use lighthouse_network::rpc::methods::{BlobsByRangeRequest, MetaDataV3};
|
||||
use lighthouse_network::rpc::{RequestId, SubstreamId};
|
||||
use lighthouse_network::{
|
||||
discv5::enr::{self, CombinedKey},
|
||||
@@ -198,11 +198,21 @@ impl TestRig {
|
||||
let (sync_tx, _sync_rx) = mpsc::unbounded_channel();
|
||||
|
||||
// Default metadata
|
||||
let meta_data = MetaData::V2(MetaDataV2 {
|
||||
seq_number: SEQ_NUMBER,
|
||||
attnets: EnrAttestationBitfield::<MainnetEthSpec>::default(),
|
||||
syncnets: EnrSyncCommitteeBitfield::<MainnetEthSpec>::default(),
|
||||
});
|
||||
let meta_data = if spec.is_peer_das_scheduled() {
|
||||
MetaData::V3(MetaDataV3 {
|
||||
seq_number: SEQ_NUMBER,
|
||||
attnets: EnrAttestationBitfield::<MainnetEthSpec>::default(),
|
||||
syncnets: EnrSyncCommitteeBitfield::<MainnetEthSpec>::default(),
|
||||
custody_group_count: spec.custody_requirement,
|
||||
})
|
||||
} else {
|
||||
MetaData::V2(MetaDataV2 {
|
||||
seq_number: SEQ_NUMBER,
|
||||
attnets: EnrAttestationBitfield::<MainnetEthSpec>::default(),
|
||||
syncnets: EnrSyncCommitteeBitfield::<MainnetEthSpec>::default(),
|
||||
})
|
||||
};
|
||||
|
||||
let enr_key = CombinedKey::generate_secp256k1();
|
||||
let enr = enr::Enr::builder().build(&enr_key).unwrap();
|
||||
let network_config = Arc::new(NetworkConfig::default());
|
||||
@@ -342,6 +352,7 @@ impl TestRig {
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn enqueue_single_lookup_rpc_blobs(&self) {
|
||||
if let Some(blobs) = self.next_blobs.clone() {
|
||||
let blobs = FixedBlobSidecarList::new(blobs.into_iter().map(Some).collect::<Vec<_>>());
|
||||
@@ -350,7 +361,7 @@ impl TestRig {
|
||||
self.next_block.canonical_root(),
|
||||
blobs,
|
||||
std::time::Duration::default(),
|
||||
BlockProcessType::SingleBlock { id: 1 },
|
||||
BlockProcessType::SingleBlob { id: 1 },
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -751,11 +751,6 @@ impl<T: BeaconChainTypes> NetworkService<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(das): This is added here for the purpose of testing, *without* having to
|
||||
// activate Electra. This should happen as part of the Electra upgrade and we should
|
||||
// move the subscription logic once it's ready to rebase PeerDAS on Electra, or if
|
||||
// we decide to activate via the soft fork route:
|
||||
// https://github.com/sigp/lighthouse/pull/5899
|
||||
if self.fork_context.spec.is_peer_das_scheduled() {
|
||||
self.subscribe_to_peer_das_topics(&mut subscribed_topics);
|
||||
}
|
||||
@@ -806,32 +801,32 @@ impl<T: BeaconChainTypes> NetworkService<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeping these separate from core topics because it has custom logic:
|
||||
/// 1. Data column subscription logic depends on subscription configuration.
|
||||
/// 2. Data column topic subscriptions will be dynamic based on validator balances due to
|
||||
/// validator custody.
|
||||
///
|
||||
/// TODO(das): The downside with not including it in core fork topic is - we subscribe to
|
||||
/// PeerDAS topics on startup if Fulu is scheduled, rather than waiting until the fork.
|
||||
/// If this is an issue we could potentially consider adding the logic to
|
||||
/// `network.subscribe_new_fork_topics()`.
|
||||
fn subscribe_to_peer_das_topics(&mut self, subscribed_topics: &mut Vec<GossipTopic>) {
|
||||
if self.subscribe_all_data_column_subnets {
|
||||
for column_subnet in 0..self.fork_context.spec.data_column_sidecar_subnet_count {
|
||||
for fork_digest in self.required_gossip_fork_digests() {
|
||||
let gossip_kind =
|
||||
Subnet::DataColumn(DataColumnSubnetId::new(column_subnet)).into();
|
||||
let topic =
|
||||
GossipTopic::new(gossip_kind, GossipEncoding::default(), fork_digest);
|
||||
if self.libp2p.subscribe(topic.clone()) {
|
||||
subscribed_topics.push(topic);
|
||||
} else {
|
||||
warn!(self.log, "Could not subscribe to topic"; "topic" => %topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
let column_subnets_to_subscribe = if self.subscribe_all_data_column_subnets {
|
||||
&(0..self.fork_context.spec.data_column_sidecar_subnet_count)
|
||||
.map(DataColumnSubnetId::new)
|
||||
.collect()
|
||||
} else {
|
||||
for column_subnet in &self.network_globals.sampling_subnets {
|
||||
for fork_digest in self.required_gossip_fork_digests() {
|
||||
let gossip_kind = Subnet::DataColumn(*column_subnet).into();
|
||||
let topic =
|
||||
GossipTopic::new(gossip_kind, GossipEncoding::default(), fork_digest);
|
||||
if self.libp2p.subscribe(topic.clone()) {
|
||||
subscribed_topics.push(topic);
|
||||
} else {
|
||||
warn!(self.log, "Could not subscribe to topic"; "topic" => %topic);
|
||||
}
|
||||
&self.network_globals.sampling_subnets
|
||||
};
|
||||
|
||||
for column_subnet in column_subnets_to_subscribe.iter() {
|
||||
for fork_digest in self.required_gossip_fork_digests() {
|
||||
let gossip_kind = Subnet::DataColumn(*column_subnet).into();
|
||||
let topic = GossipTopic::new(gossip_kind, GossipEncoding::default(), fork_digest);
|
||||
if self.libp2p.subscribe(topic.clone()) {
|
||||
subscribed_topics.push(topic);
|
||||
} else {
|
||||
warn!(self.log, "Could not subscribe to topic"; "topic" => %topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,7 +321,7 @@ mod tests {
|
||||
let blocks = (0..4)
|
||||
.map(|_| {
|
||||
generate_rand_block_and_data_columns::<E>(
|
||||
ForkName::Deneb,
|
||||
ForkName::Fulu,
|
||||
NumBlobs::Number(1),
|
||||
&mut rng,
|
||||
&spec,
|
||||
@@ -384,7 +384,7 @@ mod tests {
|
||||
let blocks = (0..4)
|
||||
.map(|_| {
|
||||
generate_rand_block_and_data_columns::<E>(
|
||||
ForkName::Deneb,
|
||||
ForkName::Fulu,
|
||||
NumBlobs::Number(1),
|
||||
&mut rng,
|
||||
&spec,
|
||||
|
||||
@@ -373,6 +373,7 @@ impl<T: BeaconChainTypes> SyncNetworkContext<T> {
|
||||
"count" => request.count(),
|
||||
"epoch" => epoch,
|
||||
"peer" => %peer_id,
|
||||
"id" => id,
|
||||
);
|
||||
let rpc_request = match request {
|
||||
BlocksByRangeRequest::V1(ref req) => {
|
||||
@@ -442,6 +443,7 @@ impl<T: BeaconChainTypes> SyncNetworkContext<T> {
|
||||
"epoch" => epoch,
|
||||
"columns" => ?columns_by_range_request.columns,
|
||||
"peer" => %peer_id,
|
||||
"id" => id,
|
||||
);
|
||||
|
||||
self.send_network_msg(NetworkMessage::SendRequest {
|
||||
|
||||
@@ -43,8 +43,8 @@ use types::ForkContext;
|
||||
use types::{
|
||||
data_column_sidecar::ColumnIndex,
|
||||
test_utils::{SeedableRng, TestRandom, XorShiftRng},
|
||||
BeaconState, BeaconStateBase, BlobSidecar, DataColumnSidecar, Epoch, EthSpec, ForkName,
|
||||
Hash256, MinimalEthSpec as E, SignedBeaconBlock, Slot,
|
||||
BeaconState, BeaconStateBase, BlobSidecar, DataColumnSidecar, EthSpec, ForkName, Hash256,
|
||||
MinimalEthSpec as E, SignedBeaconBlock, Slot,
|
||||
};
|
||||
|
||||
const D: Duration = Duration::new(0, 0);
|
||||
@@ -54,12 +54,8 @@ const SAMPLING_REQUIRED_SUCCESSES: usize = 2;
|
||||
type DCByRootIds = Vec<DCByRootId>;
|
||||
type DCByRootId = (SyncRequestId, Vec<ColumnIndex>);
|
||||
|
||||
struct TestRigConfig {
|
||||
peer_das_enabled: bool,
|
||||
}
|
||||
|
||||
impl TestRig {
|
||||
fn test_setup_with_config(config: Option<TestRigConfig>) -> Self {
|
||||
pub fn test_setup() -> Self {
|
||||
let logger_type = if cfg!(feature = "test_logger") {
|
||||
LoggerType::Test
|
||||
} else if cfg!(feature = "ci_logger") {
|
||||
@@ -70,13 +66,7 @@ impl TestRig {
|
||||
let log = build_log(slog::Level::Trace, logger_type);
|
||||
|
||||
// Use `fork_from_env` logic to set correct fork epochs
|
||||
let mut spec = test_spec::<E>();
|
||||
|
||||
if let Some(config) = config {
|
||||
if config.peer_das_enabled {
|
||||
spec.eip7594_fork_epoch = Some(Epoch::new(0));
|
||||
}
|
||||
}
|
||||
let spec = test_spec::<E>();
|
||||
|
||||
// Initialise a new beacon chain
|
||||
let harness = BeaconChainHarness::<EphemeralHarnessType<E>>::builder(E)
|
||||
@@ -155,24 +145,18 @@ impl TestRig {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_setup() -> Self {
|
||||
Self::test_setup_with_config(None)
|
||||
}
|
||||
|
||||
fn test_setup_after_deneb() -> Option<Self> {
|
||||
fn test_setup_after_deneb_before_fulu() -> Option<Self> {
|
||||
let r = Self::test_setup();
|
||||
if r.after_deneb() {
|
||||
if r.after_deneb() && !r.fork_name.fulu_enabled() {
|
||||
Some(r)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn test_setup_after_peerdas() -> Option<Self> {
|
||||
let r = Self::test_setup_with_config(Some(TestRigConfig {
|
||||
peer_das_enabled: true,
|
||||
}));
|
||||
if r.after_deneb() {
|
||||
fn test_setup_after_fulu() -> Option<Self> {
|
||||
let r = Self::test_setup();
|
||||
if r.fork_name.fulu_enabled() {
|
||||
Some(r)
|
||||
} else {
|
||||
None
|
||||
@@ -187,6 +171,10 @@ impl TestRig {
|
||||
self.fork_name.deneb_enabled()
|
||||
}
|
||||
|
||||
pub fn after_fulu(&self) -> bool {
|
||||
self.fork_name.fulu_enabled()
|
||||
}
|
||||
|
||||
fn trigger_unknown_parent_block(&mut self, peer_id: PeerId, block: Arc<SignedBeaconBlock<E>>) {
|
||||
let block_root = block.canonical_root();
|
||||
self.send_sync_message(SyncMessage::UnknownParentBlock(peer_id, block, block_root))
|
||||
@@ -387,7 +375,7 @@ impl TestRig {
|
||||
.__add_connected_peer_testing_only(false, &self.harness.spec)
|
||||
}
|
||||
|
||||
fn new_connected_supernode_peer(&mut self) -> PeerId {
|
||||
pub fn new_connected_supernode_peer(&mut self) -> PeerId {
|
||||
self.network_globals
|
||||
.peers
|
||||
.write()
|
||||
@@ -1945,7 +1933,7 @@ fn test_same_chain_race_condition() {
|
||||
|
||||
#[test]
|
||||
fn block_in_da_checker_skips_download() {
|
||||
let Some(mut r) = TestRig::test_setup_after_deneb() else {
|
||||
let Some(mut r) = TestRig::test_setup_after_deneb_before_fulu() else {
|
||||
return;
|
||||
};
|
||||
let (block, blobs) = r.rand_block_and_blobs(NumBlobs::Number(1));
|
||||
@@ -1963,7 +1951,7 @@ fn block_in_da_checker_skips_download() {
|
||||
|
||||
#[test]
|
||||
fn block_in_processing_cache_becomes_invalid() {
|
||||
let Some(mut r) = TestRig::test_setup_after_deneb() else {
|
||||
let Some(mut r) = TestRig::test_setup_after_deneb_before_fulu() else {
|
||||
return;
|
||||
};
|
||||
let (block, blobs) = r.rand_block_and_blobs(NumBlobs::Number(1));
|
||||
@@ -1989,7 +1977,7 @@ fn block_in_processing_cache_becomes_invalid() {
|
||||
|
||||
#[test]
|
||||
fn block_in_processing_cache_becomes_valid_imported() {
|
||||
let Some(mut r) = TestRig::test_setup_after_deneb() else {
|
||||
let Some(mut r) = TestRig::test_setup_after_deneb_before_fulu() else {
|
||||
return;
|
||||
};
|
||||
let (block, blobs) = r.rand_block_and_blobs(NumBlobs::Number(1));
|
||||
@@ -2014,7 +2002,7 @@ fn block_in_processing_cache_becomes_valid_imported() {
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn blobs_in_da_checker_skip_download() {
|
||||
let Some(mut r) = TestRig::test_setup_after_deneb() else {
|
||||
let Some(mut r) = TestRig::test_setup_after_deneb_before_fulu() else {
|
||||
return;
|
||||
};
|
||||
let (block, blobs) = r.rand_block_and_blobs(NumBlobs::Number(1));
|
||||
@@ -2033,7 +2021,7 @@ fn blobs_in_da_checker_skip_download() {
|
||||
|
||||
#[test]
|
||||
fn sampling_happy_path() {
|
||||
let Some(mut r) = TestRig::test_setup_after_peerdas() else {
|
||||
let Some(mut r) = TestRig::test_setup_after_fulu() else {
|
||||
return;
|
||||
};
|
||||
r.new_connected_peers_for_peerdas();
|
||||
@@ -2050,7 +2038,7 @@ fn sampling_happy_path() {
|
||||
|
||||
#[test]
|
||||
fn sampling_with_retries() {
|
||||
let Some(mut r) = TestRig::test_setup_after_peerdas() else {
|
||||
let Some(mut r) = TestRig::test_setup_after_fulu() else {
|
||||
return;
|
||||
};
|
||||
r.new_connected_peers_for_peerdas();
|
||||
@@ -2072,7 +2060,7 @@ fn sampling_with_retries() {
|
||||
|
||||
#[test]
|
||||
fn sampling_avoid_retrying_same_peer() {
|
||||
let Some(mut r) = TestRig::test_setup_after_peerdas() else {
|
||||
let Some(mut r) = TestRig::test_setup_after_fulu() else {
|
||||
return;
|
||||
};
|
||||
let peer_id_1 = r.new_connected_supernode_peer();
|
||||
@@ -2093,7 +2081,7 @@ fn sampling_avoid_retrying_same_peer() {
|
||||
|
||||
#[test]
|
||||
fn sampling_batch_requests() {
|
||||
let Some(mut r) = TestRig::test_setup_after_peerdas() else {
|
||||
let Some(mut r) = TestRig::test_setup_after_fulu() else {
|
||||
return;
|
||||
};
|
||||
let _supernode = r.new_connected_supernode_peer();
|
||||
@@ -2119,7 +2107,7 @@ fn sampling_batch_requests() {
|
||||
|
||||
#[test]
|
||||
fn sampling_batch_requests_not_enough_responses_returned() {
|
||||
let Some(mut r) = TestRig::test_setup_after_peerdas() else {
|
||||
let Some(mut r) = TestRig::test_setup_after_fulu() else {
|
||||
return;
|
||||
};
|
||||
let _supernode = r.new_connected_supernode_peer();
|
||||
@@ -2164,7 +2152,7 @@ fn sampling_batch_requests_not_enough_responses_returned() {
|
||||
|
||||
#[test]
|
||||
fn custody_lookup_happy_path() {
|
||||
let Some(mut r) = TestRig::test_setup_after_peerdas() else {
|
||||
let Some(mut r) = TestRig::test_setup_after_fulu() else {
|
||||
return;
|
||||
};
|
||||
let spec = E::default_spec();
|
||||
@@ -2238,7 +2226,7 @@ mod deneb_only {
|
||||
|
||||
impl DenebTester {
|
||||
fn new(request_trigger: RequestTrigger) -> Option<Self> {
|
||||
let Some(mut rig) = TestRig::test_setup_after_deneb() else {
|
||||
let Some(mut rig) = TestRig::test_setup_after_deneb_before_fulu() else {
|
||||
return None;
|
||||
};
|
||||
let (block, blobs) = rig.rand_block_and_blobs(NumBlobs::Random);
|
||||
@@ -2963,7 +2951,7 @@ mod deneb_only {
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn no_peer_penalty_when_rpc_response_already_known_from_gossip() {
|
||||
let Some(mut r) = TestRig::test_setup_after_deneb() else {
|
||||
let Some(mut r) = TestRig::test_setup_after_deneb_before_fulu() else {
|
||||
return;
|
||||
};
|
||||
let (block, blobs) = r.rand_block_and_blobs(NumBlobs::Number(2));
|
||||
|
||||
@@ -3,8 +3,13 @@ use crate::status::ToStatusMessage;
|
||||
use crate::sync::manager::SLOT_IMPORT_TOLERANCE;
|
||||
use crate::sync::range_sync::RangeSyncType;
|
||||
use crate::sync::SyncMessage;
|
||||
use beacon_chain::data_column_verification::CustodyDataColumn;
|
||||
use beacon_chain::test_utils::{AttestationStrategy, BlockStrategy};
|
||||
use beacon_chain::{block_verification_types::RpcBlock, EngineState, NotifyExecutionLayer};
|
||||
use lighthouse_network::rpc::methods::{
|
||||
BlobsByRangeRequest, DataColumnsByRangeRequest, OldBlocksByRangeRequest,
|
||||
OldBlocksByRangeRequestV2,
|
||||
};
|
||||
use lighthouse_network::rpc::{RequestType, StatusMessage};
|
||||
use lighthouse_network::service::api_types::{AppRequestId, Id, SyncRequestId};
|
||||
use lighthouse_network::{PeerId, SyncInfo};
|
||||
@@ -16,6 +21,47 @@ use types::{
|
||||
|
||||
const D: Duration = Duration::new(0, 0);
|
||||
|
||||
pub(crate) enum DataSidecars<E: EthSpec> {
|
||||
Blobs(BlobSidecarList<E>),
|
||||
DataColumns(Vec<CustodyDataColumn<E>>),
|
||||
}
|
||||
|
||||
enum ByRangeDataRequestIds {
|
||||
PreDeneb,
|
||||
PrePeerDAS(Id, PeerId),
|
||||
PostPeerDAS(Vec<(Id, PeerId)>),
|
||||
}
|
||||
|
||||
/// Sync tests are usually written in the form:
|
||||
/// - Do some action
|
||||
/// - Expect a request to be sent
|
||||
/// - Complete the above request
|
||||
///
|
||||
/// To make writting tests succint, the machinery in this testing rig automatically identifies
|
||||
/// _which_ request to complete. Picking the right request is critical for tests to pass, so this
|
||||
/// filter allows better expressivity on the criteria to identify the right request.
|
||||
#[derive(Default)]
|
||||
struct RequestFilter {
|
||||
peer: Option<PeerId>,
|
||||
epoch: Option<u64>,
|
||||
}
|
||||
|
||||
impl RequestFilter {
|
||||
fn peer(mut self, peer: PeerId) -> Self {
|
||||
self.peer = Some(peer);
|
||||
self
|
||||
}
|
||||
|
||||
fn epoch(mut self, epoch: u64) -> Self {
|
||||
self.epoch = Some(epoch);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn filter() -> RequestFilter {
|
||||
RequestFilter::default()
|
||||
}
|
||||
|
||||
impl TestRig {
|
||||
/// Produce a head peer with an advanced head
|
||||
fn add_head_peer(&mut self) -> PeerId {
|
||||
@@ -67,7 +113,9 @@ impl TestRig {
|
||||
|
||||
fn add_peer(&mut self, remote_info: SyncInfo) -> PeerId {
|
||||
// Create valid peer known to network globals
|
||||
let peer_id = self.new_connected_peer();
|
||||
// TODO(fulu): Using supernode peers to ensure we have peer across all column
|
||||
// subnets for syncing. Should add tests connecting to full node peers.
|
||||
let peer_id = self.new_connected_supernode_peer();
|
||||
// Send peer to sync
|
||||
self.send_sync_message(SyncMessage::AddPeer(peer_id, remote_info.clone()));
|
||||
peer_id
|
||||
@@ -86,11 +134,13 @@ impl TestRig {
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn expect_chain_segment(&mut self) {
|
||||
self.pop_received_processor_event(|ev| {
|
||||
(ev.work_type() == beacon_processor::WorkType::ChainSegment).then_some(())
|
||||
})
|
||||
.unwrap_or_else(|e| panic!("Expect ChainSegment work event: {e:?}"));
|
||||
fn expect_chain_segments(&mut self, count: usize) {
|
||||
for i in 0..count {
|
||||
self.pop_received_processor_event(|ev| {
|
||||
(ev.work_type() == beacon_processor::WorkType::ChainSegment).then_some(())
|
||||
})
|
||||
.unwrap_or_else(|e| panic!("Expect ChainSegment work event count {i}: {e:?}"));
|
||||
}
|
||||
}
|
||||
|
||||
fn update_execution_engine_state(&mut self, state: EngineState) {
|
||||
@@ -98,39 +148,80 @@ impl TestRig {
|
||||
self.sync_manager.update_execution_engine_state(state);
|
||||
}
|
||||
|
||||
fn find_blocks_by_range_request(&mut self, target_peer_id: &PeerId) -> (Id, Option<Id>) {
|
||||
fn find_blocks_by_range_request(
|
||||
&mut self,
|
||||
request_filter: RequestFilter,
|
||||
) -> ((Id, PeerId), ByRangeDataRequestIds) {
|
||||
let filter_f = |peer: PeerId, start_slot: u64| {
|
||||
if let Some(expected_epoch) = request_filter.epoch {
|
||||
let epoch = Slot::new(start_slot).epoch(E::slots_per_epoch()).as_u64();
|
||||
if epoch != expected_epoch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if let Some(expected_peer) = request_filter.peer {
|
||||
if peer != expected_peer {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
};
|
||||
|
||||
let block_req_id = self
|
||||
.pop_received_network_event(|ev| match ev {
|
||||
NetworkMessage::SendRequest {
|
||||
peer_id,
|
||||
request: RequestType::BlocksByRange(_),
|
||||
request:
|
||||
RequestType::BlocksByRange(OldBlocksByRangeRequest::V2(
|
||||
OldBlocksByRangeRequestV2 { start_slot, .. },
|
||||
)),
|
||||
request_id: AppRequestId::Sync(SyncRequestId::RangeBlockAndBlobs { id }),
|
||||
} if peer_id == target_peer_id => Some(*id),
|
||||
} if filter_f(*peer_id, *start_slot) => Some((*id, *peer_id)),
|
||||
_ => None,
|
||||
})
|
||||
.expect("Should have a blocks by range request");
|
||||
|
||||
let blob_req_id = if self.after_deneb() {
|
||||
Some(
|
||||
self.pop_received_network_event(|ev| match ev {
|
||||
let by_range_data_requests = if self.after_fulu() {
|
||||
let mut data_columns_requests = vec![];
|
||||
while let Ok(data_columns_request) = self.pop_received_network_event(|ev| match ev {
|
||||
NetworkMessage::SendRequest {
|
||||
peer_id,
|
||||
request:
|
||||
RequestType::DataColumnsByRange(DataColumnsByRangeRequest {
|
||||
start_slot, ..
|
||||
}),
|
||||
request_id: AppRequestId::Sync(SyncRequestId::RangeBlockAndBlobs { id }),
|
||||
} if filter_f(*peer_id, *start_slot) => Some((*id, *peer_id)),
|
||||
_ => None,
|
||||
}) {
|
||||
data_columns_requests.push(data_columns_request);
|
||||
}
|
||||
if data_columns_requests.is_empty() {
|
||||
panic!("Found zero DataColumnsByRange requests");
|
||||
}
|
||||
ByRangeDataRequestIds::PostPeerDAS(data_columns_requests)
|
||||
} else if self.after_deneb() {
|
||||
let (id, peer) = self
|
||||
.pop_received_network_event(|ev| match ev {
|
||||
NetworkMessage::SendRequest {
|
||||
peer_id,
|
||||
request: RequestType::BlobsByRange(_),
|
||||
request: RequestType::BlobsByRange(BlobsByRangeRequest { start_slot, .. }),
|
||||
request_id: AppRequestId::Sync(SyncRequestId::RangeBlockAndBlobs { id }),
|
||||
} if peer_id == target_peer_id => Some(*id),
|
||||
} if filter_f(*peer_id, *start_slot) => Some((*id, *peer_id)),
|
||||
_ => None,
|
||||
})
|
||||
.expect("Should have a blobs by range request"),
|
||||
)
|
||||
.expect("Should have a blobs by range request");
|
||||
ByRangeDataRequestIds::PrePeerDAS(id, peer)
|
||||
} else {
|
||||
None
|
||||
ByRangeDataRequestIds::PreDeneb
|
||||
};
|
||||
|
||||
(block_req_id, blob_req_id)
|
||||
(block_req_id, by_range_data_requests)
|
||||
}
|
||||
|
||||
fn find_and_complete_blocks_by_range_request(&mut self, target_peer_id: PeerId) {
|
||||
let (blocks_req_id, blobs_req_id) = self.find_blocks_by_range_request(&target_peer_id);
|
||||
fn find_and_complete_blocks_by_range_request(&mut self, request_filter: RequestFilter) {
|
||||
let ((blocks_req_id, block_peer), by_range_data_request_ids) =
|
||||
self.find_blocks_by_range_request(request_filter);
|
||||
|
||||
// Complete the request with a single stream termination
|
||||
self.log(&format!(
|
||||
@@ -138,28 +229,43 @@ impl TestRig {
|
||||
));
|
||||
self.send_sync_message(SyncMessage::RpcBlock {
|
||||
request_id: SyncRequestId::RangeBlockAndBlobs { id: blocks_req_id },
|
||||
peer_id: target_peer_id,
|
||||
peer_id: block_peer,
|
||||
beacon_block: None,
|
||||
seen_timestamp: D,
|
||||
});
|
||||
|
||||
if let Some(blobs_req_id) = blobs_req_id {
|
||||
// Complete the request with a single stream termination
|
||||
self.log(&format!(
|
||||
"Completing BlobsByRange request {blobs_req_id} with empty stream"
|
||||
));
|
||||
self.send_sync_message(SyncMessage::RpcBlob {
|
||||
request_id: SyncRequestId::RangeBlockAndBlobs { id: blobs_req_id },
|
||||
peer_id: target_peer_id,
|
||||
blob_sidecar: None,
|
||||
seen_timestamp: D,
|
||||
});
|
||||
match by_range_data_request_ids {
|
||||
ByRangeDataRequestIds::PreDeneb => {}
|
||||
ByRangeDataRequestIds::PrePeerDAS(id, peer_id) => {
|
||||
// Complete the request with a single stream termination
|
||||
self.log(&format!(
|
||||
"Completing BlobsByRange request {id} with empty stream"
|
||||
));
|
||||
self.send_sync_message(SyncMessage::RpcBlob {
|
||||
request_id: SyncRequestId::RangeBlockAndBlobs { id },
|
||||
peer_id,
|
||||
blob_sidecar: None,
|
||||
seen_timestamp: D,
|
||||
});
|
||||
}
|
||||
ByRangeDataRequestIds::PostPeerDAS(data_column_req_ids) => {
|
||||
// Complete the request with a single stream termination
|
||||
for (id, peer_id) in data_column_req_ids {
|
||||
self.log(&format!(
|
||||
"Completing DataColumnsByRange request {id} with empty stream"
|
||||
));
|
||||
self.send_sync_message(SyncMessage::RpcDataColumn {
|
||||
request_id: SyncRequestId::RangeBlockAndBlobs { id },
|
||||
peer_id,
|
||||
data_column: None,
|
||||
seen_timestamp: D,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_canonical_block(
|
||||
&mut self,
|
||||
) -> (SignedBeaconBlock<E>, Option<BlobSidecarList<E>>) {
|
||||
async fn create_canonical_block(&mut self) -> (SignedBeaconBlock<E>, Option<DataSidecars<E>>) {
|
||||
self.harness.advance_slot();
|
||||
|
||||
let block_root = self
|
||||
@@ -170,20 +276,38 @@ impl TestRig {
|
||||
AttestationStrategy::AllValidators,
|
||||
)
|
||||
.await;
|
||||
// TODO(das): this does not handle data columns yet
|
||||
|
||||
let store = &self.harness.chain.store;
|
||||
let block = store.get_full_block(&block_root).unwrap().unwrap();
|
||||
let blobs = if block.fork_name_unchecked().deneb_enabled() {
|
||||
store.get_blobs(&block_root).unwrap().blobs()
|
||||
let fork = block.fork_name_unchecked();
|
||||
|
||||
let data_sidecars = if fork.fulu_enabled() {
|
||||
store
|
||||
.get_data_columns(&block_root)
|
||||
.unwrap()
|
||||
.map(|columns| {
|
||||
columns
|
||||
.into_iter()
|
||||
.map(CustodyDataColumn::from_asserted_custody)
|
||||
.collect()
|
||||
})
|
||||
.map(DataSidecars::DataColumns)
|
||||
} else if fork.deneb_enabled() {
|
||||
store
|
||||
.get_blobs(&block_root)
|
||||
.unwrap()
|
||||
.blobs()
|
||||
.map(DataSidecars::Blobs)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(block, blobs)
|
||||
|
||||
(block, data_sidecars)
|
||||
}
|
||||
|
||||
async fn remember_block(
|
||||
&mut self,
|
||||
(block, blob_sidecars): (SignedBeaconBlock<E>, Option<BlobSidecarList<E>>),
|
||||
(block, data_sidecars): (SignedBeaconBlock<E>, Option<DataSidecars<E>>),
|
||||
) {
|
||||
// This code is kind of duplicated from Harness::process_block, but takes sidecars directly.
|
||||
let block_root = block.canonical_root();
|
||||
@@ -193,7 +317,7 @@ impl TestRig {
|
||||
.chain
|
||||
.process_block(
|
||||
block_root,
|
||||
RpcBlock::new(Some(block_root), block.into(), blob_sidecars).unwrap(),
|
||||
build_rpc_block(block.into(), &data_sidecars, &self.spec),
|
||||
NotifyExecutionLayer::Yes,
|
||||
BlockImportSource::RangeSync,
|
||||
|| Ok(()),
|
||||
@@ -206,6 +330,22 @@ impl TestRig {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_rpc_block(
|
||||
block: Arc<SignedBeaconBlock<E>>,
|
||||
data_sidecars: &Option<DataSidecars<E>>,
|
||||
spec: &ChainSpec,
|
||||
) -> RpcBlock<E> {
|
||||
match data_sidecars {
|
||||
Some(DataSidecars::Blobs(blobs)) => {
|
||||
RpcBlock::new(None, block, Some(blobs.clone())).unwrap()
|
||||
}
|
||||
Some(DataSidecars::DataColumns(columns)) => {
|
||||
RpcBlock::new_with_custody_columns(None, block, columns.clone(), spec).unwrap()
|
||||
}
|
||||
None => RpcBlock::new_without_blobs(None, block),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn head_chain_removed_while_finalized_syncing() {
|
||||
// NOTE: this is a regression test.
|
||||
@@ -217,14 +357,14 @@ fn head_chain_removed_while_finalized_syncing() {
|
||||
rig.assert_state(RangeSyncType::Head);
|
||||
|
||||
// Sync should have requested a batch, grab the request.
|
||||
let _ = rig.find_blocks_by_range_request(&head_peer);
|
||||
let _ = rig.find_blocks_by_range_request(filter().peer(head_peer));
|
||||
|
||||
// Now get a peer with an advanced finalized epoch.
|
||||
let finalized_peer = rig.add_finalized_peer();
|
||||
rig.assert_state(RangeSyncType::Finalized);
|
||||
|
||||
// Sync should have requested a batch, grab the request
|
||||
let _ = rig.find_blocks_by_range_request(&finalized_peer);
|
||||
let _ = rig.find_blocks_by_range_request(filter().peer(finalized_peer));
|
||||
|
||||
// Fail the head chain by disconnecting the peer.
|
||||
rig.peer_disconnected(head_peer);
|
||||
@@ -251,14 +391,14 @@ async fn state_update_while_purging() {
|
||||
rig.assert_state(RangeSyncType::Head);
|
||||
|
||||
// Sync should have requested a batch, grab the request.
|
||||
let _ = rig.find_blocks_by_range_request(&head_peer);
|
||||
let _ = rig.find_blocks_by_range_request(filter().peer(head_peer));
|
||||
|
||||
// Now get a peer with an advanced finalized epoch.
|
||||
let finalized_peer = rig.add_finalized_peer_with_root(finalized_peer_root);
|
||||
rig.assert_state(RangeSyncType::Finalized);
|
||||
|
||||
// Sync should have requested a batch, grab the request
|
||||
let _ = rig.find_blocks_by_range_request(&finalized_peer);
|
||||
let _ = rig.find_blocks_by_range_request(filter().peer(finalized_peer));
|
||||
|
||||
// Now the chain knows both chains target roots.
|
||||
rig.remember_block(head_peer_block).await;
|
||||
@@ -277,15 +417,18 @@ fn pause_and_resume_on_ee_offline() {
|
||||
// make the ee offline
|
||||
rig.update_execution_engine_state(EngineState::Offline);
|
||||
// send the response to the request
|
||||
rig.find_and_complete_blocks_by_range_request(peer1);
|
||||
rig.find_and_complete_blocks_by_range_request(filter().peer(peer1).epoch(0));
|
||||
// the beacon processor shouldn't have received any work
|
||||
rig.expect_empty_processor();
|
||||
|
||||
// while the ee is offline, more peers might arrive. Add a new finalized peer.
|
||||
let peer2 = rig.add_finalized_peer();
|
||||
let _peer2 = rig.add_finalized_peer();
|
||||
|
||||
// send the response to the request
|
||||
rig.find_and_complete_blocks_by_range_request(peer2);
|
||||
// Don't filter requests and the columns requests may be sent to peer1 or peer2
|
||||
// We need to filter by epoch, because the previous batch eagerly sent requests for the next
|
||||
// epoch for the other batch. So we can either filter by epoch of by sync type.
|
||||
rig.find_and_complete_blocks_by_range_request(filter().epoch(0));
|
||||
// the beacon processor shouldn't have received any work
|
||||
rig.expect_empty_processor();
|
||||
// make the beacon processor available again.
|
||||
@@ -293,6 +436,6 @@ fn pause_and_resume_on_ee_offline() {
|
||||
// now resume range, we should have two processing requests in the beacon processor.
|
||||
rig.update_execution_engine_state(EngineState::Online);
|
||||
|
||||
rig.expect_chain_segment();
|
||||
rig.expect_chain_segment();
|
||||
// The head chain and finalized chain (2) should be in the processing queue
|
||||
rig.expect_chain_segments(2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user