From 2e1155490583ea654fa4ee0247fcf6556084365d Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Mon, 13 Jan 2025 14:45:55 +1100 Subject: [PATCH 01/16] Implement PeerDAS Fulu fork activation. --- .../overflow_lru_cache.rs | 2 - beacon_node/beacon_chain/src/test_utils.rs | 2 +- .../lighthouse_network/src/discovery/enr.rs | 8 +-- .../lighthouse_network/src/rpc/codec.rs | 40 ++++++------- .../lighthouse_network/src/rpc/protocol.rs | 19 +++---- .../lighthouse_network/src/types/globals.rs | 4 +- .../lighthouse_network/src/types/pubsub.rs | 22 ++----- beacon_node/network/src/service.rs | 57 ++++++++----------- beacon_node/network/src/sync/tests/lookups.rs | 38 ++++--------- beacon_node/store/src/hot_cold_store.rs | 13 ++--- beacon_node/store/src/metadata.rs | 4 +- .../mainnet/config.yaml | 2 - consensus/types/presets/gnosis/deneb.yaml | 2 - consensus/types/presets/gnosis/eip7594.yaml | 10 ---- consensus/types/presets/gnosis/fulu.yaml | 9 ++- consensus/types/presets/mainnet/eip7594.yaml | 10 ---- consensus/types/presets/mainnet/fulu.yaml | 9 ++- consensus/types/presets/minimal/eip7594.yaml | 10 ---- consensus/types/presets/minimal/fulu.yaml | 9 ++- consensus/types/src/chain_spec.rs | 43 ++------------ consensus/types/src/data_column_sidecar.rs | 14 ----- consensus/types/src/preset.rs | 20 +------ scripts/local_testnet/network_params_das.yaml | 2 +- testing/ef_tests/check_all_files_accessed.py | 2 +- testing/ef_tests/src/cases.rs | 8 +-- .../ef_tests/src/cases/get_custody_columns.rs | 2 +- .../cases/kzg_compute_cells_and_kzg_proofs.rs | 2 +- .../cases/kzg_recover_cells_and_kzg_proofs.rs | 2 +- .../cases/kzg_verify_cell_kzg_proof_batch.rs | 2 +- testing/ef_tests/src/handler.rs | 8 +-- testing/ef_tests/tests/tests.rs | 18 +++--- 31 files changed, 139 insertions(+), 254 deletions(-) delete mode 100644 consensus/types/presets/gnosis/eip7594.yaml delete mode 100644 consensus/types/presets/mainnet/eip7594.yaml delete mode 100644 consensus/types/presets/minimal/eip7594.yaml diff --git a/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs b/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs index 44148922f48..e1ccc431b63 100644 --- a/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs +++ b/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs @@ -22,8 +22,6 @@ use types::{ /// /// The blobs are all gossip and kzg verified. /// The block has completed all verifications except the availability check. -/// TODO(das): this struct can potentially be reafactored as blobs and data columns are mutually -/// exclusive and this could simplify `is_importable`. #[derive(Clone)] pub struct PendingComponents { pub block_root: Hash256, diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index fd3cc496260..2d3f3318ea7 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -104,7 +104,7 @@ static KZG_NO_PRECOMP: LazyLock> = LazyLock::new(|| { }); pub fn get_kzg(spec: &ChainSpec) -> Arc { - if spec.eip7594_fork_epoch.is_some() { + if spec.fulu_fork_epoch.is_some() { KZG_PEERDAS.clone() } else if spec.deneb_fork_epoch.is_some() { KZG.clone() diff --git a/beacon_node/lighthouse_network/src/discovery/enr.rs b/beacon_node/lighthouse_network/src/discovery/enr.rs index ce29480ffdb..fe1b76e8bdb 100644 --- a/beacon_node/lighthouse_network/src/discovery/enr.rs +++ b/beacon_node/lighthouse_network/src/discovery/enr.rs @@ -333,9 +333,9 @@ mod test { type E = MainnetEthSpec; - fn make_eip7594_spec() -> ChainSpec { + fn make_fulu_spec() -> ChainSpec { let mut spec = E::default_spec(); - spec.eip7594_fork_epoch = Some(Epoch::new(10)); + spec.fulu_fork_epoch = Some(Epoch::new(10)); spec } @@ -353,7 +353,7 @@ mod test { subscribe_all_data_column_subnets: false, ..NetworkConfig::default() }; - let spec = make_eip7594_spec(); + let spec = make_fulu_spec(); let enr = build_enr_with_config(config, &spec).0; @@ -369,7 +369,7 @@ mod test { subscribe_all_data_column_subnets: true, ..NetworkConfig::default() }; - let spec = make_eip7594_spec(); + let spec = make_fulu_spec(); let enr = build_enr_with_config(config, &spec).0; assert_eq!( diff --git a/beacon_node/lighthouse_network/src/rpc/codec.rs b/beacon_node/lighthouse_network/src/rpc/codec.rs index 61b2699ac5a..dc417a317fb 100644 --- a/beacon_node/lighthouse_network/src/rpc/codec.rs +++ b/beacon_node/lighthouse_network/src/rpc/codec.rs @@ -485,17 +485,9 @@ fn context_bytes( RpcSuccessResponse::BlobsByRange(_) | RpcSuccessResponse::BlobsByRoot(_) => { return fork_context.to_context_bytes(ForkName::Deneb); } - RpcSuccessResponse::DataColumnsByRoot(d) - | RpcSuccessResponse::DataColumnsByRange(d) => { - // TODO(das): Remove deneb fork after `peerdas-devnet-2`. - return if matches!( - fork_context.spec.fork_name_at_slot::(d.slot()), - ForkName::Deneb - ) { - fork_context.to_context_bytes(ForkName::Deneb) - } else { - fork_context.to_context_bytes(ForkName::Electra) - }; + RpcSuccessResponse::DataColumnsByRoot(_) + | RpcSuccessResponse::DataColumnsByRange(_) => { + return fork_context.to_context_bytes(ForkName::Fulu); } RpcSuccessResponse::LightClientBootstrap(lc_bootstrap) => { return lc_bootstrap @@ -744,10 +736,7 @@ fn handle_rpc_response( }, SupportedProtocol::DataColumnsByRootV1 => match fork_name { Some(fork_name) => { - // TODO(das): PeerDAS is currently supported for both deneb and electra. This check - // does not advertise the topic on deneb, simply allows it to decode it. Advertise - // logic is in `SupportedTopic::currently_supported`. - if fork_name.deneb_enabled() { + if fork_name.fulu_enabled() { Ok(Some(RpcSuccessResponse::DataColumnsByRoot(Arc::new( DataColumnSidecar::from_ssz_bytes(decoded_buffer)?, )))) @@ -768,7 +757,7 @@ fn handle_rpc_response( }, SupportedProtocol::DataColumnsByRangeV1 => match fork_name { Some(fork_name) => { - if fork_name.deneb_enabled() { + if fork_name.fulu_enabled() { Ok(Some(RpcSuccessResponse::DataColumnsByRange(Arc::new( DataColumnSidecar::from_ssz_bytes(decoded_buffer)?, )))) @@ -959,9 +948,10 @@ mod tests { use crate::rpc::protocol::*; use crate::types::{EnrAttestationBitfield, EnrSyncCommitteeBitfield}; use types::{ - blob_sidecar::BlobIdentifier, BeaconBlock, BeaconBlockAltair, BeaconBlockBase, - BeaconBlockBellatrix, DataColumnIdentifier, EmptyBlock, Epoch, FixedBytesExtended, - FullPayload, Signature, Slot, + blob_sidecar::BlobIdentifier, data_column_sidecar::Cell, BeaconBlock, BeaconBlockAltair, + BeaconBlockBase, BeaconBlockBellatrix, BeaconBlockHeader, DataColumnIdentifier, EmptyBlock, + Epoch, FixedBytesExtended, FullPayload, KzgCommitment, KzgProof, Signature, + SignedBeaconBlockHeader, Slot, }; type Spec = types::MainnetEthSpec; @@ -1012,7 +1002,17 @@ mod tests { } fn empty_data_column_sidecar() -> Arc> { - Arc::new(DataColumnSidecar::empty()) + Arc::new(DataColumnSidecar { + index: 0, + column: VariableList::new(vec![Cell::::default()]).unwrap(), + kzg_commitments: VariableList::new(vec![KzgCommitment::empty_for_testing()]).unwrap(), + kzg_proofs: VariableList::new(vec![KzgProof::empty()]).unwrap(), + signed_block_header: SignedBeaconBlockHeader { + message: BeaconBlockHeader::empty(), + signature: Signature::empty(), + }, + kzg_commitments_inclusion_proof: Default::default(), + }) } /// Bellatrix block with length < max_rpc_size. diff --git a/beacon_node/lighthouse_network/src/rpc/protocol.rs b/beacon_node/lighthouse_network/src/rpc/protocol.rs index 681b739d598..0db4cb47977 100644 --- a/beacon_node/lighthouse_network/src/rpc/protocol.rs +++ b/beacon_node/lighthouse_network/src/rpc/protocol.rs @@ -459,7 +459,7 @@ impl SupportedProtocol { ProtocolId::new(Self::BlocksByRootV1, Encoding::SSZSnappy), ProtocolId::new(Self::PingV1, Encoding::SSZSnappy), ]; - if fork_context.spec.is_peer_das_scheduled() { + if fork_context.fork_exists(ForkName::Fulu) { supported.extend_from_slice(&[ // V3 variants have higher preference for protocol negotation ProtocolId::new(Self::MetaDataV3, Encoding::SSZSnappy), @@ -478,7 +478,7 @@ impl SupportedProtocol { ProtocolId::new(SupportedProtocol::BlobsByRangeV1, Encoding::SSZSnappy), ]); } - if fork_context.spec.is_peer_das_scheduled() { + if fork_context.fork_exists(ForkName::Fulu) { supported.extend_from_slice(&[ ProtocolId::new(SupportedProtocol::DataColumnsByRootV1, Encoding::SSZSnappy), ProtocolId::new(SupportedProtocol::DataColumnsByRangeV1, Encoding::SSZSnappy), @@ -627,9 +627,11 @@ impl ProtocolId { Protocol::BlocksByRoot => rpc_block_limits_by_fork(fork_context.current_fork()), Protocol::BlobsByRange => rpc_blob_limits::(), Protocol::BlobsByRoot => rpc_blob_limits::(), - Protocol::DataColumnsByRoot => rpc_data_column_limits::(fork_context.current_fork()), + Protocol::DataColumnsByRoot => { + rpc_data_column_limits::(fork_context.current_fork(), &fork_context.spec) + } Protocol::DataColumnsByRange => { - rpc_data_column_limits::(fork_context.current_fork()) + rpc_data_column_limits::(fork_context.current_fork(), &fork_context.spec) } Protocol::Ping => RpcLimits::new( ::ssz_fixed_len(), @@ -710,13 +712,10 @@ pub fn rpc_blob_limits() -> RpcLimits { } } -// TODO(peerdas): fix hardcoded max here -pub fn rpc_data_column_limits(fork_name: ForkName) -> RpcLimits { +pub fn rpc_data_column_limits(fork_name: ForkName, spec: &ChainSpec) -> RpcLimits { RpcLimits::new( - DataColumnSidecar::::empty().as_ssz_bytes().len(), - DataColumnSidecar::::max_size( - E::default_spec().max_blobs_per_block_by_fork(fork_name) as usize - ), + DataColumnSidecar::::min_size(), + DataColumnSidecar::::max_size(spec.max_blobs_per_block_by_fork(fork_name) as usize), ) } diff --git a/beacon_node/lighthouse_network/src/types/globals.rs b/beacon_node/lighthouse_network/src/types/globals.rs index 92583b7b5d0..1a698a4e0c0 100644 --- a/beacon_node/lighthouse_network/src/types/globals.rs +++ b/beacon_node/lighthouse_network/src/types/globals.rs @@ -207,7 +207,7 @@ mod test { fn test_sampling_subnets() { let log = logging::test_logger(); let mut spec = E::default_spec(); - spec.eip7594_fork_epoch = Some(Epoch::new(0)); + spec.fulu_fork_epoch = Some(Epoch::new(0)); let custody_subnet_count = spec.data_column_sidecar_subnet_count / 2; let subnet_sampling_size = std::cmp::max(custody_subnet_count, spec.samples_per_slot); @@ -231,7 +231,7 @@ mod test { fn test_sampling_columns() { let log = logging::test_logger(); let mut spec = E::default_spec(); - spec.eip7594_fork_epoch = Some(Epoch::new(0)); + spec.fulu_fork_epoch = Some(Epoch::new(0)); let custody_subnet_count = spec.data_column_sidecar_subnet_count / 2; let subnet_sampling_size = std::cmp::max(custody_subnet_count, spec.samples_per_slot); diff --git a/beacon_node/lighthouse_network/src/types/pubsub.rs b/beacon_node/lighthouse_network/src/types/pubsub.rs index c9769594708..f6452d4b5bb 100644 --- a/beacon_node/lighthouse_network/src/types/pubsub.rs +++ b/beacon_node/lighthouse_network/src/types/pubsub.rs @@ -279,27 +279,15 @@ impl PubsubMessage { } GossipKind::DataColumnSidecar(subnet_id) => { match fork_context.from_context_bytes(gossip_topic.fork_digest) { - // TODO(das): Remove Deneb fork - Some(fork) if fork.deneb_enabled() => { + Some(fork) if fork.fulu_enabled() => { let col_sidecar = Arc::new( DataColumnSidecar::from_ssz_bytes(data) .map_err(|e| format!("{:?}", e))?, ); - let peer_das_enabled = - fork_context.spec.is_peer_das_enabled_for_epoch( - col_sidecar.slot().epoch(E::slots_per_epoch()), - ); - if peer_das_enabled { - Ok(PubsubMessage::DataColumnSidecar(Box::new(( - *subnet_id, - col_sidecar, - )))) - } else { - Err(format!( - "data_column_sidecar topic invalid for given fork digest {:?}", - gossip_topic.fork_digest - )) - } + Ok(PubsubMessage::DataColumnSidecar(Box::new(( + *subnet_id, + col_sidecar, + )))) } Some(_) | None => Err(format!( "data_column_sidecar topic invalid for given fork digest {:?}", diff --git a/beacon_node/network/src/service.rs b/beacon_node/network/src/service.rs index 7826807e035..d7feb1600cc 100644 --- a/beacon_node/network/src/service.rs +++ b/beacon_node/network/src/service.rs @@ -25,6 +25,7 @@ use lighthouse_network::{ MessageId, NetworkEvent, NetworkGlobals, PeerId, }; use slog::{crit, debug, error, info, o, trace, warn}; +use std::borrow::Cow; use std::collections::BTreeSet; use std::{collections::HashSet, pin::Pin, sync::Arc, time::Duration}; use store::HotColdDB; @@ -33,8 +34,8 @@ use task_executor::ShutdownReason; use tokio::sync::mpsc; use tokio::time::Sleep; use types::{ - ChainSpec, DataColumnSubnetId, EthSpec, ForkContext, Slot, SubnetId, SyncCommitteeSubscription, - SyncSubnetId, Unsigned, ValidatorSubscription, + ChainSpec, DataColumnSubnetId, EthSpec, ForkContext, ForkName, Slot, SubnetId, + SyncCommitteeSubscription, SyncSubnetId, Unsigned, ValidatorSubscription, }; mod tests; @@ -734,12 +735,7 @@ impl NetworkService { } } - // 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() { + if self.fork_context.fork_exists(ForkName::Fulu) { self.subscribe_to_peer_das_topics(&mut subscribed_topics); } @@ -789,32 +785,29 @@ impl NetworkService { } } + /// 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. fn subscribe_to_peer_das_topics(&mut self, subscribed_topics: &mut Vec) { - 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 { + Cow::Owned( + (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); - } + Cow::Borrowed(&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); } } } diff --git a/beacon_node/network/src/sync/tests/lookups.rs b/beacon_node/network/src/sync/tests/lookups.rs index b9e38237c58..8e20843f649 100644 --- a/beacon_node/network/src/sync/tests/lookups.rs +++ b/beacon_node/network/src/sync/tests/lookups.rs @@ -53,12 +53,8 @@ const SAMPLING_REQUIRED_SUCCESSES: usize = 2; type DCByRootIds = Vec; type DCByRootId = (SyncRequestId, Vec); -struct TestRigConfig { - peer_das_enabled: bool, -} - impl TestRig { - fn test_setup_with_config(config: Option) -> Self { + fn test_setup() -> Self { let logger_type = if cfg!(feature = "test_logger") { LoggerType::Test } else if cfg!(feature = "ci_logger") { @@ -69,13 +65,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::(); - - if let Some(config) = config { - if config.peer_das_enabled { - spec.eip7594_fork_epoch = Some(Epoch::new(0)); - } - } + let spec = test_spec::(); // Initialise a new beacon chain let harness = BeaconChainHarness::>::builder(E) @@ -148,10 +138,6 @@ impl TestRig { } } - pub fn test_setup() -> Self { - Self::test_setup_with_config(None) - } - fn test_setup_after_deneb() -> Option { let r = Self::test_setup(); if r.after_deneb() { @@ -161,11 +147,9 @@ impl TestRig { } } - fn test_setup_after_peerdas() -> Option { - let r = Self::test_setup_with_config(Some(TestRigConfig { - peer_das_enabled: true, - })); - if r.after_deneb() { + fn test_setup_after_fulu() -> Option { + let r = Self::test_setup(); + if r.fork_name.fulu_enabled() { Some(r) } else { None @@ -2026,7 +2010,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(); @@ -2043,7 +2027,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(); @@ -2065,7 +2049,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(); @@ -2086,7 +2070,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(); @@ -2112,7 +2096,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(); @@ -2157,7 +2141,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(); diff --git a/beacon_node/store/src/hot_cold_store.rs b/beacon_node/store/src/hot_cold_store.rs index c29305f9831..c56c82a0fb5 100644 --- a/beacon_node/store/src/hot_cold_store.rs +++ b/beacon_node/store/src/hot_cold_store.rs @@ -324,16 +324,15 @@ impl HotColdDB, LevelDB> { db.compare_and_set_blob_info_with_write(<_>::default(), new_blob_info.clone())?; let data_column_info = db.load_data_column_info()?; - let eip7594_fork_slot = db + let fulu_fork_slot = db .spec - .eip7594_fork_epoch + .fulu_fork_epoch .map(|epoch| epoch.start_slot(E::slots_per_epoch())); let new_data_column_info = match &data_column_info { Some(data_column_info) => { // Set the oldest data column slot to the fork slot if it is not yet set. - let oldest_data_column_slot = data_column_info - .oldest_data_column_slot - .or(eip7594_fork_slot); + let oldest_data_column_slot = + data_column_info.oldest_data_column_slot.or(fulu_fork_slot); DataColumnInfo { oldest_data_column_slot, } @@ -341,7 +340,7 @@ impl HotColdDB, LevelDB> { // First start. None => DataColumnInfo { // Set the oldest data column slot to the fork slot if it is not yet set. - oldest_data_column_slot: eip7594_fork_slot, + oldest_data_column_slot: fulu_fork_slot, }, }; db.compare_and_set_data_column_info_with_write( @@ -2278,7 +2277,7 @@ impl, Cold: ItemStore> HotColdDB /// Initialize the `DataColumnInfo` when starting from genesis or a checkpoint. pub fn init_data_column_info(&self, anchor_slot: Slot) -> Result { - let oldest_data_column_slot = self.spec.eip7594_fork_epoch.map(|fork_epoch| { + let oldest_data_column_slot = self.spec.fulu_fork_epoch.map(|fork_epoch| { std::cmp::max(anchor_slot, fork_epoch.start_slot(E::slots_per_epoch())) }); let data_column_info = DataColumnInfo { diff --git a/beacon_node/store/src/metadata.rs b/beacon_node/store/src/metadata.rs index 3f076a767ac..1d70e105b94 100644 --- a/beacon_node/store/src/metadata.rs +++ b/beacon_node/store/src/metadata.rs @@ -225,10 +225,10 @@ impl StoreItem for BlobInfo { pub struct DataColumnInfo { /// The slot after which data columns are or *will be* available (>=). /// - /// If this slot is in the future, then it is the first slot of the EIP-7594 fork, from which + /// If this slot is in the future, then it is the first slot of the Fulu fork, from which /// data columns will be available. /// - /// If the `oldest_data_column_slot` is `None` then this means that the EIP-7594 fork epoch is + /// If the `oldest_data_column_slot` is `None` then this means that the Fulu fork epoch is /// not yet known. pub oldest_data_column_slot: Option, } diff --git a/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml b/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml index 638f6fe42f8..be676c4b990 100644 --- a/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/mainnet/config.yaml @@ -53,8 +53,6 @@ ELECTRA_FORK_EPOCH: 18446744073709551615 # Fulu FULU_FORK_VERSION: 0x06000000 FULU_FORK_EPOCH: 18446744073709551615 -# PeerDAS -EIP7594_FORK_EPOCH: 18446744073709551615 # Time parameters # --------------------------------------------------------------- diff --git a/consensus/types/presets/gnosis/deneb.yaml b/consensus/types/presets/gnosis/deneb.yaml index 9a46a6dafe5..d25c4d3d381 100644 --- a/consensus/types/presets/gnosis/deneb.yaml +++ b/consensus/types/presets/gnosis/deneb.yaml @@ -1,6 +1,4 @@ # Gnosis preset - Deneb -# NOTE: The below are PLACEHOLDER values from Mainnet. -# Gnosis preset for the Deneb fork TBD: https://github.com/gnosischain/configs/tree/main/presets/gnosis # Misc # --------------------------------------------------------------- diff --git a/consensus/types/presets/gnosis/eip7594.yaml b/consensus/types/presets/gnosis/eip7594.yaml deleted file mode 100644 index 813febf26d5..00000000000 --- a/consensus/types/presets/gnosis/eip7594.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Mainnet preset - EIP7594 - -# Misc -# --------------------------------------------------------------- -# `uint64(2**6)` (= 64) -FIELD_ELEMENTS_PER_CELL: 64 -# `uint64(2 * 4096)` (= 8192) -FIELD_ELEMENTS_PER_EXT_BLOB: 8192 -# uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) -KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4 diff --git a/consensus/types/presets/gnosis/fulu.yaml b/consensus/types/presets/gnosis/fulu.yaml index 35a7c98fbf3..e5f3ce02122 100644 --- a/consensus/types/presets/gnosis/fulu.yaml +++ b/consensus/types/presets/gnosis/fulu.yaml @@ -1,3 +1,10 @@ # Gnosis preset - Fulu -FULU_PLACEHOLDER: 0 +# Misc +# --------------------------------------------------------------- +# `uint64(2**6)` (= 64) +FIELD_ELEMENTS_PER_CELL: 64 +# `uint64(2 * 4096)` (= 8192) +FIELD_ELEMENTS_PER_EXT_BLOB: 8192 +# uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) +KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4 diff --git a/consensus/types/presets/mainnet/eip7594.yaml b/consensus/types/presets/mainnet/eip7594.yaml deleted file mode 100644 index 813febf26d5..00000000000 --- a/consensus/types/presets/mainnet/eip7594.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Mainnet preset - EIP7594 - -# Misc -# --------------------------------------------------------------- -# `uint64(2**6)` (= 64) -FIELD_ELEMENTS_PER_CELL: 64 -# `uint64(2 * 4096)` (= 8192) -FIELD_ELEMENTS_PER_EXT_BLOB: 8192 -# uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) -KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4 diff --git a/consensus/types/presets/mainnet/fulu.yaml b/consensus/types/presets/mainnet/fulu.yaml index 8aa9ccdcc37..394f335f903 100644 --- a/consensus/types/presets/mainnet/fulu.yaml +++ b/consensus/types/presets/mainnet/fulu.yaml @@ -1,3 +1,10 @@ # Mainnet preset - Fulu -FULU_PLACEHOLDER: 0 +# Misc +# --------------------------------------------------------------- +# `uint64(2**6)` (= 64) +FIELD_ELEMENTS_PER_CELL: 64 +# `uint64(2 * 4096)` (= 8192) +FIELD_ELEMENTS_PER_EXT_BLOB: 8192 +# uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) +KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4 diff --git a/consensus/types/presets/minimal/eip7594.yaml b/consensus/types/presets/minimal/eip7594.yaml deleted file mode 100644 index 847719a4210..00000000000 --- a/consensus/types/presets/minimal/eip7594.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Minimal preset - EIP7594 - -# Misc -# --------------------------------------------------------------- -# `uint64(2**6)` (= 64) -FIELD_ELEMENTS_PER_CELL: 64 -# `uint64(2 * 4096)` (= 8192) -FIELD_ELEMENTS_PER_EXT_BLOB: 8192 -# uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) -KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4 diff --git a/consensus/types/presets/minimal/fulu.yaml b/consensus/types/presets/minimal/fulu.yaml index 121c9858f41..c961eb7f3ca 100644 --- a/consensus/types/presets/minimal/fulu.yaml +++ b/consensus/types/presets/minimal/fulu.yaml @@ -1,3 +1,10 @@ # Minimal preset - Fulu -FULU_PLACEHOLDER: 0 +# Misc +# --------------------------------------------------------------- +# `uint64(2**6)` (= 64) +FIELD_ELEMENTS_PER_CELL: 64 +# `uint64(2 * 4096)` (= 8192) +FIELD_ELEMENTS_PER_EXT_BLOB: 8192 +# uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) +KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4 diff --git a/consensus/types/src/chain_spec.rs b/consensus/types/src/chain_spec.rs index 65f4c37aa15..709334e0442 100644 --- a/consensus/types/src/chain_spec.rs +++ b/consensus/types/src/chain_spec.rs @@ -198,12 +198,6 @@ pub struct ChainSpec { pub fulu_fork_version: [u8; 4], /// The Fulu fork epoch is optional, with `None` representing "Fulu never happens". pub fulu_fork_epoch: Option, - pub fulu_placeholder: u64, - - /* - * DAS params - */ - pub eip7594_fork_epoch: Option, pub custody_requirement: u64, pub data_column_sidecar_subnet_count: u64, pub number_of_columns: usize, @@ -432,16 +426,16 @@ impl ChainSpec { } } - /// Returns true if the given epoch is greater than or equal to the `EIP7594_FORK_EPOCH`. + /// Returns true if the given epoch is greater than or equal to the `FULU_FORK_EPOCH`. pub fn is_peer_das_enabled_for_epoch(&self, block_epoch: Epoch) -> bool { - self.eip7594_fork_epoch - .is_some_and(|eip7594_fork_epoch| block_epoch >= eip7594_fork_epoch) + self.fulu_fork_epoch + .is_some_and(|fulu_fork_epoch| block_epoch >= fulu_fork_epoch) } - /// Returns true if `EIP7594_FORK_EPOCH` is set and is not set to `FAR_FUTURE_EPOCH`. + /// Returns true if `FULU_FORK_EPOCH` is set and is not set to `FAR_FUTURE_EPOCH`. pub fn is_peer_das_scheduled(&self) -> bool { - self.eip7594_fork_epoch - .is_some_and(|eip7594_fork_epoch| eip7594_fork_epoch != self.far_future_epoch) + self.fulu_fork_epoch + .is_some_and(|fulu_fork_epoch| fulu_fork_epoch != self.far_future_epoch) } /// Returns a full `Fork` struct for a given epoch. @@ -832,12 +826,6 @@ impl ChainSpec { */ fulu_fork_version: [0x06, 0x00, 0x00, 0x00], fulu_fork_epoch: None, - fulu_placeholder: 0, - - /* - * DAS params - */ - eip7594_fork_epoch: None, custody_requirement: 4, data_column_sidecar_subnet_count: 128, number_of_columns: 128, @@ -953,8 +941,6 @@ impl ChainSpec { // Fulu fulu_fork_version: [0x06, 0x00, 0x00, 0x01], fulu_fork_epoch: None, - // PeerDAS - eip7594_fork_epoch: None, // Other network_id: 2, // lighthouse testnet network id deposit_chain_id: 5, @@ -1162,12 +1148,6 @@ impl ChainSpec { */ fulu_fork_version: [0x06, 0x00, 0x00, 0x64], fulu_fork_epoch: None, - fulu_placeholder: 0, - - /* - * DAS params - */ - eip7594_fork_epoch: None, custody_requirement: 4, data_column_sidecar_subnet_count: 128, number_of_columns: 128, @@ -1307,11 +1287,6 @@ pub struct Config { #[serde(deserialize_with = "deserialize_fork_epoch")] pub fulu_fork_epoch: Option>, - #[serde(default)] - #[serde(serialize_with = "serialize_fork_epoch")] - #[serde(deserialize_with = "deserialize_fork_epoch")] - pub eip7594_fork_epoch: Option>, - #[serde(with = "serde_utils::quoted_u64")] seconds_per_slot: u64, #[serde(with = "serde_utils::quoted_u64")] @@ -1726,10 +1701,6 @@ impl Config { .fulu_fork_epoch .map(|epoch| MaybeQuoted { value: epoch }), - eip7594_fork_epoch: spec - .eip7594_fork_epoch - .map(|epoch| MaybeQuoted { value: epoch }), - seconds_per_slot: spec.seconds_per_slot, seconds_per_eth1_block: spec.seconds_per_eth1_block, min_validator_withdrawability_delay: spec.min_validator_withdrawability_delay, @@ -1812,7 +1783,6 @@ impl Config { electra_fork_version, fulu_fork_epoch, fulu_fork_version, - eip7594_fork_epoch, seconds_per_slot, seconds_per_eth1_block, min_validator_withdrawability_delay, @@ -1878,7 +1848,6 @@ impl Config { electra_fork_version, fulu_fork_epoch: fulu_fork_epoch.map(|q| q.value), fulu_fork_version, - eip7594_fork_epoch: eip7594_fork_epoch.map(|q| q.value), seconds_per_slot, seconds_per_eth1_block, min_validator_withdrawability_delay, diff --git a/consensus/types/src/data_column_sidecar.rs b/consensus/types/src/data_column_sidecar.rs index b2a050e9d5e..90a914dfae3 100644 --- a/consensus/types/src/data_column_sidecar.rs +++ b/consensus/types/src/data_column_sidecar.rs @@ -133,20 +133,6 @@ impl DataColumnSidecar { .len() } - pub fn empty() -> Self { - Self { - index: 0, - column: DataColumn::::default(), - kzg_commitments: VariableList::default(), - kzg_proofs: VariableList::default(), - signed_block_header: SignedBeaconBlockHeader { - message: BeaconBlockHeader::empty(), - signature: Signature::empty(), - }, - kzg_commitments_inclusion_proof: Default::default(), - } - } - pub fn id(&self) -> DataColumnIdentifier { DataColumnIdentifier { block_root: self.block_root(), diff --git a/consensus/types/src/preset.rs b/consensus/types/src/preset.rs index f64b7051e5f..26f583cd1a8 100644 --- a/consensus/types/src/preset.rs +++ b/consensus/types/src/preset.rs @@ -276,21 +276,6 @@ impl ElectraPreset { #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] pub struct FuluPreset { - #[serde(with = "serde_utils::quoted_u64")] - pub fulu_placeholder: u64, -} - -impl FuluPreset { - pub fn from_chain_spec(spec: &ChainSpec) -> Self { - Self { - fulu_placeholder: spec.fulu_placeholder, - } - } -} - -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] -#[serde(rename_all = "UPPERCASE")] -pub struct Eip7594Preset { #[serde(with = "serde_utils::quoted_u64")] pub field_elements_per_cell: u64, #[serde(with = "serde_utils::quoted_u64")] @@ -299,7 +284,7 @@ pub struct Eip7594Preset { pub kzg_commitments_inclusion_proof_depth: u64, } -impl Eip7594Preset { +impl FuluPreset { pub fn from_chain_spec(_spec: &ChainSpec) -> Self { Self { field_elements_per_cell: E::field_elements_per_cell() as u64, @@ -357,9 +342,6 @@ mod test { let fulu: FuluPreset = preset_from_file(&preset_name, "fulu.yaml"); assert_eq!(fulu, FuluPreset::from_chain_spec::(&spec)); - - let eip7594: Eip7594Preset = preset_from_file(&preset_name, "eip7594.yaml"); - assert_eq!(eip7594, Eip7594Preset::from_chain_spec::(&spec)); } #[test] diff --git a/scripts/local_testnet/network_params_das.yaml b/scripts/local_testnet/network_params_das.yaml index ab2f07a24ec..ef1a2663fe1 100644 --- a/scripts/local_testnet/network_params_das.yaml +++ b/scripts/local_testnet/network_params_das.yaml @@ -11,7 +11,7 @@ participants: - --target-peers=3 count: 2 network_params: - eip7594_fork_epoch: 0 + fulu_fork_epoch: 0 seconds_per_slot: 6 snooper_enabled: false global_log_level: debug diff --git a/testing/ef_tests/check_all_files_accessed.py b/testing/ef_tests/check_all_files_accessed.py index dacca204c19..b6711e92201 100755 --- a/testing/ef_tests/check_all_files_accessed.py +++ b/testing/ef_tests/check_all_files_accessed.py @@ -47,7 +47,7 @@ "bls12-381-tests/hash_to_G2", "tests/.*/eip6110", "tests/.*/whisk", - "tests/.*/eip7594", + "tests/.*/fulu", ] diff --git a/testing/ef_tests/src/cases.rs b/testing/ef_tests/src/cases.rs index 8f5571d64a6..e6f9f259827 100644 --- a/testing/ef_tests/src/cases.rs +++ b/testing/ef_tests/src/cases.rs @@ -89,18 +89,18 @@ pub use transition::TransitionTest; /// to return `true` for the feature in order for the feature test vector to be tested. #[derive(Debug, PartialEq, Clone, Copy)] pub enum FeatureName { - Eip7594, + Fulu, } impl FeatureName { pub fn list_all() -> Vec { - vec![FeatureName::Eip7594] + vec![FeatureName::Fulu] } /// `ForkName` to use when running the feature tests. pub fn fork_name(&self) -> ForkName { match self { - FeatureName::Eip7594 => ForkName::Deneb, + FeatureName::Fulu => ForkName::Deneb, } } } @@ -108,7 +108,7 @@ impl FeatureName { impl Display for FeatureName { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - FeatureName::Eip7594 => f.write_str("eip7594"), + FeatureName::Fulu => f.write_str("fulu"), } } } diff --git a/testing/ef_tests/src/cases/get_custody_columns.rs b/testing/ef_tests/src/cases/get_custody_columns.rs index 71b17aeaa3f..5ccb5977c38 100644 --- a/testing/ef_tests/src/cases/get_custody_columns.rs +++ b/testing/ef_tests/src/cases/get_custody_columns.rs @@ -26,7 +26,7 @@ impl Case for GetCustodyColumns { } fn is_enabled_for_feature(feature_name: FeatureName) -> bool { - feature_name == FeatureName::Eip7594 + feature_name == FeatureName::Fulu } fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> { diff --git a/testing/ef_tests/src/cases/kzg_compute_cells_and_kzg_proofs.rs b/testing/ef_tests/src/cases/kzg_compute_cells_and_kzg_proofs.rs index a7219f0629a..8df43bb2671 100644 --- a/testing/ef_tests/src/cases/kzg_compute_cells_and_kzg_proofs.rs +++ b/testing/ef_tests/src/cases/kzg_compute_cells_and_kzg_proofs.rs @@ -31,7 +31,7 @@ impl Case for KZGComputeCellsAndKZGProofs { } fn is_enabled_for_feature(feature_name: FeatureName) -> bool { - feature_name == FeatureName::Eip7594 + feature_name == FeatureName::Fulu } fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> { diff --git a/testing/ef_tests/src/cases/kzg_recover_cells_and_kzg_proofs.rs b/testing/ef_tests/src/cases/kzg_recover_cells_and_kzg_proofs.rs index b72b3a05cd6..26ab4e96b59 100644 --- a/testing/ef_tests/src/cases/kzg_recover_cells_and_kzg_proofs.rs +++ b/testing/ef_tests/src/cases/kzg_recover_cells_and_kzg_proofs.rs @@ -32,7 +32,7 @@ impl Case for KZGRecoverCellsAndKZGProofs { } fn is_enabled_for_feature(feature_name: FeatureName) -> bool { - feature_name == FeatureName::Eip7594 + feature_name == FeatureName::Fulu } fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> { diff --git a/testing/ef_tests/src/cases/kzg_verify_cell_kzg_proof_batch.rs b/testing/ef_tests/src/cases/kzg_verify_cell_kzg_proof_batch.rs index 815ad7a5bcf..fc625063b11 100644 --- a/testing/ef_tests/src/cases/kzg_verify_cell_kzg_proof_batch.rs +++ b/testing/ef_tests/src/cases/kzg_verify_cell_kzg_proof_batch.rs @@ -34,7 +34,7 @@ impl Case for KZGVerifyCellKZGProofBatch { } fn is_enabled_for_feature(feature_name: FeatureName) -> bool { - feature_name == FeatureName::Eip7594 + feature_name == FeatureName::Fulu } fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> { diff --git a/testing/ef_tests/src/handler.rs b/testing/ef_tests/src/handler.rs index d8fe061061a..b0f00f16187 100644 --- a/testing/ef_tests/src/handler.rs +++ b/testing/ef_tests/src/handler.rs @@ -362,7 +362,7 @@ where // SszStaticHandler::, MainnetEthSpec>::pre_electra().run(); // SszStaticHandler::, MainnetEthSpec>::electra_only().run(); // ``` - feature_name == FeatureName::Eip7594 + feature_name == FeatureName::Fulu && self.supported_forks.contains(&feature_name.fork_name()) } } @@ -386,7 +386,7 @@ where } fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Eip7594 + feature_name == FeatureName::Fulu } } @@ -411,7 +411,7 @@ where } fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Eip7594 + feature_name == FeatureName::Fulu } } @@ -996,7 +996,7 @@ impl Handler for KzgInclusionMerkleProofValidityHandler bool { - feature_name == FeatureName::Eip7594 + feature_name == FeatureName::Fulu } } diff --git a/testing/ef_tests/tests/tests.rs b/testing/ef_tests/tests/tests.rs index 691d27951ad..7001c803dcd 100644 --- a/testing/ef_tests/tests/tests.rs +++ b/testing/ef_tests/tests/tests.rs @@ -627,17 +627,17 @@ mod ssz_static { #[test] fn data_column_sidecar() { SszStaticHandler::, MinimalEthSpec>::deneb_only() - .run_for_feature(FeatureName::Eip7594); + .run_for_feature(FeatureName::Fulu); SszStaticHandler::, MainnetEthSpec>::deneb_only() - .run_for_feature(FeatureName::Eip7594); + .run_for_feature(FeatureName::Fulu); } #[test] fn data_column_identifier() { SszStaticHandler::::deneb_only() - .run_for_feature(FeatureName::Eip7594); + .run_for_feature(FeatureName::Fulu); SszStaticHandler::::deneb_only() - .run_for_feature(FeatureName::Eip7594); + .run_for_feature(FeatureName::Fulu); } #[test] @@ -902,19 +902,19 @@ fn kzg_verify_kzg_proof() { #[test] fn kzg_compute_cells_and_proofs() { KZGComputeCellsAndKZGProofHandler::::default() - .run_for_feature(FeatureName::Eip7594); + .run_for_feature(FeatureName::Fulu); } #[test] fn kzg_verify_cell_proof_batch() { KZGVerifyCellKZGProofBatchHandler::::default() - .run_for_feature(FeatureName::Eip7594); + .run_for_feature(FeatureName::Fulu); } #[test] fn kzg_recover_cells_and_proofs() { KZGRecoverCellsAndKZGProofHandler::::default() - .run_for_feature(FeatureName::Eip7594); + .run_for_feature(FeatureName::Fulu); } #[test] @@ -949,6 +949,6 @@ fn rewards() { #[test] fn get_custody_columns() { - GetCustodyColumnsHandler::::default().run_for_feature(FeatureName::Eip7594); - GetCustodyColumnsHandler::::default().run_for_feature(FeatureName::Eip7594); + GetCustodyColumnsHandler::::default().run_for_feature(FeatureName::Fulu); + GetCustodyColumnsHandler::::default().run_for_feature(FeatureName::Fulu); } From cd77b2c5579d83cee0cc9d22983345afa9d085e6 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Mon, 13 Jan 2025 16:16:18 +1100 Subject: [PATCH 02/16] Update spec tests. --- testing/ef_tests/src/cases.rs | 8 +++--- .../ef_tests/src/cases/get_custody_columns.rs | 8 ++---- .../cases/kzg_compute_cells_and_kzg_proofs.rs | 8 ++---- .../cases/kzg_recover_cells_and_kzg_proofs.rs | 8 ++---- .../cases/kzg_verify_cell_kzg_proof_batch.rs | 8 ++---- testing/ef_tests/src/handler.rs | 28 ------------------- testing/ef_tests/tests/tests.rs | 27 ++++++++---------- 7 files changed, 23 insertions(+), 72 deletions(-) diff --git a/testing/ef_tests/src/cases.rs b/testing/ef_tests/src/cases.rs index e6f9f259827..298b1ba8d54 100644 --- a/testing/ef_tests/src/cases.rs +++ b/testing/ef_tests/src/cases.rs @@ -89,18 +89,18 @@ pub use transition::TransitionTest; /// to return `true` for the feature in order for the feature test vector to be tested. #[derive(Debug, PartialEq, Clone, Copy)] pub enum FeatureName { - Fulu, + Placeholder, } impl FeatureName { pub fn list_all() -> Vec { - vec![FeatureName::Fulu] + vec![FeatureName::Placeholder] } /// `ForkName` to use when running the feature tests. pub fn fork_name(&self) -> ForkName { match self { - FeatureName::Fulu => ForkName::Deneb, + FeatureName::Placeholder => ForkName::Fulu, } } } @@ -108,7 +108,7 @@ impl FeatureName { impl Display for FeatureName { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - FeatureName::Fulu => f.write_str("fulu"), + FeatureName::Placeholder => f.write_str("placeholder"), } } } diff --git a/testing/ef_tests/src/cases/get_custody_columns.rs b/testing/ef_tests/src/cases/get_custody_columns.rs index 5ccb5977c38..8222267055e 100644 --- a/testing/ef_tests/src/cases/get_custody_columns.rs +++ b/testing/ef_tests/src/cases/get_custody_columns.rs @@ -21,12 +21,8 @@ impl LoadCase for GetCustodyColumns { } impl Case for GetCustodyColumns { - fn is_enabled_for_fork(_fork_name: ForkName) -> bool { - false - } - - fn is_enabled_for_feature(feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu + fn is_enabled_for_fork(fork_name: ForkName) -> bool { + fork_name.fulu_enabled() } fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> { diff --git a/testing/ef_tests/src/cases/kzg_compute_cells_and_kzg_proofs.rs b/testing/ef_tests/src/cases/kzg_compute_cells_and_kzg_proofs.rs index 8df43bb2671..6ab9a8db65a 100644 --- a/testing/ef_tests/src/cases/kzg_compute_cells_and_kzg_proofs.rs +++ b/testing/ef_tests/src/cases/kzg_compute_cells_and_kzg_proofs.rs @@ -26,12 +26,8 @@ impl LoadCase for KZGComputeCellsAndKZGProofs { } impl Case for KZGComputeCellsAndKZGProofs { - fn is_enabled_for_fork(_fork_name: ForkName) -> bool { - false - } - - fn is_enabled_for_feature(feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu + fn is_enabled_for_fork(fork_name: ForkName) -> bool { + fork_name.fulu_enabled() } fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> { diff --git a/testing/ef_tests/src/cases/kzg_recover_cells_and_kzg_proofs.rs b/testing/ef_tests/src/cases/kzg_recover_cells_and_kzg_proofs.rs index 26ab4e96b59..732cb54f318 100644 --- a/testing/ef_tests/src/cases/kzg_recover_cells_and_kzg_proofs.rs +++ b/testing/ef_tests/src/cases/kzg_recover_cells_and_kzg_proofs.rs @@ -27,12 +27,8 @@ impl LoadCase for KZGRecoverCellsAndKZGProofs { } impl Case for KZGRecoverCellsAndKZGProofs { - fn is_enabled_for_fork(_fork_name: ForkName) -> bool { - false - } - - fn is_enabled_for_feature(feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu + fn is_enabled_for_fork(fork_name: ForkName) -> bool { + fork_name.fulu_enabled() } fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> { diff --git a/testing/ef_tests/src/cases/kzg_verify_cell_kzg_proof_batch.rs b/testing/ef_tests/src/cases/kzg_verify_cell_kzg_proof_batch.rs index fc625063b11..e3edc0df0a1 100644 --- a/testing/ef_tests/src/cases/kzg_verify_cell_kzg_proof_batch.rs +++ b/testing/ef_tests/src/cases/kzg_verify_cell_kzg_proof_batch.rs @@ -29,12 +29,8 @@ impl LoadCase for KZGVerifyCellKZGProofBatch { } impl Case for KZGVerifyCellKZGProofBatch { - fn is_enabled_for_fork(_fork_name: ForkName) -> bool { - false - } - - fn is_enabled_for_feature(feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu + fn is_enabled_for_fork(fork_name: ForkName) -> bool { + fork_name.fulu_enabled() } fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> { diff --git a/testing/ef_tests/src/handler.rs b/testing/ef_tests/src/handler.rs index b0f00f16187..0c9eaec1fb7 100644 --- a/testing/ef_tests/src/handler.rs +++ b/testing/ef_tests/src/handler.rs @@ -349,22 +349,6 @@ where fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool { self.supported_forks.contains(&fork_name) } - - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - // This ensures we only run the tests **once** for `Eip7594`, using the types matching the - // correct fork, e.g. `Eip7594` uses SSZ types from `Deneb` as of spec test version - // `v1.5.0-alpha.8`, therefore the `Eip7594` tests should get included when testing Deneb types. - // - // e.g. Eip7594 test vectors are executed in the first line below, but excluded in the 2nd - // line when testing the type `AttestationElectra`: - // - // ``` - // SszStaticHandler::, MainnetEthSpec>::pre_electra().run(); - // SszStaticHandler::, MainnetEthSpec>::electra_only().run(); - // ``` - feature_name == FeatureName::Fulu - && self.supported_forks.contains(&feature_name.fork_name()) - } } impl Handler for SszStaticTHCHandler, E> @@ -384,10 +368,6 @@ where fn handler_name(&self) -> String { BeaconState::::name().into() } - - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu - } } impl Handler for SszStaticWithSpecHandler @@ -409,10 +389,6 @@ where fn handler_name(&self) -> String { T::name().into() } - - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu - } } #[derive(Derivative)] @@ -994,10 +970,6 @@ impl Handler for KzgInclusionMerkleProofValidityHandler bool { fork_name.deneb_enabled() } - - fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { - feature_name == FeatureName::Fulu - } } #[derive(Derivative)] diff --git a/testing/ef_tests/tests/tests.rs b/testing/ef_tests/tests/tests.rs index 7001c803dcd..b4001a13526 100644 --- a/testing/ef_tests/tests/tests.rs +++ b/testing/ef_tests/tests/tests.rs @@ -626,18 +626,16 @@ mod ssz_static { #[test] fn data_column_sidecar() { - SszStaticHandler::, MinimalEthSpec>::deneb_only() - .run_for_feature(FeatureName::Fulu); - SszStaticHandler::, MainnetEthSpec>::deneb_only() - .run_for_feature(FeatureName::Fulu); + SszStaticHandler::, MinimalEthSpec>::fulu_and_later() + .run(); + SszStaticHandler::, MainnetEthSpec>::fulu_and_later() + .run(); } #[test] fn data_column_identifier() { - SszStaticHandler::::deneb_only() - .run_for_feature(FeatureName::Fulu); - SszStaticHandler::::deneb_only() - .run_for_feature(FeatureName::Fulu); + SszStaticHandler::::fulu_and_later().run(); + SszStaticHandler::::fulu_and_later().run(); } #[test] @@ -901,20 +899,17 @@ fn kzg_verify_kzg_proof() { #[test] fn kzg_compute_cells_and_proofs() { - KZGComputeCellsAndKZGProofHandler::::default() - .run_for_feature(FeatureName::Fulu); + KZGComputeCellsAndKZGProofHandler::::default().run(); } #[test] fn kzg_verify_cell_proof_batch() { - KZGVerifyCellKZGProofBatchHandler::::default() - .run_for_feature(FeatureName::Fulu); + KZGVerifyCellKZGProofBatchHandler::::default().run(); } #[test] fn kzg_recover_cells_and_proofs() { - KZGRecoverCellsAndKZGProofHandler::::default() - .run_for_feature(FeatureName::Fulu); + KZGRecoverCellsAndKZGProofHandler::::default().run(); } #[test] @@ -949,6 +944,6 @@ fn rewards() { #[test] fn get_custody_columns() { - GetCustodyColumnsHandler::::default().run_for_feature(FeatureName::Fulu); - GetCustodyColumnsHandler::::default().run_for_feature(FeatureName::Fulu); + GetCustodyColumnsHandler::::default().run(); + GetCustodyColumnsHandler::::default().run() } From b0293423f8ceeda9254dfe83b82e1293dcab77e8 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Mon, 13 Jan 2025 16:30:34 +1100 Subject: [PATCH 03/16] Fix compilation and update Kurtosis test config for PeerDAS. --- beacon_node/network/src/sync/tests/lookups.rs | 6 +++--- scripts/local_testnet/network_params_das.yaml | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/beacon_node/network/src/sync/tests/lookups.rs b/beacon_node/network/src/sync/tests/lookups.rs index 8e20843f649..48b2bcf0211 100644 --- a/beacon_node/network/src/sync/tests/lookups.rs +++ b/beacon_node/network/src/sync/tests/lookups.rs @@ -42,8 +42,8 @@ use tokio::sync::mpsc; 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,7 +54,7 @@ type DCByRootIds = Vec; type DCByRootId = (SyncRequestId, Vec); impl TestRig { - fn test_setup() -> Self { + pub fn test_setup() -> Self { let logger_type = if cfg!(feature = "test_logger") { LoggerType::Test } else if cfg!(feature = "ci_logger") { diff --git a/scripts/local_testnet/network_params_das.yaml b/scripts/local_testnet/network_params_das.yaml index ef1a2663fe1..3c4253cdd92 100644 --- a/scripts/local_testnet/network_params_das.yaml +++ b/scripts/local_testnet/network_params_das.yaml @@ -11,11 +11,12 @@ participants: - --target-peers=3 count: 2 network_params: + electra_fork_epoch: 0 fulu_fork_epoch: 0 seconds_per_slot: 6 snooper_enabled: false global_log_level: debug additional_services: - dora - - goomy_blob + - spamoor_blob - prometheus_grafana From 64e44e137a59a810e40846e6e2979038e3c94b19 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Tue, 14 Jan 2025 14:45:09 +1100 Subject: [PATCH 04/16] Fix failing tests now `fulu` fork is included. --- beacon_node/beacon_chain/src/test_utils.rs | 54 +++++++++++++++++----- testing/ef_tests/tests/tests.rs | 4 +- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 2d3f3318ea7..6b06c5bf471 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -1,4 +1,5 @@ use crate::block_verification_types::{AsBlock, RpcBlock}; +use crate::data_column_verification::CustodyDataColumn; use crate::kzg_utils::blobs_to_data_column_sidecars; use crate::observed_operations::ObservationOutcome; pub use crate::persisted_beacon_chain::PersistedBeaconChain; @@ -2019,22 +2020,19 @@ where self.set_current_slot(slot); let (block, blob_items) = block_contents; - let sidecars = blob_items - .map(|(proofs, blobs)| BlobSidecar::build_sidecars(blobs, &block, proofs, &self.spec)) - .transpose() - .unwrap(); + let rpc_block = self.build_rpc_block(block_root, block, blob_items)?; let block_hash: SignedBeaconBlockHash = self .chain .process_block( block_root, - RpcBlock::new(Some(block_root), block, sidecars).unwrap(), + rpc_block, NotifyExecutionLayer::Yes, BlockImportSource::RangeSync, || Ok(()), ) .await? .try_into() - .unwrap(); + .expect("block blobs are available"); self.chain.recompute_head_at_current_slot().await; Ok(block_hash) } @@ -2045,16 +2043,13 @@ where ) -> Result { let (block, blob_items) = block_contents; - let sidecars = blob_items - .map(|(proofs, blobs)| BlobSidecar::build_sidecars(blobs, &block, proofs, &self.spec)) - .transpose() - .unwrap(); let block_root = block.canonical_root(); + let rpc_block = self.build_rpc_block(block_root, block, blob_items)?; let block_hash: SignedBeaconBlockHash = self .chain .process_block( block_root, - RpcBlock::new(Some(block_root), block, sidecars).unwrap(), + rpc_block, NotifyExecutionLayer::Yes, BlockImportSource::RangeSync, || Ok(()), @@ -2066,6 +2061,43 @@ where Ok(block_hash) } + fn build_rpc_block( + &self, + block_root: Hash256, + block: Arc>>, + blob_items: Option<(KzgProofs, BlobsList)>, + ) -> Result, BlockError> { + Ok(if self.spec.is_peer_das_enabled_for_epoch(block.epoch()) { + let columns = blob_items + .map(|(_proofs, blobs)| { + blobs_to_data_column_sidecars( + &blobs.iter().collect::>(), + &block, + &self.chain.kzg, + &self.spec, + ) + .map(|column_sidecars| { + column_sidecars + .into_iter() + .map(CustodyDataColumn::from_asserted_custody) + .collect::>() + }) + }) + .transpose() + .expect("should convert blobs to columns") + .unwrap_or(vec![]); + RpcBlock::new_with_custody_columns(Some(block_root), block, columns, &self.spec)? + } else { + let blobs = blob_items + .map(|(proofs, blobs)| { + BlobSidecar::build_sidecars(blobs, &block, proofs, &self.spec) + }) + .transpose() + .unwrap(); + RpcBlock::new(Some(block_root), block, blobs)? + }) + } + pub fn process_attestations(&self, attestations: HarnessAttestations) { let num_validators = self.validator_keypairs.len(); let mut unaggregated = Vec::with_capacity(num_validators); diff --git a/testing/ef_tests/tests/tests.rs b/testing/ef_tests/tests/tests.rs index b4001a13526..d12656f74e3 100644 --- a/testing/ef_tests/tests/tests.rs +++ b/testing/ef_tests/tests/tests.rs @@ -237,9 +237,7 @@ macro_rules! ssz_static_test_no_run { #[cfg(feature = "fake_crypto")] mod ssz_static { - use ef_tests::{ - FeatureName, Handler, SszStaticHandler, SszStaticTHCHandler, SszStaticWithSpecHandler, - }; + use ef_tests::{Handler, SszStaticHandler, SszStaticTHCHandler, SszStaticWithSpecHandler}; use types::historical_summary::HistoricalSummary; use types::{ AttesterSlashingBase, AttesterSlashingElectra, ConsolidationRequest, DepositRequest, From 4e2530221ee9ee143cc7fb448998067a378ec7d8 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Wed, 15 Jan 2025 13:07:43 +1100 Subject: [PATCH 05/16] Address review comments and fix lint. --- beacon_node/beacon_chain/src/test_utils.rs | 2 +- beacon_node/network/src/service.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 6b06c5bf471..f0baf4f4607 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -2085,7 +2085,7 @@ where }) .transpose() .expect("should convert blobs to columns") - .unwrap_or(vec![]); + .unwrap_or_default(); RpcBlock::new_with_custody_columns(Some(block_root), block, columns, &self.spec)? } else { let blobs = blob_items diff --git a/beacon_node/network/src/service.rs b/beacon_node/network/src/service.rs index d7feb1600cc..27e46e93446 100644 --- a/beacon_node/network/src/service.rs +++ b/beacon_node/network/src/service.rs @@ -789,6 +789,11 @@ impl NetworkService { /// 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) { let column_subnets_to_subscribe = if self.subscribe_all_data_column_subnets { Cow::Owned( From 8cdf82ea763b1594e4adbbc2f1a047d08b931c61 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Wed, 15 Jan 2025 14:31:29 +1100 Subject: [PATCH 06/16] Use engine v4 methods for Fulu (v5 methods do not exist yet). Update kurtosis config for PeerDAS as electra genesis is not yet supported. --- .../beacon_chain/src/fulu_readiness.rs | 11 ++--- .../execution_layer/src/engine_api/http.rs | 30 ++++++++++--- .../src/test_utils/handle_rpc.rs | 43 +++++++++++++------ scripts/local_testnet/network_params_das.yaml | 4 +- 4 files changed, 62 insertions(+), 26 deletions(-) diff --git a/beacon_node/beacon_chain/src/fulu_readiness.rs b/beacon_node/beacon_chain/src/fulu_readiness.rs index 71494623f83..872fe58f2ba 100644 --- a/beacon_node/beacon_chain/src/fulu_readiness.rs +++ b/beacon_node/beacon_chain/src/fulu_readiness.rs @@ -1,7 +1,7 @@ //! Provides tools for checking if a node is ready for the Fulu upgrade. use crate::{BeaconChain, BeaconChainTypes}; -use execution_layer::http::{ENGINE_GET_PAYLOAD_V5, ENGINE_NEW_PAYLOAD_V5}; +use execution_layer::http::{ENGINE_GET_PAYLOAD_V4, ENGINE_NEW_PAYLOAD_V4}; use serde::{Deserialize, Serialize}; use std::fmt; use std::time::Duration; @@ -87,14 +87,15 @@ impl BeaconChain { Ok(capabilities) => { let mut missing_methods = String::from("Required Methods Unsupported:"); let mut all_good = true; - if !capabilities.get_payload_v5 { + // TODO(fulu) switch to v5 when the EL is ready + if !capabilities.get_payload_v4 { missing_methods.push(' '); - missing_methods.push_str(ENGINE_GET_PAYLOAD_V5); + missing_methods.push_str(ENGINE_GET_PAYLOAD_V4); all_good = false; } - if !capabilities.new_payload_v5 { + if !capabilities.new_payload_v4 { missing_methods.push(' '); - missing_methods.push_str(ENGINE_NEW_PAYLOAD_V5); + missing_methods.push_str(ENGINE_NEW_PAYLOAD_V4); all_good = false; } diff --git a/beacon_node/execution_layer/src/engine_api/http.rs b/beacon_node/execution_layer/src/engine_api/http.rs index daf2bf6ed4b..747383754a5 100644 --- a/beacon_node/execution_layer/src/engine_api/http.rs +++ b/beacon_node/execution_layer/src/engine_api/http.rs @@ -829,7 +829,8 @@ impl HttpJsonRpc { Ok(response.into()) } - pub async fn new_payload_v5_fulu( + // TODO(fulu): switch to v5 endpoint when the EL is ready for Fulu + pub async fn new_payload_v4_fulu( &self, new_payload_request_fulu: NewPayloadRequestFulu<'_, E>, ) -> Result { @@ -844,7 +845,7 @@ impl HttpJsonRpc { let response: JsonPayloadStatusV1 = self .rpc_request( - ENGINE_NEW_PAYLOAD_V5, + ENGINE_NEW_PAYLOAD_V4, params, ENGINE_NEW_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, ) @@ -962,6 +963,19 @@ impl HttpJsonRpc { .try_into() .map_err(Error::BadResponse) } + // TODO(fulu): remove when v5 method is ready. + ForkName::Fulu => { + let response: JsonGetPayloadResponseV5 = self + .rpc_request( + ENGINE_GET_PAYLOAD_V4, + params, + ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier, + ) + .await?; + JsonGetPayloadResponse::V5(response) + .try_into() + .map_err(Error::BadResponse) + } _ => Err(Error::UnsupportedForkVariant(format!( "called get_payload_v4 with {}", fork_name @@ -1263,10 +1277,11 @@ impl HttpJsonRpc { } } NewPayloadRequest::Fulu(new_payload_request_fulu) => { - if engine_capabilities.new_payload_v5 { - self.new_payload_v5_fulu(new_payload_request_fulu).await + // TODO(fulu): switch to v5 endpoint when the EL is ready for Fulu + if engine_capabilities.new_payload_v4 { + self.new_payload_v4_fulu(new_payload_request_fulu).await } else { - Err(Error::RequiredMethodUnsupported("engine_newPayloadV5")) + Err(Error::RequiredMethodUnsupported("engine_newPayloadV4")) } } } @@ -1305,8 +1320,9 @@ impl HttpJsonRpc { } } ForkName::Fulu => { - if engine_capabilities.get_payload_v5 { - self.get_payload_v5(fork_name, payload_id).await + // TODO(fulu): switch to v5 when the EL is ready + if engine_capabilities.get_payload_v4 { + self.get_payload_v4(fork_name, payload_id).await } else { Err(Error::RequiredMethodUnsupported("engine_getPayloadv5")) } diff --git a/beacon_node/execution_layer/src/test_utils/handle_rpc.rs b/beacon_node/execution_layer/src/test_utils/handle_rpc.rs index 0babb9d1a3e..d727d2c1590 100644 --- a/beacon_node/execution_layer/src/test_utils/handle_rpc.rs +++ b/beacon_node/execution_layer/src/test_utils/handle_rpc.rs @@ -230,7 +230,8 @@ pub async fn handle_rpc( if method == ENGINE_NEW_PAYLOAD_V1 || method == ENGINE_NEW_PAYLOAD_V2 || method == ENGINE_NEW_PAYLOAD_V3 - || method == ENGINE_NEW_PAYLOAD_V4 + // TODO(fulu): Uncomment this once v5 method is ready for Fulu + // || method == ENGINE_NEW_PAYLOAD_V4 { return Err(( format!("{} called after Fulu fork!", method), @@ -264,15 +265,16 @@ pub async fn handle_rpc( GENERIC_ERROR_CODE, )); } - if matches!(request, JsonExecutionPayload::V4(_)) { - return Err(( - format!( - "{} called with `ExecutionPayloadV4` after Fulu fork!", - method - ), - GENERIC_ERROR_CODE, - )); - } + // TODO(fulu): remove once we switch to v5 + // if matches!(request, JsonExecutionPayload::V4(_)) { + // return Err(( + // format!( + // "{} called with `ExecutionPayloadV4` after Fulu fork!", + // method + // ), + // GENERIC_ERROR_CODE, + // )); + // } } _ => unreachable!(), }; @@ -381,8 +383,9 @@ pub async fn handle_rpc( == ForkName::Fulu && (method == ENGINE_GET_PAYLOAD_V1 || method == ENGINE_GET_PAYLOAD_V2 - || method == ENGINE_GET_PAYLOAD_V3 - || method == ENGINE_GET_PAYLOAD_V4) + || method == ENGINE_GET_PAYLOAD_V3) + // TODO(fulu): Uncomment this once v5 method is ready for Fulu + // || method == ENGINE_GET_PAYLOAD_V4) { return Err(( format!("{} called after Fulu fork!", method), @@ -448,6 +451,22 @@ pub async fn handle_rpc( }) .unwrap() } + // TODO(fulu): remove this once we switch to v5 method + JsonExecutionPayload::V5(execution_payload) => { + serde_json::to_value(JsonGetPayloadResponseV5 { + execution_payload, + block_value: Uint256::from(DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI), + blobs_bundle: maybe_blobs + .ok_or(( + "No blobs returned despite V5 Payload".to_string(), + GENERIC_ERROR_CODE, + ))? + .into(), + should_override_builder: false, + execution_requests: Default::default(), + }) + .unwrap() + } _ => unreachable!(), }), ENGINE_GET_PAYLOAD_V5 => Ok(match JsonExecutionPayload::from(response) { diff --git a/scripts/local_testnet/network_params_das.yaml b/scripts/local_testnet/network_params_das.yaml index 3c4253cdd92..ee3810ef28b 100644 --- a/scripts/local_testnet/network_params_das.yaml +++ b/scripts/local_testnet/network_params_das.yaml @@ -11,8 +11,8 @@ participants: - --target-peers=3 count: 2 network_params: - electra_fork_epoch: 0 - fulu_fork_epoch: 0 + electra_fork_epoch: 1 + fulu_fork_epoch: 2 seconds_per_slot: 6 snooper_enabled: false global_log_level: debug From 8980832f7195e67eaa4550184b617ddcf588593e Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Fri, 17 Jan 2025 11:41:46 +1100 Subject: [PATCH 07/16] Update Fulu spec tests. Revert back to testing Fulu as "feature", because all non-PeerDAS Fulu SSZ types are the same as Electra, and serde deserializes the vectors into Electra types. --- testing/ef_tests/check_all_files_accessed.py | 6 +- testing/ef_tests/src/cases.rs | 11 ++-- testing/ef_tests/src/handler.rs | 51 ++++++++++++++++ testing/ef_tests/src/type_name.rs | 11 ++++ testing/ef_tests/tests/tests.rs | 64 ++++++++++++++------ 5 files changed, 119 insertions(+), 24 deletions(-) diff --git a/testing/ef_tests/check_all_files_accessed.py b/testing/ef_tests/check_all_files_accessed.py index bf7e6a55fc8..02a01555b4e 100755 --- a/testing/ef_tests/check_all_files_accessed.py +++ b/testing/ef_tests/check_all_files_accessed.py @@ -49,10 +49,10 @@ "bls12-381-tests/hash_to_G2", "tests/.*/eip6110", "tests/.*/whisk", - # Fulu tests are not yet being run - "tests/.*/fulu", # TODO(electra): SingleAttestation tests are waiting on Eitan's PR - "tests/.*/electra/ssz_static/SingleAttestation" + "tests/.*/electra/ssz_static/SingleAttestation", + "tests/.*/fulu/ssz_static/SingleAttestation", + "tests/.*/fulu/ssz_static/MatrixEntry", ] diff --git a/testing/ef_tests/src/cases.rs b/testing/ef_tests/src/cases.rs index e724d587dd9..4a202ee3d2d 100644 --- a/testing/ef_tests/src/cases.rs +++ b/testing/ef_tests/src/cases.rs @@ -91,18 +91,21 @@ pub use transition::TransitionTest; /// to return `true` for the feature in order for the feature test vector to be tested. #[derive(Debug, PartialEq, Clone, Copy)] pub enum FeatureName { - Placeholder, + // TODO(fulu): to be removed once we start using Fulu types for test vectors. + // Existing SSZ types for PeerDAS (Fulu) are the same as Electra, so the test vectors get + // loaded as Electra types (default serde behaviour for untagged enums). + Fulu, } impl FeatureName { pub fn list_all() -> Vec { - vec![FeatureName::Placeholder] + vec![FeatureName::Fulu] } /// `ForkName` to use when running the feature tests. pub fn fork_name(&self) -> ForkName { match self { - FeatureName::Placeholder => ForkName::Fulu, + FeatureName::Fulu => ForkName::Electra, } } } @@ -110,7 +113,7 @@ impl FeatureName { impl Display for FeatureName { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - FeatureName::Placeholder => f.write_str("placeholder"), + FeatureName::Fulu => f.write_str("fulu"), } } } diff --git a/testing/ef_tests/src/handler.rs b/testing/ef_tests/src/handler.rs index ec7734061d1..d1ddd6a48ff 100644 --- a/testing/ef_tests/src/handler.rs +++ b/testing/ef_tests/src/handler.rs @@ -353,6 +353,25 @@ where fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool { self.supported_forks.contains(&fork_name) } + + fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { + // TODO(fulu): to be removed once Fulu types start differing from Electra. We currently run Fulu tests as a + // "feature" - this means we use Electra types for Fulu SSZ tests (except for PeerDAS types, e.g. `DataColumnSidecar`). + // + // This ensures we only run the tests **once** for `Fulu`, using the types matching the + // correct fork, e.g. `Fulu` uses SSZ types from `Electra` as of spec test version + // `v1.5.0-beta.0`, therefore the `Fulu` tests should get included when testing Deneb types. + // + // e.g. Fulu test vectors are executed in the 2nd line below, but excluded in the 1st + // line when testing the type `AttestationElectra`: + // + // ``` + // SszStaticHandler::, MainnetEthSpec>::pre_electra().run(); + // SszStaticHandler::, MainnetEthSpec>::electra_only().run(); + // ``` + feature_name == FeatureName::Fulu + && self.supported_forks.contains(&feature_name.fork_name()) + } } impl Handler for SszStaticTHCHandler, E> @@ -372,6 +391,10 @@ where fn handler_name(&self) -> String { BeaconState::::name().into() } + + fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { + feature_name == FeatureName::Fulu + } } impl Handler for SszStaticWithSpecHandler @@ -393,6 +416,10 @@ where fn handler_name(&self) -> String { T::name().into() } + + fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { + feature_name == FeatureName::Fulu + } } #[derive(Derivative)] @@ -866,6 +893,10 @@ impl Handler for GetCustodyGroupsHandler { fn handler_name(&self) -> String { "get_custody_groups".into() } + + fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { + feature_name == FeatureName::Fulu + } } #[derive(Derivative)] @@ -886,6 +917,10 @@ impl Handler for ComputeColumnsForCustodyGroupHandler fn handler_name(&self) -> String { "compute_columns_for_custody_group".into() } + + fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { + feature_name == FeatureName::Fulu + } } #[derive(Derivative)] @@ -906,6 +941,10 @@ impl Handler for KZGComputeCellsAndKZGProofHandler { fn handler_name(&self) -> String { "compute_cells_and_kzg_proofs".into() } + + fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { + feature_name == FeatureName::Fulu + } } #[derive(Derivative)] @@ -926,6 +965,10 @@ impl Handler for KZGVerifyCellKZGProofBatchHandler { fn handler_name(&self) -> String { "verify_cell_kzg_proof_batch".into() } + + fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { + feature_name == FeatureName::Fulu + } } #[derive(Derivative)] @@ -946,6 +989,10 @@ impl Handler for KZGRecoverCellsAndKZGProofHandler { fn handler_name(&self) -> String { "recover_cells_and_kzg_proofs".into() } + + fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { + feature_name == FeatureName::Fulu + } } #[derive(Derivative)] @@ -994,6 +1041,10 @@ impl Handler for KzgInclusionMerkleProofValidityHandler bool { fork_name.deneb_enabled() } + + fn is_enabled_for_feature(&self, feature_name: FeatureName) -> bool { + feature_name == FeatureName::Fulu + } } #[derive(Derivative)] diff --git a/testing/ef_tests/src/type_name.rs b/testing/ef_tests/src/type_name.rs index c50032a63de..285ac951a6e 100644 --- a/testing/ef_tests/src/type_name.rs +++ b/testing/ef_tests/src/type_name.rs @@ -54,6 +54,7 @@ type_name_generic!(BeaconBlockBodyBellatrix, "BeaconBlockBody"); type_name_generic!(BeaconBlockBodyCapella, "BeaconBlockBody"); type_name_generic!(BeaconBlockBodyDeneb, "BeaconBlockBody"); type_name_generic!(BeaconBlockBodyElectra, "BeaconBlockBody"); +type_name_generic!(BeaconBlockBodyFulu, "BeaconBlockBody"); type_name!(BeaconBlockHeader); type_name_generic!(BeaconState); type_name!(BlobIdentifier); @@ -74,12 +75,14 @@ type_name_generic!(ExecutionPayloadBellatrix, "ExecutionPayload"); type_name_generic!(ExecutionPayloadCapella, "ExecutionPayload"); type_name_generic!(ExecutionPayloadDeneb, "ExecutionPayload"); type_name_generic!(ExecutionPayloadElectra, "ExecutionPayload"); +type_name_generic!(ExecutionPayloadFulu, "ExecutionPayload"); type_name_generic!(FullPayload, "ExecutionPayload"); type_name_generic!(ExecutionPayloadHeader); type_name_generic!(ExecutionPayloadHeaderBellatrix, "ExecutionPayloadHeader"); type_name_generic!(ExecutionPayloadHeaderCapella, "ExecutionPayloadHeader"); type_name_generic!(ExecutionPayloadHeaderDeneb, "ExecutionPayloadHeader"); type_name_generic!(ExecutionPayloadHeaderElectra, "ExecutionPayloadHeader"); +type_name_generic!(ExecutionPayloadHeaderFulu, "ExecutionPayloadHeader"); type_name_generic!(ExecutionRequests); type_name_generic!(BlindedPayload, "ExecutionPayloadHeader"); type_name!(Fork); @@ -93,6 +96,7 @@ type_name_generic!(LightClientBootstrapAltair, "LightClientBootstrap"); type_name_generic!(LightClientBootstrapCapella, "LightClientBootstrap"); type_name_generic!(LightClientBootstrapDeneb, "LightClientBootstrap"); type_name_generic!(LightClientBootstrapElectra, "LightClientBootstrap"); +type_name_generic!(LightClientBootstrapFulu, "LightClientBootstrap"); type_name_generic!(LightClientFinalityUpdate); type_name_generic!(LightClientFinalityUpdateAltair, "LightClientFinalityUpdate"); type_name_generic!( @@ -104,11 +108,13 @@ type_name_generic!( LightClientFinalityUpdateElectra, "LightClientFinalityUpdate" ); +type_name_generic!(LightClientFinalityUpdateFulu, "LightClientFinalityUpdate"); type_name_generic!(LightClientHeader); type_name_generic!(LightClientHeaderAltair, "LightClientHeader"); type_name_generic!(LightClientHeaderCapella, "LightClientHeader"); type_name_generic!(LightClientHeaderDeneb, "LightClientHeader"); type_name_generic!(LightClientHeaderElectra, "LightClientHeader"); +type_name_generic!(LightClientHeaderFulu, "LightClientHeader"); type_name_generic!(LightClientOptimisticUpdate); type_name_generic!( LightClientOptimisticUpdateAltair, @@ -126,11 +132,16 @@ type_name_generic!( LightClientOptimisticUpdateElectra, "LightClientOptimisticUpdate" ); +type_name_generic!( + LightClientOptimisticUpdateFulu, + "LightClientOptimisticUpdate" +); type_name_generic!(LightClientUpdate); type_name_generic!(LightClientUpdateAltair, "LightClientUpdate"); type_name_generic!(LightClientUpdateCapella, "LightClientUpdate"); type_name_generic!(LightClientUpdateDeneb, "LightClientUpdate"); type_name_generic!(LightClientUpdateElectra, "LightClientUpdate"); +type_name_generic!(LightClientUpdateFulu, "LightClientUpdate"); type_name_generic!(PendingAttestation); type_name!(PendingConsolidation); type_name!(PendingPartialWithdrawal); diff --git a/testing/ef_tests/tests/tests.rs b/testing/ef_tests/tests/tests.rs index 6fe1aedb30e..bba7efde495 100644 --- a/testing/ef_tests/tests/tests.rs +++ b/testing/ef_tests/tests/tests.rs @@ -276,9 +276,9 @@ mod ssz_static { fn attestation() { SszStaticHandler::, MinimalEthSpec>::pre_electra().run(); SszStaticHandler::, MainnetEthSpec>::pre_electra().run(); - SszStaticHandler::, MinimalEthSpec>::electra_only() + SszStaticHandler::, MinimalEthSpec>::electra_and_later() .run(); - SszStaticHandler::, MainnetEthSpec>::electra_only() + SszStaticHandler::, MainnetEthSpec>::electra_and_later() .run(); } @@ -288,9 +288,9 @@ mod ssz_static { .run(); SszStaticHandler::, MainnetEthSpec>::pre_electra() .run(); - SszStaticHandler::, MinimalEthSpec>::electra_only() + SszStaticHandler::, MinimalEthSpec>::electra_and_later() .run(); - SszStaticHandler::, MainnetEthSpec>::electra_only() + SszStaticHandler::, MainnetEthSpec>::electra_and_later() .run(); } @@ -300,9 +300,9 @@ mod ssz_static { .run(); SszStaticHandler::, MainnetEthSpec>::pre_electra() .run(); - SszStaticHandler::, MinimalEthSpec>::electra_only() + SszStaticHandler::, MinimalEthSpec>::electra_and_later() .run(); - SszStaticHandler::, MainnetEthSpec>::electra_only() + SszStaticHandler::, MainnetEthSpec>::electra_and_later() .run(); } @@ -314,10 +314,10 @@ mod ssz_static { SszStaticHandler::, MainnetEthSpec>::pre_electra( ) .run(); - SszStaticHandler::, MinimalEthSpec>::electra_only( + SszStaticHandler::, MinimalEthSpec>::electra_and_later( ) .run(); - SszStaticHandler::, MainnetEthSpec>::electra_only( + SszStaticHandler::, MainnetEthSpec>::electra_and_later( ) .run(); } @@ -328,10 +328,10 @@ mod ssz_static { .run(); SszStaticHandler::, MainnetEthSpec>::pre_electra() .run(); - SszStaticHandler::, MinimalEthSpec>::electra_only( + SszStaticHandler::, MinimalEthSpec>::electra_and_later( ) .run(); - SszStaticHandler::, MainnetEthSpec>::electra_only( + SszStaticHandler::, MainnetEthSpec>::electra_and_later( ) .run(); } @@ -361,6 +361,8 @@ mod ssz_static { .run(); SszStaticHandler::, MainnetEthSpec>::electra_only() .run(); + SszStaticHandler::, MinimalEthSpec>::fulu_only().run(); + SszStaticHandler::, MainnetEthSpec>::fulu_only().run(); } // Altair and later @@ -399,6 +401,10 @@ mod ssz_static { .run(); SszStaticHandler::, MainnetEthSpec>::electra_only() .run(); + SszStaticHandler::, MinimalEthSpec>::fulu_only() + .run(); + SszStaticHandler::, MainnetEthSpec>::fulu_only() + .run(); } // LightClientHeader has no internal indicator of which fork it is for, so we test it separately. @@ -430,6 +436,10 @@ mod ssz_static { SszStaticHandler::, MainnetEthSpec>::electra_only( ) .run(); + SszStaticHandler::, MinimalEthSpec>::fulu_only() + .run(); + SszStaticHandler::, MainnetEthSpec>::fulu_only() + .run(); } // LightClientOptimisticUpdate has no internal indicator of which fork it is for, so we test it separately. @@ -445,6 +455,8 @@ mod ssz_static { SszStaticHandler::, MainnetEthSpec>::deneb_only().run(); SszStaticHandler::, MinimalEthSpec>::electra_only().run(); SszStaticHandler::, MainnetEthSpec>::electra_only().run(); + SszStaticHandler::, MinimalEthSpec>::fulu_only().run(); + SszStaticHandler::, MainnetEthSpec>::fulu_only().run(); } // LightClientFinalityUpdate has no internal indicator of which fork it is for, so we test it separately. @@ -480,6 +492,12 @@ mod ssz_static { SszStaticHandler::, MainnetEthSpec>::electra_only( ) .run(); + SszStaticHandler::, MinimalEthSpec>::fulu_only( + ) + .run(); + SszStaticHandler::, MainnetEthSpec>::fulu_only( + ) + .run(); } // LightClientUpdate has no internal indicator of which fork it is for, so we test it separately. @@ -509,6 +527,10 @@ mod ssz_static { SszStaticHandler::, MainnetEthSpec>::electra_only( ) .run(); + SszStaticHandler::, MinimalEthSpec>::fulu_only() + .run(); + SszStaticHandler::, MainnetEthSpec>::fulu_only() + .run(); } #[test] @@ -566,6 +588,8 @@ mod ssz_static { .run(); SszStaticHandler::, MainnetEthSpec>::electra_only() .run(); + SszStaticHandler::, MinimalEthSpec>::fulu_only().run(); + SszStaticHandler::, MainnetEthSpec>::fulu_only().run(); } #[test] @@ -586,6 +610,10 @@ mod ssz_static { ::electra_only().run(); SszStaticHandler::, MainnetEthSpec> ::electra_only().run(); + SszStaticHandler::, MinimalEthSpec>::fulu_only() + .run(); + SszStaticHandler::, MainnetEthSpec>::fulu_only() + .run(); } #[test] @@ -626,16 +654,18 @@ mod ssz_static { #[test] fn data_column_sidecar() { - SszStaticHandler::, MinimalEthSpec>::fulu_and_later() - .run(); - SszStaticHandler::, MainnetEthSpec>::fulu_and_later() - .run(); + SszStaticHandler::, MinimalEthSpec>::default() + .run_for_feature(FeatureName::Fulu); + SszStaticHandler::, MainnetEthSpec>::default() + .run_for_feature(FeatureName::Fulu); } #[test] fn data_column_identifier() { - SszStaticHandler::::fulu_and_later().run(); - SszStaticHandler::::fulu_and_later().run(); + SszStaticHandler::::default() + .run_for_feature(FeatureName::Fulu); + SszStaticHandler::::default() + .run_for_feature(FeatureName::Fulu); } #[test] @@ -943,7 +973,7 @@ fn rewards() { } #[test] -fn get_custody_columns() { +fn get_custody_groups() { GetCustodyGroupsHandler::::default().run(); GetCustodyGroupsHandler::::default().run() } From b7da07573c69b1d1c0eafaa6d656656bba42031f Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Mon, 20 Jan 2025 16:03:36 +1100 Subject: [PATCH 08/16] More test fixes for Fulu. --- beacon_node/beacon_chain/src/beacon_chain.rs | 12 ++ .../src/data_column_verification.rs | 2 +- beacon_node/beacon_chain/src/fetch_blobs.rs | 12 +- beacon_node/beacon_chain/src/test_utils.rs | 104 +++++++++- .../tests/attestation_production.rs | 15 +- .../beacon_chain/tests/block_verification.rs | 182 ++++++++++++------ beacon_node/beacon_processor/src/lib.rs | 2 +- beacon_node/http_api/src/test_utils.rs | 21 +- .../tests/broadcast_validation_tests.rs | 126 ++++++------ .../src/network_beacon_processor/tests.rs | 25 ++- beacon_node/network/src/sync/tests/lookups.rs | 16 +- beacon_node/store/src/hot_cold_store.rs | 35 ++++ beacon_node/store/src/lib.rs | 4 +- beacon_node/store/src/memory_store.rs | 17 +- consensus/fork_choice/tests/tests.rs | 2 +- consensus/types/src/fork_name.rs | 7 + 16 files changed, 408 insertions(+), 174 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index d0c294b44ff..10f303bd7fd 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -1249,6 +1249,17 @@ impl BeaconChain { self.store.get_blobs(block_root).map_err(Error::from) } + /// Returns the data columns at the given root, if any. + /// + /// ## Errors + /// May return a database error. + pub fn get_data_columns( + &self, + block_root: &Hash256, + ) -> Result>, Error> { + self.store.get_data_columns(block_root).map_err(Error::from) + } + /// Returns the data columns at the given root, if any. /// /// ## Errors @@ -5850,6 +5861,7 @@ impl BeaconChain { let kzg = self.kzg.as_ref(); + // TODO(fulu): we no longer need blob proofs from PeerDAS and could avoid computing. kzg_utils::validate_blobs::( kzg, expected_kzg_commitments, diff --git a/beacon_node/beacon_chain/src/data_column_verification.rs b/beacon_node/beacon_chain/src/data_column_verification.rs index 1bd17485ab6..565e76704ef 100644 --- a/beacon_node/beacon_chain/src/data_column_verification.rs +++ b/beacon_node/beacon_chain/src/data_column_verification.rs @@ -699,7 +699,7 @@ mod test { #[tokio::test] async fn empty_data_column_sidecars_fails_validation() { - let spec = ForkName::latest().make_genesis_spec(E::default_spec()); + let spec = ForkName::Fulu.make_genesis_spec(E::default_spec()); let harness = BeaconChainHarness::builder(E::default()) .spec(spec.into()) .deterministic_keypairs(64) diff --git a/beacon_node/beacon_chain/src/fetch_blobs.rs b/beacon_node/beacon_chain/src/fetch_blobs.rs index 49e46a50fec..b4197c17c21 100644 --- a/beacon_node/beacon_chain/src/fetch_blobs.rs +++ b/beacon_node/beacon_chain/src/fetch_blobs.rs @@ -14,7 +14,7 @@ use crate::{metrics, AvailabilityProcessingStatus, BeaconChain, BeaconChainTypes use execution_layer::json_structures::BlobAndProofV1; use execution_layer::Error as ExecutionLayerError; use metrics::{inc_counter, inc_counter_by, TryExt}; -use slog::{debug, error, o, Logger}; +use slog::{debug, error, o, warn, Logger}; use ssz_types::FixedVector; use state_processing::per_block_processing::deneb::kzg_commitment_to_versioned_hash; use std::sync::Arc; @@ -248,8 +248,14 @@ fn spawn_compute_and_publish_data_columns_task( } }; - if let Err(e) = data_columns_sender.send(all_data_columns.clone()) { - error!(log, "Failed to send computed data columns"; "error" => ?e); + if data_columns_sender.send(all_data_columns.clone()).is_err() { + // Data column receiver have been dropped - this may not be an issue if the block is + // already fully imported. This should not happen after the race condition + // described in #6816 is fixed. + warn!( + log, + "Failed to send computed data columns"; + ); }; // Check indices from cache before sending the columns, to make sure we don't diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 8a94137a120..bea33f202bd 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -1,9 +1,9 @@ +use crate::blob_verification::GossipVerifiedBlob; use crate::block_verification_types::{AsBlock, RpcBlock}; use crate::data_column_verification::CustodyDataColumn; use crate::kzg_utils::blobs_to_data_column_sidecars; use crate::observed_operations::ObservationOutcome; pub use crate::persisted_beacon_chain::PersistedBeaconChain; -use crate::BeaconBlockResponseWrapper; pub use crate::{ beacon_chain::{BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY}, migrate::MigratorConfig, @@ -17,6 +17,7 @@ use crate::{ BeaconChain, BeaconChainTypes, BlockError, ChainConfig, ServerSentEventHandler, StateSkipConfig, }; +use crate::{get_block_root, BeaconBlockResponseWrapper}; use bls::get_withdrawal_credentials; use eth2::types::SignedBlockContentsTuple; use execution_layer::test_utils::generate_genesis_header; @@ -756,15 +757,13 @@ where pub fn get_head_block(&self) -> RpcBlock { let block = self.chain.head_beacon_block(); let block_root = block.canonical_root(); - let blobs = self.chain.get_blobs(&block_root).unwrap().blobs(); - RpcBlock::new(Some(block_root), block, blobs).unwrap() + self.build_rpc_block_from_store_blobs(Some(block_root), block) } pub fn get_full_block(&self, block_root: &Hash256) -> RpcBlock { let block = self.chain.get_blinded_block(block_root).unwrap().unwrap(); let full_block = self.chain.store.make_full_block(block_root, block).unwrap(); - let blobs = self.chain.get_blobs(block_root).unwrap().blobs(); - RpcBlock::new(Some(*block_root), Arc::new(full_block), blobs).unwrap() + self.build_rpc_block_from_store_blobs(Some(*block_root), Arc::new(full_block)) } pub fn get_all_validators(&self) -> Vec { @@ -2265,7 +2264,7 @@ where self.set_current_slot(slot); let (block, blob_items) = block_contents; - let rpc_block = self.build_rpc_block(block_root, block, blob_items)?; + let rpc_block = self.build_rpc_block_from_blobs(block_root, block, blob_items)?; let block_hash: SignedBeaconBlockHash = self .chain .process_block( @@ -2289,7 +2288,7 @@ where let (block, blob_items) = block_contents; let block_root = block.canonical_root(); - let rpc_block = self.build_rpc_block(block_root, block, blob_items)?; + let rpc_block = self.build_rpc_block_from_blobs(block_root, block, blob_items)?; let block_hash: SignedBeaconBlockHash = self .chain .process_block( @@ -2306,13 +2305,51 @@ where Ok(block_hash) } - fn build_rpc_block( + /// Builds an `Rpc` block from a `SignedBeaconBlock` and blobs or data columns retrieved from + /// the database. + pub fn build_rpc_block_from_store_blobs( + &self, + block_root: Option, + block: Arc>, + ) -> RpcBlock { + let block_root = block_root.unwrap_or_else(|| get_block_root(&block)); + let has_blobs = block + .message() + .body() + .blob_kzg_commitments() + .is_ok_and(|c| !c.is_empty()); + if !has_blobs { + return RpcBlock::new_without_blobs(Some(block_root), block); + } + + // Blobs are stored as data columns from Fulu (PeerDAS) + if self.spec.is_peer_das_enabled_for_epoch(block.epoch()) { + let columns = self.chain.get_data_columns(&block_root).unwrap().unwrap(); + let custody_columns = columns + .into_iter() + .map(CustodyDataColumn::from_asserted_custody) + .collect::>(); + RpcBlock::new_with_custody_columns(Some(block_root), block, custody_columns, &self.spec) + .unwrap() + } else { + let blobs = self.chain.get_blobs(&block_root).unwrap().blobs(); + RpcBlock::new(Some(block_root), block, blobs).unwrap() + } + } + + /// Builds an `RpcBlock` from a `SignedBeaconBlock` and `BlobsList`. + fn build_rpc_block_from_blobs( &self, block_root: Hash256, block: Arc>>, blob_items: Option<(KzgProofs, BlobsList)>, ) -> Result, BlockError> { Ok(if self.spec.is_peer_das_enabled_for_epoch(block.epoch()) { + let sampling_column_count = self + .chain + .data_availability_checker + .get_sampling_column_count(); + let columns = blob_items .map(|(_proofs, blobs)| { blobs_to_data_column_sidecars( @@ -2324,6 +2361,7 @@ where .map(|column_sidecars| { column_sidecars .into_iter() + .take(sampling_column_count) .map(CustodyDataColumn::from_asserted_custody) .collect::>() }) @@ -3016,6 +3054,56 @@ where Ok(()) } + + /// Simulate some of the blobs / data columns being seen on gossip. + /// Converts the blobs to data columns if the slot is Fulu or later. + pub async fn process_gossip_blobs_or_columns<'a>( + &self, + block: &SignedBeaconBlock, + blobs: impl Iterator>, + proofs: impl Iterator, + custody_columns_opt: Option>, + ) { + let is_peerdas_enabled = self.chain.spec.is_peer_das_enabled_for_epoch(block.epoch()); + if is_peerdas_enabled { + let sidecars = blobs_to_data_column_sidecars( + &blobs.collect::>(), + block, + &self.chain.kzg, + &self.spec, + ) + .unwrap(); + + let custody_columns = custody_columns_opt.unwrap_or_else(|| { + let spec = &self.chain.spec; + let sampling_size = spec.sampling_size(spec.custody_requirement).unwrap(); + (0..sampling_size).collect() + }); + + let verified_columns = sidecars + .into_iter() + .filter(|c| custody_columns.contains(&c.index)) + .map(|sidecar| { + let column_index = sidecar.index; + self.chain + .verify_data_column_sidecar_for_gossip(sidecar, column_index) + }) + .collect::, _>>() + .unwrap(); + + self.chain + .process_gossip_data_columns(verified_columns, || Ok(())) + .await + .unwrap(); + } else { + for (i, (kzg_proof, blob)) in proofs.into_iter().zip(blobs).enumerate() { + let sidecar = + Arc::new(BlobSidecar::new(i, blob.clone(), block, *kzg_proof).unwrap()); + let gossip_blob = GossipVerifiedBlob::new(sidecar, i as u64, &self.chain).unwrap(); + self.chain.process_gossip_blob(gossip_blob).await.unwrap(); + } + } + } } // Junk `Debug` impl to satistfy certain trait bounds during testing. diff --git a/beacon_node/beacon_chain/tests/attestation_production.rs b/beacon_node/beacon_chain/tests/attestation_production.rs index 60001159938..621475a3ece 100644 --- a/beacon_node/beacon_chain/tests/attestation_production.rs +++ b/beacon_node/beacon_chain/tests/attestation_production.rs @@ -1,7 +1,6 @@ #![cfg(not(debug_assertions))] use beacon_chain::attestation_simulator::produce_unaggregated_attestation; -use beacon_chain::block_verification_types::RpcBlock; use beacon_chain::test_utils::{AttestationStrategy, BeaconChainHarness, BlockStrategy}; use beacon_chain::validator_monitor::UNAGGREGATED_ATTESTATION_LAG_SLOTS; use beacon_chain::{metrics, StateSkipConfig, WhenSlotSkipped}; @@ -155,7 +154,6 @@ async fn produces_attestations() { .store .make_full_block(&block_root, blinded_block) .unwrap(); - let blobs = chain.get_blobs(&block_root).unwrap().blobs(); let epoch_boundary_slot = state .current_epoch() @@ -223,8 +221,7 @@ async fn produces_attestations() { assert_eq!(data.target.root, target_root, "bad target root"); let rpc_block = - RpcBlock::::new(None, Arc::new(block.clone()), blobs.clone()) - .unwrap(); + harness.build_rpc_block_from_store_blobs(Some(block_root), Arc::new(block.clone())); let beacon_chain::data_availability_checker::MaybeAvailableBlock::Available( available_block, ) = chain @@ -296,14 +293,8 @@ async fn early_attester_cache_old_request() { .get_block(&head.beacon_block_root) .unwrap(); - let head_blobs = harness - .chain - .get_blobs(&head.beacon_block_root) - .expect("should get blobs") - .blobs(); - - let rpc_block = - RpcBlock::::new(None, head.beacon_block.clone(), head_blobs).unwrap(); + let rpc_block = harness + .build_rpc_block_from_store_blobs(Some(head.beacon_block_root), head.beacon_block.clone()); let beacon_chain::data_availability_checker::MaybeAvailableBlock::Available(available_block) = harness .chain diff --git a/beacon_node/beacon_chain/tests/block_verification.rs b/beacon_node/beacon_chain/tests/block_verification.rs index 1a651332ade..f85553ee4ab 100644 --- a/beacon_node/beacon_chain/tests/block_verification.rs +++ b/beacon_node/beacon_chain/tests/block_verification.rs @@ -1,6 +1,7 @@ #![cfg(not(debug_assertions))] use beacon_chain::block_verification_types::{AsBlock, ExecutedBlock, RpcBlock}; +use beacon_chain::data_column_verification::CustodyDataColumn; use beacon_chain::{ test_utils::{ test_spec, AttestationStrategy, BeaconChainHarness, BlockStrategy, EphemeralHarnessType, @@ -34,7 +35,12 @@ const BLOCK_INDICES: &[usize] = &[0, 1, 32, 64, 68 + 1, 129, CHAIN_SEGMENT_LENGT static KEYPAIRS: LazyLock> = LazyLock::new(|| types::test_utils::generate_deterministic_keypairs(VALIDATOR_COUNT)); -async fn get_chain_segment() -> (Vec>, Vec>>) { +enum DataSidecars { + Blobs(BlobSidecarList), + DataColumns(Vec>), +} + +async fn get_chain_segment() -> (Vec>, Vec>>) { let harness = get_harness(VALIDATOR_COUNT); harness @@ -46,7 +52,7 @@ async fn get_chain_segment() -> (Vec>, Vec (Vec>, Vec BeaconChainHarness], - blobs: &[Option>], + chain_segment_sidecars: &[Option>], + spec: &ChainSpec, ) -> Vec> { chain_segment .iter() - .zip(blobs.iter()) - .map(|(snapshot, blobs)| { - RpcBlock::new(None, snapshot.beacon_block.clone(), blobs.clone()).unwrap() + .zip(chain_segment_sidecars.iter()) + .map(|(snapshot, data_sidecars)| { + let block = snapshot.beacon_block.clone(); + build_rpc_block(block, data_sidecars, spec) }) .collect() } +fn build_rpc_block( + block: Arc>, + data_sidecars: &Option>, + spec: &ChainSpec, +) -> RpcBlock { + 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), + } +} + fn junk_signature() -> Signature { let kp = generate_deterministic_keypair(VALIDATOR_COUNT); let message = Hash256::from_slice(&[42; 32]); @@ -188,7 +230,7 @@ fn update_proposal_signatures( fn update_parent_roots( snapshots: &mut [BeaconSnapshot], - blobs: &mut [Option>], + blobs: &mut Vec>>, ) { for i in 0..snapshots.len() { let root = snapshots[i].beacon_block.canonical_root(); @@ -196,8 +238,15 @@ fn update_parent_roots( let (mut block, signature) = child.beacon_block.as_ref().clone().deconstruct(); *block.parent_root_mut() = root; let new_child = Arc::new(SignedBeaconBlock::from_block(block, signature)); - if let Some(blobs) = child_blobs { - update_blob_signed_header(&new_child, blobs); + if let Some(data_sidecars) = child_blobs { + match data_sidecars { + DataSidecars::Blobs(blobs) => { + update_blob_signed_header(&new_child, blobs); + } + DataSidecars::DataColumns(columns) => { + update_data_column_signed_header(&new_child, columns); + } + } } child.beacon_block = new_child; } @@ -225,13 +274,36 @@ fn update_blob_signed_header( } } +fn update_data_column_signed_header( + signed_block: &SignedBeaconBlock, + data_columns: &mut Vec>, +) { + for old_custody_column_sidecar in data_columns.as_mut_slice() { + let old_column_sidecar = old_custody_column_sidecar.as_data_column(); + let new_column_sidecar = Arc::new(DataColumnSidecar:: { + index: old_column_sidecar.index, + column: old_column_sidecar.column.clone(), + kzg_commitments: old_column_sidecar.kzg_commitments.clone(), + kzg_proofs: old_column_sidecar.kzg_proofs.clone(), + signed_block_header: signed_block.signed_block_header(), + kzg_commitments_inclusion_proof: signed_block + .message() + .body() + .kzg_commitments_merkle_proof() + .unwrap(), + }); + *old_custody_column_sidecar = CustodyDataColumn::from_asserted_custody(new_column_sidecar); + } +} + #[tokio::test] async fn chain_segment_full_segment() { let harness = get_harness(VALIDATOR_COUNT); let (chain_segment, chain_segment_blobs) = get_chain_segment().await; - let blocks: Vec> = chain_segment_blocks(&chain_segment, &chain_segment_blobs) - .into_iter() - .collect(); + let blocks: Vec> = + chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec) + .into_iter() + .collect(); harness .chain @@ -267,9 +339,10 @@ async fn chain_segment_varying_chunk_size() { for chunk_size in &[1, 2, 3, 5, 31, 32, 33, 42] { let harness = get_harness(VALIDATOR_COUNT); let (chain_segment, chain_segment_blobs) = get_chain_segment().await; - let blocks: Vec> = chain_segment_blocks(&chain_segment, &chain_segment_blobs) - .into_iter() - .collect(); + let blocks: Vec> = + chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec) + .into_iter() + .collect(); harness .chain @@ -308,9 +381,10 @@ async fn chain_segment_non_linear_parent_roots() { /* * Test with a block removed. */ - let mut blocks: Vec> = chain_segment_blocks(&chain_segment, &chain_segment_blobs) - .into_iter() - .collect(); + let mut blocks: Vec> = + chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec) + .into_iter() + .collect(); blocks.remove(2); assert!( @@ -328,9 +402,10 @@ async fn chain_segment_non_linear_parent_roots() { /* * Test with a modified parent root. */ - let mut blocks: Vec> = chain_segment_blocks(&chain_segment, &chain_segment_blobs) - .into_iter() - .collect(); + let mut blocks: Vec> = + chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec) + .into_iter() + .collect(); let (mut block, signature) = blocks[3].as_block().clone().deconstruct(); *block.parent_root_mut() = Hash256::zero(); @@ -365,9 +440,10 @@ async fn chain_segment_non_linear_slots() { * Test where a child is lower than the parent. */ - let mut blocks: Vec> = chain_segment_blocks(&chain_segment, &chain_segment_blobs) - .into_iter() - .collect(); + let mut blocks: Vec> = + chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec) + .into_iter() + .collect(); let (mut block, signature) = blocks[3].as_block().clone().deconstruct(); *block.slot_mut() = Slot::new(0); blocks[3] = RpcBlock::new_without_blobs( @@ -391,9 +467,10 @@ async fn chain_segment_non_linear_slots() { * Test where a child is equal to the parent. */ - let mut blocks: Vec> = chain_segment_blocks(&chain_segment, &chain_segment_blobs) - .into_iter() - .collect(); + let mut blocks: Vec> = + chain_segment_blocks(&chain_segment, &chain_segment_blobs, &harness.spec) + .into_iter() + .collect(); let (mut block, signature) = blocks[3].as_block().clone().deconstruct(); *block.slot_mut() = blocks[2].slot(); blocks[3] = RpcBlock::new_without_blobs( @@ -416,7 +493,7 @@ async fn chain_segment_non_linear_slots() { async fn assert_invalid_signature( chain_segment: &[BeaconSnapshot], - chain_segment_blobs: &[Option>], + chain_segment_blobs: &[Option>], harness: &BeaconChainHarness>, block_index: usize, snapshots: &[BeaconSnapshot], @@ -426,7 +503,7 @@ async fn assert_invalid_signature( .iter() .zip(chain_segment_blobs.iter()) .map(|(snapshot, blobs)| { - RpcBlock::new(None, snapshot.beacon_block.clone(), blobs.clone()).unwrap() + build_rpc_block(snapshot.beacon_block.clone(), blobs, &harness.spec) }) .collect(); @@ -453,7 +530,7 @@ async fn assert_invalid_signature( .take(block_index) .zip(chain_segment_blobs.iter()) .map(|(snapshot, blobs)| { - RpcBlock::new(None, snapshot.beacon_block.clone(), blobs.clone()).unwrap() + build_rpc_block(snapshot.beacon_block.clone(), blobs, &harness.spec) }) .collect(); // We don't care if this fails, we just call this to ensure that all prior blocks have been @@ -468,12 +545,11 @@ async fn assert_invalid_signature( .chain .process_block( snapshots[block_index].beacon_block.canonical_root(), - RpcBlock::new( - None, + build_rpc_block( snapshots[block_index].beacon_block.clone(), - chain_segment_blobs[block_index].clone(), - ) - .unwrap(), + &chain_segment_blobs[block_index], + &harness.spec, + ), NotifyExecutionLayer::Yes, BlockImportSource::Lookup, || Ok(()), @@ -526,7 +602,7 @@ async fn invalid_signature_gossip_block() { .take(block_index) .zip(chain_segment_blobs.iter()) .map(|(snapshot, blobs)| { - RpcBlock::new(None, snapshot.beacon_block.clone(), blobs.clone()).unwrap() + build_rpc_block(snapshot.beacon_block.clone(), blobs, &harness.spec) }) .collect(); harness @@ -574,7 +650,7 @@ async fn invalid_signature_block_proposal() { .iter() .zip(chain_segment_blobs.iter()) .map(|(snapshot, blobs)| { - RpcBlock::new(None, snapshot.beacon_block.clone(), blobs.clone()).unwrap() + build_rpc_block(snapshot.beacon_block.clone(), blobs, &harness.spec) }) .collect::>(); // Ensure the block will be rejected if imported in a chain segment. @@ -880,7 +956,7 @@ async fn invalid_signature_deposit() { .iter() .zip(chain_segment_blobs.iter()) .map(|(snapshot, blobs)| { - RpcBlock::new(None, snapshot.beacon_block.clone(), blobs.clone()).unwrap() + build_rpc_block(snapshot.beacon_block.clone(), blobs, &harness.spec) }) .collect(); assert!( @@ -1248,20 +1324,14 @@ async fn verify_block_for_gossip_slashing_detection() { let verified_block = harness.chain.verify_block_for_gossip(block1).await.unwrap(); if let Some((kzg_proofs, blobs)) = blobs1 { - let sidecars = - BlobSidecar::build_sidecars(blobs, verified_block.block(), kzg_proofs, &spec).unwrap(); - for sidecar in sidecars { - let blob_index = sidecar.index; - let verified_blob = harness - .chain - .verify_blob_sidecar_for_gossip(sidecar, blob_index) - .unwrap(); - harness - .chain - .process_gossip_blob(verified_blob) - .await - .unwrap(); - } + harness + .process_gossip_blobs_or_columns( + verified_block.block(), + blobs.iter(), + kzg_proofs.iter(), + None, + ) + .await; } harness .chain diff --git a/beacon_node/beacon_processor/src/lib.rs b/beacon_node/beacon_processor/src/lib.rs index 0edda2f95b8..07d2a90df93 100644 --- a/beacon_node/beacon_processor/src/lib.rs +++ b/beacon_node/beacon_processor/src/lib.rs @@ -1717,7 +1717,7 @@ mod tests { #[test] fn min_queue_len() { // State with no validators. - let spec = ForkName::latest().make_genesis_spec(ChainSpec::mainnet()); + let spec = ForkName::latest_stable().make_genesis_spec(ChainSpec::mainnet()); let genesis_time = 0; let state = BeaconState::::new(genesis_time, Eth1Data::default(), &spec); assert_eq!(state.validators().len(), 0); diff --git a/beacon_node/http_api/src/test_utils.rs b/beacon_node/http_api/src/test_utils.rs index 7b48d64e36f..fbc92a45cce 100644 --- a/beacon_node/http_api/src/test_utils.rs +++ b/beacon_node/http_api/src/test_utils.rs @@ -8,6 +8,7 @@ use beacon_processor::{ }; use directory::DEFAULT_ROOT_DIR; use eth2::{BeaconNodeHttpClient, Timeouts}; +use lighthouse_network::rpc::methods::MetaDataV3; use lighthouse_network::{ discv5::enr::CombinedKey, libp2p::swarm::{ @@ -150,11 +151,21 @@ pub async fn create_api_server_with_config( let (network_senders, network_receivers) = NetworkSenders::new(); // Default metadata - let meta_data = MetaData::V2(MetaDataV2 { - seq_number: SEQ_NUMBER, - attnets: EnrAttestationBitfield::::default(), - syncnets: EnrSyncCommitteeBitfield::::default(), - }); + let meta_data = if chain.spec.is_peer_das_scheduled() { + MetaData::V3(MetaDataV3 { + seq_number: SEQ_NUMBER, + attnets: EnrAttestationBitfield::::default(), + syncnets: EnrSyncCommitteeBitfield::::default(), + custody_group_count: chain.spec.custody_requirement, + }) + } else { + MetaData::V2(MetaDataV2 { + seq_number: SEQ_NUMBER, + attnets: EnrAttestationBitfield::::default(), + syncnets: EnrSyncCommitteeBitfield::::default(), + }) + }; + let enr_key = CombinedKey::generate_secp256k1(); let enr = Enr::builder().build(&enr_key).unwrap(); let network_config = Arc::new(NetworkConfig::default()); diff --git a/beacon_node/http_api/tests/broadcast_validation_tests.rs b/beacon_node/http_api/tests/broadcast_validation_tests.rs index db4ef002579..1baa71699c7 100644 --- a/beacon_node/http_api/tests/broadcast_validation_tests.rs +++ b/beacon_node/http_api/tests/broadcast_validation_tests.rs @@ -1,4 +1,3 @@ -use beacon_chain::blob_verification::GossipVerifiedBlob; use beacon_chain::{ test_utils::{AttestationStrategy, BlockStrategy}, GossipVerifiedBlock, IntoGossipVerifiedBlock, @@ -7,9 +6,10 @@ use eth2::reqwest::StatusCode; use eth2::types::{BroadcastValidation, PublishBlockRequest}; use http_api::test_utils::InteractiveTester; use http_api::{publish_blinded_block, publish_block, reconstruct_block, Config, ProvenancedBlock}; +use std::collections::HashSet; use std::sync::Arc; use types::{ - BlobSidecar, Epoch, EthSpec, FixedBytesExtended, ForkName, Hash256, MainnetEthSpec, Slot, + ColumnIndex, Epoch, EthSpec, FixedBytesExtended, ForkName, Hash256, MainnetEthSpec, Slot, }; use warp::Rejection; use warp_utils::reject::CustomBadRequest; @@ -17,6 +17,8 @@ use warp_utils::reject::CustomBadRequest; type E = MainnetEthSpec; /* + * TODO(fulu): write PeerDAS equivalent tests for these. + * * We have the following test cases, which are duplicated for the blinded variant of the route: * * - `broadcast_validation=gossip` @@ -1375,7 +1377,7 @@ pub async fn block_seen_on_gossip_without_blobs() { // `validator_count // 32`. let validator_count = 64; let num_initial: u64 = 31; - let spec = ForkName::latest().make_genesis_spec(E::default_spec()); + let spec = ForkName::latest_stable().make_genesis_spec(E::default_spec()); let tester = InteractiveTester::::new(Some(spec), validator_count).await; // Create some chain depth. @@ -1437,7 +1439,7 @@ pub async fn block_seen_on_gossip_with_some_blobs() { // `validator_count // 32`. let validator_count = 64; let num_initial: u64 = 31; - let spec = ForkName::latest().make_genesis_spec(E::default_spec()); + let spec = ForkName::latest_stable().make_genesis_spec(E::default_spec()); let tester = InteractiveTester::::new(Some(spec), validator_count).await; // Create some chain depth. @@ -1464,8 +1466,8 @@ pub async fn block_seen_on_gossip_with_some_blobs() { blobs.0.len() ); - let partial_kzg_proofs = vec![*blobs.0.first().unwrap()]; - let partial_blobs = vec![blobs.1.first().unwrap().clone()]; + let partial_kzg_proofs = [*blobs.0.first().unwrap()]; + let partial_blobs = [blobs.1.first().unwrap().clone()]; // Simulate the block being seen on gossip. block @@ -1474,21 +1476,15 @@ pub async fn block_seen_on_gossip_with_some_blobs() { .unwrap(); // Simulate some of the blobs being seen on gossip. - for (i, (kzg_proof, blob)) in partial_kzg_proofs - .into_iter() - .zip(partial_blobs) - .enumerate() - { - let sidecar = Arc::new(BlobSidecar::new(i, blob, &block, kzg_proof).unwrap()); - let gossip_blob = - GossipVerifiedBlob::new(sidecar, i as u64, &tester.harness.chain).unwrap(); - tester - .harness - .chain - .process_gossip_blob(gossip_blob) - .await - .unwrap(); - } + tester + .harness + .process_gossip_blobs_or_columns( + &block, + partial_blobs.iter(), + partial_kzg_proofs.iter(), + Some(get_custody_columns(&tester)), + ) + .await; // It should not yet be added to fork choice because all blobs have not been seen. assert!(!tester @@ -1523,7 +1519,7 @@ pub async fn blobs_seen_on_gossip_without_block() { // `validator_count // 32`. let validator_count = 64; let num_initial: u64 = 31; - let spec = ForkName::latest().make_genesis_spec(E::default_spec()); + let spec = ForkName::latest_stable().make_genesis_spec(E::default_spec()); let tester = InteractiveTester::::new(Some(spec), validator_count).await; // Create some chain depth. @@ -1546,22 +1542,15 @@ pub async fn blobs_seen_on_gossip_without_block() { let (kzg_proofs, blobs) = blobs.expect("should have some blobs"); // Simulate the blobs being seen on gossip. - for (i, (kzg_proof, blob)) in kzg_proofs - .clone() - .into_iter() - .zip(blobs.clone()) - .enumerate() - { - let sidecar = Arc::new(BlobSidecar::new(i, blob, &block, kzg_proof).unwrap()); - let gossip_blob = - GossipVerifiedBlob::new(sidecar, i as u64, &tester.harness.chain).unwrap(); - tester - .harness - .chain - .process_gossip_blob(gossip_blob) - .await - .unwrap(); - } + tester + .harness + .process_gossip_blobs_or_columns( + &block, + blobs.iter(), + kzg_proofs.iter(), + Some(get_custody_columns(&tester)), + ) + .await; // It should not yet be added to fork choice because the block has not been seen. assert!(!tester @@ -1596,7 +1585,7 @@ pub async fn blobs_seen_on_gossip_without_block_and_no_http_blobs() { // `validator_count // 32`. let validator_count = 64; let num_initial: u64 = 31; - let spec = ForkName::latest().make_genesis_spec(E::default_spec()); + let spec = ForkName::latest_stable().make_genesis_spec(E::default_spec()); let tester = InteractiveTester::::new(Some(spec), validator_count).await; // Create some chain depth. @@ -1620,22 +1609,15 @@ pub async fn blobs_seen_on_gossip_without_block_and_no_http_blobs() { assert!(!blobs.is_empty()); // Simulate the blobs being seen on gossip. - for (i, (kzg_proof, blob)) in kzg_proofs - .clone() - .into_iter() - .zip(blobs.clone()) - .enumerate() - { - let sidecar = Arc::new(BlobSidecar::new(i, blob, &block, kzg_proof).unwrap()); - let gossip_blob = - GossipVerifiedBlob::new(sidecar, i as u64, &tester.harness.chain).unwrap(); - tester - .harness - .chain - .process_gossip_blob(gossip_blob) - .await - .unwrap(); - } + tester + .harness + .process_gossip_blobs_or_columns( + &block, + blobs.iter(), + kzg_proofs.iter(), + Some(get_custody_columns(&tester)), + ) + .await; // It should not yet be added to fork choice because the block has not been seen. assert!(!tester @@ -1672,7 +1654,7 @@ pub async fn slashable_blobs_seen_on_gossip_cause_failure() { // `validator_count // 32`. let validator_count = 64; let num_initial: u64 = 31; - let spec = ForkName::latest().make_genesis_spec(E::default_spec()); + let spec = ForkName::latest_stable().make_genesis_spec(E::default_spec()); let tester = InteractiveTester::::new(Some(spec), validator_count).await; // Create some chain depth. @@ -1697,17 +1679,15 @@ pub async fn slashable_blobs_seen_on_gossip_cause_failure() { let (kzg_proofs_b, blobs_b) = blobs_b.expect("should have some blobs"); // Simulate the blobs of block B being seen on gossip. - for (i, (kzg_proof, blob)) in kzg_proofs_b.into_iter().zip(blobs_b).enumerate() { - let sidecar = Arc::new(BlobSidecar::new(i, blob, &block_b, kzg_proof).unwrap()); - let gossip_blob = - GossipVerifiedBlob::new(sidecar, i as u64, &tester.harness.chain).unwrap(); - tester - .harness - .chain - .process_gossip_blob(gossip_blob) - .await - .unwrap(); - } + tester + .harness + .process_gossip_blobs_or_columns( + &block_b, + blobs_b.iter(), + kzg_proofs_b.iter(), + Some(get_custody_columns(&tester)), + ) + .await; // It should not yet be added to fork choice because block B has not been seen. assert!(!tester @@ -1742,7 +1722,7 @@ pub async fn duplicate_block_status_code() { // `validator_count // 32`. let validator_count = 64; let num_initial: u64 = 31; - let spec = ForkName::latest().make_genesis_spec(E::default_spec()); + let spec = ForkName::latest_stable().make_genesis_spec(E::default_spec()); let duplicate_block_status_code = StatusCode::IM_A_TEAPOT; let tester = InteractiveTester::::new_with_initializer_and_mutator( Some(spec), @@ -1804,3 +1784,13 @@ fn assert_server_message_error(error_response: eth2::Error, expected_message: St }; assert_eq!(err.message, expected_message); } + +fn get_custody_columns(tester: &InteractiveTester) -> HashSet { + tester + .ctx + .network_globals + .as_ref() + .unwrap() + .sampling_columns + .clone() +} diff --git a/beacon_node/network/src/network_beacon_processor/tests.rs b/beacon_node/network/src/network_beacon_processor/tests.rs index 8238fa146dd..8415ece6383 100644 --- a/beacon_node/network/src/network_beacon_processor/tests.rs +++ b/beacon_node/network/src/network_beacon_processor/tests.rs @@ -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::::default(), - syncnets: EnrSyncCommitteeBitfield::::default(), - }); + let meta_data = if spec.is_peer_das_scheduled() { + MetaData::V3(MetaDataV3 { + seq_number: SEQ_NUMBER, + attnets: EnrAttestationBitfield::::default(), + syncnets: EnrSyncCommitteeBitfield::::default(), + custody_group_count: spec.custody_requirement, + }) + } else { + MetaData::V2(MetaDataV2 { + seq_number: SEQ_NUMBER, + attnets: EnrAttestationBitfield::::default(), + syncnets: EnrSyncCommitteeBitfield::::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::>()); @@ -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(); } diff --git a/beacon_node/network/src/sync/tests/lookups.rs b/beacon_node/network/src/sync/tests/lookups.rs index de050083566..f91eda27026 100644 --- a/beacon_node/network/src/sync/tests/lookups.rs +++ b/beacon_node/network/src/sync/tests/lookups.rs @@ -138,9 +138,9 @@ impl TestRig { } } - fn test_setup_after_deneb() -> Option { + fn test_setup_after_deneb_before_fulu() -> Option { let r = Self::test_setup(); - if r.after_deneb() { + if r.after_deneb() && !r.fork_name.fulu_enabled() { Some(r) } else { None @@ -1922,7 +1922,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)); @@ -1940,7 +1940,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)); @@ -1966,7 +1966,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)); @@ -1991,7 +1991,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)); @@ -2215,7 +2215,7 @@ mod deneb_only { impl DenebTester { fn new(request_trigger: RequestTrigger) -> Option { - 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); @@ -2940,7 +2940,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)); diff --git a/beacon_node/store/src/hot_cold_store.rs b/beacon_node/store/src/hot_cold_store.rs index c56c82a0fb5..7429adfd255 100644 --- a/beacon_node/store/src/hot_cold_store.rs +++ b/beacon_node/store/src/hot_cold_store.rs @@ -121,6 +121,11 @@ impl BlockCache { pub fn get_blobs<'a>(&'a mut self, block_root: &Hash256) -> Option<&'a BlobSidecarList> { self.blob_cache.get(block_root) } + pub fn get_data_columns(&mut self, block_root: &Hash256) -> Option> { + self.data_column_cache + .get(block_root) + .map(|map| map.values().cloned().collect::>()) + } pub fn get_data_column<'a>( &'a mut self, block_root: &Hash256, @@ -2043,6 +2048,36 @@ impl, Cold: ItemStore> HotColdDB }) } + /// Fetch columns for a given block from the store. + pub fn get_data_columns( + &self, + block_root: &Hash256, + ) -> Result>, Error> { + if let Some(columns) = self.block_cache.lock().get_data_columns(block_root) { + metrics::inc_counter(&metrics::BEACON_DATA_COLUMNS_CACHE_HIT_COUNT); + return Ok(Some(columns)); + } + + let columns = self + .blobs_db + .iter_raw_entries(DBColumn::BeaconDataColumn, block_root.as_slice()) + .map(|result| { + let (_key, value) = result?; + let column = DataColumnSidecar::::from_ssz_bytes(&value).map(Arc::new)?; + self.block_cache + .lock() + .put_data_column(*block_root, column.clone()); + Ok(column) + }) + .collect::, Error>>()?; + + if columns.is_empty() { + Ok(None) + } else { + Ok(Some(columns)) + } + } + /// Fetch blobs for a given block from the store. pub fn get_blobs(&self, block_root: &Hash256) -> Result, Error> { // Check the cache. diff --git a/beacon_node/store/src/lib.rs b/beacon_node/store/src/lib.rs index 1458fa846c6..fc644869205 100644 --- a/beacon_node/store/src/lib.rs +++ b/beacon_node/store/src/lib.rs @@ -108,9 +108,7 @@ pub trait KeyValueStore: Sync + Send + Sized + 'static { /// Iterate through all keys and values in a column from a given starting point. fn iter_column_from(&self, column: DBColumn, from: &[u8]) -> ColumnIter; - fn iter_raw_entries(&self, _column: DBColumn, _prefix: &[u8]) -> RawEntryIter { - Box::new(std::iter::empty()) - } + fn iter_raw_entries(&self, _column: DBColumn, _prefix: &[u8]) -> RawEntryIter; fn iter_raw_keys(&self, column: DBColumn, prefix: &[u8]) -> RawKeyIter; diff --git a/beacon_node/store/src/memory_store.rs b/beacon_node/store/src/memory_store.rs index 4c7bfdf10ff..4e7f51621dd 100644 --- a/beacon_node/store/src/memory_store.rs +++ b/beacon_node/store/src/memory_store.rs @@ -1,6 +1,6 @@ use crate::{ get_key_for_col, leveldb_store::BytesKey, ColumnIter, ColumnKeyIter, DBColumn, Error, - ItemStore, Key, KeyValueStore, KeyValueStoreOp, RawKeyIter, + ItemStore, Key, KeyValueStore, KeyValueStoreOp, RawEntryIter, RawKeyIter, }; use parking_lot::{Mutex, MutexGuard, RwLock}; use std::collections::BTreeMap; @@ -112,6 +112,21 @@ impl KeyValueStore for MemoryStore { Box::new(keys.into_iter().map(Ok)) } + fn iter_raw_entries(&self, column: DBColumn, prefix: &[u8]) -> RawEntryIter { + let start_key = BytesKey::from_vec(get_key_for_col(column.as_str(), prefix)); + let keys_and_vals = self + .db + .read() + .range(start_key.clone()..) + .take_while(|(k, _)| k.starts_with(&start_key)) + .filter_map(|(k, v)| { + k.remove_column_variable(column) + .map(|k| (k.to_vec(), v.clone())) + }) + .collect::>(); + Box::new(keys_and_vals.into_iter().map(Ok)) + } + fn iter_column_keys(&self, column: DBColumn) -> ColumnKeyIter { Box::new(self.iter_column(column).map(|res| res.map(|(k, _)| k))) } diff --git a/consensus/fork_choice/tests/tests.rs b/consensus/fork_choice/tests/tests.rs index 70b4b73d528..b224cde048e 100644 --- a/consensus/fork_choice/tests/tests.rs +++ b/consensus/fork_choice/tests/tests.rs @@ -54,7 +54,7 @@ impl ForkChoiceTest { /// Creates a new tester with a custom chain config. pub fn new_with_chain_config(chain_config: ChainConfig) -> Self { // Run fork choice tests against the latest fork. - let spec = ForkName::latest().make_genesis_spec(ChainSpec::default()); + let spec = ForkName::latest_stable().make_genesis_spec(ChainSpec::default()); let harness = BeaconChainHarness::builder(MainnetEthSpec) .spec(spec.into()) .chain_config(chain_config) diff --git a/consensus/types/src/fork_name.rs b/consensus/types/src/fork_name.rs index b61e0a4d4a5..40557e0cb97 100644 --- a/consensus/types/src/fork_name.rs +++ b/consensus/types/src/fork_name.rs @@ -49,6 +49,13 @@ impl ForkName { *ForkName::list_all().last().unwrap() } + /// Returns the fork primarily used for testing purposes. + /// This fork serves as the baseline for many tests, and the goal + /// is to ensure features are passing on this fork. + pub fn latest_stable() -> ForkName { + ForkName::Electra + } + /// Set the activation slots in the given `ChainSpec` so that the fork named by `self` /// is the only fork in effect from genesis. pub fn make_genesis_spec(&self, mut spec: ChainSpec) -> ChainSpec { From eff9a5b97abe29d05787aa1776e85ba9636aa935 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Tue, 21 Jan 2025 10:02:19 +1100 Subject: [PATCH 09/16] More test fixes for Fulu. --- beacon_node/beacon_chain/src/test_utils.rs | 8 +- .../beacon_chain/tests/block_verification.rs | 107 ++++++++---------- 2 files changed, 55 insertions(+), 60 deletions(-) diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index bea33f202bd..6412fac4f99 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -3099,8 +3099,12 @@ where for (i, (kzg_proof, blob)) in proofs.into_iter().zip(blobs).enumerate() { let sidecar = Arc::new(BlobSidecar::new(i, blob.clone(), block, *kzg_proof).unwrap()); - let gossip_blob = GossipVerifiedBlob::new(sidecar, i as u64, &self.chain).unwrap(); - self.chain.process_gossip_blob(gossip_blob).await.unwrap(); + let gossip_blob = GossipVerifiedBlob::new(sidecar, i as u64, &self.chain) + .expect("should obtain gossip verified blob"); + self.chain + .process_gossip_blob(gossip_blob) + .await + .expect("should import valid gossip verified blob"); } } } diff --git a/beacon_node/beacon_chain/tests/block_verification.rs b/beacon_node/beacon_chain/tests/block_verification.rs index ff80222a6e8..284316652da 100644 --- a/beacon_node/beacon_chain/tests/block_verification.rs +++ b/beacon_node/beacon_chain/tests/block_verification.rs @@ -100,48 +100,6 @@ async fn get_chain_segment() -> (Vec>, Vec (Vec>, Vec>>) { - let harness = get_harness(VALIDATOR_COUNT); - - harness - .extend_chain( - CHAIN_SEGMENT_LENGTH, - BlockStrategy::OnCanonicalHead, - AttestationStrategy::AllValidators, - ) - .await; - - let mut segment = Vec::with_capacity(CHAIN_SEGMENT_LENGTH); - let mut segment_blobs = Vec::with_capacity(CHAIN_SEGMENT_LENGTH); - for snapshot in harness - .chain - .chain_dump() - .expect("should dump chain") - .into_iter() - .skip(1) - { - let full_block = harness - .chain - .get_block(&snapshot.beacon_block_root) - .await - .unwrap() - .unwrap(); - segment.push(BeaconSnapshot { - beacon_block_root: snapshot.beacon_block_root, - beacon_block: Arc::new(full_block), - beacon_state: snapshot.beacon_state, - }); - let blob_sidecars = harness - .chain - .get_blobs(&snapshot.beacon_block_root) - .unwrap() - .blobs(); - segment_blobs.push(blob_sidecars) - } - (segment, segment_blobs) -} - fn get_harness(validator_count: usize) -> BeaconChainHarness> { let harness = BeaconChainHarness::builder(MainnetEthSpec) .default_spec() @@ -1019,7 +977,7 @@ fn unwrap_err(result: Result) -> U { #[tokio::test] async fn block_gossip_verification() { let harness = get_harness(VALIDATOR_COUNT); - let (chain_segment, chain_segment_blobs) = get_chain_segment_with_blob_sidecars().await; + let (chain_segment, chain_segment_blobs) = get_chain_segment().await; let block_index = CHAIN_SEGMENT_LENGTH - 2; @@ -1031,7 +989,7 @@ async fn block_gossip_verification() { // Import the ancestors prior to the block we're testing. for (snapshot, blobs_opt) in chain_segment[0..block_index] .iter() - .zip(chain_segment_blobs.iter()) + .zip(chain_segment_blobs.into_iter()) { let gossip_verified = harness .chain @@ -1050,20 +1008,8 @@ async fn block_gossip_verification() { ) .await .expect("should import valid gossip verified block"); - if let Some(blob_sidecars) = blobs_opt { - for blob_sidecar in blob_sidecars { - let blob_index = blob_sidecar.index; - let gossip_verified = harness - .chain - .verify_blob_sidecar_for_gossip(blob_sidecar.clone(), blob_index) - .expect("should obtain gossip verified blob"); - - harness - .chain - .process_gossip_blob(gossip_verified) - .await - .expect("should import valid gossip verified blob"); - } + if let Some(data_sidecars) = blobs_opt { + verify_and_process_gossip_data_sidecars(&harness, data_sidecars).await; } } @@ -1291,6 +1237,51 @@ async fn block_gossip_verification() { ); } +async fn verify_and_process_gossip_data_sidecars( + harness: &BeaconChainHarness>, + data_sidecars: DataSidecars, +) { + match data_sidecars { + DataSidecars::Blobs(blob_sidecars) => { + for blob_sidecar in blob_sidecars { + let blob_index = blob_sidecar.index; + let gossip_verified = harness + .chain + .verify_blob_sidecar_for_gossip(blob_sidecar.clone(), blob_index) + .expect("should obtain gossip verified blob"); + + harness + .chain + .process_gossip_blob(gossip_verified) + .await + .expect("should import valid gossip verified blob"); + } + } + DataSidecars::DataColumns(column_sidecars) => { + let gossip_verified = column_sidecars + .into_iter() + .map(|column_sidecar| { + let subnet_id = DataColumnSubnetId::from_column_index( + column_sidecar.index(), + &harness.spec, + ); + harness.chain.verify_data_column_sidecar_for_gossip( + column_sidecar.into_inner(), + *subnet_id, + ) + }) + .collect::, _>>() + .expect("should obtain gossip verified columns"); + + harness + .chain + .process_gossip_data_columns(gossip_verified, || Ok(())) + .await + .expect("should import valid gossip verified columns"); + } + } +} + #[tokio::test] async fn verify_block_for_gossip_slashing_detection() { let slasher_dir = tempdir().unwrap(); From 614f98452fb3eda2d910f956d2ef7190530be318 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Tue, 21 Jan 2025 15:59:22 +1100 Subject: [PATCH 10/16] Fix range sync to select custody peers from its syncing chain instead of the global peer list. --- .../network/src/sync/backfill_sync/mod.rs | 7 ++ .../network/src/sync/network_context.rs | 32 ++++-- .../network/src/sync/range_sync/chain.rs | 4 + beacon_node/network/src/sync/tests/lookups.rs | 6 +- beacon_node/network/src/sync/tests/range.rs | 107 ++++++++++++++---- 5 files changed, 127 insertions(+), 29 deletions(-) diff --git a/beacon_node/network/src/sync/backfill_sync/mod.rs b/beacon_node/network/src/sync/backfill_sync/mod.rs index 5703ed35046..332016b5bc0 100644 --- a/beacon_node/network/src/sync/backfill_sync/mod.rs +++ b/beacon_node/network/src/sync/backfill_sync/mod.rs @@ -941,11 +941,18 @@ impl BackFillSync { ) -> Result<(), BackFillError> { if let Some(batch) = self.batches.get_mut(&batch_id) { let (request, is_blob_batch) = batch.to_blocks_by_range_request(); + + let synced_peers = { + let peers = self.network_globals.peers.read(); + peers.synced_peers().copied().collect::>() + }; + match network.block_components_by_range_request( peer, is_blob_batch, request, RangeRequestId::BackfillSync { batch_id }, + synced_peers.into_iter(), ) { Ok(request_id) => { // inform the batch about the new request diff --git a/beacon_node/network/src/sync/network_context.rs b/beacon_node/network/src/sync/network_context.rs index f8999361288..5ec26ef4c65 100644 --- a/beacon_node/network/src/sync/network_context.rs +++ b/beacon_node/network/src/sync/network_context.rs @@ -43,8 +43,8 @@ use std::time::Duration; use tokio::sync::mpsc; use types::blob_sidecar::FixedBlobSidecarList; use types::{ - BlobSidecar, ColumnIndex, DataColumnSidecar, DataColumnSidecarList, EthSpec, Hash256, - SignedBeaconBlock, Slot, + BlobSidecar, ColumnIndex, DataColumnSidecar, DataColumnSidecarList, DataColumnSubnetId, + EthSpec, Hash256, SignedBeaconBlock, Slot, }; pub mod custody; @@ -307,10 +307,23 @@ impl SyncNetworkContext { .custody_peers_for_column(column_index) } - pub fn get_random_custodial_peer(&self, column_index: ColumnIndex) -> Option { - self.get_custodial_peers(column_index) - .into_iter() + /// Chooses a random peer assigned to custody `column_index` from the provided `syncing_peers`. + pub fn choose_random_custodial_peer<'a>( + &self, + column_index: ColumnIndex, + syncing_peers: impl Iterator, + ) -> Option { + let peer_db_read_lock = self.network_globals().peers.read(); + let subnet_id = DataColumnSubnetId::from_column_index(column_index, &self.chain.spec); + + syncing_peers + .filter(|peer_id| { + peer_db_read_lock + .peer_info(peer_id) + .is_some_and(|peer_info| peer_info.is_assigned_to_custody_subnet(&subnet_id)) + }) .choose(&mut thread_rng()) + .copied() } pub fn network_globals(&self) -> &NetworkGlobals { @@ -358,6 +371,7 @@ impl SyncNetworkContext { batch_type: ByRangeRequestType, request: BlocksByRangeRequest, sender_id: RangeRequestId, + syncing_peers: impl Iterator, ) -> Result { let epoch = Slot::new(*request.start_slot()).epoch(T::EthSpec::slots_per_epoch()); let id = self.next_id(); @@ -426,7 +440,7 @@ impl SyncNetworkContext { let mut num_of_custody_column_req = 0; for (peer_id, columns_by_range_request) in - self.make_columns_by_range_requests(request, &column_indexes)? + self.make_columns_by_range_requests(request, &column_indexes, syncing_peers)? { requested_peers.push(peer_id); @@ -473,14 +487,18 @@ impl SyncNetworkContext { &self, request: BlocksByRangeRequest, custody_indexes: &HashSet, + syncing_peers: impl Iterator, ) -> Result, RpcRequestSendError> { let mut peer_id_to_request_map = HashMap::new(); + let syncing_peers = syncing_peers.collect::>(); for column_index in custody_indexes { // TODO(das): The peer selection logic here needs to be improved - we should probably // avoid retrying from failed peers, however `BatchState` currently only tracks the peer // serving the blocks. - let Some(custody_peer) = self.get_random_custodial_peer(*column_index) else { + let Some(custody_peer) = + self.choose_random_custodial_peer(*column_index, syncing_peers.iter()) + else { // TODO(das): this will be pretty bad UX. To improve we should: // - Attempt to fetch custody requests first, before requesting blocks // - Handle the no peers case gracefully, maybe add some timeout and give a few diff --git a/beacon_node/network/src/sync/range_sync/chain.rs b/beacon_node/network/src/sync/range_sync/chain.rs index 51d9d9da37f..d4445d098b0 100644 --- a/beacon_node/network/src/sync/range_sync/chain.rs +++ b/beacon_node/network/src/sync/range_sync/chain.rs @@ -963,8 +963,11 @@ impl SyncingChain { peer: PeerId, ) -> ProcessingResult { let batch_state = self.visualize_batch_state(); + let syncing_peers = self.peers().collect::>(); + if let Some(batch) = self.batches.get_mut(&batch_id) { let (request, batch_type) = batch.to_blocks_by_range_request(); + match network.block_components_by_range_request( peer, batch_type, @@ -973,6 +976,7 @@ impl SyncingChain { chain_id: self.id, batch_id, }, + syncing_peers.into_iter(), ) { Ok(request_id) => { // inform the batch about the new request diff --git a/beacon_node/network/src/sync/tests/lookups.rs b/beacon_node/network/src/sync/tests/lookups.rs index f91eda27026..576094aa2e8 100644 --- a/beacon_node/network/src/sync/tests/lookups.rs +++ b/beacon_node/network/src/sync/tests/lookups.rs @@ -164,6 +164,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>) { let block_root = block.canonical_root(); self.send_sync_message(SyncMessage::UnknownParentBlock(peer_id, block, block_root)) @@ -364,7 +368,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() diff --git a/beacon_node/network/src/sync/tests/range.rs b/beacon_node/network/src/sync/tests/range.rs index 05d5e4a4143..11084d045c5 100644 --- a/beacon_node/network/src/sync/tests/range.rs +++ b/beacon_node/network/src/sync/tests/range.rs @@ -3,6 +3,7 @@ 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::{RequestType, StatusMessage}; @@ -16,6 +17,11 @@ use types::{ const D: Duration = Duration::new(0, 0); +pub(crate) enum DataSidecars { + Blobs(BlobSidecarList), + DataColumns(Vec>), +} + impl TestRig { /// Produce a head peer with an advanced head fn add_head_peer(&mut self) -> PeerId { @@ -67,7 +73,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 @@ -110,7 +118,19 @@ impl TestRig { }) .expect("Should have a blocks by range request"); - let blob_req_id = if self.after_deneb() { + let blob_req_id = if self.after_fulu() { + Some( + self.pop_received_network_event(|ev| match ev { + NetworkMessage::SendRequest { + peer_id, + request: RequestType::DataColumnsByRange(_), + request_id: AppRequestId::Sync(SyncRequestId::RangeBlockAndBlobs { id }), + } if peer_id == target_peer_id => Some(*id), + _ => None, + }) + .expect("Should have a data columns by range request"), + ) + } else if self.after_deneb() { Some( self.pop_received_network_event(|ev| match ev { NetworkMessage::SendRequest { @@ -144,22 +164,33 @@ impl TestRig { }); 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, - }); + if self.after_fulu() { + // Complete the request with a single stream termination + self.log(&format!( + "Completing DataColumnsByRange request {blobs_req_id} with empty stream" + )); + self.send_sync_message(SyncMessage::RpcDataColumn { + request_id: SyncRequestId::RangeBlockAndBlobs { id: blobs_req_id }, + peer_id: target_peer_id, + data_column: None, + seen_timestamp: D, + }); + } else { + // 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, + }); + } } } - async fn create_canonical_block( - &mut self, - ) -> (SignedBeaconBlock, Option>) { + async fn create_canonical_block(&mut self) -> (SignedBeaconBlock, Option>) { self.harness.advance_slot(); let block_root = self @@ -170,20 +201,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, Option>), + (block, data_sidecars): (SignedBeaconBlock, Option>), ) { // This code is kind of duplicated from Harness::process_block, but takes sidecars directly. let block_root = block.canonical_root(); @@ -193,7 +242,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 +255,22 @@ impl TestRig { } } +fn build_rpc_block( + block: Arc>, + data_sidecars: &Option>, + spec: &ChainSpec, +) -> RpcBlock { + 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. From e813532e8867c1a9226ecbb973cc5ebd9756653f Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Tue, 21 Jan 2025 17:44:19 +1100 Subject: [PATCH 11/16] Skip blob pruning tests for Fulu. --- beacon_node/beacon_chain/tests/store_tests.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/beacon_node/beacon_chain/tests/store_tests.rs b/beacon_node/beacon_chain/tests/store_tests.rs index 60d46e8269d..5c49c5694f2 100644 --- a/beacon_node/beacon_chain/tests/store_tests.rs +++ b/beacon_node/beacon_chain/tests/store_tests.rs @@ -3075,6 +3075,10 @@ async fn deneb_prune_blobs_happy_case() { let db_path = tempdir().unwrap(); let store = get_store(&db_path); + if store.get_chain_spec().is_peer_das_scheduled() { + // TODO(fulu): add prune tests for Fulu / PeerDAS data columns. + return; + } let Some(deneb_fork_epoch) = store.get_chain_spec().deneb_fork_epoch else { // No-op prior to Deneb. return; @@ -3122,6 +3126,10 @@ async fn deneb_prune_blobs_no_finalization() { let db_path = tempdir().unwrap(); let store = get_store(&db_path); + if store.get_chain_spec().is_peer_das_scheduled() { + // TODO(fulu): add prune tests for Fulu / PeerDAS data columns. + return; + } let Some(deneb_fork_epoch) = store.get_chain_spec().deneb_fork_epoch else { // No-op prior to Deneb. return; @@ -3266,6 +3274,10 @@ async fn deneb_prune_blobs_margin_test(margin: u64) { let db_path = tempdir().unwrap(); let store = get_store_generic(&db_path, config, test_spec::()); + if store.get_chain_spec().is_peer_das_scheduled() { + // TODO(fulu): add prune tests for Fulu / PeerDAS data columns. + return; + } let Some(deneb_fork_epoch) = store.get_chain_spec().deneb_fork_epoch else { // No-op prior to Deneb. return; From 492c1c6e3d8ccf1ef3edf80478e19f4526997a48 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Fri, 24 Jan 2025 13:00:44 +1100 Subject: [PATCH 12/16] Use pre-computed data columns for testing and fix tests. --- beacon_node/beacon_chain/src/kzg_utils.rs | 2 +- beacon_node/beacon_chain/src/test_utils.rs | 127 ++++++++++++------ .../fixtures/test_data_column_sidecars.ssz | Bin 0 -> 320512 bytes .../src/sync/block_sidecar_coupling.rs | 4 +- crypto/kzg/src/lib.rs | 3 + 5 files changed, 91 insertions(+), 45 deletions(-) create mode 100644 beacon_node/beacon_chain/src/test_utils/fixtures/test_data_column_sidecars.ssz diff --git a/beacon_node/beacon_chain/src/kzg_utils.rs b/beacon_node/beacon_chain/src/kzg_utils.rs index dcb3864f785..06cce141444 100644 --- a/beacon_node/beacon_chain/src/kzg_utils.rs +++ b/beacon_node/beacon_chain/src/kzg_utils.rs @@ -186,7 +186,7 @@ pub fn blobs_to_data_column_sidecars( .map_err(DataColumnSidecarError::BuildSidecarFailed) } -fn build_data_column_sidecars( +pub(crate) fn build_data_column_sidecars( kzg_commitments: KzgCommitments, kzg_commitments_inclusion_proof: FixedVector, signed_block_header: SignedBeaconBlockHeader, diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 4261ee11297..58a275f576e 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -1,7 +1,7 @@ use crate::blob_verification::GossipVerifiedBlob; use crate::block_verification_types::{AsBlock, RpcBlock}; use crate::data_column_verification::CustodyDataColumn; -use crate::kzg_utils::blobs_to_data_column_sidecars; +use crate::kzg_utils::build_data_column_sidecars; use crate::observed_operations::ObservationOutcome; pub use crate::persisted_beacon_chain::PersistedBeaconChain; pub use crate::{ @@ -76,6 +76,11 @@ pub const FORK_NAME_ENV_VAR: &str = "FORK_NAME"; // Environment variable to read if `ci_logger` feature is enabled. pub const CI_LOGGER_DIR_ENV_VAR: &str = "CI_LOGGER_DIR"; +// Pre-computed data column sidecar using a single static blob from: +// `beacon_node/execution_layer/src/test_utils/fixtures/mainnet/test_blobs_bundle.ssz` +const TEST_DATA_COLUMN_SIDECARS_SSZ: &[u8] = + include_bytes!("test_utils/fixtures/test_data_column_sidecars.ssz"); + // Default target aggregators to set during testing, this ensures an aggregator at each slot. // // You should mutate the `ChainSpec` prior to initialising the harness if you would like to use @@ -2357,26 +2362,19 @@ where .data_availability_checker .get_sampling_column_count(); - let columns = blob_items - .map(|(_proofs, blobs)| { - blobs_to_data_column_sidecars( - &blobs.iter().collect::>(), - &block, - &self.chain.kzg, - &self.spec, - ) - .map(|column_sidecars| { - column_sidecars - .into_iter() - .take(sampling_column_count) - .map(CustodyDataColumn::from_asserted_custody) - .collect::>() - }) - }) - .transpose() - .expect("should convert blobs to columns") - .unwrap_or_default(); - RpcBlock::new_with_custody_columns(Some(block_root), block, columns, &self.spec)? + if blob_items.is_some_and(|(_, blobs)| !blobs.is_empty()) { + // Note: this method ignores the actual custody columns and just take the first + // `sampling_column_count` for testing purpose only, because the chain does not + // currently have any knowledge of the columns being custodied. + let columns = generate_data_column_sidecars_from_block(&block, &self.spec) + .into_iter() + .take(sampling_column_count) + .map(CustodyDataColumn::from_asserted_custody) + .collect::>(); + RpcBlock::new_with_custody_columns(Some(block_root), block, columns, &self.spec)? + } else { + RpcBlock::new_without_blobs(Some(block_root), block) + } } else { let blobs = blob_items .map(|(proofs, blobs)| { @@ -3073,21 +3071,15 @@ where ) { let is_peerdas_enabled = self.chain.spec.is_peer_das_enabled_for_epoch(block.epoch()); if is_peerdas_enabled { - let sidecars = blobs_to_data_column_sidecars( - &blobs.collect::>(), - block, - &self.chain.kzg, - &self.spec, - ) - .unwrap(); - let custody_columns = custody_columns_opt.unwrap_or_else(|| { - let spec = &self.chain.spec; - let sampling_size = spec.sampling_size(spec.custody_requirement).unwrap(); - (0..sampling_size).collect() + let sampling_column_count = self + .chain + .data_availability_checker + .get_sampling_column_count() as u64; + (0..sampling_column_count).collect() }); - let verified_columns = sidecars + let verified_columns = generate_data_column_sidecars_from_block(block, &self.spec) .into_iter() .filter(|c| custody_columns.contains(&c.index)) .map(|sidecar| { @@ -3098,10 +3090,12 @@ where .collect::, _>>() .unwrap(); - self.chain - .process_gossip_data_columns(verified_columns, || Ok(())) - .await - .unwrap(); + if !verified_columns.is_empty() { + self.chain + .process_gossip_data_columns(verified_columns, || Ok(())) + .await + .unwrap(); + } } else { for (i, (kzg_proof, blob)) in proofs.into_iter().zip(blobs).enumerate() { let sidecar = @@ -3300,10 +3294,59 @@ pub fn generate_rand_block_and_data_columns( SignedBeaconBlock>, DataColumnSidecarList, ) { - let kzg = get_kzg(spec); - let (block, blobs) = generate_rand_block_and_blobs(fork_name, num_blobs, rng, spec); - let blob_refs = blobs.iter().map(|b| &b.blob).collect::>(); - let data_columns = blobs_to_data_column_sidecars(&blob_refs, &block, &kzg, spec).unwrap(); - + let (block, _blobs) = generate_rand_block_and_blobs(fork_name, num_blobs, rng, spec); + let data_columns = generate_data_column_sidecars_from_block(&block, spec); (block, data_columns) } + +/// Generate data column sidecars from pre-computed cells and proofs. +fn generate_data_column_sidecars_from_block( + block: &SignedBeaconBlock, + spec: &ChainSpec, +) -> DataColumnSidecarList { + let kzg_commitments = block.message().body().blob_kzg_commitments().unwrap(); + if kzg_commitments.is_empty() { + return vec![]; + } + + let kzg_commitments_inclusion_proof = block + .message() + .body() + .kzg_commitments_merkle_proof() + .unwrap(); + let signed_block_header = block.signed_block_header(); + + // load the precomputed column sidecar to avoid computing them for every block in the tests. + let template_data_columns = RuntimeVariableList::>::from_ssz_bytes( + TEST_DATA_COLUMN_SIDECARS_SSZ, + spec.number_of_columns as usize, + ) + .unwrap(); + + let (cells, proofs) = template_data_columns + .into_iter() + .map(|sidecar| { + let DataColumnSidecar { + column, kzg_proofs, .. + } = sidecar; + // There's only one cell per column for a single blob + let cell_bytes: Vec = column.into_iter().next().unwrap().into(); + let kzg_cell = cell_bytes.try_into().unwrap(); + let kzg_proof = kzg_proofs.into_iter().next().unwrap(); + (kzg_cell, kzg_proof) + }) + .collect::<(Vec<_>, Vec<_>)>(); + + // Repeat the cells and proofs for every blob + let blob_cells_and_proofs_vec = + vec![(cells.try_into().unwrap(), proofs.try_into().unwrap()); kzg_commitments.len()]; + + build_data_column_sidecars( + kzg_commitments.clone(), + kzg_commitments_inclusion_proof, + signed_block_header, + blob_cells_and_proofs_vec, + spec, + ) + .unwrap() +} diff --git a/beacon_node/beacon_chain/src/test_utils/fixtures/test_data_column_sidecars.ssz b/beacon_node/beacon_chain/src/test_utils/fixtures/test_data_column_sidecars.ssz new file mode 100644 index 0000000000000000000000000000000000000000..112dd43b0474b1d1263d951429f7865f24bf1033 GIT binary patch literal 320512 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

    +LNuEGP|yFMT_Kz&dT2zpi3;|j-N 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�#1xBpIjtg|4A)qc{TJd=@{c++dnU&aO%$ttolQ1_VD1GZw?>$@@ zf{?G49sQv+j7T|&q*gyu*TF1Ewq)*6>M011FAeumd5&&r_~a(CS;=1?20ACHJU6_n zx}a|hj80izrdw3Ysn+a%FF+N|@FmMmuOm8 zc9ir*p-({Ha;8a2#6$(vJN^I?6Wiqvr%%6**0mRDwe8>pjt}ZH0e*Gz0;%f`wF z*Jo#fN@Bwxf5%jcG&cBV|%%M-t&M#%f* z^j9>~R|}r>*N|oLDt41rST6JUf(np&P0DDA-Evk9EEmw$e0Zj7(1}+#o~h~Derr|l z?r({9sw&J9`o~|qI(860vv`on1z?kDGj43o<_E($N_n+ZZys_;PpLSD1bat{2w0k) z$n!m*n7IWVZ-QlxAdqoX(F&!2g-k$DvRQM}6^ZDSD0D3I13a3#Rw2XK6jiRfVT@)VN! z(PK|!K3DfBu_!=QDi~I>Q!5i@(Zu0*`!`}=e)D=a0lu2Q?~1-3U*ZG}3g?n7V%Xq1 z^Qp~}O-*7DURHKZ!H!GRo#`wT%}KKLAA_zWHOtKBtdeY}8Z(2p#MQ2~6z;;Tu0EV^ ze^p7a7EyL(@k*b6|xunLo0Bk3K=u7Yd#lpOFriKYl6&0$jn%u zEo=6F!~M?pq*621 zXxbT5>xDXSv!uCie-k1eNl@3BQ`U^8L@lE$wmMGv%tMyr%t4_-CltyMd-QL(pGaG1 zFH|e-INd}@GPD3SLJO3J)YFJ%r$)QI#yATn38&W=Y}#&L;~m&u>dIOeD7iORmjY)Q z#<69zr{X;fJF&9KCzBeATD49o(eO2fubF>X#0_OwiWS~?yYnx}V%rDILst>wHXetm z-5TqPt8nQS#6>CWaTcCPqyhF*N{#GwtQ;qff3o+sYzYg9%sA$H8as%9?+ereW+dS*Fc(ng68P3J=7XL(#_eiFnCRV}m4C2H|U`q(O7xG>~zR&N~;F zTPYgQXatPZ<=4__e@Jx?8w13 z(u?Lzx?~hfgd%+2pQ8Dv;#=1=m^IjcH`pv zJj35Kd*GScCsYSU-i*?`fS$tVI`RnCGD>Y{&xHrgiX@GZB2P zxiu#@Oe4+jH5|OJu(;%xjOsXO4^3771yFOM!w>J|H{Gg=!DB4 zh%OCYNR?ctM_dhNobs)nOxVQhRIlLjz(m!C}lv_b{)HsT?5wEKHb)gz?{)&?o; z$L*Xm!7uZ$Jh}li^&_0l_H~(s)$R6*c5m)SQ=$t%_35L2MXRXjLrd|QQk+1%ktm-l zwUZ<(k@hqRgSR&+!baelT<4p9i5(`v4;sYrr-}}JLIKb2)KJYxseV|b?>v556$Qbj z460jPXg`1~-c%A>q%1FpDi7K-y%Y7}#2%5fHf|~@mZMODMx5z}P6(R9#(fBcaW@N- zw_g>2=fFB14IM$yG+s@W`7sYSU&2Nr428t4h~a63BxkW%-gk331pEPxrZ(O;oSQTZ z_1<)mTIY}VI4VzHL|*xhsr?))Nm><3A^Mvmv{wCN0TK3)%mSodxEw8tBfi4dNP*)S zwKt#^aVG-Cv-D9RW}9CGe~x3WDW9{Qm-w~}85mR;DH(wC@)iz$a25;J-NB{;j8A2- zBaY^>@>jit4!$xGr49jP@**s_hQ-RYAN*u1et$WffH^c#^9B9An~}4Vg{MJmUZ#Gg z8Ls1D1i9`UmaggWwfwCpGP+*347XvKtEW(6^ooiLXJ(YH4 z586WKOSdJ(=@6rXqjTwiBVItZ&DVKc; zHCT(AH*;))^t>QIJmY%FC(Rp<^}x3AZ4Bz;fCoHDcQ?{i5wrmFSRm+_YI02=!)GFv z0VkZruymt}i=~Ga-qS!Az;ZA-wJ-@3O&OblD?g%XfL>1lk{%bzM#+lGMYD%|yRO0j zK(o9G2ZBqBr1+Z*CbA+yC13V_qeGvG6NBNi-1mx7)pYuq%_XZ_DI)v~A zk}hOksCxG)+U!Gp1X218#0+G;gHJnF)ShLOnAGWFV|;$5!kHgAL8e`!%@j9ueS$o` zh50W53yV8T^m;y}I7a~WwdxK4Et>~+v*Y{Tqe!>YB}((*=GNz;l69M)rvgxQMerh% zj?O!vtnp8tA%9F0+Yz3>fHIxy3 zKXw`&yWkZ1DBx1&Wyz|XBu?{42Ln3F;VsxFbQX9jTcnu7xRS2`g9?Zf54fanr)#}3 z|Ek~n`eY~Zs)6r_MhUu5HHxdD<-WF+t%lNqvEWP&r%L5*FJW7ti=RD(SwPCzMOL%n z#Qe;RwX-K$_Hg(#3W68V(19+xE0zb&5rOu_A|RpcZUsm&h?^K}*R}A{Nn2_Vs8#8w zNl-@O3MHkw1EU;H12Gwz$AnS3UWJ^1q^F5fP+pk)0dvBNKCrT)2kl>?+NOb5GI$Td zs}K1}QOILlBwnnFUoc_F-oVtY>~-JT8_3dg%%NKd;gTy0j%s|>c?{3%#QWMDWOeSk zXI5uX{8Fq7&jXpCar1;V!nto$au^j`%*~1iIIxF5;Sh;Jb1!4}!LE6N^14G}x?RA( z7xr*bE;R}o^Tj?-!6 z0xnmj&eYm*8X%@W4cs zkc=#OcJpXG1~b#<9kmrJ|DfzoTs`cP7b#$niGfq>XwYnad_!)(bzQQm(l0@Q;IbuB z`G%vAJaQ7;&zpbYpd+@d1IGOd^@(yHHJE3jOZ?@*l&^w_)v^rE-}m-u>^AgaxvX^1 zVDwnl{)NJ{*>5h{$UAL#e>VmzzHL0GFjYfgJl3_^-b13%5Rpm@dSRIDOq3;k$WghswE zP-VXCTCOaE@7M=6m0&k;8#0wi;pI_5WuJOr%7%UlS$E>UepX8*Z$p$P#6SQ-41<0x z5HcWbC^b+DAragZm_>zd>@GO*W`SdhmAfOxjABhz^txq#YpB35WJ#o80%FIKqUQSyYx0R@LjR8r0pvm%e6WcZYZ1+s(_2uk=b>m3sND^~LCeXc}UV~u~pYm1-4?Xf+%HU(5M z|7c!%^V(>{ofCoc%-6$Q)3psw(eWBs(WL=)Y`((+J&C*96pwf54L%HAK|?(!SQ@HG z7Y0+bb$TBuvLcrj%X4!~G}QV9MVP>DT~%hd+9@%N zx@*P4g#2#}S7d|jeNCE_U1ArS9(#CJyy;E_(v18O*eIY5gho|D=?AYk5-(VTaY)Sp zNNKQ+XYqf-{m%`j4!l{ncQ~Ada69e**I>`9QrRf`#Jaza{>goXXZgs>MU4OcjD7L% zI~t##fjMeLbF~&;pHI@}LSkfR?7j{;s%~K7{#8<6c!tDg3g9eq=OcaNcRt9Seg7qn zg*fXwt0PX6cjtOH4LspD-=$m-VA{iiXf#R;x6J`XrklB`7SBU(&34=DVB+ zVSf+x1?Z>gM7!vAAnIC`;ZlUQr<9%TP%_i`*4ahsv%YQrMn3`w zNupyis@y=m;~i0T>~MrN{HyahOW|CpsHaMm++^{e^_NpPk#O2xE2qKo0am6s}xHlsC=?_3@Z7bpvZsHnIzq5d#W$=;e7V zZ9Q(0g^n4dGH6w^XsV_G6_>js9Pu<(xGX4Qi$l=tuoZPEs>tg;=2Z!5EcWqoVHi!_ z#6)#wB|0vD9Ig`z4JJl5`nU6qJ4DG?gq^zX^O%<9MX1bK|IVR;PuynWV%nIU$OKNZ zDC^(|Q^@Ld7bWx9>(Q<8~3ytrYFZ|IgO!yKA ztj#HKjfO+;;|LMinLKdJpVeBDXWV6x(^PIcn05johzxyah2t3K4Yvz5qV*}S1NL(wl^LoAk_ z%Ftxdo)zHs-X!Z-pp`f$DvQ<>tou8o|{<3W5}$hKzAIP>8i;zEYRfX z;@_g1;m(=evQt75S#e3gg@yMkayvDjg}juqrnXi_pv3Sj{`3J&ZC7MXSAxxgtmeVQ z=(;xlwhZ0Hch~SuUuxsX#auqHH#zFxYG#_mUmRRm93B_+P;gvUx?mGhz+z4!`8jR_ z5|_b63P{itcQ&6W(Ev1Ug@uf|vGhXuc%2_fA8*GX>gc_ZlfxoGO>kjHe&m+Df2Y$& zrOIQEYUe249rzHu$(24OijBH9KeL$4HQ=4KkuB2Ov=n%+1y0lwGE0%X!y9wD2xscGol>@#hPJ~gWzjZ-ZOo6&QBpuwpv3=~$ zFe{IeVdjp@E>g^tXDEs!noe-!6TfpXhc)obfd=OO&FddmwQDr&W} z3SuO;dX{1x4cYD zKg1it!rF$Em){gt-B!aZ1}7ZesZu@z@;EVObU@)XEK$Mlq7BRER;GvypG*+>4zo*g z?wE2ofE}Xhbl3{g&L~yn?n%g*5Ppo_SC-t6ZI$o>Fj{hyLM`nJ#%Ew|Ayk)%bEkYO z$|T|Rlwgj|!zmLEN8vV93Ib(nrw(A!+pNzb*{aU6K*!BRI=s}JL>5xA?woA0l z^xUJskMff#BN_p4iFI&sb8^OanbyOiLa%qQZ}1KOepIGl4B!|0WYAuS8gMj;pO@c0 z?G`*lnoly06%}Qwnlqa)mYE&Z3@yQ%%1%Gg_#?pNGr!YYy@E z5g~Q}G43BN;64uhsEFpEE*;2AJ@=MjSaAa!g}d2^XVERC*QQ|(aL?=!y4z6PdKnp%owh**8`EISpsOpg;$@)8p!_&&9;Hz-xo>&%=aFM2T{B- zLP&&ScjeX^f)812h6fDQ;WC7>?DgC_m8A`Y%Zfqo(N7ExvM7!Zwn^oppi6E*=hc?Q z{5yHM1Ww=O-@#SVbG6>dnRWy+R7=4QkS9b1`H?i#lM_5;)aGxo-KAQrby>c11vz0P zvnEN*8j3<+RsL%W4klNoHlLUDDF7o!a}_U%*f$q#aGjvOTK#d;gnNm)25kvTp9fOo z&6{^Q(1WBVQ11qPBQ52+0%~QbZ{E-{L0?h;t+<+61sMSHBpBc)FwO1hk)`%vIi6J?DhN) zbr$Vfk{9DAl>qEt#pz*Tc)mpKywisSh|R~Sp%W5;g{-3RW#!@$;z(gC_!nmnK6FD$ zz!P3vVmDH%kEa^vMGQ{tc|qL{UHs{Ik}iiy!V9?$Ykgl;q0jxZb?yw!Xu8CagmB|R z(jp>ExIvSkr3*RO$5pUdh!eOv96sQvQ(5Q4Xw1s-$}Q`tM}93U_Cg8eqp0-%1wt zBu})DX1i=UQO-_FrpKR8D=$+uEXG(A!{XQ}e8iqCqr8b6Q0hS^dN8xF3qs5DYeb}59EdE($JmQyx{m>Z~BmIuHg~EbCBxI zXgHclT~W58!+Y0b9v`Um-JbBaa;HUR3K&f2mv6H8B2V1?Wv9O`IIusPsA!;`+y-Fz z&#BsgkICxY;*^-h*^}hE`7K$p0o>tT-Z10%Z*a$cRUc z(NSzX3CQw>zWlN&m;@t7mz*4jVe*Z>mX|AS@hwFv9ViCQ^$@(|YZS-6!%ftNl9R?U zkR<&lIEsBaRBIm&!miU>;M@uFuvw@RzHp1rFIyXg48va}(D%yr2^t8;G^$pbIE=#F zlMi=^nA=$JtZR-9B1s_PLLi{?v|w*L6Dx3p}H_oh;Ol@-V4+Z9>*0d*A3c% z-}zM{WJo_ta1@a7jOkuwjN2)heOg0_B1wf#2}&^}6qnRfCrO%%)f<_@pZbFw;M&Dh zI4*!xH(?9+n4|!)B#8@SeFvxTN-TDGx*4qv?j>H3xYB}laY%IvpU6lkz22d^i!EXm zz$t)z@a(zAL}=k=ix=AH#G2v_3f^cQt_ByrcfvZ!K|D_3`++;bB#&{W+5fhq!D0OdFYbRUg|6qZhd84qBKBgtdp=fJ}@Q}WR`TmDva zy(NR{7Q76&=WVbAhrBfnV){NkRV-u|$%wbx>B_I8AMwLO zW9i?!g=|%gvj37l6v4ohrmj@CVtU1b$obrNVsB(a@^^UOCWuCIsMCj;_)Mi03+SyG z*m+6e7m)o}q8xfO16Jj4r~!-tS7e;~$PHb|o%{nnA!|T~IjWANsRkM0Dqa;I8Kgj$ z*!TWJ-K3e#NsMHk(haHnWy)KoF>K1AaVJEF0fFd&ry^(0h!tLtge?xdx^H+Xj35fG zK6$3`*IecdR7r3y*lJ+l%uNumeEaSvTgqal7>;iH{AgZfWMYzB*iSO5|IlogTYAPj zRjhr2n6{m!gKns;7cTZL7|&4yL$K4r7l?T(V|=5@j$&;C-TiPzKNAdlJniB^1IXK- z!XVwSl3Hl&)uz}jS>P{5RcHv%pkvM40ap>!G<81N-G?^Xz}ZlMljepS=Wv_>gyO&K zn}x|!zydg_lvsksOSCtZ#S`ZXJT9EW`?yt__JrVYASJ&RHuUW8W=?2!zLFbsu1y|bxE%VHZE#A^7} z{4#$rRSKG60C$7#xF zuWK<2NY17t+d}z_>291H4kt~LX0@Gf0xlM#3|2`OsjZVEy;3T$JUU|@X*r*+INrM4 zBwL0x#{qEcE?S{OLo=X$jIv?T4SqK!3pIxLcv@hM)TX>-bN>MCL~OQ^@T7vtmT%CT z2j48NLW_`tV4i=&atJ8@t@Lcdv*G@*h_(&bCvgnh-W06Xh|6GmnGr#x_7z>ABtkrz z|B{t94Ea5i1vFK%uO?iVuk8J-V}{0V2k!@``uz~Jvgeo-`s14hZWV_{WoE0k2Tj|| z>S9n>fz)U!v?v+teOc3C_s1%NP{XuiffChVEk|0*;Z1Fks>H7@sHp{3W)n?z;KeD{ zN5b-0SZ^u`6E6{8Q^e0qZpwp#0zvF>-U%86!PmLkOttskLEI3js*xQ*z_gCKaT*Nv z2WVu6EPOi(Tf+)AXAn=Re&Ta@%MYE4dejwd=M=~T@!hk`F_QYX{Tny+DI1-mwKA0| z^VHsyCSYu`vT!VCq-AVR#U7}u-~*Vhm#E)dgyB`-PY0wp$Ds*j<{cRSt6wGoDltj^ zU(IPD6eL@>q_6~-sgmMIh%hyK&;54zrk5H9>Z4U7&{DDb7pVw+B&(82<5###M+V&f z2v}THZZ$Q%d3G)%3N6YF1$nY!QC7J$t)}!<-q8E>{M->sY${hf&Sk?<^TMLQH4=51 z64)55ZN?z&QB11e>mSN~bZG``RZ#2|D`AB=WTn!uxY)Wi_>ucc7aM;x7*6`U&KCpUxOHmx^`_sLQI$ zgnJ$R6X#ba>vm#*ZZxk`uzA3>f^u zfgo{M8}dG(CX~p|D=fml?}k&_bvA++5kt)NE@(PfwYiY5SXHxt0$<#QlXYb z`{-85*sOus&V{r3o?vOs2cVJWU2DFDZ9JEhA_Ertkz3F!^AautR`i10My+?T4m(nVGLrs0yG&8VHNAy!>3_PA)W^r z9^>rT@K~rOFmXRiV#t2hg-5BK!C76JVms2x)v9tCQL(L7SG#qPjayzNY0jlAwT9D1 z{DqU9MJj;)0tSITHgP_*l1pZCXexl_K9-M(1ora7DuWQ0I&V7 z(N6(CVQBWAtE`r_P8P<8h_O7rCS4?I-~>yVX8(2pK8GTdT`>+8?$H6@|AnR12j35) zO-8{E1t{*tu!f_sA~?vRPrxyf3|*l)R__TyCpnmK*jW+TV{e*a-&YEXinshFSws805VF08oBr<@)JSC@fj&OUFggLws;NcGkI0?kcx%C|l1r!oqK z7^0x43q+w%WY8Ed?|WaEWnE(_%FXN(>OpW@-&{;B21IC<>rd6P?ndwfpnKXIJgBOs zXTXqR5aVDDTUpG3Y@&fFIC1l5e@_^UY@+E?Ss?*-vfDQ$MVj3K1327>tkXB{!1BirX4h9+dKDubRj_VC#J*t|AzaY8xDsEzBEzPQcmR4 z=V-9?T3jwhO^yy6Oql8Oxw@@X``@KVL+>GDH}q=^o$YA`>b1HjOUU|g|HCWYZX6y1 zd7PcCbiI+i41%QS3vOdj8pC%6@suawz~RgmAj6h|8#mA8+JzS0i7>E0_%}1D>f)?I zDCns%5HMyJkF;}nzb|A_S(gKJff_Wubfnw4G6aMICf+p(65i8Pt1zd@C?=jG@0M)^ z5}HJI*7-Zqn9~t9a_Fxy(4E;h$oQ+3`kQV9TyWGPEz))~$v#hi&YUtDm-?=&Orc;w*D#bGfxaSbZsK{9qiY=8_iuiWC~V z>M@w>;Gnxxc4J}5<~3_)8}CEA6Bt6sW59A>Ur!ct70yYvV3p7d*CQjbyqXHCQ%j%k z%xU!RrSHZkivvk1i<}&k@-HHX@-)-$GN)&a?KQab!5BqNQf9TeSTr1pHeWrE3FEuM zpI!7LWd^^xqKZW4L+=p^kcQ5@Ryp5tdn8YIa zSD%L%V8>ZAGR^;izgZVt_%O;YZ!6*y>ewf~@c6@DrYILHmU})q>LRr)CqVFARs$G|M)|2FRbW@S8ynGu41CLcU#Z2y`U7z8%!5 zo!&fOfE23zq8xw`PRTguU@fxRx?BO>-XhnOp-@E+0)PF93Ous@vo^TnCuq)Rp2pyY z&v4QUaBfWtK15XSLbX%#Ljn^8o?D?tQ`BWtSIRMa%m9X78okBH^hQH^Y|h=YuVq%K z;vs~}x<D3lgLQA|L8NZzO;Rm5w znzeP@ECjag9;Z^gG(Mu(QPdAMc&^=F57N+So1(vAFO_PKHgC1yi-#3PG4QXI0iSzzT(bp0R0>K$YgK)91Ir`ABybHVbnfd@kI zm17WXuZiCefCWX}kP5V=r=(TE&~swUVT&vec+2Be6u%}xiPhiDwJ$iIUMS*d3z1B3 zEXmy(JS^5~3X0)SLkG+bRW$j$3SGdP9!`TMj$jyJU-U_>`Zs$tNB}8G@^?uswIVhzhV+ zXFoV%zy)8Gtu#PWv*YZAj)w~vs{hgp&krA{lPUg%9uYwhE|djgUkB0l56ia z^Td0g4pYtG>EsUR^hVBaIEeg;1CmmyX}!nqnFbM#POo%*)%|t5prBLRrR^uL7;B$b z-RGM)a*c`p;=nhv-r>L0j1QyJ27{j0!xw~T4;<;Qj(B}E4q-3w?`5ubd}pE#fq=?a z@FvVvcME-p(x8<1tg;z%0u{)w;j+dUr}#}ES+#ncawt?q5HpJf7c+ysGZO9X*A2S4 zd(*oMCv(xE<<1??3`qs&aWd090iY)i)|;MSyua#P1}K0X{4dOwlN^51Q_!2$xOru~ zcHtMNf8{v-kjvtt>+wv@68ocBz%X;Y+W5RVm6EGF%Z*1=wuccGQj}AQeKjVX=#eG+ zT!pc0G)gMfQmm_d*?>>}D4X+-4)Rwul5fz2;^Jcs&TUw5>ilJvV)`nsyo#cdV_AOHB{u&0e=IYqb#I*5mmc*s2X;^ z_XE9>3@JbiiqjfmPPc{|W%4ll0NFc8ww`(?Cu8h+!1HX8BYEPb=19^ay9p}I`Ys1_ zvtR9CYcevGhGSeW{!_pu`YJ_NyAtb^mu6O#Jax)cjs}nrA$y5`$Az=_-k<2WJ^kGD z-opbL*?GnCdt|N`_Fo&0STvBac%t)1LTj#UtynKPj*b z+oH$sJ!q*+s%_Qg|AzaY8x93{(CghMAJc#K8P5+l_}#n6nccmC`x`UL|LOxZ7}1VZ z24Jk$Q>yXs+|${)h4n7eYX`oy{=n3JcF38ta9IvdAPB}z_nlt2ljThlfwOkSMtp!O5SCdtCno*MO1BQ&;2OZsRe0-uh{kp0J0!IT3(wahh7looxu zMS^NgD4t7`B#|Wf*_4Lg<}`=Q?z`<)s~?ORIbLGG!A@l)cf$mIH?xjXh>Y`64>{nY z%Mx`qQ;R3W1Cm=Ocnx=#k6|~uv*hSCtxw7t!%R!B%W74KIAhf#xY*1`9D=QLGipig z=akCvql~jn;C7%e-Ep|ev)_y*n^FF0p$#|fK^Fa743ca+Tv%n?yUuy@YQn8x`Z5%& zaq9EDGDlG~0K=vJ?p5L>@{V9a<;4DT2S4acdwX<_<8;>vfsm0N);RIO@nJR+ews{M=r{fQE9}SDHg@}sbRV#8tXlqH=Ljs#9 zI~ouewPI}xrr5t5!=0MhUX#^BfjgF2pm5aY@J=FIV$oj0e3joI^pWEpdcY3_=w;o@ z%KMJn^u*)_OnO?K2kW`U%*~1GoNp6*(y9)9yPiE{fK0=O#m1Itrrw(19386#klrlR z;eWZeIX#?)hDmSv2g+#~!}fB7(6T(h7GuNz3jCDKu{<3gKH7}}=IocsTPA2Y?p_c6 zoAb+2KODGmwIrd(*@y<-CPuJ`+b+~45zA@I(MhvnRa{(wuM5`}!q=U;KZTM|e)nf$@e6dlGvxndU(7fJ-ee>vSCJHN>{W7mPHn{*zMOpq+95s zm)YXRUW?O+3vhFhLN1g<5bsC8hmeK(LpAKytLkuSDqw_7f>8&hCiJJ;s=e0Y2oE>O z6bqF==k#Jf&RdBkLE1&U6o8`|1zrD%>SgyR7J^*TTIr#FFfV?DgfHDdo-K97LTMl$?Rafkm6o`)HGIMWD91?dQ5>WEMV&7Gqsy z6RWLu+sevEuL#<`v9u#MYp=4mt8RYh$ow;wARYW|t-9b5L|gO4y!c}Fqu<~UD*M1T zs#tC_(HzYdiz8qbrW9H9m@XVzjoncS5fOvUi%wC)pKD{b=*Lu}oqaYZZ*0mwE@tS7 zdN2QtTEo=x`+SVODCeMqFY=={A-#qB6vA}eTLA|dU~2znh|)(a=SY<=y<{d%U4}~c zg(TIeBUmAYX4x99`>&C70`Cjs_7q_C(`G?8JMyaQVxE2BLb(A?&5q&lWRY6(_8z*K2EB5Y_0yIL!j4^N;ula&`G&kFZ0-sGvEmuxO7l$i= zOAxb3xTNzC2$9@9ND?F=m=zCwRzVFn1VyiO@(sXG>`%BY?1c zFE6SEX1mCvmgsEh+j{gtcgVFqsx-`@Suu*iTrUeqoV%7Zg!i|q^_xAa>IuVbGVr); zcXT44C)S@#w4!a34`cbLKc?}co+GVZ5UUZb`On+uF2`%ghKP5NSTN?uUwdhJnTZ|R zJMw+Q5N=VcfiKKpVP0y)2$+u!{|c-|{)Ko|BEb$y9_U=X+fjCr^=VNea!AeIX2j%j z0%1@+iIh$OOdL-J0zN1RNNbSIDl$53~3AQI~5XG_|d!{lKREzGJZYkH7=B=4~yA&NXw?n=+5 zq4f9bdr2gt^LPE~*}QcgehF$H#cD0qwl5#D(B{BWjBHn~tkxUcLfInrO3QL&haDBd z1cAxdos_dcfz??k+7hs;;o7`v2Fo^f9{Br-kd_U zMT2^xNLD;3$s18ju#31=x%xHcQ;v5#Uz`DuV%2-D9gl@aiRvWu7k_fP9=<;ds8s*Z znJnx{(O`Ly+7mYK0x<#GGdmk=Jh;KRzZ#4p2chO8yXlSTt56@0oiTp;wP9XRdG;pI zgM5NoGqa@31ECJE#3DI4FIpx%6n7JsoxbtsaIK#uNtjL7@etVD9&SQyW%d6|8cWT$ zL5|%>JrBm3Gy5oaGS<;@-hE8Jh`3ajk!&e`{MY+%fyP7zOiD!Gf1|GX5Lg$n9&}E5 z=Um|>p1@q!&6YGymAX%^x~h=Yl!UP{4|y}Ke>}R@NmqJxmu~XuYQY>W z15WJjrE%BbMyRjof0EPoDuKEmQ{#q7J=X*~x@qwoU~z+A5^KRuTffmGx;`MoBLpzi z&@(u0dV6T%=?kPzxX$?|b^6!<*xX^?qfRGl0@&I#2X-*%Rls*KKU@(qEu|OS1_cD@^NtNt zN3_`G?h!XvsY}4*&`9J(;b>ZZzzgCJfLykFGYD=E$cZVJ)3D7L?3;Twf>%v<^6 zU0cJYpSh5uKxAZbkX7Wz)nR3s>PMXUXnuc5={9J9pfM4V_>o@H&vUk0|L{_)&}Ty) ze|XH|Q~MT5Tc%ht(I*G?7N_)UJgfg0he)7vyfKQ{edTY?4UoX!M)@uRcy6a z298Cfmir3St<`4F9y}Ui#eq^E6QBRkE0tHa3yr8B4^h8ODR>yW5Es)7M1&b2(WCfz zAuHN#Ipts^x=0KUrPAI=Vq{1ca?KCvfn6op{bU$%U#6So4?5*|A5*gh#-cPJCwckd z{-J1l3B(lHP8*V;s|(6H@t=#sVF4IaM`zGJW&4kgYP?jzHS)tamXD)!L=C5v6JlV( z9f@8LZ04rGGd|%GuDw1BC#c;iern&qWuo|)XW;{~$0qi1jXvtNowALM1U%w2G|!{= z=8a!uL;3)3@Am_!N<={pC}p3C-cDi#9a#WzLk}G_xunvKvdf!J6z+D#gLn=Pd`IuE zR-mQ@MyFV+&I*BmoMd_QHnA+yBNWNQZ>hC6HN5(U_ib;*F^9TWyY?kUN-Ay6xY~l> zzTa}+e3}6ODLFjN1LmL9@N#7&%}FY6=%LEo2XM)K=i3)E!>{2hWGK~em0y4!UQ@3V z8k+ao*8>oWOAbU?-69ssaEd+$3&mfh;Ox0IaQVGTn>t1R- z>15DWB_7LV<-?4t!QmWpLS7$DNGJD5L!Iz^>BMsn$GAM!)&MDG{;4l_8Nm>^QQ>~$ zKGU=JK9-REGv?JpO7fX)g{(OvaeA{oi0v&Nq?C#_$BI61@8!?1)ona-F)=~303QcV zXTIr&mx?S47VNcU-)-U7VFulcRDNtBL}7~!z^G|_akd~vWk`%9Zs%7U_ZR7SMq0&?L=iB$s z^ufgj$cDLs1QQdxrjvqSMD`o%kSTI5QdiT0tswDmut=t>ziwtOjqzM{^5SY?Tu}9_ zyvdmyt^qy_YmmpG4ym1mToJlaK-|;?=d){vrKgyiCl&}`lPm*$1>-7AKqx1W3`NS{ zZ&qY*=cvr*Fm25(WX4+219<^zKky(+e-MDo_67D8w z7Yp~)3{V#yN_%ibCAuG)!TzCU6tC|^xIrYj!sE%5RthF`tA+u}-(vLAbr7#hj_=FX zVK4T)9&p+7o=p4l`1F4)cZBk{-&rkvKYuOAfx@)93ZNl}8>z)X6u4Ys{z@({VPZ3ee{yfDXz z6-mr1Y0=xt@!#PgiJT6=PY;p4SO2&-wsA7cL3l}}5G$Odqvz9`&xNvA#oG9rFg7JS zKdJJG;sXeHxdED$^a|G&9hwDA9uortBVR~vclO(_WXwDe;Re@?Q?y_g^r)z;Chi<6 zC^Z6bWNgT*yX%*F#jbH+OYA3)QG2(U+!4q05OaUwM`IMavw)rNi^q8aPJv-myt;l3 zH6D+W1pc?@aBaZSYYRO4MqZ_e9884zIgfUDYxMEG!&-OCPiAUs5a)Vq=1WXuzIgl{ zBN@k>TK4}p-2dEgLPr4E<#F8>QysJ<6(BuXo>IQjrF<%r?F@NKf(3l5az2BP`U`8) zPgsLu`6ev_;iFZY0riW@6GiW6d@kcN2CP5w(A!_syAWw~2SWqG$E{Z>+Aw$M)7}01 zA{NZVMr~uqp|T{sehH8I28(6Nalz>~?QF+}sWHh)07OGhOa#>uiS(3R>BmOgFJWNk zW1n}tK4D-029zF*MR^kjhNQmyo{!nfp&dAGbJr$1hgpd z>SsM8{-{&SqgJYYg^8ab;G#z^kASSb!9zkME zy%qwue--tkG1s5x;P=t9+PBRX%qBEsj`?Rct8;>@&7tImz;TuoI-Rp$X!HiWA@rxr z&s52riVs!1kZ^|BXhP48kmk*wO6)_Y0hNh}N|1gUP+MYQgTx+_Y<9Z))6LIJWfJVw zO*#wDc_}^@&pX_i#G6yXS|UOqI1P}N9x)~DP6u9;ROmu4(SN88QhWd>nkw9X!;avf zcSP*Uw>8v3oA3ufM(4(YK--615gOS%Jd_)%=c1nKH>J0q8<%Gnd)xsGhWy_Tq1t_X z1j!COHEO1>;4c*wE)^ z0I^EIEZhaR6)AQdb_rF3t%j;tauM5-S{o%s6jo*NJ$^Kk%fvs?6LrS=_a~Uz)y90s|j?jss|)r z+emCTL&|(V;&sG+xbe5$lpdr}RCvBLispI{03+6q#-CeD*TOFny3*vWZCQecT?e_;Dzjj~~S-oU62&25CB?&)5sqi*~kczJdHOVKfD3&e# zfO{u9Idae1Om@F@T{jGl5Qf>D>aLsT>dKrXE^VZS0Ygd^$fbQzZK}e%4g6c5Ht`Y7 z9IxzN;2PsXtSK`FP=N~~{dqaeP^ridI{LNNS4!gKYT)O*ngdw3vqKbt1U4;S)4-`T zC|gF3mG3Yru$pA-XDoscB0578vu&u(k5M*48hb=ZkccML4>+BCau!hceOsLvgngaE zMv42HWgiC&g>*w2la(F7`X~aX?RZET2SJEDAIWsHd+`<(+`^QB(PM zO>*i>Aq+ZLl?_*kH~f9Ygr9DlU#9c8?fD4hJj|8AAA+}u4}Za3ReMtg8BHXF1=>M3d+5n6eV?N1A#t+vmIEttBh7#7{1%Gf45;kM0|z5=0F{ zD_J~CDg}}M6#EvMoLu~0aru2`?Z{38pHFEoKsY%yI1%iiUSN>Q^(X23< zI{!8BO75YeCzuk%r-0zkYtRW5b zN0hXYXZqgX|AfoB(=ZUF$kZ8B&RtBy8a+EqKN)EMWaa>KGN(darbb<`ffjWv_}qoZ zllqFthKqVs+QM+ElQtLPl}U{_LjdZe_lYbw9}#dm=QX1lBAyAkuhX>sj8hzK&&rW(QRRNY zX<)?v6lPKHm9?50m20DhpJdQQ%t&ywp@<*SQP0LU8b8pbk*xL$XhHL`NpSMdF9*{I z#+JJD{SwzP-O|_|Py069=qNln9FbPe6ldI7juA?<0WVRLXht zLm_ise2TM3ufj3`KT#P)C4$)yL^NSRpf$M)8fa@Ot5UJ9$zTAoi!1ovm1nazbK!An z;9C~RT&Eo&_x>SF*(AS}>X2Qug*B@2h||L6+fy`oB~Du8M~i;*(!M{9s^HtXsFrQR zb+KV~S#s4Y{?S=_fOj&hAEo6Nj1_uCC!=mX3CcTDUTQdR!25&$Jgzv~cZ;Zun6n1h ztH#4n#R8#1rgp7|D-{b6N;3Ig6vM|eQPE&Z(+xI4DWK}a5(Ab!+LJ!xcmx-PWvE)( z@r{f%*>F)C52TT&Iop%p;rK7Y({~ae1rcuhPO8Y*JN(}oE)+#T8u+j763Ug*@0L~* zb^dhnpNXn3D{>xaABC^udR&RdRs==Kbng^kI#3bRHi!Qk?tgALBw`cW>8Xv4K{xIF z^&*%RHHw_=5-!;H9@^R)_;K*z({p=4~fOxsb~^ zaZJu7Vmk*v#yJg9P;Jnq(rP!|($ZKvx_j(8%SH1mYvQj%&<7+gC*#V}1Ya$X?fO8^ zeE-atVNqF?vq>m+ZtWb}SjF{(;Fq-DSYJjo&rbq_YTZ0=zNyjFl%Ajy0_XW}kEO2I zLrMS%;jA>s9S&TS8-1MU<;4Z}RjLCx0RY)DAZUl?&@;$`#l0nE21qtqMEv;|PEqkUR+;U&RqDqMkv#vWN@rGGt{Ywb)(8n1d3(OLC4a0w zfAElDd6EFWd}Ud4M+JBeMR8C~rgHI#Ne>^jD~;i(wPo*?h(x%K@O4HJx!|iJc07LS z3;@D!X6IN4XD4Uu*Jf*x#VsHJfA$6u4aEv5WbMRBH%JNluMdqPU%|e!~Rwg1NPMSuWPWw8&X^r#RLVlxM;{wq|juc=24-YfZax zx(#UT#&w=i@B7$oBvu|%lBGLPTpIF`EI3f1@I1I`{M;RlX`(OtddB8rGjY5aXt>~L z+Y=2;ge2bE(*&iBK=d0tsN)0Hh*8PYB2^k2V7KX9=gY&SpyE;=Pe5RJJMJj`g;5z1PD}z#=%v;4bjvHP-8q6{CDOAEoX+|%|hzi z(#Y;$9;@zyAl@;)ZuEMp`^}UbwM?lZTou)FH=NGMW#(>Gxm_knsLnS%j-`A`9Dfcv zAp9TZ5fPw_x({Fmqvb?4bw(3KTz$RV$EWU7J?^J4w=g^a{#))V1~1Zq1&KcqFxv0I z7UhX;A`ueLpC;odE4 zIV#;zgWojP@CQj=!3sLPH22v}pZ94UqQI;16{Y@M2N&dxiCjUxj@krY2XKk+0U(Mn z5ikXwUy3zioZOyXN6~l8WY2i^N=FNjyr9G-TLCbn&?j%5DK151{VCP&=31^ZZLpJu zh|b1X%3Ch+A_g(?qb8-bL#)}Pdl1QwS2SFhbe;Pmly-!gYd#RGiJIC4b)$nwd}{nq z!4%fJMyKdOI6~>jfN&F@@cPM$J4~18)F$L6ILdAtt>2sB))noJuAD|;;wWdb!)nu< zj3=&>xQ6K<(WP(dah#Yo>WFAu+4tiPJU)lYE6;)z@(jW3WISS5Wpjre(M#v}kd}v& z#=HyC5`8A~0Gj7+dStV6ub@;!dzP=HyXhbCqFH-??ku<-;RR4dJ%-gS32%>czQ2o~ zBA9D)@f{zg!2tPJ_?#7BoyT$=#n@lAP!`Agr?(FlgIxzNYZ?}LzhF&9n4cTAnGuA_ zP0k|DyZmfBdNzV>csVe$QSIHpEQJ=Kppj0wXmJKJe*Huq@IXX8id(U6F*z{yq$ph;O_L1}7=61}MoS_GSH0MwND zbJX9m{agL_Cp=0I-}I3&u@Cd=%uom*5T~JT6r#qd!g07NgG-T1Et6S~og>h}Dsw7U zS7eFW*M&i$b5`E{B;!-srZv`uE`P36h4fXY+l-0lO?OLa_vLukr+MK!GkqPkK0 zoh>#dXIyA2>B$RJUE$D9SGV>uAl8vPp&J(EJKTt(py{9=x~mcP10DD@KU-iZhwd0u z8bCJK3u6+2DWc5FS1*qeV^kxhfG{AHy{h6+1xT0q_O%J=5qP;W9oYH2f{f)gUJlGkIWBaL!rzhsA=><(tOdQE4xepnZ8_aN!tm+ITv&{Zj%B&1I z1LCU}lKFsHjhPdVc}78+V);UUe#9liLET7ZPTwT>9vS>=l_8rx-AT74{{of>V9Mq< z98TW{$tayvp*AfBMv9J)EkR{XV@%3@lE0O-a0suWht6Hf!RSjR3l`gxJ7OCK>QV6d zc0N#Ns}byc|MkZ2p8&?~N`R&|@?NeuTmRk%N z2T=v#Bh@p~u)md0F_tr@c}UX7o$c<)j|$}K>061k;&m(&mGWEG8LagH$f^=mK0rMU zv*m<@L-8eg8KaJ2MVpl_lD#)UEY|0+apR_uDZzwZG0Q~&*(x4VIcM6Q%g4DicvYxm z0dQp`NxPxy>y1VfDIa#lh9MgSrl0->3%wclt;0!c!G}N2sc9^4$P(o;zdSor8_c;{ z9$=-kT6dXz5sn8a)*TLTE%M#l7~q8WITaH13dyAi10@;qoW+4kWwkfV?c0r>Y;fGh zTT)VFvEFiDYeoZ-M%Rxv!t|ux8g$21lg7f8$w8lV#aYh!8Q=LocIPTipT}s(g5?;4 zSKr+`R}i<8KPGdx`tY8Ykv(^6=9g|Tl9pF>XhyGMf~e5QF$^3Qsc(J*CX~ke12|7; z9si<4FnROtgxVG6=3s#srKio;J4Nf-{kF-pQO-vQ*vbuno{AIbMoIfM82D4Epo#_@Z3|M2*o!qYyz(zh< zWm#NJ#CbJZb?d{I8@rX)K|ACXBGkxSf+1nvxuUBLqz8nIVcp}R8@++cMi(J6Zh~W+oE(FG>X?PUX^A0sN_OB|lP6d$bUxSJ?b|O3Ci7y~9 zz-67x!o#DDI7pL4@X4&zjL~M`qt4v>tVFs$UM#^>kfj|S<$lg}9x!|DBjnknTTt6z z;*(JI)WHt*Y{jK_tsjzY?f7W~0gj9ntVEx-vGv_EFk`=Ni7W};vHYJ6x@XfpI7)ZC$e{3nL=QcqwNhk1l);1_3F<={DqzV!h~ftuGNp0LKsI_ zFux53b)W=7T(RszmxQZeWVI{&fL@+V`Cb_jHOUM5eR0&hh`qjybvY|Owquz=mDh(_ zN_HePNKHkZR*kp`t+R+Xx~mZmNv(V(s_(t_CdX3$vFEro07To?1f4o zTW-AGvKX7I+rs{&BQ;MC@Mff|8lv#3ZCBO6hKr8=k^?fDvB4p=gu!T@T_)GFdt%=R zj?0bJD9oxbc7HkQaZG`9Ons}DAntq5io9^1yKFJQcE)Ba(n!+ug62KT- zr&l6o%|YEjYg*r(;>iK|v(>5!^WQDm!fm??uC=kZ)~N%hW(GD`Y8B?vh4ixl2)-p{ z>?%#95&}0AJ)9)swY+NTcYQ4cYF6L-6kJ@Gwzv^aJTiin z_gUT{58kMdb-?us9ob-wqXBTDm2Y19_Vf#3L%ftcdf?DTqF&`qA#2dec-*0cJJ!>d z^d5d+lr)nOKLqZaaa&-G&oz^Jb4#{DP2aHitxw=9Nv>1CkXAa6gI#cr$~wP;GnrBU_t?Ty7$V#gOK>*}K@v)PS3rhXSES3}J>!CSAhfj01V^xPa-ja| z0Um?D^~AE){>Rbm)(Y~ZB&dSa#o+xc>u{3~G_EylVy`e+VAbT=c#^t?5qo#{q5P{< zA?pN~_r|Uej!Vs~_71N;zL$ndX~Zy__tJbWEbJ4q(=rWR9cM-RJi(2S5*5PlycEZ8 zkyvWS1EMva3N<@HXdy($=C6uPBpEsOO9uq@hrD?;UbT-4p)@e3|b*BOd6l(V6G z7Br>aWP+6P-`>_bL0y~TK~8T)vdu?x0GLzdG0=`n?#iGLgo zDQu}rdfa1D+j_&K)49sX@j=V;7*}(fyF()O)tbHb=gfkxw|{XVqAyxpA?m+au4N+# zXbBOt2lv_s=W5|Hv6sSnuL#j)D0!=1Agdq)w*8eP$tp%x&bFdj4=+sp0c3n%tC0d( z%?3`IvG!l=M^okm`DzBWBJ!T-?2m1#SIi!&CDOTBf^q+A?e(04>YFjH3Uvx)k*YbF z0MQY1Tk|BivdskxYhhT%jvj_WP5h~9tuHaLO8PN!=-_;>uERKNz)H?#VP7hIYX8@ z-jAg>M0KyN-W2M==cwE!^>0A|Sc)y~p&Rg`3d9gh_(C76M+`vZm72stX*09a+>zX~ zgF+LegGVrXn!agy3znJ@#|MMCxloQYg5RuKXNgk_A6*?Fwn7ovUfT;`j8k#SoUaGV zwFEB9l#WP%C2S&B+X@^m5LI!xCnddeM|kfeh#ME4e)@v*Fc2zS!`o`JPDx|=HJT(I znv3ao$F_1u5J+8_km?6B5_8&!3w*bDgvB0doF%RuHsKB;zg0l(57E?)$iyz~0CHWm zF(@QttYzHK3-LEA}?@iu@Ik1Ar6;^=_#V z-Pb(h5Svm8fT_B#_=oU(@o@QpiV$o7sIC!&s6MjkC3yC^zo=+X)c*B`kQ)o>GtxV|=neoFVRL_#lu)u8$K0{+ zKGH9vq^D{SB~fBSRTXv+w*3$oo;1!PXrCwTQTDqA4wen<0Fz75)QEg^Hy3M$_?{EY zfKi!JRA6+9P_QnFi4^>Yg(XYD4TAGC%j#YS`f}@$Hl&}y@K7=XdKH0;GW-lZ8@K!sAR-0XzsWQ`$wU{+=|z*mhc1}ykVU;hM7Q$RzNM}B>gka$@>C8)dhQe=!%&VXfLum z9IROMRt1-cum~Fix?_%+&iFkgv6W#VoDW}YL2+XSa;JGE@AALG+aBLJKr64kVCM?l zrwpX=T8<}wl+_fD6!h#kY%)}K9sAc8j^ltC>3oB$p<2B*Q0?4cPSpdOjbEL>KzMje zsD}{DHVDS?=mt$3LdU`_nIHGOC5gW6k{orlf-2^Se-F%pRXFkA!Z8kJW}MWxpV zE%}JhW4tn8VZ*^TP4%p;Da4AR009!!n>tXjGW^E`y4H;}fO3%RrkC25bwwtR%exuu zIymx*a9?d>+i+bo(u#EJqsmdPm*h_N{((7`wAx( zEu5-!8e23%*YDmtxe<108jf=#=SRo~IiUC1Z!)Y3C1IR=7DA0irY``$ruCks!+KSz z-Q7UAYb34MXr>KgvLuMTgTyhm7U|Na7|OYe`osm*$Q2q6m!!OA`}v+!+>UPe%pfNb z4)YJbYyZ7fmof8(3=Ywg!S?>)84F4?z$$<=g!<*?*VGtq*ZWO{@Ce*zOo93Sljt-n zfb1_E8c@zytYW*G>PzjO#IDEpOLvDGUX&#_Rm|$qmhGZ{-4)SfD-*f^TlD$N@xjob zmApNvw>{?TI;^L8VD2qM>2*4j zj=seP<|;H2GH4NjB~%wsU;6e!k9yBEmq|~~??b%&l98?Ohc>I+` z*bzlWe_1c@mj>yr%$oQ1httTRbHb?O(&FbTwY!%cpvM{#(Npz4$F(K`g=;5BFol3~}W8R9iGps zjEB1sgxiP=EPG5i|KD)`bHj<36a~y*hkP#Ce==NI7ezBX+tRsm+qsKX&lQ=vZH@Br z!0CR6rvo8e?w0Om&bV0FfZJ#3VEvt;kW0BXpzy^=zZ|h}z6uq1>-e#fu$0Tzeqq6-|y2W0zA<*pBC*E#_7Y&sbf zY2=U!j_xb}4$?LKgt(_-ITMcJ-ZvI9Ar?-C;$Last>Yu$T#vS51iJ>oQ%qcO{}}iM zpL=(yAH5e~Jo;N6Si4C`_y_y;;V(f0o1DM3S4coIz>`RkoU>-5JS2y@KQ5V|8p~L{ z8CT2@5Z4108r^}I-nJ8CL0ebRy$E}Nb^j9&4OH&<1gF~5hO$nwaA0xrGDRkH{ZOnW zJ30&XuD6vHWpF2T<(NDog<`Jamycbqvi4>x@4Y{aP$UJVa*x9{%zks? z{8~&x#zV9-mMSx!H$_+L?py=ro)<|z$-=kn9mC3=sc-SdC;!cw^b}q;&R*z~BC3g# z)vrQ!4zA#BltKTevQzxNnuA@bh$-N+-I)JUR*5~{4sTk2{ z-hf0~d*Hk^3L1_=P=vSv&6)+_FH%#JE*L5cu0bU~uoXZ+ZTx91?`Z+_=sDv?M=Py; z_U*DO5rqNx46ZgEzMH&r2K5Or%3!Lo)oo}un6^M2?DMh-A@Z#(Hx$jTeXquJBoATn z545#o=3uklvro3Gwf9TvsJv2{O;h%V`VC?N+qHpjfUpTW&@i1hKM;FKg3t@{{8~F7 zRPC{Qh}mM<3$Dk?{d6PJkhK6FD(v`GdAl|^?kl3(O3`w23F>fw9IRf|u$LjBT+@VZ zSrqH_Sa9?M*$;<)Vme8T6VOIHY#q7}V0AwC>r7{r|HXY0-xHMqW>ci}jLkWKxYI=} z4K_TvFiIi)cy>mQ-^lk+4x=I2+xrg5HbFhnen;KuiCkCPEV=?V4ee} zJk25*(Ez?6@Y*6&*$0o2$KzSO_$=KayhmSg?TslRjt zcwQ{f4wMz1-880lFoHOkY=-d*h&Y*X=a^b_mS}x~14#E}k>^vLwM>bevYUrip1j%A zS^d;e(FtzD{ER7r6`^VYmLiAqh|tBSTt>h14T^^wrd!9|`bR)JxUE2vikKq9_`N}v z2@Ti5bgY=(${y?i%GB*2K}7iA%@UvF>*naz`p38#)x4JJTtsc9xB`q(>PC>q6(-QB zdFw(UTep#6g1UWUT}B*7=W{D`E2PD4DF}$oG~h3K=a1;N)S&5WYIy-Pq8Fm|dHGp1 zmbkbN1k$IABk40Jr+-OwCM}E0T}Itq ztUB*|(77-sCH9&KqmCJXZGTbf)aNV4y~|d+W2r3JmkxqZ*{8gFy2Q6`9~b|V_cNRn z5-Vd)nwgo6rln`#BBz`;6CSKBvJzXFPrBjEQ|t-jlh`kTKYSyA>5VI8@0A=0UIiEw zjF+fMr9ZCykgym%xM;AB0J_mO->s}I27Z$g?dz+LC)#B7;=acFrr$MjUk_((h2CK zWbm0&`^e40)mJWh78+rjyxR-V?MX;pHGKfiFOK$zlZurdqCwVD2y8}QO?od9I!dsY zjoK~8QpF3`tDO8GrVl(|xiH=)A6Lx6Ybh*3P?Q?E5eaZ&K2?AlW;lSeywB2N5-rCj zbf-isCrb(7@KWw_o-bb3<3yDZO&KIHKKz20N^PrPS!P)TSN)mYMKHdLa_2RX^xjeo zY%LL6SPOqoKI*F_ob=qae3L=Ns97@Ebhh^fEF8c6pAr#ftR|NPKIUumY>+Gr{VqCk zt2stIuR}txDfDSiC<&v)i&)g2WWCRrX(h zC97K_BFVOrdU22nF(&r=#UQKTS?B2aC$GTTw@)0>wr|SV}v>w4f;9OmrP4#eL^x}c8JPzl9uoSzL_V&%1)i~=2%6nQe377WAgJf8% zBid}XQcnj5>ifI|cgBf$i-mZaG$^@ND=-bca8>{hmfzkJuTGHoZw(g=oV-~{hs7N; zr9Z^j>1T?r;-2XAs)8ZISrBE%#ZZKT*V9Vt3XD_WX43GB7 z$F9_E>E)Z|Y8Q2(HC=yIRERp_0m2ZoD?6*Oc}pZuT+a5>N0 zP)+XJrPp-mjcUOI@@rI8O=a?e&x;W@=oDMSi4jfGUt&tLle@)(vJ2B#zoD7k*l@rMN&zY{Jy41Bh_=tu>j@C2FD z*ObwSn&q62LVo10R#zX0){oFvszN!_lvlxKFoL(ceW{K;`uBZO$!93wsF2jEqS79PE*R5{G~aS&A5*ClS0}wQ{BXf0oAxVW8_68X`TEWV57AxOl*1;r_7;?@d zK7-oGftZh?wHjL-IHCwOi_2|&qPf$1%0~5+CbPnMD~NW9oXgjqGXs`a8e$=)zq_8` z7<&HV`5j3qXEU&bKVbf7*Lfx54ZSZfCgx68#R0;#e?hXN9$eBOj!t|rZSCWxZLTIk zW9NLIr>Zla-bO-~kyJB~Ez)vP;Dsx>p@Ne6Pc+xI^5Vu~SDa+HXy%_f=FD-`Ek)=f z?lk<-nAmKduoNyrXcdQeJMn|3u^l`6_w?fQi4In#7eVVo#EIm@RlO8MbD8RKD;K6A zuHFALdt>7P=|y`IvikvD_5BH zpi|t9h_PL)Pn7HVfG+8{Zn1&SXw&j{cHN2Zj21+XooCpG^u3tW-FzV6L6!Mb$59pOulMC z)VNFH^dws*_T2dzvX#U}UG{ZMQNU>T??S}`hTNC2cb|S|~v*0B-EQhI_%fundJzWGa94g7~ktk;bWW9aJ zlImJ+xNeJrN>0>A~MREL90VcEj-cc#5iq)htv6_44NK?K>sbxQQkzuYc6CG zLeGV2G`I{isomBq*b6s8{9092BjzrxKKaZ^m&*^{KfOA`*M$!xuS9O}?buk#Qw0vK z6dBo+^J<-3FSAIYbJ;?NEk(Fl8_rA?K@ra)2C zYPe@&r;C{el_JGFo98ZvV>LP6ih(YBC-Tws%_KUN!QakefBTs)Y@Z$JRcV)|%(xuh zwMg0#&X?<%>vXA1Aqbx%xs`ryA=Ynl;WeSnZWr`BOADO$D7B0-S4ro`_ zj5-#KW7{taNvoiXl<$2&g_3kdZh}O)42X~{PP#jeC_O6LacyZ#y8C}PHE4Q`Q*}X^ zPQU?*$Q?1s_(|u;n)vSYWcNz*?0lbfaRT46ikx#zQaNH}yhpY+!v{@5TUZLWs)YJw z5WL+J&VB=>a#Nc_M^n14NyPbUMU@FnR@l&i}u z3t53*pNy35O*L$oO#E+iY;j8yv$aZWfhW%SjAkZVFl!~b;T-;&L;-A1&&E|0=`3o1 zaVPdF8+K`6P>}_oTzxC%??$+)Sc~MDvn%8AmlL45i=X|~2dj3iaQq}<$ zLZFufv3o^8tefkFCOJ}IVYQl+-$2JsaC&9x_w&3@bO(+aw+nTlsw3mrHtBQ;c*-%VTQ#-x~GpK zhk59dp_p$5ggLKMZ~C{d{~6%~#`&ZO-VHz==VGu6iO*c!uD@wgi?*C$mmDH`~#F*t>@>(ZQHhO+qThV+qP}nw$){8`WI#~yZMPcH!~yNbJp#w-P(~_(ZvfMvo?XZbrRK= zYJqwU!o6NMD`&c;3)ZGXV6_K{YJ+*eL}~dneLMP{whsM^m}JPt9AlxmlW}sCF}3GR_DHf6F^QH9MAXdoM4~ z5EE7k5X6=M7Mrv>&q5{P`3dSq8P)7Bp7l!BU>G)S_ULbvBu*-yfc}YP*W93F6IgF2 zS%7=je$(9JDKZE|f(LJKEFP}rPp*yXT=wv#Umj3gJ))d_c>#ASB7=B2YTW3rtz zrxY~023WE;z`K+hc^=cM!VVB zQYi284t6tFJ#VW^RB^u~C*KA*DYl*q`b3(C{}yvbae>c0i~F$-UGocyT?)E;R=1c4u%h4X$|>d=}zFo zTgYs&7&?4PYQ5u?{)}|vLDE;MJ*R$!L2%<9DSQ#G`DXkK?4WS7IDrg+MW6m%Tp_mE zh@%?h6zWbV!$~_}>s~)G{sRg*;L5(IRA+i;&{1;rjNea2*_%N@L731=ynSO666CWu1PMys% zeaY46h`D|xA=^}}Vnv*m^wp;ev;cNlh0fu%s=Rv-q}2TKn$GRfDg7J?@Q4dv>^nM~ zxM+A!pzgo!L^rEL?VS9o6@Obvkxh{a;6EYOyE`y`K*{;Qm~e4d!(_*A(Bu3k*Sa`q z&MKTAigJ7rtzS0ph;RCvq{{LFL-ot!Nmgzq5NDi|TYzHbb2_vSzw5rkf*w%k$|DPF z5#4x7!!?AX-0ifAS>ZT*e+^N;iPwB$kZd4?A!R|?j+Ta?K=;9r^*+6BfXnkQoRLk7 zwK#!WgP|oG<`sF#0Ir;lasW?is49_ACrq@Ru{o@8_c1^lYJwv?t3+Mny}m$s>Kkbf zi_AVrihDd{a=gj@PY21k%xAunB>x>Y3*F5WO7fQhX)|ZU7ON);TsB8$PLIh!JFK^{ zh@=6B7MJXci`Kefcj(smC{*{$r_Ks#7%Y-Y*+f`FnCDw59g+Yb{Ko=2*kJXV>D*-Y z>B@+GC@LA3mXlqV3P;?W1?@|PRX?X_t6uQwff zo1Hmx2|3bs9c|`s5!&1*k}I;1+8?l{)P?;3Ui6yLL)zE4o4;khT`KR{9us0|A-3`2 zHtS(N2i87uF`gg-EHIQ5KIWudToNV}y4pD*YX@SVAokpe*qP-bk(oihGl>jVbR-c9 z<|HF1<#*Dte2dcgJm?K_uNt6ZFP`Tcwc=J|JXyUckGM$TCLkX3M~i~S*3T=+5GyNh_QB|);ihfn;%+|N!O1rYYV4sO!9}smB8%}SdD!A zvk*?eqr1KtYAz+rw^IzKpl;!nwe*!GOlh1)L?HdmTFuBks+ODy@Px&Jep{sGE$Uxe zQQC-R$QiF1$?bwpS%fVGqtzcazYX~cGveM4yZm0Ni2tqOO0^r8{+chj<4Qcmo#2k3 z&5Kq)fjC>6!V*`mX13CC5d^w9-<44*+S#36S5OIdx&7a8|8v6`kX=+S3X0WPtMp4u z51*j(oPMYk3kxN`8hji}Q;4G)a(awid#RuQGO?r9K4k5ZLb}S~Y=n&-FclmgojcjW zi((AJm(RW!U@5B45;ykc?H4fqGL9OKA8j5Gl&?DwQ^E2)YnXECU;AAft7Wwz%?hGJPpqlqRX z_Teo!?v=3pfq!~zCsmh(WpJA>gwb{Oq{2XLClVg2)Ae>R5$=lsmMA@EgN!8@B~5Z zkHF5)2T+@*4M|vEd!(ApYe!E;GIEi}<|v9DX9fPerB>6@^*oq$dgE>D=@!!(?3AQB zIv!H?ogBA%PXurFp48<#8L(&Yt}#7tU>N8PU3uzos7#2V z`jcXE1eF)o@W*aOrVzf(gqa@z<JLLeD_@?>np&~O z>h+;fogYPUOG`B)oO1Yrp0)JW^SR?Q1VRDsT13DZP6 zHneQtmMhcn%l4%kV)EJ#QSL}1Tej(B=di20=tfSB4gs)m0)1~pOJF52&oNOZuAJKU zQ1uj@lxg3C-$J9`K)^Xb53rZS1F|nJwy0T|wr2E{SyDB$jr<+n?{NVZ47&n@ROqBs z4`A9|vA*b21_}fU_Yt_!ptLe$#4W8nChH$3dB7{c9QzS2{If7SjvBAA&KC#RY(Ieq zl^p2so%`I@aqHpyV=89~Xx%?`C~`CU=Dq##Vyt8WB zTr{O07X(e2K!<}qhSiIP=2tdjI?*!ooOlw1nL}V}^!|FrYAu}62WInySI*^9)~2V^ zOYekJlg#1l(X63?u#*2W+mfJL9pM6CB#YC+!m2gG0YG%k@x0XnKLqCtoBVayU(c6w zXrw`AGh|~3#7*=V8neT%} zirvM-ghibb0Uk!xP}{rJs(3AEMukjOXRr0yd>t8(hY*)?%UTX9E6M|;y^HE`IJ_&R)BKyo%TdEl)K4a?~DCh$9X{eJ$ksZfm+l^ z_tRTf;0A!b*hI>&1R@g&@{K~zb&+(dWaEn=_`y~?jnp4kM(NCXxAhvgL-Wmy2>}u- zGv)n|gI-$eEAN$yt$Ci|p=;d#ZsR$Y~vl@W<0|Sqj*!!fO|rTy_x453-52%i$bKhOs$uJbxIj zN)x}DKU|5G76BBqRcvtk10v45&sH%Qeu;M^*K0d@5mTGch%?>f;IMPRGI)bn{?Z*6e_D7S%T0ofmYuCQPme>DWVZ~Hl*n#4=Fi@&9G`ddoA zU=Ei9jg2lPT7pGBS>P5)P!MB_;a8}7ymyDJUY$dBzB7DZ&`x{B#I@ZR$VdeBl}l83 zzfdI?QRd*3gbd~yFfVcRZDMgACcPAr==WH$8ivMJ%9^Xpw@qYz88i42I?!TH3*k?-@U+p2^yov( z&;TPIVBzvztmPU6L#9|BCu%vtD8LV8jD*pCp*UxhyP4iBX6G2giGVJj5{l&v?g|lYOTn?G^GEOLi;`@+5JkdouqtLsF z-LHw8(OB~DL5-KT__zirR28pmo~lFI69IeMCh7Kx&d;#&K~}CiHmsYL z0?+7OfG-0geoMKM`5f?5xK;(umXPnvB!5vAcZvL;F;(!zC;(fl;QdJ`#6i8p0U<4>q zHYT3Zmr361*;Jp2HyN2X)v`fKmUQsO;3sC;x9t6knjYk#@rbv4JB~Cnq@=`dVM88+JV{(2 zZfN;CcwLl3uA8XZGvB>)z_)o*ICY9+F`5yc+&N!Y?f9t(u6S)wAryF7;d1V=0v7>6 zXFy|BY_jn_ln((|cDG~dugl{-W z8BfPk`REYKBZb4`AQLG4vtav5R%RR*>JXQ!ux_BXwWRN30DlD+#EeP73gye`2d5Fe z7H;3{BY| z)`ovU*7e0fqWKCzh$z#j{E`KUF*MDv@lQ4v@!gDy2hJ$u9qpVvOPAIi4`QnwkIF01ONUzT7`H$sms@@JR?B_n6i#x&rJy5CiZPWq&e$Xl$k%&cwWWH4$2 zFC4CMrH)k1!1fj7)o_CggJt%2f)E&96N$&%QfIf9S3*}rN^tJ=0JI&!_+b7TnecfFhf@8vm=mi$pTKBPEfGs)FrjygxF{q^pc3gc2BB z`n3*J4a`1nfDg*R8BPS z^)IdX*EoLD?cYO>>d?%V7PzPy4KO0RA?a=Gfj{JEbd4r#nh+={lwX1)qq$HiNpr}* zk3wR0K5*h#$if+PFB^i*%*Kzmf>zCw;gu29&`-XK#r*Lly|^|cH|K*=*rOs%9?A&w zdbfcVk%`re`%aNzO4??fK;8?wVQgnUdM{{#pPnjF>t7f341duG7|Qy^N1pWg7i6MM z@@yNVIIxxskFf96z+56+bwG~8Y`1gm`o!4m{Qg`}d@d00L8*+GvROCLEM?KxTs})w zD4KfCLzgkcbO#x!O0tWLntgL8V4bBS1SC2#3Q2sgMU4$6a~kJbLPYG#r6d5>#@~x0 zlG~|0nG;;bBp!YKx#1)1O~qgNknGfizfD=sC7D!3)qjf}V}+Wi7@( zR17IZeyyfNuqgt#%+~pcu@x+z?emqF6oq%^1xpxi$c~=Mw5f7TUO~+TfKPLVOTc1c;YiUn$75K>TBDh<%Cyxuc zUwwqY%BS8I6xQWdPxCJcTsX;l(T!lWF`O6DF(y}f&yBc)Dl|kk#S87|^a#Ii|vj8Qd?Ql6(iSYA}V9>5W5K_P+w>!>JW4}gs z^rFtmd|mXpcCGx7wygvPCf>lD@4b!^WjG4|ZfR~#W1bxwODl+M0pZGox|>dv{b6CH zQTd&z|7xqW0GqVvw&568XBRelmecLyV?iHUIQUZb_Us+s1r_3*^1~SYH2?S^SGJOH zKBeta2oB#S;Y_!%AD4c({#PclWrY4SPg>p*TF^8QNT{7k$blwdp1)0;BPZddyS%m; zw63Fo7j=M}%8{5d9P_6PD|83oG)N^Brox~JEJSU?0EchEhHUreBrPrz!K@yO!n~R$ zgF-MGXD1wfB0(t=Vk3dYRiuWM+qnhLRvBm4W>v#u*1rd!6zEY}WapI9r37!g&!ad8 zFb_=Mc9@`-N2*0e5fM{r^unL1W6FmGd!B>#YsCjWQCVRlQ!Ct*$AgjMwBC5#9@6i_ zmhsbxHRs#G>kD1|bnO{fm84rh%Ew!~w`KGYq-hO~D4X7bM7(g*@;-F{x4}q7v)~e5 zq%0n*5w}oc2CevAY)Vy=RvLub$W8?$oZ>fNB1YcTY(Eywldao@+jVRN+*YW?c9?s- zl2KJq`9b!b2~ z!Bw2W7JakmlW41J?1~GgUST9GAEylwERNC-A--j9vGPOyzc!pV15J3S`HM9?X>@<= zn~5Ae(&WEtO~Z}@<80S1mwT@FU@{XK0~W;rP9(eGQc;ip8}5H@I0;CsKklOg{JI}S zebYN*f{Pa7)2aQ5r0W<(WCz!45g1n{kB?($foL5|g6H!Mb{H%8_8-=*J(df3O^{_p zw(_1>#pleVr1`rh?tiMWIQZ$N=x2%7T9C*dTB zn?7To8Y&F5JWpRZCd_|2%O?GOlJvyLo*P&(~wl>vM9oy1Rc1yWq9&fc+oWEG6N{l3Il+0%Gl2d-lYZT2F zz*7-5i$9vKtT!Nc!c|$s_e9MA!j{_uk0cc?LxGvhaIO1^yI3J4Yz$9ZNST!a?5e61(}Yl-fhA7WRG_Z@U)lbWWR2O>ysq-C73W0BC4oLJ=| zGz7m?NGa2}JQzvZgOZ36XpBja4o6PcQFE18djKs=Wg7BaMi_x-6lF%d#kYq1GEpmw z-+AKvNVfchYJo_-8{4Y?t>OlV3poyFE(C)#T8i)&@KeK0ZFqv ziaI(=59~p9UYqAD%Ier}-N+Q{=1n{g<%vx5(z9k5=9`8@3pp?;K=HGOP3}RBJJ?qG z=lu|eL27o#7!fO+^E)6TUo6ZWc2o6Z|A+Y>TJ=4<-p^kD2QGCgQ4lMDJ?~s93jul6HbQ7WgAQG>!^0S&6 zYA>OV!GVMH38u!sz;L7@Gs0sXclE>m{kUyn6(Qf+d~C!Zq5Qpr)yidG_LvV8Tfxw# zq5v}-w^6T@h*I(oGmZRk73_2Ja`akk)2I?kAk<7S2vJcbcSi^Tffk$@n$X%)Dhh$~L=t#V(UwQROpElgZAHJ8f z^t-9x(BF1iMWy&_j#e@^K^zqNjBgtfd_&ZK&=|ZLO7bmp)+fn*08}D(lkUYtX-sXi zKe9J+yTFlIt0nrg!)wAoX!9NwH@Q28Sa>%+-$~7!&NdRK$=Taa1I>M4j#h16?-500 z%%<-C`W`zktqJWL{S(G037M*=2iLr)7yy2NisceKX1de8=Iw$h^e^&#=5Ey(tcZPj zJI7x~2%T>~ls{NvNWXv3r%FHx%PHwO4TVgaqL7ojI8jZ$tD@*oH*_{*?+P`DRN9~j zN71-`La9O<N-vTC-EBbgU^hm~|gVb6R{dj1b#N$Kz${-0M(c-k4WUrs~ z_uFek^0nt+lvNpEZa2kL3{0rl>bFP3PErdrs}cG2%g%W1GWzPwDIhw2yBrOC`mjZ? zre-BTs~U#P%0-H78k#(d$a@qKn`v)|&6B}jUY3wQ6qZw6-;y5kp*gDKIF|>g<}y=R zis?5}V<~ZI@16fj^CSZcvpn!z`;dK(c&K9)&uIf$nw$kpT3I4KOGpZL1%KhDn=CV7 zKY#51jCnX*(kPiYYBD=6s4OUQ<(=okP9=to&qOm1|AbSaXi3_UDA9wrfq`K* z@98Q!87uzl(`<5{?-9cT!P16^q~WNXyxfpRv2sS8NXvWe-FcyOl9L z4K)A{*TJk@Co0eX8}5H@IPkVG^4j~Xdk`ft^`4So>Px5mX&2rG#du_VeQ=W*U0K+- zuiQP_=kqn#2On{D@K_9l`CugPAL@f?gJlZl9{!gYBoq$G^-~f2La+rdJWzXbx0hk` z>7gB>@K3g~eYG_HTxQg`!BApdZ9>i%&F~dLWbZm;&jcH3svm!^3DQ9+rzz&AxW_so z8ST;wHIxZgu!hkoBJ3w)gF-@g7U{B87z`@9jpl2s%V01g| zq(F)h+l}+61coR*v8^jgx98{ghF^`nP|9YMe4KMA0GsL!#`K0S(dN-yel&!}^8tTLzj$x*q3zt2ePec^d3 z{4N4|+5fbEl^A;<2+%!6QA=KgA7IDkm+ zCvNb>b>Z$$FY#IEbFzmb4Pf5Q>$FYCLoKc;KL4S-B6t+ z9$7vWp&2RkMj_?Pa9*-bknQrL!}d|PHJO2k#-|o(P!me4u}Z$Y)IN6Gy?iB@Up!sY za=SbPVFv$b{_uFiRWa!^y5&83Mfm&%dKBBn1ps9S@noABl)|mDhcS!t=>V@f&iCx* zZL%|$IveJWVz24$ag78k=(Q2N+0nS|-LZWE%~-0P6A3IgTqcsgMHATC`W?aX2n8|b z<^KgwQss8tLzMMgkva-SG2Ly+vIgLG6zg~l@)s|$+iD(yLvZj|tD}Dy)fNer-um#A zt?3*RZHVvz38-zzH1plkt08n+l|v`f*z;_5_8j-nmirghxp^~^GLW>?guva)GwSq} zhL1$kj*-+05Tc&fyB+eq!sLg7tTaiX+zqP3U8gU`KJ6)Ski;JTx@R7`rV1I20Z*sw zo1X8@($t2D6T^q86Yj_e6CYjl?~2{;xM;MT;=_kfBx&uUmA&wSj)Q0TRp$s43?zL= zoMMW?cpEH&GJOtp$b1y2{}D*QEKZZz`4_yoz0ej?af`>>v|S?NRGv`DW$t8#_5p5A zd)+h%{kJZWAN+K)5E|_~Z>V2Jn*UxIU!xH3eQYSQIZj?%Z2)vV;g-rZRIR%2aJaI} zpFs(zaIP}2DfyR!C5cE?z;;EYo`YEr!Acu5R`S_az2xzwn3Mcm`HykB>^NkX2=|4; zGn)SG4%^5=^9=aky-k>Mm&XD;!hZ2lreS`}1De>G6%dSS6T&9aN3U?a!X_9DO*stp zz3jO2JV|I{gu7zDg?!`rD@nJj#++&i!kc@?d5JUdZk{*A7M>wzPi|(>N=7jO(N8GCqCxw zVkiyCP|$uhwEXRr2tg+6JhraqWOtKaFM0I{RxhH;#Q@b|^e>Nv^l#IeEEoq(Ex4vZ zG|V6<2jU<~ggTxnsNXz~YxvfAKz9*^Y|1RiPF2!`(7TqjPm!~#loecL#-#DqB zGi`>uD*@0_CnDI1;eBQ&{-AN6znpEUa2aGP$Y9x?aA_-DWABIX7=*y6Bqi7a>j)WK z*l23*N0StR`BJsXy}WjVCGGGxj_nAzmeVXd?*Q>P0wSUX-+7sJ6(E^FV*$;6<(OJ? z6*gF8F}84kfz)6!QKfR>CGQm31JiE==VOWBBEPuEDO`Om`vd>2;lks#pNEo$tg%K+ z;|t%E2JvN>4I`oqrlup6hDswvL3;<%WWF0*5_dm`PfC}>yS@Hzxc|A~q&q`s{fBVe z>%@)y~u4D7hp{`%8WmjUc%DnuG{C{@nQcB z0uQIe3fN$4Hs9v^7+I(1B+x8}lnk6F%5Y^yZ{PcNd+I0$-d~ z0{$0MnsMvdz%e)-@?poLA!rCj^YKo5VJ22FRf63$>HyoY@ZJ(hP!i^nQu_>`VOx70 zpG6%vG@weJ0ewb?!-e$O{dEh37C@hmD5_cNCcC#LaJxz5tGKNWjImg`2PnN-Sw4@j zcHQ}TKz5oUP+RW3DH=Bte5odHex{V;AU;ifD#3y{iU>SDn0VuK;c|mhqUh)Zu_P`Y zIV!dZWK6)mZ))XN`&-Vp;?asgS5r?3JzvJ`+*f0<44&{Mf-MFT{62Q4RrZNEXNlS| zS92n@jmqw$pa5tc-;E0m?&zx&zC`5km8nMvw}=k#6kT{4lhlyrxOss2;+b z$%I2Ukk&+Z(r;rMdW?CVlb9v+Z`iIRTJutIKpTQ5SNZR&;NPA7(lCwF1>m)~k@b40 z=%=z(%xS8<1q{!@LwPId!i0|9Mp!T5OGvY*#j=EQfZn6>C=-3ueH5vt`IcBf(6(-c>6YH9j6)3u9_jfGx|MqRXxDZ!-#PRoxch=lwVxb=q>&(~ z7-s+0vzWH8Aiyp{X3g#x5e&O1+P3V?iz&uj5N4g1Q9TEM_2GSB?Bn+mGGNXf%h6+X zW`Yw4;nB78+r%s@!Kk ztqB7kVXT2Y*J=Oslmlv&@rr^SP-}xsgYSmnP|gYiqQIt5sp-L|e`mTY6asAZ_vd&IT5xO;{}IyHz1 zvzYoIYOTKT-!rhI3PkPnt75uX%a-;g+^kECf`xsE)SU)!@=TFKYto861zeK5tEKEE z5Dk!4_vWNeYj<;>4q!B!Zd79p_d0q7`Yvn&WTB+ngNo;D6-nHaKvBMz$Z_xs4a5&b_9n3-?IRR@8eIw4W(O|Q&vr*JKB4Ng6PYd zQ*0C06e(b7=!$n~_%*Xh3e_@8TK#IIH^ulUJFAxJbxjklzRkHyP`68@hv(366lA?G zGE^dtwHZF~my;Mrz0Tq32jc<(j=*TL+q-00dSIhKIz_N}UQ0Z`tCMCTtq+-$T798u zTeC4548)K(Enp^u{UABc;%om;>kGcektQ}e?GXcSB#%@gEvc4!8+ar&lnC#!7;E*j z1hx~Yj)fLYoEmQASr9YvWkmCK#>czd1oDO;?dPM ztNM8@bK@am8`SA(ROdLj0~{|aIp)hW(#$m+cr07oZ8+3?YFC{TCeKm9l8rM4UO0#S zsrI%HCd89SuzFrpuZwA5`gpZ{?V#Yp?UUV595mf+5d#?Zp6&a-8Z9yt9#yyickDcP zCOv~IPbKj^i-k=-c7Ehz#{!;ilwCqvZnaJ@k_gv1~>y+HxZ{>e$xIba{)}Lx5{wGXLQf;^ zR%I{iJd_Sn!o7yK(%%0!-2dEg%sDq>0~*rRtO|M~%z~EAGw4x!T2r+I@)Ngt(L79`E9n`eaM0e{~X7+ZIUeFIULSn0ah8~K(JiddjVM*@5@`Gb3!#lO^ z?TKRD!yuNsSLoo58ts9o`g07p#3SQEmpR)`B?<&Mt5}Kt(SYuYGxZmtEHg{z6j7O} zeQxg6gew03>R7WMc&O@Lb6=kDYmV7cm9ZF`hRS9vzVl>HeLI27NYj0k;`q&-y^~_%uk{X#rX9cJ4 z#HTD$Ms|ilQ7-SoIxwHdV;_r{9o=j|&cq%J1U-mVQ%SHNBpc-BBd*ncHtATN9%cGz zwSqVEnkguuoM<`4B{ydt9ceu3jLZ#8oheZ*(AKNtTBGm|Hjl zqju0Tfp3}dncO%=o&JdB$gEoX`VcZZT<5reWF75QCtjbnAx0C+h{SEJ9+EJSBy5@v z!kd)kyfATe%BxOthjzWjQMe7=JrE0yT;$m3+S@@Rc8|Id#viF0{eeWA1`tSanYT_x zRmv;4yzwBjlkYEg&xf6@S4&xXW~n$Aq)9Ap3c!P_grjra>>0CEro)&MU{`W^Z7lPn zzIC!MweB0lAl(%25Zbr$+I*74y*}c$+%iX1zi8?d|0zbWr-mN|J&W=X^aVWQ%*RKl zec-ZVF-a@(u+h^dpO-~*x4)3zqetCYAT!ZC}#!;UY>Z*5vZrz zomWcV6D_oKU7cp-0*fm}tcegYoe_AmuoD%3gtBT%ph)3@e419O|xPe8j0Tj?t)6n-Rck+da3e01+8~R)%&eE? ziZuNOLkPPDgh8Jq=9$6&v1n&RQ)0&5xvALnwg%(MC~jG zw5S1#npp!f1|Gu>Qg=+KA1da1gaBm!N_$vc{IIK)n0I-}J(pgQfz~$8*!uN*czr$! zq|;uH`Y+Hw*-Jjb1$rP&4&W;oL3qE-{<0UfFWG#`5zfnP(OaR^i|TYm(z0JUHA@3m zUNzkrC5T`bdq>o~s6iN8pZ@(+DVz&8Ax`8xW!q22f9q9j?d~Sk^Rml|g)1vv+IbG1 z2XCbwU;JI&Q@C-;<)&y>&X1!##RzKL&Vn~ZhY|Lcq(^qrX;D+;j6o)7`CV6B2#)md#=M(;r2v~z+Bp-Q`%B2tr+nlbA!$ZfMajYn6Uh5snqP`-+n~VoyN+)h zy(1$%fjlNN{M}yGZG}RT>d4t^>2YBI5u13kD20Hca)1TfcK^wr-sy z_G6j7yv3fp&A#uK(G=Ap_i%}NP%Swk#Q`$Dw#K{}hkp8lHyi_@%V#GW{1{F9PT*#W zZ=rDXju26oF`&0xFD+I-7~@`Bn8ZF<$qGH3C5cGftFJV-$=HF!=tFRJDVT7da@T)R z0l8SW;#k;zUKB85B$1aOICcN*Q;cCt!@D1wZK}S`gjrc=$`A&c z@F6vuE>xUroGye{yZC19I=tY90@LPxOM4IDIscB(aiQz{$Sf=#pk}m_lP({7RVxoC z82Nr@plOQB6GR#?wp@q3qBc}n8% z)xTyvjIoiEs~LG`8w>eMcL)ClLlFb*M@MXC+#t3B2n>12?{{@`?Tb@>p z1YIA(F-ik)rfZDyaG%))DW-AnEgIYdYGaH;vCCw>^L6cKs1ad*{L<^MNJ6Wn`(4Uh zmq%OeJxe){{IPc;`LSw{8a#Ti>^|<$nD>9@hP8U&!Zv5u?MC-ugai0*4cAh5^L=%E zQUiDlvuK0s(d8sPS|m6*&JM42$8Note~$pqhZzL)2xw6#$36-P!|L;Y!~M?Sp6Pz>AufjJu(=FhBYqaH)-LC z($8%)a$yM4%6CZA=U*jD+ey!`wxR1v4R)mWZkNJ!=E=^PpY{qC%^X-Psw|Ku2VeLM z%CON0yH;ktIFt+?mY`g(IW&H-Kq5oc@bLzv(_O#^qoaiA{2sobtRMF2rzuM?k4X0Z zEW?xFhf?Rh^|uPte83d3l!Hh-?8J>{!^DAJCVA?Qn%#2&E_1~DQ}?@jo7}Lb67-UJ zrX&i{a(LT3vf zL9|ddpBT%mt2>yL~UG*W?d$lYhmzpG}795cY>822PsDlQrvq%+SN8@RER7g@e>hU6mR@g2%28VRX{sh_#B5t!GJ(Jz`_K zE=M0~4YDP6#35i9ivL~viA5sp%HiFSIsug1dB_-^mXv4nJYkC9YC%r(J&4S0Q|uiT zQtP~#cJt{Gti>NqCW1I?uiFQ{7~q>yE|=Pm05>(8??j8-3 z0**7?k42FHn;!_9HwD)4G^dPtCvI_Y4CC`70r>1_6{u}kap)A_Ub_Z$%ah+m!XCQ< z8azLWgy3h5hEBt|EQ9*Tj?IGg79daI{n3wS;_Zo~iB8!oYRNIFr#4v+m*1h;9gs34 zQox7kLL0X_@geqdZ0q>$#jo}wz0L_vjh81%JcIOrSH;CFa8{+}FCxEUX+^(gVfYYr zSI6B^FhMcug$q}oN(jRam&M&`!nNZ#zG6>Q_nm;a?HL#xiu!RucY#zOAj7g)?!u0% zjP;e}%u$n%km%Y4j@Mz^EA@sKPcw@EF<;NvejdE=@~%H5QtpwInGT9U^b^o+B+2)aA+HI2;ip z0I)v3;1@p%^qXkR1sEN;4gD86e}l&K56+qd}Z1#D_BGbNM$7TMS^L9dal)VHaN=6t`RFgdnIc` zv0eWkro*_k!wB&~M8lSZ3*R9Ol6V~7Ge*=_7}_y=aD@3W_yj%}MUSsJ_k-O=Bg5n5 zEF|(Kss{8xJ+h7deP3!A07W7R7G1V(#}(0UkGj_5xlWxNdekXfpVImu4QCN$X2f(Osxsd+H#J`LNE%UWunud%Gnq2v}(V96T zRfawm>mKSsZFp0SP#8n&O*%7d0o0z6YRNlMLo(jwMgNi7I??sjV|nYPHz80D0DJXJ8ZW-pFe)ThS!<>BjNs(p_WvF z_GynDu@{_=E*#{#xGR)u6Bs;MpKEnxrO88211C+yeIh+mTt zut>cV8jtL@okij5ztL^wJV9no<^QeWV(PpzE@cN;lY(rG@0nbvlG)H730bUo^-gby z`jUMfPJEkQnmC7}Nuld*CSO872mRl0|8v8c$x;Tb(mSxvfBlzd8*othphfSyy9Aq9 ztf>(-LWiE??179>cwN_m)3V9Dqba@}vmSNEFJM_B&I__DL7 zfULCp1n}|?aEMK;jYK8ZW>wR<6{o>hr7Fl;Y8r24^6{2kY1dJJ3z1!f)JBR8ejDb~ zPcLCf{W0^|-;tt#AvJP*iE$@ss&>7R$pBg zs=LE5qCdsR@O%NMMaSmHe3%d(bMCf*4aXXW~>Ih5k$?g7)C2HIDHw(aI1T#R>@jda29X|Sn+V-z~ zn_uqcs@q!>EG@8)YTP#M%EpdA*jAE6WcRhQk0d|yKxZsltPARYaq6Wm|B6?guB1SK zb0ct8vD&GttN1G=thYt+to0(<)%0`!BI z?-!*aU_ECW(JJ7CnUJ(1iRf$$+pYmFzn#p;8fL-5w_gZ5{db(~`GKvMxbTX_yj%4O zl#YWj6Xx1~6^@UDh?Eij);AfY`XM;OnP$H*<6qL>E*+_Ql8ia~4m#8^sMjk|1a*%pe&&s66d&p2Q7Y#G0L+rwwkO z+`l%bE{AxOAs~%u;Y=pw;__NYxC^`%&t0f6RfRZv-@8nBec2R?=iz%__v7h6k9bOd zw-eQz@a#R()L-tFlO@^IX{~Uc?RTchlxCii@iqS>G*nO;g3@tYVu6t+aPDrI4Hj(4 z#nLlof_F(mgV87CC^A-=8-0u&ab!LTfd|QYfN%$5xeXmGZJoFCm=1KQ;6@xHb)3Jm zIo~4D9(kX*bfw>_ykbiT%Bww>@(pST@DdCoyy;HZRBRn~K0B#c=MOWhZiz?j^E*xt z@m6Bj=VDDElpDKZ7Z3zoawdfI+MUwC~gQ?YHy#Y z(^s7dh>H~_tN5b1syBi85GY;xQ4dEX*7U*2rD@A?0M>OE$!e9(i!=ivujSHny#^zinrD z;v>}j`<3#VAhpY1W7~qcaoqShxR$@wowv@}X4xQYyDjdb*cWarP7a9dU_z^~=eT;{ z$A=W#A;k6f;wU0cKT^!K+0wCNiD!OTaXDP%Ec+t2Rmfd4D2H@+s>e?R_T~Q+| z5GTsvP;@@vhuR@cn$ULJqE1P!X3S>4c~)OKa(0)9_d#o$c_$DSMhbDmd}vo!=W@o; z?^r@8GAhvaWT*?OVj~a=(g^;J^F%w;QIS^ke+yuY+XTn9ovC-a4+YZ**G3)89JI|+ zcO)*$Zd1+c%^YoL%=WZxjKF`Ywm5G~-035kqv1~U)~0q$LqbG0Wdwv+SJn)NG8+d3 z`*CQeJdqA6vIB<>DN^;!$z1I1i+k&m${cH5n_Qg-dRxlsPA`TFCu5_h49s&WD1_kB z$7@-T0Em*_r#zPj9BmTSKimt5?~u`HG~LfG72T!^yZ=NN*kJ4BX4XkZo%lEr z6B#0B@^%G>tGGgjBS1cPyXHC=(-i}NylTOD+(tou@)pj>*z9lS!O!4pggKTP^A}rTg`0rnP6C%YhChgLnw>!wosMNMvRpKk2HxHS!TE zhs7L)m~MLH9lRB<&_u(}4?uPbAdIEkrKc(l)R318{GtA1!=Qp2KivV}vF%pq{)1WO zFnQ#{>7FQ=DR8R2j^P%gi7u>nKW;eB;wap-=De)i@UUA(V|{LN3CfD5Vvb>&g+_}N zH+7QHDS{6C_)Wv?4{s7&%%0Oj;r!G}AF9RyQnh)Rh~dR`U?7aAzOZ?ImZ2Bg>3;0w z^_p08gEWi_&_>ty8SI-3n?uI(jZW#{igr&3|jS z#7*q7iF91Fo>E+ic&HCW&C7cW=YuFh!QS`++-wbde4r{5SwU4gp%TO_&NHDl`2U9c zpBs)ANkro(mjn)mevO~X=~wK#Yav{Hk6I(FGKas`U(idAd8kZD z1cv$hh0WagrJnWZx-7NKp0yRK2lycD0w^|yNU8)H2{w>RHnK*`Qm)r5J||1Wj94t|3w5u6t{TWU{1`>q68T;!l-x)_b8L z7vwJ(>q8c}Ah^Jz(N&lu7N!^(2onYuhatiExJm`_u)xa8d*UmZTKQh2rcKq@0XtQW zR$=81)|qYwP#kcd`t2rjbV~qzWUdy^ng+HzNi*MLKTnE0=aaQWY`$$!7UK#F_Teoit7-u5>PpsfMSSRcj#k^N z9bSm@hzN7QHj1YiUTY*_b3ETOxkTP861jY`Emp^L8+&%#hm~Bps)SKAX`dw*k!C6` z-T;Xvd)Uywvjs!%&DI}vsmgTI7mj)?2skvzw3uFkUSQ*C-~v`FAm4LGIKgfBLe!W= z^Kj0{pTyB8&EXXSl81s|-{LT~4r5S;jdIDtjXv3yuc{k0Upcg6uwU*zmJ0S}9|-5L@$#5G zL7EG+tubJ@L%t#%OBFL-&>zOu!{2Pcu)X{9eR#D>_nt1lno~|}2T2!Rwp!zJ_Jwpnn%YENC?(S|%jx{9}txWZNF^T*5-ecx&+w-+=6sapJ#``HZP3Nvx z#;V|P=<@D?2VwONujr3IMdhIoZvO>g_q?#0@ZDahAG-~+q!ZGKL`{O8DP5K)zW{wA zQ*0OzDh%b{r{86} zLMe&hS86Nz?!3~X5JpCfA?zoZ3K7%c{GBDbq8dUs=ZWbGUE$LyugAF;A2JRDf3cML z(F(6i&>(oxbPU_c*9+poQm6@eaeNxOed>H*Ju814bP7sKy3=`=}tPA_}4xnNxr3D8Hv5?3Awi#w?i_w(1=BM>SBT zga~)ZTS%F3ir5p>SNzy2f?j39%MyEl8{R%Js6RO~5HAnXJ`Z>m%`ZDy-mec4rV{VQ z#@M0O|FxFz+>u+%kK+Ky;~#8XOOv7*u8U_sD0R|LA>;@se-vAz?kf{krwY>%@w@rh+ms}Ypfk^QA37h5hGWvLrfF3sUgFN!{Or9kzCCC zeCD9^_WezeD*LMOS_^5WnaBuxxN0t-#Xlaa+fv4k?SnztZHQnmB!k*f@RI;WR}a1i zjg8V?m*RYOxQLGNNgkb)@bx22RPCyFt9w;k`mOuoyr@ooVbaR80A{F>6osG+xBOS_ zr;t4PGM{db$YwxqYBLD8W@y|0?x`JLAs0L7%g~{6c{2a%*9px~iZ!t{!=S4@fE#3z z#KO0)xRGK###8Wje^fYG8+8{W)1;8~k7yG2zF7T{0r3OeM-!?+Aa6`k5XO^2vkqvC z396Na5)IZ@T=NAm{0N^${!4L-O>8+UR8rT}^OsWhv{3EG@J6hF(jEzRb%e?u5dyGt)Z^~h?nI7`UPz8)<}TEFP@Jo zGT&T333c?ZXWjXldcf}ucOQS?a5g|pWzTl&b6#J>mtVq2s(5bNKP=`0Ga)d~dW90IpdRWJhFJIHz( zmSJJ(U5644QNxoe77 z4XV!Y;$6eR+8d6lafoaP^xg1t;_8P-D+lo7K2ZO6MwR3b@-~TnUi;G-3+idcebnIj zrqPj04MBD${q4^+-~3krns7g9+%E53Rf*`S>+J`_|7{mIl-Z|##oZf?bhTY0p*9F` zK9A>ss1s;)>ddRlV~2ailOEPPI)ndP!~IaS1j}83Y;m_tx2>TthV(p!O>i8#H8yHH zE;snRav8DF%7&pHzIn);hUIJdgcg-i>ML0>;yU_bqo0WYpnG07ug@EX6cE zJFQehEFcUy)hI+9r*?rFO))YAzd$_)R(80*A7DB0)1k{_7@pS27e|{l|CT?8A-DGi zhAXJZ?U(USH%1h4D=xV29oBQ(#T^&Ro5b}7%^AP7<$8{lFtStSF*)plqflCCyt&M( z^nc`4SFF<4W-KP1OPQ#pNBwAeGnTNh#NWEc-$c{aWIsLcj;?2g@oeeTriGxoHug~0l+vC|p8w^62)kij znD5B@pAh8LDE6Bxt6{iZYZ>s9qO?VJye)oe#TD{R6>i%jm}5YZ`?O_mM~8Mu_yOXN z%~Q0sa=Vkz5VUXib$lE%^!>XC73cagt5e{LaIy^zB(`F;wP)MKrQK^%BJG_)J_#&R zC@|T~MMr6*_67j>XIl+hkxWGD);%iMcfIofN1DE+atsJ>;_WF{ql~}`x&RY6sJQD@1rYW;~i89k|BG0JgYFA`5M;xd))`0t16(C)h9~N)9o!W2Q5sk zpwur`jKe`Z8hsro6KDsWSK#Ui0x616ZYvM|FK8g_0&d#0ZCd;G+|1c&J}KVn_l2 zHxJ$nFEyG7_7keIT6;>MO5v_h?g1hd_|T?A!UQ!vaG|CV<&03>R5GMGt5t%R$)h}K zQ8)Qm1dr0XQf+XH2tnk#st+;G7jyH9D&c@I3-<@Y-;2fW>!55|D+I}Dn%Xt)-XcWa z$^JBggiD7rS~!%7t;&hbnn$E2%0uqHK852OW?P-Fm3=vK{?zkhCQP&5vTQ~A;0;NR z)}P=>dAzeGB36E~PIvvDiduhSCI#3LQ&NWHVa^AK>4mIzMSwbX$Imk+6)~{sl?98x zwt>W179Cp?SsYE=5}F!Y3EoR!_oj;Eh;crW$#9xC z?Zpw2;?vB@0Ut?xsDk|}{dD$J!%gFCl`Kl2`TSf(_GdP_P<6rW^dvi89cKHJy%7T2 zJx|ZVo}#Nj4Ujm4PFp4DGanl)=%7lw5$+7bm+kv;~ccpAEj_7GI_GxTdsbw+*dB{Rg4rdaEV})gMsyP+WEZ zlB)EZ6?c-YB#zU1gV|~v+$lRh#*((a>*IInb?Pb+Xc8`H5gE zDVkXU39X#SZZix(P|nf;RzEn$l9*ok@_GLO597?vz3FNV<4oM&TZmBcV>V{r09&vA z4LbBTKFBZzCI)3=^I?*me__&=j@$zgR0L}1#(O3bCu)}WYyxzm6jj$~D08WQXZhL4 zrxL+*c`&U08@gM3rQ0G5qu*4mSaMC8=U0itjkg*1)>JUnAlEqP!8m%dM2DD~hR!pN ziK#llej=aDXl++0P*iFE4RD+_b>#@Ja86|%)IQ*f+T2H>^U1-fOCqLCMZaE9?7Q-0 z-%9qXL)ob{TJA?2YJ7-fN|Njylb7GnrXyXYgZkCKm^^eQ8uiGzH;jBrG*-~EG5*zB$N}p5RIYDAnTb8@ybps<9+QL2j_NBXUsCQPB9wZtJ9?-bz$)g?BPeUAtDPvPVv6O&}oitudZwa(w#GlIoMYJVCcDE4O!o zO2t4X^JjPRZqn|>gCDH|JwNS0sQaUFgYpJXZBe+mI%pZ?mp@;Ke=?z|xxIsSTUW!& zHyx?s;`rrXG--~;5W-d-%Laztgx{O2TSBs%Iq}yL22j;vpHErDa_5Ec0}xN0 zzu1R}pf2Wz)TV_M@P*E4=N3p4J+8A97KMBWYi^|k={zcpM& z_99eUI(LAWv}xjiVVrygoqi%kZF}QQIHU9B0mm?*(vUF`rweg5skn$QU)1P7txwTo literal 0 HcmV?d00001 diff --git a/beacon_node/network/src/sync/block_sidecar_coupling.rs b/beacon_node/network/src/sync/block_sidecar_coupling.rs index 7a234eaef04..70a3fe4f5aa 100644 --- a/beacon_node/network/src/sync/block_sidecar_coupling.rs +++ b/beacon_node/network/src/sync/block_sidecar_coupling.rs @@ -321,7 +321,7 @@ mod tests { let blocks = (0..4) .map(|_| { generate_rand_block_and_data_columns::( - 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::( - ForkName::Deneb, + ForkName::Fulu, NumBlobs::Number(1), &mut rng, &spec, diff --git a/crypto/kzg/src/lib.rs b/crypto/kzg/src/lib.rs index 348ed785af0..2a5c6e47f59 100644 --- a/crypto/kzg/src/lib.rs +++ b/crypto/kzg/src/lib.rs @@ -21,6 +21,9 @@ pub use rust_eth_kzg::{ Cell, CellIndex as CellID, CellRef, TrustedSetup as PeerDASTrustedSetup, }; +// Note: `spec.number_of_columns` is a config and should match `CELLS_PER_EXT_BLOB` - however this +// is a constant in the KZG library - be aware that overriding `number_of_columns` will break KZG +// operations. pub type CellsAndKzgProofs = ([Cell; CELLS_PER_EXT_BLOB], [KzgProof; CELLS_PER_EXT_BLOB]); pub type KzgBlobRef<'a> = &'a [u8; BYTES_PER_BLOB]; From e21b31e66087c32bf060795c002599f0cbd19b92 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Fri, 24 Jan 2025 16:43:41 +1100 Subject: [PATCH 13/16] More beacon chain test fixes. --- beacon_node/beacon_chain/src/beacon_chain.rs | 39 ++++++++++++++ beacon_node/beacon_chain/src/errors.rs | 4 ++ beacon_node/beacon_chain/src/test_utils.rs | 8 +++ beacon_node/beacon_chain/tests/store_tests.rs | 51 ++++++++++++------- 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 10f303bd7fd..b207f83d47b 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -34,6 +34,7 @@ use crate::execution_payload::{get_execution_payload, NotifyExecutionLayer, Prep use crate::fork_choice_signal::{ForkChoiceSignalRx, ForkChoiceSignalTx, ForkChoiceWaitResult}; use crate::graffiti_calculator::GraffitiCalculator; use crate::head_tracker::{HeadTracker, HeadTrackerReader, SszHeadTracker}; +use crate::kzg_utils::reconstruct_blobs; use crate::light_client_finality_update_verification::{ Error as LightClientFinalityUpdateError, VerifiedLightClientFinalityUpdate, }; @@ -1260,6 +1261,44 @@ impl BeaconChain { self.store.get_data_columns(block_root).map_err(Error::from) } + /// Returns the blobs at the given root, if any. + /// + /// Uses the `block.epoch()` to determine whether to retrieve blobs or columns from the store. + /// + /// If at least 50% of columns are retrieved, blobs will be reconstructed and returned, + /// otherwise an error `InsufficientColumnsToReconstructBlobs` is returned. + /// + /// ## Errors + /// May return a database error. + pub fn get_or_reconstruct_blobs( + &self, + block_root: &Hash256, + ) -> Result>, Error> { + let Some(block) = self.store.get_blinded_block(block_root)? else { + return Ok(None); + }; + + if self.spec.is_peer_das_enabled_for_epoch(block.epoch()) { + if let Some(columns) = self.store.get_data_columns(block_root)? { + let num_required_columns = self.spec.number_of_columns / 2; + let blobs_available = columns.len() >= num_required_columns as usize; + if blobs_available { + reconstruct_blobs(&self.kzg, &columns, None, &block, &self.spec) + .map(Some) + .map_err(Error::FailedToReconstructBlobs) + } else { + Err(Error::InsufficientColumnsToReconstructBlobs { + columns_found: columns.len(), + }) + } + } else { + Ok(None) + } + } else { + self.get_blobs(block_root).map(|b| b.blobs()) + } + } + /// Returns the data columns at the given root, if any. /// /// ## Errors diff --git a/beacon_node/beacon_chain/src/errors.rs b/beacon_node/beacon_chain/src/errors.rs index 2a8fd4cd015..2e13ab4090e 100644 --- a/beacon_node/beacon_chain/src/errors.rs +++ b/beacon_node/beacon_chain/src/errors.rs @@ -226,6 +226,10 @@ pub enum BeaconChainError { EmptyRpcCustodyColumns, AttestationError(AttestationError), AttestationCommitteeIndexNotSet, + InsufficientColumnsToReconstructBlobs { + columns_found: usize, + }, + FailedToReconstructBlobs(String), } easy_from_to!(SlotProcessingError, BeaconChainError); diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 58a275f576e..e88ce71a7b7 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -231,6 +231,7 @@ pub struct Builder { mock_execution_layer: Option>, testing_slot_clock: Option, validator_monitor_config: Option, + import_all_data_columns: bool, runtime: TestRuntime, log: Logger, } @@ -373,6 +374,7 @@ where mock_execution_layer: None, testing_slot_clock: None, validator_monitor_config: None, + import_all_data_columns: false, runtime, log, } @@ -465,6 +467,11 @@ where self } + pub fn import_all_data_columns(mut self, import_all_data_columns: bool) -> Self { + self.import_all_data_columns = import_all_data_columns; + self + } + pub fn execution_layer_from_url(mut self, url: &str) -> Self { assert!( self.execution_layer.is_none(), @@ -582,6 +589,7 @@ where .expect("should build dummy backend") .shutdown_sender(shutdown_tx) .chain_config(chain_config) + .import_all_data_columns(self.import_all_data_columns) .event_handler(Some(ServerSentEventHandler::new_with_capacity( log.clone(), 5, diff --git a/beacon_node/beacon_chain/tests/store_tests.rs b/beacon_node/beacon_chain/tests/store_tests.rs index bb5ad6f2bd0..2a9d5712681 100644 --- a/beacon_node/beacon_chain/tests/store_tests.rs +++ b/beacon_node/beacon_chain/tests/store_tests.rs @@ -1,7 +1,6 @@ #![cfg(not(debug_assertions))] use beacon_chain::attestation_verification::Error as AttnError; -use beacon_chain::block_verification_types::RpcBlock; use beacon_chain::builder::BeaconChainBuilder; use beacon_chain::data_availability_checker::AvailableBlock; use beacon_chain::schema_change::migrate_schema; @@ -82,13 +81,26 @@ fn get_harness( reconstruct_historic_states: true, ..ChainConfig::default() }; - get_harness_generic(store, validator_count, chain_config) + get_harness_generic(store, validator_count, chain_config, false) +} + +fn get_harness_import_all_data_columns( + store: Arc, BeaconNodeBackend>>, + validator_count: usize, +) -> TestHarness { + // Most tests expect to retain historic states, so we use this as the default. + let chain_config = ChainConfig { + reconstruct_historic_states: true, + ..ChainConfig::default() + }; + get_harness_generic(store, validator_count, chain_config, true) } fn get_harness_generic( store: Arc, BeaconNodeBackend>>, validator_count: usize, chain_config: ChainConfig, + import_all_data_columns: bool, ) -> TestHarness { let harness = TestHarness::builder(MinimalEthSpec) .spec(store.get_chain_spec().clone()) @@ -97,6 +109,7 @@ fn get_harness_generic( .fresh_disk_store(store) .mock_execution_layer() .chain_config(chain_config) + .import_all_data_columns(import_all_data_columns) .build(); harness.advance_slot(); harness @@ -2286,7 +2299,12 @@ async fn weak_subjectivity_sync_test(slots: Vec, checkpoint_slot: Slot) { let temp1 = tempdir().unwrap(); let full_store = get_store(&temp1); - let harness = get_harness(full_store.clone(), LOW_VALIDATOR_COUNT); + + // Run a supernode so the node has full blobs stored. + // This may not be required in the future if we end up implementing downloading checkpoint + // blobs from p2p peers: + // https://github.com/sigp/lighthouse/issues/6837 + let harness = get_harness_import_all_data_columns(full_store.clone(), LOW_VALIDATOR_COUNT); let all_validators = (0..LOW_VALIDATOR_COUNT).collect::>(); @@ -2319,10 +2337,8 @@ async fn weak_subjectivity_sync_test(slots: Vec, checkpoint_slot: Slot) { .unwrap(); let wss_blobs_opt = harness .chain - .store - .get_blobs(&wss_block_root) - .unwrap() - .blobs(); + .get_or_reconstruct_blobs(&wss_block_root) + .unwrap(); let wss_state = full_store .get_state(&wss_state_root, Some(checkpoint_slot)) .unwrap() @@ -2395,14 +2411,16 @@ async fn weak_subjectivity_sync_test(slots: Vec, checkpoint_slot: Slot) { .await .unwrap() .unwrap(); + // This test may break in the future if we no longer store the full checkpoint data columns. let store_wss_blobs_opt = beacon_chain - .store - .get_blobs(&wss_block_root) - .unwrap() - .blobs(); + .get_or_reconstruct_blobs(&wss_block_root) + .unwrap(); assert_eq!(store_wss_block, wss_block); - assert_eq!(store_wss_blobs_opt, wss_blobs_opt); + // TODO(fulu): Remove this condition once #6760 (PeerDAS checkpoint sync) is merged. + if !beacon_chain.spec.is_peer_das_scheduled() { + assert_eq!(store_wss_blobs_opt, wss_blobs_opt); + } // Apply blocks forward to reach head. let chain_dump = harness.chain.chain_dump().unwrap(); @@ -2418,7 +2436,7 @@ async fn weak_subjectivity_sync_test(slots: Vec, checkpoint_slot: Slot) { .await .unwrap() .unwrap(); - let blobs = harness.chain.get_blobs(&block_root).expect("blobs").blobs(); + let slot = full_block.slot(); let state_root = full_block.state_root(); @@ -2426,7 +2444,7 @@ async fn weak_subjectivity_sync_test(slots: Vec, checkpoint_slot: Slot) { beacon_chain .process_block( full_block.canonical_root(), - RpcBlock::new(Some(block_root), Arc::new(full_block), blobs).unwrap(), + harness.build_rpc_block_from_store_blobs(Some(block_root), Arc::new(full_block)), NotifyExecutionLayer::Yes, BlockImportSource::Lookup, || Ok(()), @@ -2480,13 +2498,12 @@ async fn weak_subjectivity_sync_test(slots: Vec, checkpoint_slot: Slot) { .await .expect("should get block") .expect("should get block"); - let blobs = harness.chain.get_blobs(&block_root).expect("blobs").blobs(); if let MaybeAvailableBlock::Available(block) = harness .chain .data_availability_checker .verify_kzg_for_rpc_block( - RpcBlock::new(Some(block_root), Arc::new(full_block), blobs).unwrap(), + harness.build_rpc_block_from_store_blobs(Some(block_root), Arc::new(full_block)), ) .expect("should verify kzg") { @@ -2587,7 +2604,7 @@ async fn process_blocks_and_attestations_for_unaligned_checkpoint() { reconstruct_historic_states: false, ..ChainConfig::default() }; - let harness = get_harness_generic(store.clone(), LOW_VALIDATOR_COUNT, chain_config); + let harness = get_harness_generic(store.clone(), LOW_VALIDATOR_COUNT, chain_config, false); let all_validators = (0..LOW_VALIDATOR_COUNT).collect::>(); From d8cba4bf61b55cf495560ee110fa0d36f8798986 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Tue, 28 Jan 2025 11:06:15 -0300 Subject: [PATCH 14/16] Revert change: select peers from chain for custody by range requests --- .../src/network_beacon_processor/mod.rs | 5 +++ .../network/src/sync/backfill_sync/mod.rs | 7 ---- .../network/src/sync/network_context.rs | 32 ++++--------------- .../network/src/sync/range_sync/chain.rs | 4 --- beacon_node/network/src/sync/tests/range.rs | 5 ++- 5 files changed, 16 insertions(+), 37 deletions(-) diff --git a/beacon_node/network/src/network_beacon_processor/mod.rs b/beacon_node/network/src/network_beacon_processor/mod.rs index 4a3fb28e102..8d07ef1a129 100644 --- a/beacon_node/network/src/network_beacon_processor/mod.rs +++ b/beacon_node/network/src/network_beacon_processor/mod.rs @@ -613,6 +613,11 @@ impl NetworkBeaconProcessor { blocks: Vec>, ) -> Result<(), Error> { 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 diff --git a/beacon_node/network/src/sync/backfill_sync/mod.rs b/beacon_node/network/src/sync/backfill_sync/mod.rs index 332016b5bc0..5703ed35046 100644 --- a/beacon_node/network/src/sync/backfill_sync/mod.rs +++ b/beacon_node/network/src/sync/backfill_sync/mod.rs @@ -941,18 +941,11 @@ impl BackFillSync { ) -> Result<(), BackFillError> { if let Some(batch) = self.batches.get_mut(&batch_id) { let (request, is_blob_batch) = batch.to_blocks_by_range_request(); - - let synced_peers = { - let peers = self.network_globals.peers.read(); - peers.synced_peers().copied().collect::>() - }; - match network.block_components_by_range_request( peer, is_blob_batch, request, RangeRequestId::BackfillSync { batch_id }, - synced_peers.into_iter(), ) { Ok(request_id) => { // inform the batch about the new request diff --git a/beacon_node/network/src/sync/network_context.rs b/beacon_node/network/src/sync/network_context.rs index 5ec26ef4c65..f8999361288 100644 --- a/beacon_node/network/src/sync/network_context.rs +++ b/beacon_node/network/src/sync/network_context.rs @@ -43,8 +43,8 @@ use std::time::Duration; use tokio::sync::mpsc; use types::blob_sidecar::FixedBlobSidecarList; use types::{ - BlobSidecar, ColumnIndex, DataColumnSidecar, DataColumnSidecarList, DataColumnSubnetId, - EthSpec, Hash256, SignedBeaconBlock, Slot, + BlobSidecar, ColumnIndex, DataColumnSidecar, DataColumnSidecarList, EthSpec, Hash256, + SignedBeaconBlock, Slot, }; pub mod custody; @@ -307,23 +307,10 @@ impl SyncNetworkContext { .custody_peers_for_column(column_index) } - /// Chooses a random peer assigned to custody `column_index` from the provided `syncing_peers`. - pub fn choose_random_custodial_peer<'a>( - &self, - column_index: ColumnIndex, - syncing_peers: impl Iterator, - ) -> Option { - let peer_db_read_lock = self.network_globals().peers.read(); - let subnet_id = DataColumnSubnetId::from_column_index(column_index, &self.chain.spec); - - syncing_peers - .filter(|peer_id| { - peer_db_read_lock - .peer_info(peer_id) - .is_some_and(|peer_info| peer_info.is_assigned_to_custody_subnet(&subnet_id)) - }) + pub fn get_random_custodial_peer(&self, column_index: ColumnIndex) -> Option { + self.get_custodial_peers(column_index) + .into_iter() .choose(&mut thread_rng()) - .copied() } pub fn network_globals(&self) -> &NetworkGlobals { @@ -371,7 +358,6 @@ impl SyncNetworkContext { batch_type: ByRangeRequestType, request: BlocksByRangeRequest, sender_id: RangeRequestId, - syncing_peers: impl Iterator, ) -> Result { let epoch = Slot::new(*request.start_slot()).epoch(T::EthSpec::slots_per_epoch()); let id = self.next_id(); @@ -440,7 +426,7 @@ impl SyncNetworkContext { let mut num_of_custody_column_req = 0; for (peer_id, columns_by_range_request) in - self.make_columns_by_range_requests(request, &column_indexes, syncing_peers)? + self.make_columns_by_range_requests(request, &column_indexes)? { requested_peers.push(peer_id); @@ -487,18 +473,14 @@ impl SyncNetworkContext { &self, request: BlocksByRangeRequest, custody_indexes: &HashSet, - syncing_peers: impl Iterator, ) -> Result, RpcRequestSendError> { let mut peer_id_to_request_map = HashMap::new(); - let syncing_peers = syncing_peers.collect::>(); for column_index in custody_indexes { // TODO(das): The peer selection logic here needs to be improved - we should probably // avoid retrying from failed peers, however `BatchState` currently only tracks the peer // serving the blocks. - let Some(custody_peer) = - self.choose_random_custodial_peer(*column_index, syncing_peers.iter()) - else { + let Some(custody_peer) = self.get_random_custodial_peer(*column_index) else { // TODO(das): this will be pretty bad UX. To improve we should: // - Attempt to fetch custody requests first, before requesting blocks // - Handle the no peers case gracefully, maybe add some timeout and give a few diff --git a/beacon_node/network/src/sync/range_sync/chain.rs b/beacon_node/network/src/sync/range_sync/chain.rs index d4445d098b0..51d9d9da37f 100644 --- a/beacon_node/network/src/sync/range_sync/chain.rs +++ b/beacon_node/network/src/sync/range_sync/chain.rs @@ -963,11 +963,8 @@ impl SyncingChain { peer: PeerId, ) -> ProcessingResult { let batch_state = self.visualize_batch_state(); - let syncing_peers = self.peers().collect::>(); - if let Some(batch) = self.batches.get_mut(&batch_id) { let (request, batch_type) = batch.to_blocks_by_range_request(); - match network.block_components_by_range_request( peer, batch_type, @@ -976,7 +973,6 @@ impl SyncingChain { chain_id: self.id, batch_id, }, - syncing_peers.into_iter(), ) { Ok(request_id) => { // inform the batch about the new request diff --git a/beacon_node/network/src/sync/tests/range.rs b/beacon_node/network/src/sync/tests/range.rs index 11084d045c5..e9b69edffe2 100644 --- a/beacon_node/network/src/sync/tests/range.rs +++ b/beacon_node/network/src/sync/tests/range.rs @@ -358,6 +358,9 @@ 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(); + // When adding a finalized peer, the initial head chain stops syncing. So sync only sends a + // pending batch from the finalized chain to the processor. + // TODO: Why did this sent the head chain's batch for processing before?? + // rig.expect_chain_segment(); rig.expect_chain_segment(); } From 38c7f059a4ba7ed54c5857f83781b13e6893f154 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Tue, 28 Jan 2025 14:16:26 -0300 Subject: [PATCH 15/16] Improve request identification in range sync test --- .../network/src/sync/network_context.rs | 2 + beacon_node/network/src/sync/tests/range.rs | 193 ++++++++++++------ 2 files changed, 136 insertions(+), 59 deletions(-) diff --git a/beacon_node/network/src/sync/network_context.rs b/beacon_node/network/src/sync/network_context.rs index f8999361288..7ebe1d0cbac 100644 --- a/beacon_node/network/src/sync/network_context.rs +++ b/beacon_node/network/src/sync/network_context.rs @@ -369,6 +369,7 @@ impl SyncNetworkContext { "count" => request.count(), "epoch" => epoch, "peer" => %peer_id, + "id" => id, ); let rpc_request = match request { BlocksByRangeRequest::V1(ref req) => { @@ -438,6 +439,7 @@ impl SyncNetworkContext { "epoch" => epoch, "columns" => ?columns_by_range_request.columns, "peer" => %peer_id, + "id" => id, ); self.send_network_msg(NetworkMessage::SendRequest { diff --git a/beacon_node/network/src/sync/tests/range.rs b/beacon_node/network/src/sync/tests/range.rs index e9b69edffe2..cfd89f7b44e 100644 --- a/beacon_node/network/src/sync/tests/range.rs +++ b/beacon_node/network/src/sync/tests/range.rs @@ -6,6 +6,10 @@ 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}; @@ -22,6 +26,42 @@ pub(crate) enum DataSidecars { DataColumns(Vec>), } +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, + epoch: Option, +} + +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 { @@ -94,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) { @@ -106,51 +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) { + 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_fulu() { - Some( - self.pop_received_network_event(|ev| match ev { - NetworkMessage::SendRequest { - peer_id, - request: RequestType::DataColumnsByRange(_), - request_id: AppRequestId::Sync(SyncRequestId::RangeBlockAndBlobs { id }), - } if peer_id == target_peer_id => Some(*id), - _ => None, - }) - .expect("Should have a data columns by range request"), - ) + 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() { - Some( - self.pop_received_network_event(|ev| match ev { + 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!( @@ -158,35 +229,39 @@ 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 { - if self.after_fulu() { - // Complete the request with a single stream termination - self.log(&format!( - "Completing DataColumnsByRange request {blobs_req_id} with empty stream" - )); - self.send_sync_message(SyncMessage::RpcDataColumn { - request_id: SyncRequestId::RangeBlockAndBlobs { id: blobs_req_id }, - peer_id: target_peer_id, - data_column: None, - seen_timestamp: D, - }); - } else { + 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 {blobs_req_id} with empty stream" + "Completing BlobsByRange request {id} with empty stream" )); self.send_sync_message(SyncMessage::RpcBlob { - request_id: SyncRequestId::RangeBlockAndBlobs { id: blobs_req_id }, - peer_id: target_peer_id, + 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, + }); + } + } } } @@ -282,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); @@ -316,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; @@ -342,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. @@ -358,9 +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); - // When adding a finalized peer, the initial head chain stops syncing. So sync only sends a - // pending batch from the finalized chain to the processor. - // TODO: Why did this sent the head chain's batch for processing before?? - // 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); } From ce8090d4fee69193a556e10febab7878cc24dec5 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Thu, 30 Jan 2025 15:33:22 +1100 Subject: [PATCH 16/16] Address review comments. --- beacon_node/beacon_chain/src/beacon_chain.rs | 4 ++-- beacon_node/beacon_chain/tests/store_tests.rs | 2 +- .../lighthouse_network/src/rpc/protocol.rs | 4 ++-- beacon_node/network/src/service.rs | 17 +++++++---------- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index b207f83d47b..ca21b519f15 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -1281,8 +1281,8 @@ impl BeaconChain { if self.spec.is_peer_das_enabled_for_epoch(block.epoch()) { if let Some(columns) = self.store.get_data_columns(block_root)? { let num_required_columns = self.spec.number_of_columns / 2; - let blobs_available = columns.len() >= num_required_columns as usize; - if blobs_available { + let reconstruction_possible = columns.len() >= num_required_columns as usize; + if reconstruction_possible { reconstruct_blobs(&self.kzg, &columns, None, &block, &self.spec) .map(Some) .map_err(Error::FailedToReconstructBlobs) diff --git a/beacon_node/beacon_chain/tests/store_tests.rs b/beacon_node/beacon_chain/tests/store_tests.rs index 2a9d5712681..8654b33646d 100644 --- a/beacon_node/beacon_chain/tests/store_tests.rs +++ b/beacon_node/beacon_chain/tests/store_tests.rs @@ -2300,7 +2300,7 @@ async fn weak_subjectivity_sync_test(slots: Vec, checkpoint_slot: Slot) { let temp1 = tempdir().unwrap(); let full_store = get_store(&temp1); - // Run a supernode so the node has full blobs stored. + // TODO(das): Run a supernode so the node has full blobs stored. // This may not be required in the future if we end up implementing downloading checkpoint // blobs from p2p peers: // https://github.com/sigp/lighthouse/issues/6837 diff --git a/beacon_node/lighthouse_network/src/rpc/protocol.rs b/beacon_node/lighthouse_network/src/rpc/protocol.rs index 6e5abc45ab5..eac7d67490d 100644 --- a/beacon_node/lighthouse_network/src/rpc/protocol.rs +++ b/beacon_node/lighthouse_network/src/rpc/protocol.rs @@ -386,7 +386,7 @@ impl SupportedProtocol { ProtocolId::new(Self::BlocksByRootV1, Encoding::SSZSnappy), ProtocolId::new(Self::PingV1, Encoding::SSZSnappy), ]; - if fork_context.fork_exists(ForkName::Fulu) { + if fork_context.spec.is_peer_das_scheduled() { supported.extend_from_slice(&[ // V3 variants have higher preference for protocol negotation ProtocolId::new(Self::MetaDataV3, Encoding::SSZSnappy), @@ -405,7 +405,7 @@ impl SupportedProtocol { ProtocolId::new(SupportedProtocol::BlobsByRangeV1, Encoding::SSZSnappy), ]); } - if fork_context.fork_exists(ForkName::Fulu) { + if fork_context.spec.is_peer_das_scheduled() { supported.extend_from_slice(&[ ProtocolId::new(SupportedProtocol::DataColumnsByRootV1, Encoding::SSZSnappy), ProtocolId::new(SupportedProtocol::DataColumnsByRangeV1, Encoding::SSZSnappy), diff --git a/beacon_node/network/src/service.rs b/beacon_node/network/src/service.rs index e0e82f3a3c8..49f73bf9c8d 100644 --- a/beacon_node/network/src/service.rs +++ b/beacon_node/network/src/service.rs @@ -25,7 +25,6 @@ use lighthouse_network::{ MessageId, NetworkEvent, NetworkGlobals, PeerId, }; use slog::{crit, debug, error, info, o, trace, warn}; -use std::borrow::Cow; use std::collections::BTreeSet; use std::{collections::HashSet, pin::Pin, sync::Arc, time::Duration}; use store::HotColdDB; @@ -34,8 +33,8 @@ use task_executor::ShutdownReason; use tokio::sync::mpsc; use tokio::time::Sleep; use types::{ - ChainSpec, DataColumnSubnetId, EthSpec, ForkContext, ForkName, Slot, SubnetId, - SyncCommitteeSubscription, SyncSubnetId, Unsigned, ValidatorSubscription, + ChainSpec, DataColumnSubnetId, EthSpec, ForkContext, Slot, SubnetId, SyncCommitteeSubscription, + SyncSubnetId, Unsigned, ValidatorSubscription, }; mod tests; @@ -752,7 +751,7 @@ impl NetworkService { } } - if self.fork_context.fork_exists(ForkName::Fulu) { + if self.fork_context.spec.is_peer_das_scheduled() { self.subscribe_to_peer_das_topics(&mut subscribed_topics); } @@ -813,13 +812,11 @@ impl NetworkService { /// `network.subscribe_new_fork_topics()`. fn subscribe_to_peer_das_topics(&mut self, subscribed_topics: &mut Vec) { let column_subnets_to_subscribe = if self.subscribe_all_data_column_subnets { - Cow::Owned( - (0..self.fork_context.spec.data_column_sidecar_subnet_count) - .map(DataColumnSubnetId::new) - .collect(), - ) + &(0..self.fork_context.spec.data_column_sidecar_subnet_count) + .map(DataColumnSubnetId::new) + .collect() } else { - Cow::Borrowed(&self.network_globals.sampling_subnets) + &self.network_globals.sampling_subnets }; for column_subnet in column_subnets_to_subscribe.iter() {