Skip serializing blob_schedule before Fulu (#7779)

Alternative to:

- https://github.com/sigp/lighthouse/pull/7758


  Serve the `blob_schedule` field on `/eth/v1/config/spec` _only_ when Fulu is enabled. If the blob schedule is empty, we will still serve it as `[]`, so long as Fulu is enabled.
This commit is contained in:
Michael Sproul
2025-07-25 04:14:25 +10:00
committed by GitHub
parent 9911f348bc
commit b904956074
2 changed files with 51 additions and 17 deletions

View File

@@ -2,6 +2,7 @@ use crate::application_domain::{ApplicationDomain, APPLICATION_DOMAIN_BUILDER};
use crate::blob_sidecar::BlobIdentifier; use crate::blob_sidecar::BlobIdentifier;
use crate::data_column_sidecar::DataColumnsByRootIdentifier; use crate::data_column_sidecar::DataColumnsByRootIdentifier;
use crate::*; use crate::*;
use derivative::Derivative;
use ethereum_hashing::hash; use ethereum_hashing::hash;
use int_to_bytes::int_to_bytes4; use int_to_bytes::int_to_bytes4;
use safe_arith::{ArithError, SafeArith}; use safe_arith::{ArithError, SafeArith};
@@ -1472,8 +1473,17 @@ pub struct BlobParameters {
// A wrapper around a vector of BlobParameters to ensure that the vector is reverse // A wrapper around a vector of BlobParameters to ensure that the vector is reverse
// sorted by epoch. // sorted by epoch.
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, Derivative, Clone)]
pub struct BlobSchedule(Vec<BlobParameters>); #[derivative(PartialEq)]
pub struct BlobSchedule {
schedule: Vec<BlobParameters>,
// This is a hack to prevent the blob schedule being serialized on the /eth/v1/config/spec
// endpoint prior to the Fulu fork being scheduled.
//
// We can remove this once Fulu is live on mainnet.
#[derivative(PartialEq = "ignore")]
skip_serializing: bool,
}
impl<'de> Deserialize<'de> for BlobSchedule { impl<'de> Deserialize<'de> for BlobSchedule {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
@@ -1489,30 +1499,48 @@ impl BlobSchedule {
pub fn new(mut vec: Vec<BlobParameters>) -> Self { pub fn new(mut vec: Vec<BlobParameters>) -> Self {
// reverse sort by epoch // reverse sort by epoch
vec.sort_by(|a, b| b.epoch.cmp(&a.epoch)); vec.sort_by(|a, b| b.epoch.cmp(&a.epoch));
Self(vec) Self {
schedule: vec,
skip_serializing: false,
}
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.0.is_empty() self.schedule.is_empty()
}
pub fn skip_serializing(&self) -> bool {
self.skip_serializing
}
pub fn set_skip_serializing(&mut self) {
self.skip_serializing = true;
} }
pub fn max_blobs_for_epoch(&self, epoch: Epoch) -> Option<u64> { pub fn max_blobs_for_epoch(&self, epoch: Epoch) -> Option<u64> {
self.0 self.schedule
.iter() .iter()
.find(|entry| epoch >= entry.epoch) .find(|entry| epoch >= entry.epoch)
.map(|entry| entry.max_blobs_per_block) .map(|entry| entry.max_blobs_per_block)
} }
pub fn blob_parameters_for_epoch(&self, epoch: Epoch) -> Option<BlobParameters> { pub fn blob_parameters_for_epoch(&self, epoch: Epoch) -> Option<BlobParameters> {
self.0.iter().find(|entry| epoch >= entry.epoch).cloned() self.schedule
.iter()
.find(|entry| epoch >= entry.epoch)
.cloned()
} }
pub const fn default() -> Self { pub const fn default() -> Self {
Self(vec![]) // TODO(EIP-7892): think about what the default should be
Self {
schedule: vec![],
skip_serializing: false,
}
} }
pub fn as_vec(&self) -> &Vec<BlobParameters> { pub fn as_vec(&self) -> &Vec<BlobParameters> {
&self.0 &self.schedule
} }
} }
@@ -1521,7 +1549,7 @@ impl Serialize for BlobSchedule {
where where
S: Serializer, S: Serializer,
{ {
let mut schedule = self.0.clone(); let mut schedule = self.schedule.clone();
// reversing the list to get an ascending order // reversing the list to get an ascending order
schedule.reverse(); schedule.reverse();
schedule.serialize(serializer) schedule.serialize(serializer)
@@ -1533,7 +1561,7 @@ impl<'a> IntoIterator for &'a BlobSchedule {
type IntoIter = std::slice::Iter<'a, BlobParameters>; type IntoIter = std::slice::Iter<'a, BlobParameters>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.0.iter() self.schedule.iter()
} }
} }
@@ -1542,7 +1570,7 @@ impl IntoIterator for BlobSchedule {
type IntoIter = std::vec::IntoIter<BlobParameters>; type IntoIter = std::vec::IntoIter<BlobParameters>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.0.into_iter() self.schedule.into_iter()
} }
} }
@@ -1747,8 +1775,8 @@ pub struct Config {
#[serde(with = "serde_utils::quoted_u64")] #[serde(with = "serde_utils::quoted_u64")]
custody_requirement: u64, custody_requirement: u64,
#[serde(default = "BlobSchedule::default")] #[serde(default = "BlobSchedule::default")]
#[serde(skip_serializing_if = "BlobSchedule::is_empty")] #[serde(skip_serializing_if = "BlobSchedule::skip_serializing")]
blob_schedule: BlobSchedule, pub blob_schedule: BlobSchedule,
#[serde(default = "default_validator_custody_requirement")] #[serde(default = "default_validator_custody_requirement")]
#[serde(with = "serde_utils::quoted_u64")] #[serde(with = "serde_utils::quoted_u64")]
validator_custody_requirement: u64, validator_custody_requirement: u64,

View File

@@ -43,9 +43,8 @@ pub struct ConfigAndPreset {
} }
impl ConfigAndPreset { impl ConfigAndPreset {
// DEPRECATED: the `fork_name` argument is never used, we should remove it.
pub fn from_chain_spec<E: EthSpec>(spec: &ChainSpec, fork_name: Option<ForkName>) -> Self { pub fn from_chain_spec<E: EthSpec>(spec: &ChainSpec, fork_name: Option<ForkName>) -> Self {
let config = Config::from_chain_spec::<E>(spec); let mut config = Config::from_chain_spec::<E>(spec);
let base_preset = BasePreset::from_chain_spec::<E>(spec); let base_preset = BasePreset::from_chain_spec::<E>(spec);
let altair_preset = AltairPreset::from_chain_spec::<E>(spec); let altair_preset = AltairPreset::from_chain_spec::<E>(spec);
let bellatrix_preset = BellatrixPreset::from_chain_spec::<E>(spec); let bellatrix_preset = BellatrixPreset::from_chain_spec::<E>(spec);
@@ -53,6 +52,11 @@ impl ConfigAndPreset {
let deneb_preset = DenebPreset::from_chain_spec::<E>(spec); let deneb_preset = DenebPreset::from_chain_spec::<E>(spec);
let extra_fields = get_extra_fields(spec); let extra_fields = get_extra_fields(spec);
// Remove blob schedule for backwards-compatibility.
if spec.fulu_fork_epoch.is_none() {
config.blob_schedule.set_skip_serializing();
}
if spec.fulu_fork_epoch.is_some() if spec.fulu_fork_epoch.is_some()
|| fork_name.is_none() || fork_name.is_none()
|| fork_name == Some(ForkName::Fulu) || fork_name == Some(ForkName::Fulu)
@@ -140,7 +144,7 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap<String, Value> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::MainnetEthSpec; use crate::{Epoch, MainnetEthSpec};
use std::fs::File; use std::fs::File;
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
@@ -152,7 +156,9 @@ mod test {
.write(true) .write(true)
.open(tmp_file.as_ref()) .open(tmp_file.as_ref())
.expect("error opening file"); .expect("error opening file");
let mainnet_spec = ChainSpec::mainnet(); let mut mainnet_spec = ChainSpec::mainnet();
// setting fulu_fork_epoch because we are roundtripping a fulu config
mainnet_spec.fulu_fork_epoch = Some(Epoch::new(42));
let mut yamlconfig = let mut yamlconfig =
ConfigAndPreset::from_chain_spec::<MainnetEthSpec>(&mainnet_spec, None); ConfigAndPreset::from_chain_spec::<MainnetEthSpec>(&mainnet_spec, None);
let (k1, v1) = ("SAMPLE_HARDFORK_KEY1", "123456789"); let (k1, v1) = ("SAMPLE_HARDFORK_KEY1", "123456789");