diff --git a/beacon_node/beacon_chain/src/data_availability_checker.rs b/beacon_node/beacon_chain/src/data_availability_checker.rs index 1bc95c22ac..5404718048 100644 --- a/beacon_node/beacon_chain/src/data_availability_checker.rs +++ b/beacon_node/beacon_chain/src/data_availability_checker.rs @@ -486,14 +486,9 @@ impl DataAvailabilityChecker { /// The epoch at which we require a data availability check in block processing. /// `None` if the `Deneb` fork is disabled. pub fn data_availability_boundary(&self) -> Option { - let fork_epoch = self.spec.deneb_fork_epoch?; - let current_slot = self.slot_clock.now()?; - Some(std::cmp::max( - fork_epoch, - current_slot - .epoch(T::EthSpec::slots_per_epoch()) - .saturating_sub(self.spec.min_epochs_for_blob_sidecars_requests), - )) + let current_epoch = self.slot_clock.now()?.epoch(T::EthSpec::slots_per_epoch()); + self.spec + .min_epoch_data_availability_boundary(current_epoch) } /// Returns true if the given epoch lies within the da boundary and false otherwise. @@ -670,15 +665,17 @@ async fn availability_cache_maintenance_service( .fork_choice_read_lock() .finalized_checkpoint() .epoch; + + let Some(min_epochs_for_blobs) = chain + .spec + .min_epoch_data_availability_boundary(current_epoch) + else { + // Shutdown service if deneb fork epoch not set. Unreachable as the same check is performed above. + break; + }; + // any data belonging to an epoch before this should be pruned - let cutoff_epoch = std::cmp::max( - finalized_epoch + 1, - std::cmp::max( - current_epoch - .saturating_sub(chain.spec.min_epochs_for_blob_sidecars_requests), - deneb_fork_epoch, - ), - ); + let cutoff_epoch = std::cmp::max(finalized_epoch + 1, min_epochs_for_blobs); if let Err(e) = overflow_cache.do_maintenance(cutoff_epoch) { error!(error = ?e,"Failed to maintain availability cache"); diff --git a/beacon_node/store/src/hot_cold_store.rs b/beacon_node/store/src/hot_cold_store.rs index f5e44f7ac9..0c230494b8 100644 --- a/beacon_node/store/src/hot_cold_store.rs +++ b/beacon_node/store/src/hot_cold_store.rs @@ -3074,18 +3074,17 @@ impl, Cold: ItemStore> HotColdDB /// Try to prune blobs, approximating the current epoch from the split slot. pub fn try_prune_most_blobs(&self, force: bool) -> Result<(), Error> { - let Some(deneb_fork_epoch) = self.spec.deneb_fork_epoch else { - debug!("Deneb fork is disabled"); - return Ok(()); - }; // The current epoch is >= split_epoch + 2. It could be greater if the database is // configured to delay updating the split or finalization has ceased. In this instance we // choose to also delay the pruning of blobs (we never prune without finalization anyway). let min_current_epoch = self.get_split_slot().epoch(E::slots_per_epoch()) + 2; - let min_data_availability_boundary = std::cmp::max( - deneb_fork_epoch, - min_current_epoch.saturating_sub(self.spec.min_epochs_for_blob_sidecars_requests), - ); + let Some(min_data_availability_boundary) = self + .spec + .min_epoch_data_availability_boundary(min_current_epoch) + else { + debug!("Deneb fork is disabled"); + return Ok(()); + }; self.try_prune_blobs(force, min_data_availability_boundary) } diff --git a/common/eth2_network_config/built_in_network_configs/holesky/config.yaml b/common/eth2_network_config/built_in_network_configs/holesky/config.yaml index 19a3f79cc0..76d8d482c2 100644 --- a/common/eth2_network_config/built_in_network_configs/holesky/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/holesky/config.yaml @@ -141,6 +141,9 @@ MAX_REQUEST_BLOB_SIDECARS_ELECTRA: 1152 NUMBER_OF_COLUMNS: 128 NUMBER_OF_CUSTODY_GROUPS: 128 DATA_COLUMN_SIDECAR_SUBNET_COUNT: 128 +MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384 SAMPLES_PER_SLOT: 8 CUSTODY_REQUIREMENT: 4 -MAX_BLOBS_PER_BLOCK_FULU: 12 +VALIDATOR_CUSTODY_REQUIREMENT: 8 +BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 +MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 \ No newline at end of file diff --git a/common/eth2_network_config/built_in_network_configs/hoodi/config.yaml b/common/eth2_network_config/built_in_network_configs/hoodi/config.yaml index 5cca1cd037..a1365e3464 100644 --- a/common/eth2_network_config/built_in_network_configs/hoodi/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/hoodi/config.yaml @@ -156,7 +156,8 @@ DATA_COLUMN_SIDECAR_SUBNET_COUNT: 128 MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384 SAMPLES_PER_SLOT: 8 CUSTODY_REQUIREMENT: 4 -MAX_BLOBS_PER_BLOCK_FULU: 12 +VALIDATOR_CUSTODY_REQUIREMENT: 8 +BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 # EIP7732 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 886e5d12ed..0b68a27f4d 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 @@ -156,6 +156,9 @@ MAX_REQUEST_BLOB_SIDECARS_ELECTRA: 1152 NUMBER_OF_COLUMNS: 128 NUMBER_OF_CUSTODY_GROUPS: 128 DATA_COLUMN_SIDECAR_SUBNET_COUNT: 128 +MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384 SAMPLES_PER_SLOT: 8 CUSTODY_REQUIREMENT: 4 -MAX_BLOBS_PER_BLOCK_FULU: 12 +VALIDATOR_CUSTODY_REQUIREMENT: 8 +BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 +MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 diff --git a/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml b/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml index 10be107263..ccd71cdce9 100644 --- a/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/sepolia/config.yaml @@ -142,6 +142,9 @@ MAX_REQUEST_BLOB_SIDECARS_ELECTRA: 1152 NUMBER_OF_COLUMNS: 128 NUMBER_OF_CUSTODY_GROUPS: 128 DATA_COLUMN_SIDECAR_SUBNET_COUNT: 128 +MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384 SAMPLES_PER_SLOT: 8 CUSTODY_REQUIREMENT: 4 -MAX_BLOBS_PER_BLOCK_FULU: 12 +VALIDATOR_CUSTODY_REQUIREMENT: 8 +BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 +MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 diff --git a/consensus/types/src/chain_spec.rs b/consensus/types/src/chain_spec.rs index 38cd4b9217..631389ce43 100644 --- a/consensus/types/src/chain_spec.rs +++ b/consensus/types/src/chain_spec.rs @@ -246,6 +246,7 @@ pub struct ChainSpec { * Networking Fulu */ blob_schedule: BlobSchedule, + min_epochs_for_data_column_sidecars_requests: u64, /* * Networking Derived @@ -740,6 +741,20 @@ impl ChainSpec { Ok(std::cmp::max(custody_group_count, self.samples_per_slot)) } + /// Returns the min epoch for blob / data column sidecar requests based on the current epoch. + /// Switch to use the column sidecar config once the `blob_retention_epoch` has passed Fulu fork epoch. + pub fn min_epoch_data_availability_boundary(&self, current_epoch: Epoch) -> Option { + let fork_epoch = self.deneb_fork_epoch?; + let blob_retention_epoch = + current_epoch.saturating_sub(self.min_epochs_for_blob_sidecars_requests); + match self.fulu_fork_epoch { + Some(fulu_fork_epoch) if blob_retention_epoch > fulu_fork_epoch => Some( + current_epoch.saturating_sub(self.min_epochs_for_data_column_sidecars_requests), + ), + _ => Some(std::cmp::max(fork_epoch, blob_retention_epoch)), + } + } + pub fn all_data_column_sidecar_subnets(&self) -> impl Iterator { (0..self.data_column_sidecar_subnet_count).map(DataColumnSubnetId::new) } @@ -1027,6 +1042,8 @@ impl ChainSpec { * Networking Fulu specific */ blob_schedule: BlobSchedule::default(), + min_epochs_for_data_column_sidecars_requests: + default_min_epochs_for_data_column_sidecars_requests(), /* * Application specific @@ -1363,6 +1380,8 @@ impl ChainSpec { * Networking Fulu specific */ blob_schedule: BlobSchedule::default(), + min_epochs_for_data_column_sidecars_requests: + default_min_epochs_for_data_column_sidecars_requests(), /* * Application specific @@ -1661,6 +1680,9 @@ pub struct Config { #[serde(default = "default_balance_per_additional_custody_group")] #[serde(with = "serde_utils::quoted_u64")] balance_per_additional_custody_group: u64, + #[serde(default = "default_min_epochs_for_data_column_sidecars_requests")] + #[serde(with = "serde_utils::quoted_u64")] + min_epochs_for_data_column_sidecars_requests: u64, } fn default_bellatrix_fork_version() -> [u8; 4] { @@ -1834,6 +1856,10 @@ const fn default_balance_per_additional_custody_group() -> u64 { 32000000000 } +const fn default_min_epochs_for_data_column_sidecars_requests() -> u64 { + 4096 +} + fn max_blocks_by_root_request_common(max_request_blocks: u64) -> usize { let max_request_blocks = max_request_blocks as usize; RuntimeVariableList::::from_vec( @@ -2045,6 +2071,8 @@ impl Config { blob_schedule: spec.blob_schedule.clone(), validator_custody_requirement: spec.validator_custody_requirement, balance_per_additional_custody_group: spec.balance_per_additional_custody_group, + min_epochs_for_data_column_sidecars_requests: spec + .min_epochs_for_data_column_sidecars_requests, } } @@ -2126,6 +2154,7 @@ impl Config { ref blob_schedule, validator_custody_requirement, balance_per_additional_custody_group, + min_epochs_for_data_column_sidecars_requests, } = self; if preset_base != E::spec_name().to_string().as_str() { @@ -2212,6 +2241,7 @@ impl Config { blob_schedule: blob_schedule.clone(), validator_custody_requirement, balance_per_additional_custody_group, + min_epochs_for_data_column_sidecars_requests, ..chain_spec.clone() }) @@ -2350,6 +2380,7 @@ mod tests { mod yaml_tests { use super::*; use paste::paste; + use std::sync::Arc; use tempfile::NamedTempFile; #[test] @@ -2649,4 +2680,65 @@ mod yaml_tests { let _ = spec.max_message_size(); let _ = spec.max_compressed_len(); } + + #[test] + fn min_epochs_for_data_sidecar_requests_deneb() { + type E = MainnetEthSpec; + let spec = Arc::new(ForkName::Deneb.make_genesis_spec(E::default_spec())); + let blob_retention_epochs = spec.min_epochs_for_blob_sidecars_requests; + + // `min_epochs_for_data_sidecar_requests` cannot be earlier than Deneb fork epoch. + assert_eq!( + spec.deneb_fork_epoch, + spec.min_epoch_data_availability_boundary(Epoch::new(blob_retention_epochs / 2)) + ); + + let current_epoch = Epoch::new(blob_retention_epochs * 2); + let expected_min_blob_epoch = current_epoch - blob_retention_epochs; + assert_eq!( + Some(expected_min_blob_epoch), + spec.min_epoch_data_availability_boundary(current_epoch) + ); + } + + #[test] + fn min_epochs_for_data_sidecar_requests_fulu() { + type E = MainnetEthSpec; + let spec = { + let mut spec = ForkName::Deneb.make_genesis_spec(E::default_spec()); + // 4096 * 2 = 8192 + spec.fulu_fork_epoch = Some(Epoch::new(spec.min_epochs_for_blob_sidecars_requests * 2)); + // set a different value for testing purpose, 4096 / 2 = 2048 + spec.min_epochs_for_data_column_sidecars_requests = + spec.min_epochs_for_blob_sidecars_requests / 2; + Arc::new(spec) + }; + let blob_retention_epochs = spec.min_epochs_for_blob_sidecars_requests; + let data_column_retention_epochs = spec.min_epochs_for_data_column_sidecars_requests; + + // `min_epochs_for_data_sidecar_requests` at fulu fork epoch still uses `min_epochs_for_blob_sidecars_requests` + let fulu_fork_epoch = spec.fulu_fork_epoch.unwrap(); + let expected_blob_retention_epoch = fulu_fork_epoch - blob_retention_epochs; + assert_eq!( + Some(expected_blob_retention_epoch), + spec.min_epoch_data_availability_boundary(fulu_fork_epoch) + ); + + // `min_epochs_for_data_sidecar_requests` at fulu fork epoch + min_epochs_for_blob_sidecars_request + let blob_retention_epoch_after_fulu = fulu_fork_epoch + blob_retention_epochs; + let expected_blob_retention_epoch = blob_retention_epoch_after_fulu - blob_retention_epochs; + assert_eq!( + Some(expected_blob_retention_epoch), + spec.min_epoch_data_availability_boundary(blob_retention_epoch_after_fulu) + ); + + // After the final blob retention epoch, `min_epochs_for_data_sidecar_requests` should be calculated + // using `min_epochs_for_data_column_sidecars_request` + let current_epoch = blob_retention_epoch_after_fulu + 1; + let expected_data_column_retention_epoch = current_epoch - data_column_retention_epochs; + assert_eq!( + Some(expected_data_column_retention_epoch), + spec.min_epoch_data_availability_boundary(current_epoch) + ); + } }