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::data_column_sidecar::DataColumnsByRootIdentifier;
use crate::*;
use derivative::Derivative;
use ethereum_hashing::hash;
use int_to_bytes::int_to_bytes4;
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
// sorted by epoch.
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone)]
pub struct BlobSchedule(Vec<BlobParameters>);
#[derive(Debug, Derivative, Clone)]
#[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 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
@@ -1489,30 +1499,48 @@ impl BlobSchedule {
pub fn new(mut vec: Vec<BlobParameters>) -> Self {
// reverse sort by 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 {
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> {
self.0
self.schedule
.iter()
.find(|entry| epoch >= entry.epoch)
.map(|entry| entry.max_blobs_per_block)
}
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 {
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> {
&self.0
&self.schedule
}
}
@@ -1521,7 +1549,7 @@ impl Serialize for BlobSchedule {
where
S: Serializer,
{
let mut schedule = self.0.clone();
let mut schedule = self.schedule.clone();
// reversing the list to get an ascending order
schedule.reverse();
schedule.serialize(serializer)
@@ -1533,7 +1561,7 @@ impl<'a> IntoIterator for &'a BlobSchedule {
type IntoIter = std::slice::Iter<'a, BlobParameters>;
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>;
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")]
custody_requirement: u64,
#[serde(default = "BlobSchedule::default")]
#[serde(skip_serializing_if = "BlobSchedule::is_empty")]
blob_schedule: BlobSchedule,
#[serde(skip_serializing_if = "BlobSchedule::skip_serializing")]
pub blob_schedule: BlobSchedule,
#[serde(default = "default_validator_custody_requirement")]
#[serde(with = "serde_utils::quoted_u64")]
validator_custody_requirement: u64,

View File

@@ -43,9 +43,8 @@ pub struct 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 {
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 altair_preset = AltairPreset::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 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()
|| fork_name.is_none()
|| fork_name == Some(ForkName::Fulu)
@@ -140,7 +144,7 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap<String, Value> {
#[cfg(test)]
mod test {
use super::*;
use crate::MainnetEthSpec;
use crate::{Epoch, MainnetEthSpec};
use std::fs::File;
use tempfile::NamedTempFile;
@@ -152,7 +156,9 @@ mod test {
.write(true)
.open(tmp_file.as_ref())
.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 =
ConfigAndPreset::from_chain_spec::<MainnetEthSpec>(&mainnet_spec, None);
let (k1, v1) = ("SAMPLE_HARDFORK_KEY1", "123456789");