diff --git a/beacon_node/beacon_chain/src/chain_config.rs b/beacon_node/beacon_chain/src/chain_config.rs index 1f5abc4891b..ae5b7c5abf8 100644 --- a/beacon_node/beacon_chain/src/chain_config.rs +++ b/beacon_node/beacon_chain/src/chain_config.rs @@ -5,9 +5,11 @@ use std::str::FromStr; use std::{collections::HashSet, sync::LazyLock, time::Duration}; use types::{Checkpoint, Epoch, Hash256}; +// Reorg defaults - actual defaults come from network config in common/eth2_network_config pub const DEFAULT_RE_ORG_HEAD_THRESHOLD: ReOrgThreshold = ReOrgThreshold(20); pub const DEFAULT_RE_ORG_PARENT_THRESHOLD: ReOrgThreshold = ReOrgThreshold(160); pub const DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION: Epoch = Epoch::new(2); + /// Default to 1/12th of the slot, which is 1 second on mainnet. pub const DEFAULT_RE_ORG_CUTOFF_DENOMINATOR: u32 = 12; pub const DEFAULT_FORK_CHOICE_BEFORE_PROPOSAL_TIMEOUT: u64 = 250; diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 58cd2a3bdbc..db7c1e439cd 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -70,9 +70,11 @@ pub use publish_blocks::{ ProvenancedBlock, publish_blinded_block, publish_block, reconstruct_block, }; use serde::{Deserialize, Serialize}; +use serde_json::Value; use slot_clock::SlotClock; use ssz::Encode; pub use state_id::StateId; +use std::collections::HashMap; use std::future::Future; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::PathBuf; @@ -1821,8 +1823,36 @@ pub fn serve( .then( move |task_spawner: TaskSpawner, chain: Arc>| { task_spawner.blocking_json_task(Priority::P0, move || { - let config_and_preset = - ConfigAndPreset::from_chain_spec::(&chain.spec); + let mut overrides = HashMap::new(); + overrides.insert( + "REORG_MAX_EPOCHS_SINCE_FINALIZATION".to_string(), + Value::String( + chain + .config + .re_org_max_epochs_since_finalization + .as_u64() + .to_string(), + ), + ); + if chain.config.re_org_head_threshold.is_some() { + overrides.insert( + "REORG_HEAD_WEIGHT_THRESHOLD".to_string(), + Value::String( + chain.config.re_org_head_threshold.unwrap().0.to_string(), + ), + ); + } + if chain.config.re_org_parent_threshold.is_some() { + overrides.insert( + "REORG_PARENT_WEIGHT_THRESHOLD".to_string(), + Value::String( + chain.config.re_org_parent_threshold.unwrap().0.to_string(), + ), + ); + } + let config_and_preset = ConfigAndPreset::from_chain_spec_with_overrides::< + T::EthSpec, + >(&chain.spec, Some(overrides)); Ok(api_types::GenericResponse::from(config_and_preset)) }) }, diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 26dd3b6642e..9e36ea05509 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -1,8 +1,7 @@ use account_utils::{STDIN_INPUTS_FLAG, read_input_from_user}; use beacon_chain::chain_config::{ - DEFAULT_PREPARE_PAYLOAD_LOOKAHEAD_FACTOR, DEFAULT_RE_ORG_HEAD_THRESHOLD, - DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION, DEFAULT_RE_ORG_PARENT_THRESHOLD, - DisallowedReOrgOffsets, INVALID_HOLESKY_BLOCK_ROOT, ReOrgThreshold, + DEFAULT_PREPARE_PAYLOAD_LOOKAHEAD_FACTOR, DisallowedReOrgOffsets, + INVALID_HOLESKY_BLOCK_ROOT, ReOrgThreshold, }; use beacon_chain::custody_context::NodeCustodyType; use beacon_chain::graffiti_calculator::GraffitiOrigin; @@ -730,21 +729,26 @@ pub fn get_config( client_config.chain.re_org_head_threshold = None; client_config.chain.re_org_parent_threshold = None; } else { + // Read reorg values from ChainSpec (from config YAML), then check for CLI overrides + let head_threshold_from_spec = spec.reorg_head_weight_threshold; + let parent_threshold_from_spec = spec.reorg_parent_weight_threshold; + let epochs_from_spec = Epoch::new(spec.reorg_max_epochs_since_finalization); + + // Apply CLI overrides if provided, otherwise use ChainSpec values client_config.chain.re_org_head_threshold = Some( clap_utils::parse_optional(cli_args, "proposer-reorg-threshold")? .map(ReOrgThreshold) - .unwrap_or(DEFAULT_RE_ORG_HEAD_THRESHOLD), + .unwrap_or(ReOrgThreshold(head_threshold_from_spec)), ); client_config.chain.re_org_max_epochs_since_finalization = clap_utils::parse_optional(cli_args, "proposer-reorg-epochs-since-finalization")? - .unwrap_or(DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION); + .unwrap_or(epochs_from_spec); client_config.chain.re_org_cutoff_millis = clap_utils::parse_optional(cli_args, "proposer-reorg-cutoff")?; - client_config.chain.re_org_parent_threshold = Some( clap_utils::parse_optional(cli_args, "proposer-reorg-parent-threshold")? .map(ReOrgThreshold) - .unwrap_or(DEFAULT_RE_ORG_PARENT_THRESHOLD), + .unwrap_or(ReOrgThreshold(parent_threshold_from_spec)), ); if let Some(disallowed_offsets_str) = diff --git a/consensus/types/src/core/chain_spec.rs b/consensus/types/src/core/chain_spec.rs index da3f9b90ccc..10001fb7ef5 100644 --- a/consensus/types/src/core/chain_spec.rs +++ b/consensus/types/src/core/chain_spec.rs @@ -137,8 +137,9 @@ pub struct ChainSpec { * Fork choice */ pub proposer_score_boost: Option, - pub reorg_head_weight_threshold: Option, - pub reorg_parent_weight_threshold: Option, + pub reorg_head_weight_threshold: u64, + pub reorg_parent_weight_threshold: u64, + pub reorg_max_epochs_since_finalization: u64, /* * Eth1 @@ -1028,8 +1029,9 @@ impl ChainSpec { * Fork choice */ proposer_score_boost: Some(40), - reorg_head_weight_threshold: Some(20), - reorg_parent_weight_threshold: Some(160), + reorg_head_weight_threshold: 20, + reorg_parent_weight_threshold: 160, + reorg_max_epochs_since_finalization: 2, /* * Eth1 @@ -1394,8 +1396,9 @@ impl ChainSpec { * Fork choice */ proposer_score_boost: Some(40), - reorg_head_weight_threshold: Some(20), - reorg_parent_weight_threshold: Some(160), + reorg_head_weight_threshold: 20, + reorg_parent_weight_threshold: 160, + reorg_max_epochs_since_finalization: 2, /* * Eth1 @@ -1811,6 +1814,13 @@ pub struct Config { #[serde(skip_serializing_if = "Option::is_none")] proposer_score_boost: Option>, + #[serde(with = "serde_utils::quoted_u64")] + reorg_head_weight_threshold: u64, + #[serde(with = "serde_utils::quoted_u64")] + reorg_parent_weight_threshold: u64, + #[serde(with = "serde_utils::quoted_u64")] + reorg_max_epochs_since_finalization: u64, + #[serde(with = "serde_utils::quoted_u64")] deposit_chain_id: u64, #[serde(with = "serde_utils::quoted_u64")] @@ -2263,6 +2273,10 @@ impl Config { proposer_score_boost: spec.proposer_score_boost.map(|value| MaybeQuoted { value }), + reorg_head_weight_threshold: spec.reorg_head_weight_threshold, + reorg_parent_weight_threshold: spec.reorg_parent_weight_threshold, + reorg_max_epochs_since_finalization: spec.reorg_max_epochs_since_finalization, + deposit_chain_id: spec.deposit_chain_id, deposit_network_id: spec.deposit_network_id, deposit_contract_address: spec.deposit_contract_address, @@ -2351,6 +2365,9 @@ impl Config { max_per_epoch_activation_churn_limit, churn_limit_quotient, proposer_score_boost, + reorg_head_weight_threshold, + reorg_parent_weight_threshold, + reorg_max_epochs_since_finalization, deposit_chain_id, deposit_network_id, deposit_contract_address, @@ -2423,6 +2440,9 @@ impl Config { max_per_epoch_activation_churn_limit, churn_limit_quotient, proposer_score_boost: proposer_score_boost.map(|q| q.value), + reorg_head_weight_threshold, + reorg_parent_weight_threshold, + reorg_max_epochs_since_finalization, deposit_chain_id, deposit_network_id, deposit_contract_address, diff --git a/consensus/types/src/core/config_and_preset.rs b/consensus/types/src/core/config_and_preset.rs index 08141c77311..8274ce91977 100644 --- a/consensus/types/src/core/config_and_preset.rs +++ b/consensus/types/src/core/config_and_preset.rs @@ -42,19 +42,28 @@ pub struct ConfigAndPreset { #[serde(flatten)] pub gloas_preset: GloasPreset, /// The `extra_fields` map allows us to gracefully decode fields intended for future hard forks. + /// Also allows for overrides of config.yaml values from cli props - be sure to keep this field at the bottom of the struct #[serde(flatten)] pub extra_fields: HashMap, } impl ConfigAndPreset { pub fn from_chain_spec(spec: &ChainSpec) -> Self { + Self::from_chain_spec_with_overrides::(spec, None) + } + + /// Create ConfigAndPreset with optional overrides. + pub fn from_chain_spec_with_overrides( + spec: &ChainSpec, + overrides: Option>, + ) -> Self { let mut config = Config::from_chain_spec::(spec); let base_preset = BasePreset::from_chain_spec::(spec); let altair_preset = AltairPreset::from_chain_spec::(spec); let bellatrix_preset = BellatrixPreset::from_chain_spec::(spec); let capella_preset = CapellaPreset::from_chain_spec::(spec); let deneb_preset = DenebPreset::from_chain_spec::(spec); - let extra_fields = get_extra_fields(spec); + let extra_fields = get_extra_fields(spec, overrides); if spec.is_gloas_scheduled() { let electra_preset = ElectraPreset::from_chain_spec::(spec); @@ -109,11 +118,15 @@ impl ConfigAndPreset { } /// Get a hashmap of constants to add to the `PresetAndConfig` -pub fn get_extra_fields(spec: &ChainSpec) -> HashMap { +pub fn get_extra_fields( + spec: &ChainSpec, + overrides: Option>, +) -> HashMap { let hex_string = |value: &[u8]| format!("0x{}", hex::encode(value)).into(); let u32_hex = |v: u32| hex_string(&v.to_le_bytes()); let u8_hex = |v: u8| hex_string(&v.to_le_bytes()); - hashmap! { + + let mut extra_fields = hashmap! { "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), @@ -141,7 +154,24 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap { "compounding_withdrawal_prefix".to_uppercase() => u8_hex(spec.compounding_withdrawal_prefix_byte), "unset_deposit_requests_start_index".to_uppercase() => spec.unset_deposit_requests_start_index.to_string().into(), "full_exit_request_amount".to_uppercase() => spec.full_exit_request_amount.to_string().into(), + }; + + // Handle overrides + if let Some(ref overrides_map) = overrides { + for (key, value) in overrides_map.iter() { + // Handle reorg config overrides given these configs from .yaml can be overidden from cli props + match key.as_str() { + "REORG_HEAD_WEIGHT_THRESHOLD" + | "REORG_PARENT_WEIGHT_THRESHOLD" + | "REORG_MAX_EPOCHS_SINCE_FINALIZATION" => { + extra_fields.insert(key.to_string(), value.clone().to_string().into()); + } + _ => {} + } + } } + + extra_fields } #[cfg(test)] diff --git a/lighthouse/environment/tests/testnet_dir/config.yaml b/lighthouse/environment/tests/testnet_dir/config.yaml index 24c4a67225b..ea50f978fc1 100644 --- a/lighthouse/environment/tests/testnet_dir/config.yaml +++ b/lighthouse/environment/tests/testnet_dir/config.yaml @@ -76,6 +76,10 @@ CHURN_LIMIT_QUOTIENT: 65536 # --------------------------------------------------------------- # 40% PROPOSER_SCORE_BOOST: 40 +# Proposer reorg configuration +REORG_HEAD_WEIGHT_THRESHOLD: 20 +REORG_PARENT_WEIGHT_THRESHOLD: 160 +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 # Deposit contract # --------------------------------------------------------------- diff --git a/lighthouse/tests/beacon_node.rs b/lighthouse/tests/beacon_node.rs index 207324ea33f..fb20479ec1c 100644 --- a/lighthouse/tests/beacon_node.rs +++ b/lighthouse/tests/beacon_node.rs @@ -2,7 +2,7 @@ use crate::exec::{CommandLineTestExec, CompletedTest}; use beacon_node::beacon_chain::chain_config::{ DEFAULT_RE_ORG_CUTOFF_DENOMINATOR, DEFAULT_RE_ORG_HEAD_THRESHOLD, DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION, DEFAULT_SYNC_TOLERANCE_EPOCHS, - DisallowedReOrgOffsets, + DisallowedReOrgOffsets, ReOrgThreshold, }; use beacon_node::beacon_chain::custody_context::NodeCustodyType; use beacon_node::{ @@ -2839,3 +2839,40 @@ fn invalid_block_roots_default_mainnet() { assert!(config.chain.invalid_block_roots.is_empty()); }) } + +#[test] +fn test_proposer_reorg_threshold_from_cli() { + CommandLineTest::new() + .flag("proposer-reorg-threshold", Some("21")) + .flag("proposer-reorg-parent-threshold", Some("161")) + .flag("proposer-reorg-epochs-since-finalization", Some("3")) + .run_with_zero_port() + .with_config(|config| { + assert_eq!(config.chain.re_org_head_threshold, Some(ReOrgThreshold(21))); + assert_eq!( + config.chain.re_org_parent_threshold, + Some(ReOrgThreshold(161)) + ); + assert_eq!( + config.chain.re_org_max_epochs_since_finalization, + Epoch::new(3) + ); + }); +} + +#[test] +fn test_proposer_reorg_threshold_from_yaml() { + CommandLineTest::new() + .run_with_zero_port() + .with_config(|config| { + assert_eq!(config.chain.re_org_head_threshold, Some(ReOrgThreshold(20))); + assert_eq!( + config.chain.re_org_parent_threshold, + Some(ReOrgThreshold(160)) + ); + assert_eq!( + config.chain.re_org_max_epochs_since_finalization, + Epoch::new(2) + ); + }); +}