diff --git a/common/eth2_network_config/built_in_network_configs/chiado/config.yaml b/common/eth2_network_config/built_in_network_configs/chiado/config.yaml index 6bc41113d6f..eafe1ad38c4 100644 --- a/common/eth2_network_config/built_in_network_configs/chiado/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/chiado/config.yaml @@ -125,6 +125,7 @@ SUBNETS_PER_NODE: 2 ATTESTATION_SUBNET_COUNT: 64 ATTESTATION_SUBNET_EXTRA_BITS: 0 # ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS +# computed at runtime ATTESTATION_SUBNET_PREFIX_BITS: 6 # Deneb diff --git a/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml b/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml index aa2dbb35d35..2beeb45b25e 100644 --- a/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml +++ b/common/eth2_network_config/built_in_network_configs/gnosis/config.yaml @@ -108,6 +108,8 @@ MESSAGE_DOMAIN_INVALID_SNAPPY: 0x00000000 MESSAGE_DOMAIN_VALID_SNAPPY: 0x01000000 ATTESTATION_SUBNET_COUNT: 64 ATTESTATION_SUBNET_EXTRA_BITS: 0 +# ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS +# computed at runtime ATTESTATION_SUBNET_PREFIX_BITS: 6 ATTESTATION_SUBNET_SHUFFLING_PREFIX_BITS: 3 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 b1e9faea1d6..fc5b53173be 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 @@ -127,6 +127,7 @@ SUBNETS_PER_NODE: 2 ATTESTATION_SUBNET_COUNT: 64 ATTESTATION_SUBNET_EXTRA_BITS: 0 # ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS +# computed at runtime ATTESTATION_SUBNET_PREFIX_BITS: 6 ATTESTATION_SUBNET_SHUFFLING_PREFIX_BITS: 3 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 256957e1197..947caa21f0f 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 @@ -133,6 +133,7 @@ SUBNETS_PER_NODE: 2 ATTESTATION_SUBNET_COUNT: 64 ATTESTATION_SUBNET_EXTRA_BITS: 0 # ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS +# computed at runtime ATTESTATION_SUBNET_PREFIX_BITS: 6 # Deneb 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 49168018cbe..d4df1582f32 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 @@ -154,6 +154,7 @@ ATTESTATION_SUBNET_COUNT: 64 # 0 bits ATTESTATION_SUBNET_EXTRA_BITS: 0 # ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS (= 6 + 0) bits +# computed at runtime ATTESTATION_SUBNET_PREFIX_BITS: 6 ATTESTATION_SUBNET_SHUFFLING_PREFIX_BITS: 3 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 b1a01933d70..6a655d8e84e 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 @@ -133,6 +133,7 @@ SUBNETS_PER_NODE: 2 ATTESTATION_SUBNET_COUNT: 64 ATTESTATION_SUBNET_EXTRA_BITS: 0 # ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS +# computed at runtime ATTESTATION_SUBNET_PREFIX_BITS: 6 ATTESTATION_SUBNET_SHUFFLING_PREFIX_BITS: 3 diff --git a/consensus/types/src/core/chain_spec.rs b/consensus/types/src/core/chain_spec.rs index da3f9b90ccc..a0dccc60760 100644 --- a/consensus/types/src/core/chain_spec.rs +++ b/consensus/types/src/core/chain_spec.rs @@ -164,6 +164,7 @@ pub struct ChainSpec { pub inactivity_score_bias: u64, pub inactivity_score_recovery_rate: u64, pub min_sync_committee_participants: u64, + pub update_timeout: u64, pub(crate) domain_sync_committee: u32, pub(crate) domain_sync_committee_selection_proof: u32, pub(crate) domain_contribution_and_proof: u32, @@ -252,7 +253,9 @@ pub struct ChainSpec { pub message_domain_invalid_snappy: [u8; 4], pub message_domain_valid_snappy: [u8; 4], pub subnets_per_node: u8, + pub epochs_per_subnet_subscription: u64, pub attestation_subnet_count: u64, + pub attestation_subnet_extra_bits: u8, pub attestation_subnet_prefix_bits: u8, /* @@ -1060,6 +1063,7 @@ impl ChainSpec { inactivity_score_bias: 4, inactivity_score_recovery_rate: 16, min_sync_committee_participants: 1, + update_timeout: 8192, epochs_per_sync_committee_period: Epoch::new(256), domain_sync_committee: 7, domain_sync_committee_selection_proof: 8, @@ -1152,7 +1156,9 @@ impl ChainSpec { boot_nodes: vec![], network_id: 1, // mainnet network id attestation_propagation_slot_range: default_attestation_propagation_slot_range(), + epochs_per_subnet_subscription: 256, attestation_subnet_count: 64, + attestation_subnet_extra_bits: 0, subnets_per_node: 2, maximum_gossip_clock_disparity: default_maximum_gossip_clock_disparity(), target_aggregators_per_committee: 16, @@ -1162,7 +1168,10 @@ impl ChainSpec { resp_timeout: default_resp_timeout(), message_domain_invalid_snappy: default_message_domain_invalid_snappy(), message_domain_valid_snappy: default_message_domain_valid_snappy(), - attestation_subnet_prefix_bits: default_attestation_subnet_prefix_bits(), + attestation_subnet_prefix_bits: compute_attestation_subnet_prefix_bits( + default_attestation_subnet_count(), + default_attestation_subnet_extra_bits(), + ), max_request_blocks: default_max_request_blocks(), /* @@ -1243,6 +1252,7 @@ impl ChainSpec { proportional_slashing_multiplier: 2, // Altair epochs_per_sync_committee_period: Epoch::new(8), + update_timeout: 64, altair_fork_version: [0x01, 0x00, 0x00, 0x01], altair_fork_epoch: None, // Bellatrix @@ -1426,6 +1436,7 @@ impl ChainSpec { inactivity_score_bias: 4, inactivity_score_recovery_rate: 16, min_sync_committee_participants: 1, + update_timeout: 8192, epochs_per_sync_committee_period: Epoch::new(512), domain_sync_committee: 7, domain_sync_committee_selection_proof: 8, @@ -1517,7 +1528,9 @@ impl ChainSpec { boot_nodes: vec![], network_id: 100, // Gnosis Chain network id attestation_propagation_slot_range: default_attestation_propagation_slot_range(), + epochs_per_subnet_subscription: 256, attestation_subnet_count: 64, + attestation_subnet_extra_bits: 0, subnets_per_node: 4, // Make this larger than usual to avoid network damage maximum_gossip_clock_disparity: default_maximum_gossip_clock_disparity(), target_aggregators_per_committee: 16, @@ -1528,7 +1541,10 @@ impl ChainSpec { message_domain_invalid_snappy: default_message_domain_invalid_snappy(), message_domain_valid_snappy: default_message_domain_valid_snappy(), max_request_blocks: default_max_request_blocks(), - attestation_subnet_prefix_bits: default_attestation_subnet_prefix_bits(), + attestation_subnet_prefix_bits: compute_attestation_subnet_prefix_bits( + default_attestation_subnet_count(), + default_attestation_subnet_extra_bits(), + ), /* * Networking Deneb Specific @@ -1849,9 +1865,15 @@ pub struct Config { #[serde(default = "default_message_domain_valid_snappy")] #[serde(with = "serde_utils::bytes_4_hex")] message_domain_valid_snappy: [u8; 4], - #[serde(default = "default_attestation_subnet_prefix_bits")] + #[serde(default = "default_epochs_per_subnet_subscription")] + #[serde(with = "serde_utils::quoted_u64")] + epochs_per_subnet_subscription: u64, + #[serde(default = "default_attestation_subnet_count")] + #[serde(with = "serde_utils::quoted_u64")] + attestation_subnet_count: u64, + #[serde(default = "default_attestation_subnet_extra_bits")] #[serde(with = "serde_utils::quoted_u8")] - attestation_subnet_prefix_bits: u8, + attestation_subnet_extra_bits: u8, #[serde(default = "default_max_request_blocks_deneb")] #[serde(with = "serde_utils::quoted_u64")] max_request_blocks_deneb: u64, @@ -1966,8 +1988,36 @@ fn default_subnets_per_node() -> u8 { 2u8 } -fn default_attestation_subnet_prefix_bits() -> u8 { - 6 +const fn default_epochs_per_subnet_subscription() -> u64 { + 256 +} + +const fn default_attestation_subnet_count() -> u64 { + 64 +} + +const fn default_attestation_subnet_extra_bits() -> u8 { + 0 +} + +/// Compute attestation_subnet_prefix_bits dynamically as: +/// ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS +fn compute_attestation_subnet_prefix_bits( + attestation_subnet_count: u64, + attestation_subnet_extra_bits: u8, +) -> u8 { + let default_attestation_subnet_prefix_bits = 6u8; + + // ceillog2() = next_power_of_two().ilog2() + // casting to u8 is fine given ilog2(u64::MAX) = 63 + let min_bits_needed = attestation_subnet_count + .checked_next_power_of_two() + .and_then(|x| x.checked_ilog2()) + .unwrap_or(default_attestation_subnet_prefix_bits as u32) as u8; + + min_bits_needed + .safe_add(attestation_subnet_extra_bits) + .unwrap_or(default_attestation_subnet_prefix_bits) } const fn default_max_per_epoch_activation_churn_limit() -> u64 { @@ -2252,7 +2302,9 @@ impl Config { shard_committee_period: spec.shard_committee_period, eth1_follow_distance: spec.eth1_follow_distance, subnets_per_node: spec.subnets_per_node, - attestation_subnet_prefix_bits: spec.attestation_subnet_prefix_bits, + epochs_per_subnet_subscription: spec.epochs_per_subnet_subscription, + attestation_subnet_count: spec.attestation_subnet_count, + attestation_subnet_extra_bits: spec.attestation_subnet_extra_bits, inactivity_score_bias: spec.inactivity_score_bias, inactivity_score_recovery_rate: spec.inactivity_score_recovery_rate, @@ -2343,7 +2395,9 @@ impl Config { shard_committee_period, eth1_follow_distance, subnets_per_node, - attestation_subnet_prefix_bits, + epochs_per_subnet_subscription, + attestation_subnet_count, + attestation_subnet_extra_bits, inactivity_score_bias, inactivity_score_recovery_rate, ejection_balance, @@ -2416,6 +2470,9 @@ impl Config { shard_committee_period, eth1_follow_distance, subnets_per_node, + epochs_per_subnet_subscription, + attestation_subnet_count, + attestation_subnet_extra_bits, inactivity_score_bias, inactivity_score_recovery_rate, ejection_balance, @@ -2436,7 +2493,11 @@ impl Config { resp_timeout, message_domain_invalid_snappy, message_domain_valid_snappy, - attestation_subnet_prefix_bits, + // Compute attestation_subnet_prefix_bits dynamically + attestation_subnet_prefix_bits: compute_attestation_subnet_prefix_bits( + attestation_subnet_count, + attestation_subnet_extra_bits, + ), max_request_blocks, attestation_propagation_slot_range, maximum_gossip_clock_disparity, @@ -2605,6 +2666,13 @@ mod tests { } } } + + #[test] + fn test_compute_min_bits_for_n_values_edge_cases() { + assert_eq!(compute_attestation_subnet_prefix_bits(64, 0), 6); + assert_eq!(compute_attestation_subnet_prefix_bits(65, 0), 7); + assert_eq!(compute_attestation_subnet_prefix_bits(0, 1), 1); + } } #[cfg(test)] @@ -2684,6 +2752,9 @@ mod yaml_tests { REORG_HEAD_WEIGHT_THRESHOLD: 20 REORG_PARENT_WEIGHT_THRESHOLD: 160 REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 + EPOCHS_PER_SUBNET_SUBSCRIPTION: 256 + ATTESTATION_SUBNET_COUNT: 64 + ATTESTATION_SUBNET_EXTRA_BITS: 0 DEPOSIT_CHAIN_ID: 7042643276 DEPOSIT_NETWORK_ID: 7042643276 DEPOSIT_CONTRACT_ADDRESS: 0x00000000219ab540356cBB839Cbe05303d7705Fa @@ -2833,6 +2904,9 @@ mod yaml_tests { REORG_HEAD_WEIGHT_THRESHOLD: 20 REORG_PARENT_WEIGHT_THRESHOLD: 160 REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 + EPOCHS_PER_SUBNET_SUBSCRIPTION: 256 + ATTESTATION_SUBNET_COUNT: 64 + ATTESTATION_SUBNET_EXTRA_BITS: 0 DEPOSIT_CHAIN_ID: 7042643276 DEPOSIT_NETWORK_ID: 7042643276 DEPOSIT_CONTRACT_ADDRESS: 0x00000000219ab540356cBB839Cbe05303d7705Fa @@ -2942,6 +3016,9 @@ mod yaml_tests { MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8 CHURN_LIMIT_QUOTIENT: 65536 PROPOSER_SCORE_BOOST: 40 + EPOCHS_PER_SUBNET_SUBSCRIPTION: 256 + ATTESTATION_SUBNET_COUNT: 64 + ATTESTATION_SUBNET_EXTRA_BITS: 0 DEPOSIT_CHAIN_ID: 1 DEPOSIT_NETWORK_ID: 1 DEPOSIT_CONTRACT_ADDRESS: 0x00000000219ab540356cBB839Cbe05303d7705Fa @@ -2974,7 +3051,6 @@ mod yaml_tests { check_default!(resp_timeout); check_default!(message_domain_invalid_snappy); check_default!(message_domain_valid_snappy); - check_default!(attestation_subnet_prefix_bits); assert_eq!(chain_spec.bellatrix_fork_epoch, None); } diff --git a/consensus/types/src/core/config_and_preset.rs b/consensus/types/src/core/config_and_preset.rs index 08141c77311..5b8b27b02e2 100644 --- a/consensus/types/src/core/config_and_preset.rs +++ b/consensus/types/src/core/config_and_preset.rs @@ -114,6 +114,7 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap { let u32_hex = |v: u32| hex_string(&v.to_le_bytes()); let u8_hex = |v: u8| hex_string(&v.to_le_bytes()); hashmap! { + "attestation_subnet_prefix_bits".to_uppercase() => spec.attestation_subnet_prefix_bits.to_string().into(), "bls_withdrawal_prefix".to_uppercase() => u8_hex(spec.bls_withdrawal_prefix_byte), "eth1_address_withdrawal_prefix".to_uppercase() => u8_hex(spec.eth1_address_withdrawal_prefix_byte), "domain_beacon_proposer".to_uppercase() => u32_hex(spec.domain_beacon_proposer), @@ -131,6 +132,7 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap { "domain_sync_committee".to_uppercase() => u32_hex(spec.domain_sync_committee), "domain_sync_committee_selection_proof".to_uppercase() => u32_hex(spec.domain_sync_committee_selection_proof), + "domain_bls_to_execution_change".to_uppercase() => u32_hex(spec.domain_bls_to_execution_change), "sync_committee_subnet_count".to_uppercase() => consts::altair::SYNC_COMMITTEE_SUBNET_COUNT.to_string().into(), "target_aggregators_per_sync_subcommittee".to_uppercase() => @@ -183,4 +185,38 @@ mod test { serde_yaml::from_reader(reader).expect("error while deserializing"); assert_eq!(ConfigAndPreset::Gloas(from), yamlconfig); } + + #[test] + fn test_attestation_subnet_prefix_bits_in_extra_fields() { + let mainnet_spec = ChainSpec::mainnet(); + let config = ConfigAndPreset::from_chain_spec::(&mainnet_spec); + let extra_fields = config.extra_fields(); + assert!(extra_fields.contains_key("ATTESTATION_SUBNET_PREFIX_BITS")); + + // For mainnet: 64 subnets, 0 extra bits -> ceil(log2(64)) + 0 = 6 + assert_eq!( + extra_fields.get("ATTESTATION_SUBNET_PREFIX_BITS"), + Some(&Value::String("6".to_string())) + ); + } + + // This is not exhaustive, but it can be extended as new fields are added to the spec. + #[test] + fn test_required_spec_fields_exist() { + let mainnet_spec = ChainSpec::mainnet(); + let config = ConfigAndPreset::from_chain_spec::(&mainnet_spec); + let json = serde_json::to_value(&config).expect("should serialize"); + let obj = json.as_object().expect("should be an object"); + let required_fields = [ + "EPOCHS_PER_SUBNET_SUBSCRIPTION", + "ATTESTATION_SUBNET_COUNT", + "ATTESTATION_SUBNET_EXTRA_BITS", + "ATTESTATION_SUBNET_PREFIX_BITS", + "UPDATE_TIMEOUT", + "DOMAIN_BLS_TO_EXECUTION_CHANGE", + ]; + for field in required_fields { + assert!(obj.contains_key(field), "Missing required field: {}", field); + } + } } diff --git a/consensus/types/src/core/preset.rs b/consensus/types/src/core/preset.rs index 75d2d8df6b3..5b1978f8e9e 100644 --- a/consensus/types/src/core/preset.rs +++ b/consensus/types/src/core/preset.rs @@ -134,6 +134,9 @@ pub struct AltairPreset { pub epochs_per_sync_committee_period: Epoch, #[serde(with = "serde_utils::quoted_u64")] pub min_sync_committee_participants: u64, + #[serde(default = "default_update_timeout")] + #[serde(with = "serde_utils::quoted_u64")] + pub update_timeout: u64, } impl AltairPreset { @@ -145,10 +148,15 @@ impl AltairPreset { sync_committee_size: E::SyncCommitteeSize::to_u64(), epochs_per_sync_committee_period: spec.epochs_per_sync_committee_period, min_sync_committee_participants: spec.min_sync_committee_participants, + update_timeout: spec.update_timeout, } } } +const fn default_update_timeout() -> u64 { + 8192 +} + #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] pub struct BellatrixPreset { diff --git a/lighthouse/environment/tests/testnet_dir/config.yaml b/lighthouse/environment/tests/testnet_dir/config.yaml index 24c4a67225b..7cfeb746f2d 100644 --- a/lighthouse/environment/tests/testnet_dir/config.yaml +++ b/lighthouse/environment/tests/testnet_dir/config.yaml @@ -93,6 +93,7 @@ TTFB_TIMEOUT: 5 RESP_TIMEOUT: 10 MESSAGE_DOMAIN_INVALID_SNAPPY: 0x00000000 MESSAGE_DOMAIN_VALID_SNAPPY: 0x01000000 +EPOCHS_PER_SUBNET_SUBSCRIPTION: 256 ATTESTATION_SUBNET_COUNT: 64 ATTESTATION_SUBNET_EXTRA_BITS: 0 ATTESTATION_SUBNET_PREFIX_BITS: 6