diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index c53c29438e..13dcf22108 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -223,7 +223,7 @@ pub fn test_da_checker( let slot_clock = TestingSlotClock::new( Slot::new(0), Duration::from_secs(0), - Duration::from_secs(spec.seconds_per_slot), + spec.get_slot_duration(), ); let kzg = get_kzg(&spec); let ordered_custody_column_indices = generate_data_column_indices_rand_order::(); diff --git a/beacon_node/beacon_chain/tests/store_tests.rs b/beacon_node/beacon_chain/tests/store_tests.rs index 2b4152b550..fb5262b893 100644 --- a/beacon_node/beacon_chain/tests/store_tests.rs +++ b/beacon_node/beacon_chain/tests/store_tests.rs @@ -2910,7 +2910,7 @@ async fn reproduction_unaligned_checkpoint_sync_pruned_payload() { let slot_clock = TestingSlotClock::new( Slot::new(0), Duration::from_secs(harness.chain.genesis_time), - Duration::from_secs(spec.seconds_per_slot), + spec.get_slot_duration(), ); slot_clock.set_slot(harness.get_current_slot().as_u64()); @@ -5334,8 +5334,8 @@ async fn test_safely_backfill_data_column_custody_info() { .await; let epoch_before_increase = Epoch::new(start_epochs); - let effective_delay_slots = - CUSTODY_CHANGE_DA_EFFECTIVE_DELAY_SECONDS / harness.chain.spec.seconds_per_slot; + let effective_delay_slots = CUSTODY_CHANGE_DA_EFFECTIVE_DELAY_SECONDS + / harness.chain.spec.get_slot_duration().as_secs(); let cgc_change_slot = epoch_before_increase.end_slot(E::slots_per_epoch()); @@ -6131,7 +6131,7 @@ async fn bellatrix_produce_and_store_payloads() { .genesis_time() .safe_add( slot.as_u64() - .safe_mul(harness.spec.seconds_per_slot) + .safe_mul(harness.spec.get_slot_duration().as_secs()) .unwrap(), ) .unwrap(); diff --git a/beacon_node/http_api/tests/interactive_tests.rs b/beacon_node/http_api/tests/interactive_tests.rs index e0e4029875..15f61537a0 100644 --- a/beacon_node/http_api/tests/interactive_tests.rs +++ b/beacon_node/http_api/tests/interactive_tests.rs @@ -975,9 +975,10 @@ async fn proposer_duties_with_gossip_tolerance() { assert_eq!(harness.chain.slot().unwrap(), num_initial); // Set the clock to just before the next epoch. - harness.chain.slot_clock.advance_time( - Duration::from_secs(spec.seconds_per_slot) - spec.maximum_gossip_clock_disparity(), - ); + harness + .chain + .slot_clock + .advance_time(spec.get_slot_duration() - spec.maximum_gossip_clock_disparity()); assert_eq!( harness .chain @@ -1081,9 +1082,10 @@ async fn proposer_duties_v2_with_gossip_tolerance() { assert_eq!(harness.chain.slot().unwrap(), num_initial); // Set the clock to just before the next epoch. - harness.chain.slot_clock.advance_time( - Duration::from_secs(spec.seconds_per_slot) - spec.maximum_gossip_clock_disparity(), - ); + harness + .chain + .slot_clock + .advance_time(spec.get_slot_duration() - spec.maximum_gossip_clock_disparity()); assert_eq!( harness .chain 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 5df6370abe..02bf37cb55 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 @@ -56,21 +56,18 @@ ELECTRA_FORK_EPOCH: 364032 # May 7, 2025, 10:05:11am UTC FULU_FORK_VERSION: 0x06000000 FULU_FORK_EPOCH: 411392 # December 3, 2025, 09:49:11pm UTC # Gloas -GLOAS_FORK_VERSION: 0x07000000 # temporary stub +GLOAS_FORK_VERSION: 0x07000000 GLOAS_FORK_EPOCH: 18446744073709551615 -# EIP7441 -EIP7441_FORK_VERSION: 0x08000000 # temporary stub -EIP7441_FORK_EPOCH: 18446744073709551615 -# EIP7805 -EIP7805_FORK_VERSION: 0x0a000000 # temporary stub -EIP7805_FORK_EPOCH: 18446744073709551615 +# Heze +HEZE_FORK_VERSION: 0x08000000 +HEZE_FORK_EPOCH: 18446744073709551615 # EIP7928 -EIP7928_FORK_VERSION: 0x0b000000 # temporary stub +EIP7928_FORK_VERSION: 0xe7928000 # temporary stub EIP7928_FORK_EPOCH: 18446744073709551615 # Time parameters # --------------------------------------------------------------- -# 12 seconds (*deprecated*) +# 12 seconds SECONDS_PER_SLOT: 12 # 12000 milliseconds SLOT_DURATION_MS: 12000 @@ -96,8 +93,8 @@ SYNC_MESSAGE_DUE_BPS: 3333 CONTRIBUTION_DUE_BPS: 6667 # Gloas -# 2**12 (= 4,096) epochs -MIN_BUILDER_WITHDRAWABILITY_DELAY: 4096 +# 2**6 (= 64) epochs +MIN_BUILDER_WITHDRAWABILITY_DELAY: 64 # 2500 basis points, 25% of SLOT_DURATION_MS ATTESTATION_DUE_BPS_GLOAS: 2500 # 5000 basis points, 50% of SLOT_DURATION_MS @@ -109,7 +106,7 @@ CONTRIBUTION_DUE_BPS_GLOAS: 5000 # 7500 basis points, 75% of SLOT_DURATION_MS PAYLOAD_ATTESTATION_DUE_BPS: 7500 -# EIP7805 +# Heze # 7500 basis points, 75% of SLOT_DURATION_MS VIEW_FREEZE_CUTOFF_BPS: 7500 # 6667 basis points, ~67% of SLOT_DURATION_MS @@ -166,8 +163,6 @@ MAX_PAYLOAD_SIZE: 10485760 MAX_REQUEST_BLOCKS: 1024 # 2**8 (= 256) epochs EPOCHS_PER_SUBNET_SUBSCRIPTION: 256 -# MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2 (= 33,024) epochs -MIN_EPOCHS_FOR_BLOCK_REQUESTS: 33024 # 2**5 (= 32) slots ATTESTATION_PROPAGATION_SLOT_RANGE: 32 # 500ms @@ -180,8 +175,6 @@ SUBNETS_PER_NODE: 2 ATTESTATION_SUBNET_COUNT: 64 # 0 bits ATTESTATION_SUBNET_EXTRA_BITS: 0 -# ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS (= 6 + 0) bits -ATTESTATION_SUBNET_PREFIX_BITS: 6 # Deneb # 2**7 (= 128) blocks @@ -192,24 +185,18 @@ MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096 BLOB_SIDECAR_SUBNET_COUNT: 6 # 6 blobs MAX_BLOBS_PER_BLOCK: 6 -# MAX_REQUEST_BLOCKS_DENEB * MAX_BLOBS_PER_BLOCK (= 128 * 6) sidecars -MAX_REQUEST_BLOB_SIDECARS: 768 # Electra # 9 subnets BLOB_SIDECAR_SUBNET_COUNT_ELECTRA: 9 # 9 blobs MAX_BLOBS_PER_BLOCK_ELECTRA: 9 -# MAX_REQUEST_BLOCKS_DENEB * MAX_BLOBS_PER_BLOCK_ELECTRA (= 128 * 9) sidecars -MAX_REQUEST_BLOB_SIDECARS_ELECTRA: 1152 # Fulu # 2**7 (= 128) groups NUMBER_OF_CUSTODY_GROUPS: 128 # 2**7 (= 128) subnets DATA_COLUMN_SIDECAR_SUBNET_COUNT: 128 -# MAX_REQUEST_BLOCKS_DENEB * NUMBER_OF_COLUMNS (= 128 * 128) sidecars -MAX_REQUEST_DATA_COLUMN_SIDECARS: 16384 # 2**3 (= 8) samples SAMPLES_PER_SLOT: 8 # 2**2 (= 4) sidecars @@ -225,18 +212,13 @@ MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 # 2**7 (= 128) payloads MAX_REQUEST_PAYLOADS: 128 -# EIP7441 -# 2**8 (= 256) epochs -EPOCHS_PER_SHUFFLING_PHASE: 256 -# 2**1 (= 2) epochs -PROPOSER_SELECTION_GAP: 2 - -# EIP7805 +# Heze # 2**4 (= 16) inclusion lists MAX_REQUEST_INCLUSION_LIST: 16 # 2**13 (= 8,192) bytes MAX_BYTES_PER_INCLUSION_LIST: 8192 + # Blob Scheduling # --------------------------------------------------------------- diff --git a/consensus/types/configs/mainnet.yaml b/consensus/types/configs/mainnet.yaml new file mode 100644 index 0000000000..ab85bd9e71 --- /dev/null +++ b/consensus/types/configs/mainnet.yaml @@ -0,0 +1,227 @@ +# Mainnet config + +# Extends the mainnet preset +PRESET_BASE: 'mainnet' + +# Free-form short name of the network that this configuration applies to - known +# canonical network names include: +# * 'mainnet' - there can be only one +# * 'sepolia' - testnet +# * 'holesky' - testnet +# * 'hoodi' - testnet +# Must match the regex: [a-z0-9\-] +CONFIG_NAME: 'mainnet' + +# Transition +# --------------------------------------------------------------- +# Estimated on Sept 15, 2022 +TERMINAL_TOTAL_DIFFICULTY: 58750000000000000000000 +# By default, don't use these params +TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000 +TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615 + +# Genesis +# --------------------------------------------------------------- +# 2**14 (= 16,384) validators +MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 16384 +# Dec 1, 2020, 12pm UTC +MIN_GENESIS_TIME: 1606824000 +# Initial fork version for mainnet +GENESIS_FORK_VERSION: 0x00000000 +# 7 * 24 * 3,600 (= 604,800) seconds, 7 days +GENESIS_DELAY: 604800 + +# Forking +# --------------------------------------------------------------- +# Some forks are disabled for now: +# - These may be re-assigned to another fork-version later +# - Temporarily set to max uint64 value: 2**64 - 1 + +# Altair +ALTAIR_FORK_VERSION: 0x01000000 +ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23am UTC +# Bellatrix +BELLATRIX_FORK_VERSION: 0x02000000 +BELLATRIX_FORK_EPOCH: 144896 # Sept 6, 2022, 11:34:47am UTC +# Capella +CAPELLA_FORK_VERSION: 0x03000000 +CAPELLA_FORK_EPOCH: 194048 # April 12, 2023, 10:27:35pm UTC +# Deneb +DENEB_FORK_VERSION: 0x04000000 +DENEB_FORK_EPOCH: 269568 # March 13, 2024, 01:55:35pm UTC +# Electra +ELECTRA_FORK_VERSION: 0x05000000 +ELECTRA_FORK_EPOCH: 364032 # May 7, 2025, 10:05:11am UTC +# Fulu +FULU_FORK_VERSION: 0x06000000 +FULU_FORK_EPOCH: 411392 # December 3, 2025, 09:49:11pm UTC +# Gloas +GLOAS_FORK_VERSION: 0x07000000 +GLOAS_FORK_EPOCH: 18446744073709551615 +# Heze +HEZE_FORK_VERSION: 0x08000000 +HEZE_FORK_EPOCH: 18446744073709551615 +# EIP7928 +EIP7928_FORK_VERSION: 0xe7928000 # temporary stub +EIP7928_FORK_EPOCH: 18446744073709551615 + +# Time parameters +# --------------------------------------------------------------- +# 12000 milliseconds +SLOT_DURATION_MS: 12000 +# 14 (estimate from Eth1 mainnet) +SECONDS_PER_ETH1_BLOCK: 14 +# 2**8 (= 256) epochs +MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 +# 2**8 (= 256) epochs +SHARD_COMMITTEE_PERIOD: 256 +# 2**11 (= 2,048) Eth1 blocks +ETH1_FOLLOW_DISTANCE: 2048 +# 1667 basis points, ~17% of SLOT_DURATION_MS +PROPOSER_REORG_CUTOFF_BPS: 1667 +# 3333 basis points, ~33% of SLOT_DURATION_MS +ATTESTATION_DUE_BPS: 3333 +# 6667 basis points, ~67% of SLOT_DURATION_MS +AGGREGATE_DUE_BPS: 6667 + +# Altair +# 3333 basis points, ~33% of SLOT_DURATION_MS +SYNC_MESSAGE_DUE_BPS: 3333 +# 6667 basis points, ~67% of SLOT_DURATION_MS +CONTRIBUTION_DUE_BPS: 6667 + +# Gloas +# 2**6 (= 64) epochs +MIN_BUILDER_WITHDRAWABILITY_DELAY: 64 +# 2500 basis points, 25% of SLOT_DURATION_MS +ATTESTATION_DUE_BPS_GLOAS: 2500 +# 5000 basis points, 50% of SLOT_DURATION_MS +AGGREGATE_DUE_BPS_GLOAS: 5000 +# 2500 basis points, 25% of SLOT_DURATION_MS +SYNC_MESSAGE_DUE_BPS_GLOAS: 2500 +# 5000 basis points, 50% of SLOT_DURATION_MS +CONTRIBUTION_DUE_BPS_GLOAS: 5000 +# 7500 basis points, 75% of SLOT_DURATION_MS +PAYLOAD_ATTESTATION_DUE_BPS: 7500 + +# Heze +# 7500 basis points, 75% of SLOT_DURATION_MS +VIEW_FREEZE_CUTOFF_BPS: 7500 +# 6667 basis points, ~67% of SLOT_DURATION_MS +INCLUSION_LIST_SUBMISSION_DUE_BPS: 6667 +# 9167 basis points, ~92% of SLOT_DURATION_MS +PROPOSER_INCLUSION_LIST_CUTOFF_BPS: 9167 + +# Validator cycle +# --------------------------------------------------------------- +# 2**2 (= 4) +INACTIVITY_SCORE_BIAS: 4 +# 2**4 (= 16) +INACTIVITY_SCORE_RECOVERY_RATE: 16 +# 2**4 * 10**9 (= 16,000,000,000) Gwei +EJECTION_BALANCE: 16000000000 +# 2**2 (= 4) validators +MIN_PER_EPOCH_CHURN_LIMIT: 4 +# 2**16 (= 65,536) +CHURN_LIMIT_QUOTIENT: 65536 + +# Deneb +# 2**3 (= 8) (*deprecated*) +MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8 + +# Electra +# 2**7 * 10**9 (= 128,000,000,000) Gwei +MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 128000000000 +# 2**8 * 10**9 (= 256,000,000,000) Gwei +MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 256000000000 + +# Fork choice +# --------------------------------------------------------------- +# 40% +PROPOSER_SCORE_BOOST: 40 +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# 2 epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 + +# Deposit contract +# --------------------------------------------------------------- +# Ethereum PoW Mainnet +DEPOSIT_CHAIN_ID: 1 +DEPOSIT_NETWORK_ID: 1 +DEPOSIT_CONTRACT_ADDRESS: 0x00000000219ab540356cBB839Cbe05303d7705Fa + +# Networking +# --------------------------------------------------------------- +# 10 * 2**20 (= 10,485,760) bytes, 10 MiB +MAX_PAYLOAD_SIZE: 10485760 +# 2**10 (= 1,024) blocks +MAX_REQUEST_BLOCKS: 1024 +# 2**8 (= 256) epochs +EPOCHS_PER_SUBNET_SUBSCRIPTION: 256 +# 2**5 (= 32) slots +ATTESTATION_PROPAGATION_SLOT_RANGE: 32 +# 500ms +MAXIMUM_GOSSIP_CLOCK_DISPARITY: 500 +MESSAGE_DOMAIN_INVALID_SNAPPY: 0x00000000 +MESSAGE_DOMAIN_VALID_SNAPPY: 0x01000000 +# 2 subnets per node +SUBNETS_PER_NODE: 2 +# 2**6 (= 64) subnets +ATTESTATION_SUBNET_COUNT: 64 +# 0 bits +ATTESTATION_SUBNET_EXTRA_BITS: 0 + +# Deneb +# 2**7 (= 128) blocks +MAX_REQUEST_BLOCKS_DENEB: 128 +# 2**12 (= 4,096) epochs +MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096 +# 6 subnets +BLOB_SIDECAR_SUBNET_COUNT: 6 +# 6 blobs +MAX_BLOBS_PER_BLOCK: 6 + +# Electra +# 9 subnets +BLOB_SIDECAR_SUBNET_COUNT_ELECTRA: 9 +# 9 blobs +MAX_BLOBS_PER_BLOCK_ELECTRA: 9 + +# Fulu +# 2**7 (= 128) groups +NUMBER_OF_CUSTODY_GROUPS: 128 +# 2**7 (= 128) subnets +DATA_COLUMN_SIDECAR_SUBNET_COUNT: 128 +# 2**3 (= 8) samples +SAMPLES_PER_SLOT: 8 +# 2**2 (= 4) sidecars +CUSTODY_REQUIREMENT: 4 +# 2**3 (= 8) sidecars +VALIDATOR_CUSTODY_REQUIREMENT: 8 +# 2**5 * 10**9 (= 32,000,000,000) Gwei +BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 +# 2**12 (= 4,096) epochs +MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 + +# Gloas +# 2**7 (= 128) payloads +MAX_REQUEST_PAYLOADS: 128 + +# Heze +# 2**4 (= 16) inclusion lists +MAX_REQUEST_INCLUSION_LIST: 16 +# 2**13 (= 8,192) bytes +MAX_BYTES_PER_INCLUSION_LIST: 8192 + + +# Blob Scheduling +# --------------------------------------------------------------- + +BLOB_SCHEDULE: + - EPOCH: 412672 # December 9, 2025, 02:21:11pm UTC + MAX_BLOBS_PER_BLOCK: 15 + - EPOCH: 419072 # January 7, 2026, 01:01:11am UTC + MAX_BLOBS_PER_BLOCK: 21 diff --git a/consensus/types/configs/minimal.yaml b/consensus/types/configs/minimal.yaml new file mode 100644 index 0000000000..8c0d7254fe --- /dev/null +++ b/consensus/types/configs/minimal.yaml @@ -0,0 +1,220 @@ +# Minimal config + +# Extends the minimal preset +PRESET_BASE: 'minimal' + +# Free-form short name of the network that this configuration applies to - known +# canonical network names include: +# * 'minimal' - spec-testing +# Must match the regex: [a-z0-9\-] +CONFIG_NAME: 'minimal' + +# Transition +# --------------------------------------------------------------- +# 2**256-2**10 for testing minimal network +TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129638912 +# By default, don't use these params +TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000 +TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615 + +# Genesis +# --------------------------------------------------------------- +# [customized] 2**6 (= 64) validators +MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 64 +# [customized] Jan 3, 2020, 12am UTC +MIN_GENESIS_TIME: 1578009600 +# [customized] Initial fork version for minimal +GENESIS_FORK_VERSION: 0x00000001 +# [customized] 5 * 60 (= 300) seconds +GENESIS_DELAY: 300 + +# Forking +# --------------------------------------------------------------- +# Values provided for illustrative purposes. +# Individual tests/testnets may set different values. + +# [customized] Altair +ALTAIR_FORK_VERSION: 0x01000001 +ALTAIR_FORK_EPOCH: 18446744073709551615 +# [customized] Bellatrix +BELLATRIX_FORK_VERSION: 0x02000001 +BELLATRIX_FORK_EPOCH: 18446744073709551615 +# [customized] Capella +CAPELLA_FORK_VERSION: 0x03000001 +CAPELLA_FORK_EPOCH: 18446744073709551615 +# [customized] Deneb +DENEB_FORK_VERSION: 0x04000001 +DENEB_FORK_EPOCH: 18446744073709551615 +# [customized] Electra +ELECTRA_FORK_VERSION: 0x05000001 +ELECTRA_FORK_EPOCH: 18446744073709551615 +# [customized] Fulu +FULU_FORK_VERSION: 0x06000001 +FULU_FORK_EPOCH: 18446744073709551615 +# [customized] Gloas +GLOAS_FORK_VERSION: 0x07000001 +GLOAS_FORK_EPOCH: 18446744073709551615 +# [customized] Heze +HEZE_FORK_VERSION: 0x08000001 +HEZE_FORK_EPOCH: 18446744073709551615 +# [customized] EIP7928 +EIP7928_FORK_VERSION: 0xe7928001 +EIP7928_FORK_EPOCH: 18446744073709551615 + +# Time parameters +# --------------------------------------------------------------- +# [customized] 6000 milliseconds +SLOT_DURATION_MS: 6000 +# 14 (estimate from Eth1 mainnet) +SECONDS_PER_ETH1_BLOCK: 14 +# 2**8 (= 256) epochs +MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 +# [customized] 2**6 (= 64) epochs +SHARD_COMMITTEE_PERIOD: 64 +# [customized] 2**4 (= 16) Eth1 blocks +ETH1_FOLLOW_DISTANCE: 16 +# 1667 basis points, ~17% of SLOT_DURATION_MS +PROPOSER_REORG_CUTOFF_BPS: 1667 +# 3333 basis points, ~33% of SLOT_DURATION_MS +ATTESTATION_DUE_BPS: 3333 +# 6667 basis points, ~67% of SLOT_DURATION_MS +AGGREGATE_DUE_BPS: 6667 + +# Altair +# 3333 basis points, ~33% of SLOT_DURATION_MS +SYNC_MESSAGE_DUE_BPS: 3333 +# 6667 basis points, ~67% of SLOT_DURATION_MS +CONTRIBUTION_DUE_BPS: 6667 + +# Gloas +# [customized] 2**1 (= 2) epochs +MIN_BUILDER_WITHDRAWABILITY_DELAY: 2 +# 2500 basis points, 25% of SLOT_DURATION_MS +ATTESTATION_DUE_BPS_GLOAS: 2500 +# 5000 basis points, 50% of SLOT_DURATION_MS +AGGREGATE_DUE_BPS_GLOAS: 5000 +# 2500 basis points, 25% of SLOT_DURATION_MS +SYNC_MESSAGE_DUE_BPS_GLOAS: 2500 +# 5000 basis points, 50% of SLOT_DURATION_MS +CONTRIBUTION_DUE_BPS_GLOAS: 5000 +# 7500 basis points, 75% of SLOT_DURATION_MS +PAYLOAD_ATTESTATION_DUE_BPS: 7500 + +# Heze +# 7500 basis points, 75% of SLOT_DURATION_MS +VIEW_FREEZE_CUTOFF_BPS: 7500 +# 6667 basis points, ~67% of SLOT_DURATION_MS +INCLUSION_LIST_SUBMISSION_DUE_BPS: 6667 +# 9167 basis points, ~92% of SLOT_DURATION_MS +PROPOSER_INCLUSION_LIST_CUTOFF_BPS: 9167 + +# Validator cycle +# --------------------------------------------------------------- +# 2**2 (= 4) +INACTIVITY_SCORE_BIAS: 4 +# 2**4 (= 16) +INACTIVITY_SCORE_RECOVERY_RATE: 16 +# 2**4 * 10**9 (= 16,000,000,000) Gwei +EJECTION_BALANCE: 16000000000 +# [customized] 2**1 (= 2) validators +MIN_PER_EPOCH_CHURN_LIMIT: 2 +# [customized] 2**5 (= 32) +CHURN_LIMIT_QUOTIENT: 32 + +# Deneb +# [customized] 2**2 (= 4) (*deprecated*) +MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 4 + +# Electra +# [customized] 2**6 * 10**9 (= 64,000,000,000) Gwei +MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 64000000000 +# [customized] 2**7 * 10**9 (= 128,000,000,000) Gwei +MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 128000000000 + +# Fork choice +# --------------------------------------------------------------- +# 40% +PROPOSER_SCORE_BOOST: 40 +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# 2 epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 + +# Deposit contract +# --------------------------------------------------------------- +# Ethereum Goerli testnet +DEPOSIT_CHAIN_ID: 5 +DEPOSIT_NETWORK_ID: 5 +# Configured on a per testnet basis +DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890 + +# Networking +# --------------------------------------------------------------- +# 10 * 2**20 (= 10,485,760) bytes, 10 MiB +MAX_PAYLOAD_SIZE: 10485760 +# 2**10 (= 1,024) blocks +MAX_REQUEST_BLOCKS: 1024 +# 2**8 (= 256) epochs +EPOCHS_PER_SUBNET_SUBSCRIPTION: 256 +# 2**5 (= 32) slots +ATTESTATION_PROPAGATION_SLOT_RANGE: 32 +# 500ms +MAXIMUM_GOSSIP_CLOCK_DISPARITY: 500 +MESSAGE_DOMAIN_INVALID_SNAPPY: 0x00000000 +MESSAGE_DOMAIN_VALID_SNAPPY: 0x01000000 +# 2 subnets per node +SUBNETS_PER_NODE: 2 +# 2**6 (= 64) subnets +ATTESTATION_SUBNET_COUNT: 64 +# 0 bits +ATTESTATION_SUBNET_EXTRA_BITS: 0 + +# Deneb +# 2**7 (= 128) blocks +MAX_REQUEST_BLOCKS_DENEB: 128 +# 2**12 (= 4,096) epochs +MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096 +# 6 subnets +BLOB_SIDECAR_SUBNET_COUNT: 6 +# 6 blobs +MAX_BLOBS_PER_BLOCK: 6 + +# Electra +# 9 subnets +BLOB_SIDECAR_SUBNET_COUNT_ELECTRA: 9 +# 9 blobs +MAX_BLOBS_PER_BLOCK_ELECTRA: 9 + +# Fulu +# 2**7 (= 128) groups +NUMBER_OF_CUSTODY_GROUPS: 128 +# 2**7 (= 128) subnets +DATA_COLUMN_SIDECAR_SUBNET_COUNT: 128 +# 2**3 (= 8) samples +SAMPLES_PER_SLOT: 8 +# 2**2 (= 4) sidecars +CUSTODY_REQUIREMENT: 4 +# 2**3 (= 8) sidecars +VALIDATOR_CUSTODY_REQUIREMENT: 8 +# 2**5 * 10**9 (= 32,000,000,000) Gwei +BALANCE_PER_ADDITIONAL_CUSTODY_GROUP: 32000000000 +# 2**12 (= 4,096) epochs +MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS: 4096 + +# Gloas +# 2**7 (= 128) payloads +MAX_REQUEST_PAYLOADS: 128 + +# Heze +# 2**4 (= 16) inclusion lists +MAX_REQUEST_INCLUSION_LIST: 16 +# 2**13 (= 8,192) bytes +MAX_BYTES_PER_INCLUSION_LIST: 8192 + + +# Blob Scheduling +# --------------------------------------------------------------- + +BLOB_SCHEDULE: [] diff --git a/consensus/types/src/core/chain_spec.rs b/consensus/types/src/core/chain_spec.rs index 8a2b3a23e8..01c4c7bbfd 100644 --- a/consensus/types/src/core/chain_spec.rs +++ b/consensus/types/src/core/chain_spec.rs @@ -96,8 +96,7 @@ pub struct ChainSpec { * Time parameters */ pub genesis_delay: u64, - // TODO deprecate seconds_per_slot - pub seconds_per_slot: u64, + seconds_per_slot: u64, // Private so that this value can't get changed except via the `set_slot_duration_ms` function. slot_duration_ms: u64, pub min_attestation_inclusion_delay: u64, @@ -914,6 +913,7 @@ impl ChainSpec { /// Set the duration of a slot (in ms). pub fn set_slot_duration_ms(mut self, slot_duration_ms: u64) -> Self { self.slot_duration_ms = slot_duration_ms; + self.seconds_per_slot = slot_duration_ms.saturating_div(1000); self.compute_derived_values::() } @@ -1235,7 +1235,7 @@ impl ChainSpec { gloas_fork_epoch: None, builder_payment_threshold_numerator: 6, builder_payment_threshold_denominator: 10, - min_builder_withdrawability_delay: Epoch::new(4096), + min_builder_withdrawability_delay: Epoch::new(64), max_request_payloads: 128, /* @@ -1381,6 +1381,7 @@ impl ChainSpec { // Gloas gloas_fork_version: [0x07, 0x00, 0x00, 0x01], gloas_fork_epoch: None, + min_builder_withdrawability_delay: Epoch::new(2), /* * Derived time values (set by `compute_derived_values()`) @@ -1391,6 +1392,9 @@ impl ChainSpec { sync_message_due: Duration::from_millis(1999), contribution_and_proof_due: Duration::from_millis(4000), + // Networking Fulu + blob_schedule: BlobSchedule::default(), + // Other network_id: 2, // lighthouse testnet network id deposit_chain_id: 5, @@ -1631,7 +1635,7 @@ impl ChainSpec { gloas_fork_epoch: None, builder_payment_threshold_numerator: 6, builder_payment_threshold_denominator: 10, - min_builder_withdrawability_delay: Epoch::new(4096), + min_builder_withdrawability_delay: Epoch::new(64), max_request_payloads: 128, /* @@ -1908,8 +1912,9 @@ pub struct Config { #[serde(deserialize_with = "deserialize_fork_epoch")] pub gloas_fork_epoch: Option>, - #[serde(with = "serde_utils::quoted_u64")] - seconds_per_slot: u64, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + seconds_per_slot: Option>, #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] slot_duration_ms: Option>, @@ -2064,6 +2069,10 @@ pub struct Config { #[serde(default = "default_contribution_due_bps")] #[serde(with = "serde_utils::quoted_u64")] contribution_due_bps: u64, + + #[serde(default = "default_min_builder_withdrawability_delay")] + #[serde(with = "serde_utils::quoted_u64")] + min_builder_withdrawability_delay: u64, } fn default_bellatrix_fork_version() -> [u8; 4] { @@ -2289,6 +2298,10 @@ const fn default_contribution_due_bps() -> u64 { 6667 } +const fn default_min_builder_withdrawability_delay() -> u64 { + 64 +} + fn max_blocks_by_root_request_common(max_request_blocks: u64) -> usize { let max_request_blocks = max_request_blocks as usize; RuntimeVariableList::::new( @@ -2459,7 +2472,9 @@ impl Config { .gloas_fork_epoch .map(|epoch| MaybeQuoted { value: epoch }), - seconds_per_slot: spec.seconds_per_slot, + seconds_per_slot: Some(MaybeQuoted { + value: spec.seconds_per_slot, + }), slot_duration_ms: Some(MaybeQuoted { value: spec.slot_duration_ms, }), @@ -2525,6 +2540,8 @@ impl Config { aggregate_due_bps: spec.aggregate_due_bps, sync_message_due_bps: spec.sync_message_due_bps, contribution_due_bps: spec.contribution_due_bps, + + min_builder_withdrawability_delay: spec.min_builder_withdrawability_delay.as_u64(), } } @@ -2616,12 +2633,21 @@ impl Config { aggregate_due_bps, sync_message_due_bps, contribution_due_bps, + min_builder_withdrawability_delay, } = self; if preset_base != E::spec_name().to_string().as_str() { return None; } + // Fail if seconds_per_slot and slot_duration_ms are both set but are inconsistent. + if let (Some(seconds_per_slot), Some(slot_duration_ms)) = + (seconds_per_slot, slot_duration_ms) + && seconds_per_slot.value.saturating_mul(1000) != slot_duration_ms.value + { + return None; + } + let spec = ChainSpec { config_name: config_name.clone(), min_genesis_active_validator_count, @@ -2642,10 +2668,12 @@ impl Config { fulu_fork_version, gloas_fork_version, gloas_fork_epoch: gloas_fork_epoch.map(|q| q.value), - seconds_per_slot, + seconds_per_slot: seconds_per_slot + .map(|q| q.value) + .or_else(|| slot_duration_ms.and_then(|q| q.value.checked_div(1000)))?, slot_duration_ms: slot_duration_ms .map(|q| q.value) - .unwrap_or_else(|| seconds_per_slot.saturating_mul(1000)), + .or_else(|| seconds_per_slot.map(|q| q.value.saturating_mul(1000)))?, seconds_per_eth1_block, min_validator_withdrawability_delay, shard_committee_period, @@ -2705,6 +2733,8 @@ impl Config { sync_message_due_bps, contribution_due_bps, + min_builder_withdrawability_delay: Epoch::new(min_builder_withdrawability_delay), + ..chain_spec.clone() }; Some(spec.compute_derived_values::()) @@ -2853,6 +2883,9 @@ mod yaml_tests { use super::*; use crate::core::MinimalEthSpec; use paste::paste; + use std::collections::BTreeSet; + use std::env; + use std::path::PathBuf; use std::sync::Arc; use tempfile::NamedTempFile; @@ -2902,6 +2935,67 @@ mod yaml_tests { assert_eq!(from, yamlconfig); } + #[test] + fn slot_duration_fallback_both_fields() { + let mainnet = ChainSpec::mainnet(); + let mut config = Config::from_chain_spec::(&mainnet); + config.seconds_per_slot = Some(MaybeQuoted { value: 12 }); + config.slot_duration_ms = Some(MaybeQuoted { value: 12000 }); + let spec = config + .apply_to_chain_spec::(&mainnet) + .unwrap(); + assert_eq!(spec.seconds_per_slot, 12); + assert_eq!(spec.slot_duration_ms, 12000); + } + + #[test] + fn slot_duration_fallback_both_fields_inconsistent() { + let mainnet = ChainSpec::mainnet(); + let mut config = Config::from_chain_spec::(&mainnet); + config.seconds_per_slot = Some(MaybeQuoted { value: 10 }); + config.slot_duration_ms = Some(MaybeQuoted { value: 12000 }); + assert_eq!(config.apply_to_chain_spec::(&mainnet), None); + } + + #[test] + fn slot_duration_fallback_seconds_only() { + let mainnet = ChainSpec::mainnet(); + let mut config = Config::from_chain_spec::(&mainnet); + config.seconds_per_slot = Some(MaybeQuoted { value: 12 }); + config.slot_duration_ms = None; + let spec = config + .apply_to_chain_spec::(&mainnet) + .unwrap(); + assert_eq!(spec.seconds_per_slot, 12); + assert_eq!(spec.slot_duration_ms, 12000); + } + + #[test] + fn slot_duration_fallback_ms_only() { + let mainnet = ChainSpec::mainnet(); + let mut config = Config::from_chain_spec::(&mainnet); + config.seconds_per_slot = None; + config.slot_duration_ms = Some(MaybeQuoted { value: 12000 }); + let spec = config + .apply_to_chain_spec::(&mainnet) + .unwrap(); + assert_eq!(spec.seconds_per_slot, 12); + assert_eq!(spec.slot_duration_ms, 12000); + } + + #[test] + fn slot_duration_fallback_neither() { + let mainnet = ChainSpec::mainnet(); + let mut config = Config::from_chain_spec::(&mainnet); + config.seconds_per_slot = None; + config.slot_duration_ms = None; + assert!( + config + .apply_to_chain_spec::(&mainnet) + .is_none() + ); + } + #[test] fn blob_schedule_max_blobs_per_block() { let spec_contents = r#" @@ -3375,7 +3469,6 @@ mod yaml_tests { // Test slot duration let slot_duration = spec.get_slot_duration(); assert_eq!(slot_duration, Duration::from_millis(12000)); - assert_eq!(slot_duration, Duration::from_secs(spec.seconds_per_slot)); // Test edge cases with custom spec let mut custom_spec = spec.clone(); @@ -3485,4 +3578,133 @@ mod yaml_tests { spec.attestation_due_bps = 15000; spec.compute_derived_values::(); } + + fn configs_base_path() -> PathBuf { + env::var("CARGO_MANIFEST_DIR") + .expect("should know manifest dir") + .parse::() + .expect("should parse manifest dir as path") + .join("configs") + } + + /// Upstream config keys that Lighthouse intentionally does not include in its + /// `Config` struct. These are forks/features not yet implemented. Update this + /// list as new forks are added. + const UPSTREAM_KEYS_NOT_IN_LIGHTHOUSE: &[&str] = &[ + // Forks not yet implemented + "HEZE_FORK_VERSION", + "HEZE_FORK_EPOCH", + "EIP7928_FORK_VERSION", + "EIP7928_FORK_EPOCH", + // Gloas params not yet in Config + "ATTESTATION_DUE_BPS_GLOAS", + "AGGREGATE_DUE_BPS_GLOAS", + "SYNC_MESSAGE_DUE_BPS_GLOAS", + "CONTRIBUTION_DUE_BPS_GLOAS", + "PAYLOAD_ATTESTATION_DUE_BPS", + "MAX_REQUEST_PAYLOADS", + // Gloas fork choice params not yet in Config + "REORG_HEAD_WEIGHT_THRESHOLD", + "REORG_PARENT_WEIGHT_THRESHOLD", + "REORG_MAX_EPOCHS_SINCE_FINALIZATION", + // Heze networking + "VIEW_FREEZE_CUTOFF_BPS", + "INCLUSION_LIST_SUBMISSION_DUE_BPS", + "PROPOSER_INCLUSION_LIST_CUTOFF_BPS", + "MAX_REQUEST_INCLUSION_LIST", + "MAX_BYTES_PER_INCLUSION_LIST", + ]; + + /// Compare a `ChainSpec` against an upstream consensus-specs config YAML file. + /// + /// 1. Extracts keys from the raw YAML text (to avoid yaml_serde's inability + /// to parse integers > u64 into `Value`/`Mapping` types) and checks that + /// every key is either known to `Config` or explicitly listed in + /// `UPSTREAM_KEYS_NOT_IN_LIGHTHOUSE`. + /// 2. Deserializes the upstream YAML as `Config` (which has custom + /// deserializers for large values like `TERMINAL_TOTAL_DIFFICULTY`) and + /// compares against `Config::from_chain_spec`. + fn config_test(spec: &ChainSpec, config_name: &str) { + let file_path = configs_base_path().join(format!("{config_name}.yaml")); + let upstream_yaml = std::fs::read_to_string(&file_path) + .unwrap_or_else(|e| panic!("failed to read {}: {e}", file_path.display())); + + // Extract top-level keys from the raw YAML text. We can't parse as + // yaml_serde::Mapping because yaml_serde cannot represent integers + // exceeding u64 (e.g. TERMINAL_TOTAL_DIFFICULTY). Config YAML uses a + // simple `KEY: value` format with no indentation for top-level keys. + let upstream_keys: BTreeSet = upstream_yaml + .lines() + .filter_map(|line| { + // Skip comments, blank lines, and indented lines (nested YAML). + if line.is_empty() + || line.starts_with('#') + || line.starts_with(' ') + || line.starts_with('\t') + { + return None; + } + line.split(':').next().map(|k| k.to_string()) + }) + .collect(); + + // Get the set of keys that Config knows about by serializing and collecting + // keys. Also include keys for optional fields that may be skipped during + // serialization (e.g. CONFIG_NAME). + let our_config = Config::from_chain_spec::(spec); + let our_yaml = yaml_serde::to_string(&our_config).expect("failed to serialize Config"); + let our_mapping: yaml_serde::Mapping = + yaml_serde::from_str(&our_yaml).expect("failed to re-parse our Config"); + let mut known_keys: BTreeSet = our_mapping + .keys() + .filter_map(|k| k.as_str().map(String::from)) + .collect(); + // Fields that Config knows but may skip during serialization. + known_keys.insert("CONFIG_NAME".to_string()); + + // Check for upstream keys that our Config doesn't know about. + let mut missing_keys: Vec<&String> = upstream_keys + .iter() + .filter(|k| { + !known_keys.contains(k.as_str()) + && !UPSTREAM_KEYS_NOT_IN_LIGHTHOUSE.contains(&k.as_str()) + }) + .collect(); + missing_keys.sort(); + + assert!( + missing_keys.is_empty(), + "Upstream {config_name} config has keys not present in Lighthouse Config \ + (add to Config or to UPSTREAM_KEYS_NOT_IN_LIGHTHOUSE): {missing_keys:?}" + ); + + // Compare values for all fields Config knows about. + let mut upstream_config: Config = yaml_serde::from_str(&upstream_yaml) + .unwrap_or_else(|e| panic!("failed to parse {config_name} as Config: {e}")); + + // CONFIG_NAME is network metadata (not a spec parameter), so align it + // before comparing. + upstream_config.config_name = our_config.config_name.clone(); + // SECONDS_PER_SLOT is deprecated upstream but we still emit it, so + // fill it in if the upstream YAML omitted it. + if upstream_config.seconds_per_slot.is_none() { + upstream_config.seconds_per_slot = our_config.seconds_per_slot; + } + assert_eq!( + upstream_config, our_config, + "Config mismatch for {config_name}" + ); + } + + #[test] + fn mainnet_config_consistent() { + let spec = ChainSpec::mainnet(); + config_test::(&spec, "mainnet"); + } + + #[test] + fn minimal_config_consistent() { + let spec = ChainSpec::minimal(); + config_test::(&spec, "minimal"); + } }