mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 18:32:42 +00:00
Serve Bellatrix preset in BN API (#3425)
## Issue Addressed Resolves #3388 Resolves #2638 ## Proposed Changes - Return the `BellatrixPreset` on `/eth/v1/config/spec` by default. - Allow users to opt out of this by providing `--http-spec-fork=altair` (unless there's a Bellatrix fork epoch set). - Add the Altair constants from #2638 and make serving the constants non-optional (the `http-disable-legacy-spec` flag is deprecated). - Modify the VC to only read the `Config` and not to log extra fields. This prevents it from having to muck around parsing the `ConfigAndPreset` fields it doesn't need. ## Additional Info This change is backwards-compatible for the VC and the BN, but is marked as a breaking change for the removal of `--http-disable-legacy-spec`. I tried making `Config` a `superstruct` too, but getting the automatic decoding to work was a huge pain and was going to require a lot of hacks, so I gave up in favour of keeping the default-based approach we have now.
This commit is contained in:
@@ -803,6 +803,10 @@ impl Default for ChainSpec {
|
||||
}
|
||||
|
||||
/// Exact implementation of the *config* object from the Ethereum spec (YAML/JSON).
|
||||
///
|
||||
/// Fields relevant to hard forks after Altair should be optional so that we can continue
|
||||
/// to parse Altair configs. This default approach turns out to be much simpler than trying to
|
||||
/// make `Config` a superstruct because of the hassle of deserializing an untagged enum.
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub struct Config {
|
||||
@@ -813,17 +817,13 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub preset_base: String,
|
||||
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_terminal_total_difficulty")]
|
||||
#[serde(with = "eth2_serde_utils::quoted_u256")]
|
||||
pub terminal_total_difficulty: Uint256,
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_terminal_block_hash")]
|
||||
pub terminal_block_hash: ExecutionBlockHash,
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_terminal_block_hash_activation_epoch")]
|
||||
pub terminal_block_hash_activation_epoch: Epoch,
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_safe_slots_to_import_optimistically")]
|
||||
#[serde(with = "eth2_serde_utils::quoted_u64")]
|
||||
pub safe_slots_to_import_optimistically: u64,
|
||||
@@ -843,12 +843,10 @@ pub struct Config {
|
||||
#[serde(deserialize_with = "deserialize_fork_epoch")]
|
||||
pub altair_fork_epoch: Option<MaybeQuoted<Epoch>>,
|
||||
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_bellatrix_fork_version")]
|
||||
#[serde(with = "eth2_serde_utils::bytes_4_hex")]
|
||||
bellatrix_fork_version: [u8; 4],
|
||||
// TODO(merge): remove this default
|
||||
#[serde(default = "default_bellatrix_fork_epoch")]
|
||||
#[serde(default)]
|
||||
#[serde(serialize_with = "serialize_fork_epoch")]
|
||||
#[serde(deserialize_with = "deserialize_fork_epoch")]
|
||||
pub bellatrix_fork_epoch: Option<MaybeQuoted<Epoch>>,
|
||||
@@ -890,10 +888,6 @@ fn default_bellatrix_fork_version() -> [u8; 4] {
|
||||
[0xff, 0xff, 0xff, 0xff]
|
||||
}
|
||||
|
||||
fn default_bellatrix_fork_epoch() -> Option<MaybeQuoted<Epoch>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Placeholder value: 2^256-2^10 (115792089237316195423570985008687907853269984665640564039457584007913129638912).
|
||||
///
|
||||
/// Taken from https://github.com/ethereum/consensus-specs/blob/d5e4828aecafaf1c57ef67a5f23c4ae7b08c5137/configs/mainnet.yaml#L15-L16
|
||||
@@ -1335,10 +1329,7 @@ mod yaml_tests {
|
||||
default_safe_slots_to_import_optimistically()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
chain_spec.bellatrix_fork_epoch,
|
||||
default_bellatrix_fork_epoch()
|
||||
);
|
||||
assert_eq!(chain_spec.bellatrix_fork_epoch, None);
|
||||
|
||||
assert_eq!(
|
||||
chain_spec.bellatrix_fork_version,
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
use crate::{AltairPreset, BasePreset, BellatrixPreset, ChainSpec, Config, EthSpec};
|
||||
use crate::{
|
||||
consts::altair, AltairPreset, BasePreset, BellatrixPreset, ChainSpec, Config, EthSpec, ForkName,
|
||||
};
|
||||
use maplit::hashmap;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use superstruct::superstruct;
|
||||
|
||||
/// Fusion of a runtime-config with the compile-time preset values.
|
||||
///
|
||||
/// Mostly useful for the API.
|
||||
#[superstruct(
|
||||
variants(Altair, Bellatrix),
|
||||
variant_attributes(derive(Serialize, Deserialize, Debug, PartialEq, Clone))
|
||||
)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub struct ConfigAndPreset {
|
||||
#[serde(flatten)]
|
||||
pub config: Config,
|
||||
@@ -15,80 +24,75 @@ pub struct ConfigAndPreset {
|
||||
pub base_preset: BasePreset,
|
||||
#[serde(flatten)]
|
||||
pub altair_preset: AltairPreset,
|
||||
// TODO(merge): re-enable
|
||||
// #[serde(flatten)]
|
||||
// pub bellatrix_preset: BellatrixPreset,
|
||||
#[superstruct(only(Bellatrix))]
|
||||
#[serde(flatten)]
|
||||
pub bellatrix_preset: BellatrixPreset,
|
||||
/// The `extra_fields` map allows us to gracefully decode fields intended for future hard forks.
|
||||
#[serde(flatten)]
|
||||
pub extra_fields: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
impl ConfigAndPreset {
|
||||
pub fn from_chain_spec<T: EthSpec>(spec: &ChainSpec) -> Self {
|
||||
pub fn from_chain_spec<T: EthSpec>(spec: &ChainSpec, fork_name: Option<ForkName>) -> Self {
|
||||
let config = Config::from_chain_spec::<T>(spec);
|
||||
let base_preset = BasePreset::from_chain_spec::<T>(spec);
|
||||
let altair_preset = AltairPreset::from_chain_spec::<T>(spec);
|
||||
// TODO(merge): re-enable
|
||||
let _bellatrix_preset = BellatrixPreset::from_chain_spec::<T>(spec);
|
||||
let extra_fields = HashMap::new();
|
||||
let extra_fields = get_extra_fields(spec);
|
||||
|
||||
Self {
|
||||
config,
|
||||
base_preset,
|
||||
altair_preset,
|
||||
extra_fields,
|
||||
if spec.bellatrix_fork_epoch.is_some()
|
||||
|| fork_name == None
|
||||
|| fork_name == Some(ForkName::Merge)
|
||||
{
|
||||
let bellatrix_preset = BellatrixPreset::from_chain_spec::<T>(spec);
|
||||
|
||||
ConfigAndPreset::Bellatrix(ConfigAndPresetBellatrix {
|
||||
config,
|
||||
base_preset,
|
||||
altair_preset,
|
||||
bellatrix_preset,
|
||||
extra_fields,
|
||||
})
|
||||
} else {
|
||||
ConfigAndPreset::Altair(ConfigAndPresetAltair {
|
||||
config,
|
||||
base_preset,
|
||||
altair_preset,
|
||||
extra_fields,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add fields that were previously part of the config but are now constants.
|
||||
pub fn make_backwards_compat(&mut self, spec: &ChainSpec) {
|
||||
let hex_string = |value: &[u8]| format!("0x{}", hex::encode(&value));
|
||||
let u32_hex = |v: u32| hex_string(&v.to_le_bytes());
|
||||
let u8_hex = |v: u8| hex_string(&v.to_le_bytes());
|
||||
let fields = vec![
|
||||
(
|
||||
"bls_withdrawal_prefix",
|
||||
u8_hex(spec.bls_withdrawal_prefix_byte),
|
||||
),
|
||||
(
|
||||
"domain_beacon_proposer",
|
||||
u32_hex(spec.domain_beacon_proposer),
|
||||
),
|
||||
(
|
||||
"domain_beacon_attester",
|
||||
u32_hex(spec.domain_beacon_attester),
|
||||
),
|
||||
("domain_randao", u32_hex(spec.domain_randao)),
|
||||
("domain_deposit", u32_hex(spec.domain_deposit)),
|
||||
("domain_voluntary_exit", u32_hex(spec.domain_voluntary_exit)),
|
||||
(
|
||||
"domain_selection_proof",
|
||||
u32_hex(spec.domain_selection_proof),
|
||||
),
|
||||
(
|
||||
"domain_aggregate_and_proof",
|
||||
u32_hex(spec.domain_aggregate_and_proof),
|
||||
),
|
||||
(
|
||||
"domain_application_mask",
|
||||
u32_hex(spec.domain_application_mask),
|
||||
),
|
||||
(
|
||||
"target_aggregators_per_committee",
|
||||
spec.target_aggregators_per_committee.to_string(),
|
||||
),
|
||||
(
|
||||
"random_subnets_per_validator",
|
||||
spec.random_subnets_per_validator.to_string(),
|
||||
),
|
||||
(
|
||||
"epochs_per_random_subnet_subscription",
|
||||
spec.epochs_per_random_subnet_subscription.to_string(),
|
||||
),
|
||||
];
|
||||
for (key, value) in fields {
|
||||
self.extra_fields.insert(key.to_uppercase(), value.into());
|
||||
}
|
||||
/// Get a hashmap of constants to add to the `PresetAndConfig`
|
||||
pub fn get_extra_fields(spec: &ChainSpec) -> HashMap<String, Value> {
|
||||
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! {
|
||||
"bls_withdrawal_prefix".to_uppercase() => u8_hex(spec.bls_withdrawal_prefix_byte),
|
||||
"domain_beacon_proposer".to_uppercase() => u32_hex(spec.domain_beacon_proposer),
|
||||
"domain_beacon_attester".to_uppercase() => u32_hex(spec.domain_beacon_attester),
|
||||
"domain_randao".to_uppercase()=> u32_hex(spec.domain_randao),
|
||||
"domain_deposit".to_uppercase()=> u32_hex(spec.domain_deposit),
|
||||
"domain_voluntary_exit".to_uppercase() => u32_hex(spec.domain_voluntary_exit),
|
||||
"domain_selection_proof".to_uppercase() => u32_hex(spec.domain_selection_proof),
|
||||
"domain_aggregate_and_proof".to_uppercase() => u32_hex(spec.domain_aggregate_and_proof),
|
||||
"domain_application_mask".to_uppercase()=> u32_hex(spec.domain_application_mask),
|
||||
"target_aggregators_per_committee".to_uppercase() =>
|
||||
spec.target_aggregators_per_committee.to_string().into(),
|
||||
"random_subnets_per_validator".to_uppercase() =>
|
||||
spec.random_subnets_per_validator.to_string().into(),
|
||||
"epochs_per_random_subnet_subscription".to_uppercase() =>
|
||||
spec.epochs_per_random_subnet_subscription.to_string().into(),
|
||||
"domain_contribution_and_proof".to_uppercase() =>
|
||||
u32_hex(spec.domain_contribution_and_proof),
|
||||
"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),
|
||||
"sync_committee_subnet_count".to_uppercase() =>
|
||||
altair::SYNC_COMMITTEE_SUBNET_COUNT.to_string().into(),
|
||||
"target_aggregators_per_sync_subcommittee".to_uppercase() =>
|
||||
altair::TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE.to_string().into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,15 +112,16 @@ mod test {
|
||||
.open(tmp_file.as_ref())
|
||||
.expect("error opening file");
|
||||
let mainnet_spec = ChainSpec::mainnet();
|
||||
let mut yamlconfig = ConfigAndPreset::from_chain_spec::<MainnetEthSpec>(&mainnet_spec);
|
||||
let mut yamlconfig =
|
||||
ConfigAndPreset::from_chain_spec::<MainnetEthSpec>(&mainnet_spec, None);
|
||||
let (k1, v1) = ("SAMPLE_HARDFORK_KEY1", "123456789");
|
||||
let (k2, v2) = ("SAMPLE_HARDFORK_KEY2", "987654321");
|
||||
let (k3, v3) = ("SAMPLE_HARDFORK_KEY3", 32);
|
||||
let (k4, v4) = ("SAMPLE_HARDFORK_KEY4", Value::Null);
|
||||
yamlconfig.extra_fields.insert(k1.into(), v1.into());
|
||||
yamlconfig.extra_fields.insert(k2.into(), v2.into());
|
||||
yamlconfig.extra_fields.insert(k3.into(), v3.into());
|
||||
yamlconfig.extra_fields.insert(k4.into(), v4);
|
||||
yamlconfig.extra_fields_mut().insert(k1.into(), v1.into());
|
||||
yamlconfig.extra_fields_mut().insert(k2.into(), v2.into());
|
||||
yamlconfig.extra_fields_mut().insert(k3.into(), v3.into());
|
||||
yamlconfig.extra_fields_mut().insert(k4.into(), v4);
|
||||
|
||||
serde_yaml::to_writer(writer, &yamlconfig).expect("failed to write or serialize");
|
||||
|
||||
@@ -125,8 +130,8 @@ mod test {
|
||||
.write(false)
|
||||
.open(tmp_file.as_ref())
|
||||
.expect("error while opening the file");
|
||||
let from: ConfigAndPreset =
|
||||
let from: ConfigAndPresetBellatrix =
|
||||
serde_yaml::from_reader(reader).expect("error while deserializing");
|
||||
assert_eq!(from, yamlconfig);
|
||||
assert_eq!(ConfigAndPreset::Bellatrix(from), yamlconfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,14 +106,14 @@ macro_rules! map_fork_name_with {
|
||||
}
|
||||
|
||||
impl FromStr for ForkName {
|
||||
type Err = ();
|
||||
type Err = String;
|
||||
|
||||
fn from_str(fork_name: &str) -> Result<Self, ()> {
|
||||
fn from_str(fork_name: &str) -> Result<Self, String> {
|
||||
Ok(match fork_name.to_lowercase().as_ref() {
|
||||
"phase0" | "base" => ForkName::Base,
|
||||
"altair" => ForkName::Altair,
|
||||
"bellatrix" | "merge" => ForkName::Merge,
|
||||
_ => return Err(()),
|
||||
_ => return Err(format!("unknown fork name: {}", fork_name)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,7 @@ impl TryFrom<String> for ForkName {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
Self::from_str(&s).map_err(|()| format!("Invalid fork name: {}", s))
|
||||
Self::from_str(&s)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,8 +178,8 @@ mod test {
|
||||
assert_eq!(ForkName::from_str("AlTaIr"), Ok(ForkName::Altair));
|
||||
assert_eq!(ForkName::from_str("altair"), Ok(ForkName::Altair));
|
||||
|
||||
assert_eq!(ForkName::from_str("NO_NAME"), Err(()));
|
||||
assert_eq!(ForkName::from_str("no_name"), Err(()));
|
||||
assert!(ForkName::from_str("NO_NAME").is_err());
|
||||
assert!(ForkName::from_str("no_name").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -110,7 +110,9 @@ pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee};
|
||||
pub use crate::beacon_state::{BeaconTreeHashCache, Error as BeaconStateError, *};
|
||||
pub use crate::chain_spec::{ChainSpec, Config, Domain};
|
||||
pub use crate::checkpoint::Checkpoint;
|
||||
pub use crate::config_and_preset::ConfigAndPreset;
|
||||
pub use crate::config_and_preset::{
|
||||
ConfigAndPreset, ConfigAndPresetAltair, ConfigAndPresetBellatrix,
|
||||
};
|
||||
pub use crate::contribution_and_proof::ContributionAndProof;
|
||||
pub use crate::deposit::{Deposit, DEPOSIT_TREE_DEPTH};
|
||||
pub use crate::deposit_data::DepositData;
|
||||
|
||||
Reference in New Issue
Block a user