Skip to content

Commit 59acf65

Browse files
committed
add consensus header
1 parent 996a0b4 commit 59acf65

File tree

9 files changed

+482
-318
lines changed

9 files changed

+482
-318
lines changed

crates/common/src/config/mod.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,33 @@ impl CommitBoostConfig {
6868
let chain = match helper_config.chain {
6969
ChainLoader::Path { path, genesis_time_secs } => {
7070
// check if the file path is overridden by env var
71-
let (slot_time_secs, genesis_fork_version) =
71+
let (slot_time_secs, genesis_fork_version, fulu_fork_slot) =
7272
if let Some(path) = load_optional_env_var(CHAIN_SPEC_ENV) {
7373
load_chain_from_file(path.parse()?)?
7474
} else {
7575
load_chain_from_file(path)?
7676
};
77-
Chain::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version }
77+
Chain::Custom {
78+
genesis_time_secs,
79+
slot_time_secs,
80+
genesis_fork_version,
81+
fulu_fork_slot,
82+
}
7883
}
7984
ChainLoader::Known(known) => Chain::from(known),
80-
ChainLoader::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version } => {
85+
ChainLoader::Custom {
86+
genesis_time_secs,
87+
slot_time_secs,
88+
genesis_fork_version,
89+
fulu_fork_slot,
90+
} => {
8191
let genesis_fork_version: ForkVersion = genesis_fork_version.as_ref().try_into()?;
82-
Chain::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version }
92+
Chain::Custom {
93+
genesis_time_secs,
94+
slot_time_secs,
95+
genesis_fork_version,
96+
fulu_fork_slot,
97+
}
8398
}
8499
};
85100

crates/common/src/pbs/constants.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub const HEADER_VERSION_KEY: &str = "X-CommitBoost-Version";
1717
pub const HEADER_VERSION_VALUE: &str = COMMIT_BOOST_VERSION;
1818
pub const HEADER_START_TIME_UNIX_MS: &str = "Date-Milliseconds";
1919
pub const HEADER_TIMEOUT_MS: &str = "X-Timeout-Ms";
20+
pub const HEADER_CONSENSUS_VERSION: &str = "Eth-Consensus-Version";
2021

2122
pub const DEFAULT_PBS_JWT_KEY: &str = "DEFAULT_PBS";
2223

crates/common/src/types.rs

Lines changed: 94 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::path::PathBuf;
33
use alloy::primitives::{B256, Bytes, b256, hex};
44
use derive_more::{Deref, Display, From, Into};
55
use eyre::{Context, bail};
6+
use lh_types::ForkName;
67
use serde::{Deserialize, Serialize};
78

89
use crate::{constants::APPLICATION_BUILDER_DOMAIN, signature::compute_domain};
@@ -35,7 +36,12 @@ pub enum Chain {
3536
Sepolia,
3637
Helder,
3738
Hoodi,
38-
Custom { genesis_time_secs: u64, slot_time_secs: u64, genesis_fork_version: ForkVersion },
39+
Custom {
40+
genesis_time_secs: u64,
41+
slot_time_secs: u64,
42+
genesis_fork_version: ForkVersion,
43+
fulu_fork_slot: u64,
44+
},
3945
}
4046

4147
pub type ForkVersion = [u8; 4];
@@ -59,11 +65,17 @@ impl std::fmt::Debug for Chain {
5965
Self::Sepolia => write!(f, "Sepolia"),
6066
Self::Helder => write!(f, "Helder"),
6167
Self::Hoodi => write!(f, "Hoodi"),
62-
Self::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version } => f
68+
Self::Custom {
69+
genesis_time_secs,
70+
slot_time_secs,
71+
genesis_fork_version,
72+
fulu_fork_slot,
73+
} => f
6374
.debug_struct("Custom")
6475
.field("genesis_time_secs", genesis_time_secs)
6576
.field("slot_time_secs", slot_time_secs)
6677
.field("genesis_fork_version", &hex::encode_prefixed(genesis_fork_version))
78+
.field("fulu_fork_slot", fulu_fork_slot)
6779
.finish(),
6880
}
6981
}
@@ -126,6 +138,21 @@ impl Chain {
126138
Chain::Custom { slot_time_secs, .. } => *slot_time_secs,
127139
}
128140
}
141+
142+
pub fn fulu_fork_slot(&self) -> u64 {
143+
match self {
144+
Chain::Mainnet => KnownChain::Mainnet.fulu_fork_slot(),
145+
Chain::Holesky => KnownChain::Holesky.fulu_fork_slot(),
146+
Chain::Sepolia => KnownChain::Sepolia.fulu_fork_slot(),
147+
Chain::Helder => KnownChain::Helder.fulu_fork_slot(),
148+
Chain::Hoodi => KnownChain::Hoodi.fulu_fork_slot(),
149+
Chain::Custom { slot_time_secs, .. } => *slot_time_secs,
150+
}
151+
}
152+
153+
pub fn fork_by_slot(&self, slot: u64) -> ForkName {
154+
if slot >= self.fulu_fork_slot() { ForkName::Fulu } else { ForkName::Electra }
155+
}
129156
}
130157

131158
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
@@ -203,6 +230,15 @@ impl KnownChain {
203230
KnownChain::Hoodi => 12,
204231
}
205232
}
233+
234+
pub fn fulu_fork_slot(&self) -> u64 {
235+
match self {
236+
KnownChain::Mainnet | KnownChain::Helder => u64::MAX,
237+
KnownChain::Holesky => 5283840,
238+
KnownChain::Sepolia => 8724480,
239+
KnownChain::Hoodi => 1622016,
240+
}
241+
}
206242
}
207243

208244
impl From<KnownChain> for Chain {
@@ -233,6 +269,7 @@ pub enum ChainLoader {
233269
genesis_time_secs: u64,
234270
slot_time_secs: u64,
235271
genesis_fork_version: Bytes,
272+
fulu_fork_slot: u64,
236273
},
237274
}
238275

@@ -247,13 +284,17 @@ impl Serialize for Chain {
247284
Chain::Sepolia => ChainLoader::Known(KnownChain::Sepolia),
248285
Chain::Helder => ChainLoader::Known(KnownChain::Helder),
249286
Chain::Hoodi => ChainLoader::Known(KnownChain::Hoodi),
250-
Chain::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version } => {
251-
ChainLoader::Custom {
252-
genesis_time_secs: *genesis_time_secs,
253-
slot_time_secs: *slot_time_secs,
254-
genesis_fork_version: Bytes::from(*genesis_fork_version),
255-
}
256-
}
287+
Chain::Custom {
288+
genesis_time_secs,
289+
slot_time_secs,
290+
genesis_fork_version,
291+
fulu_fork_slot,
292+
} => ChainLoader::Custom {
293+
genesis_time_secs: *genesis_time_secs,
294+
slot_time_secs: *slot_time_secs,
295+
genesis_fork_version: Bytes::from(*genesis_fork_version),
296+
fulu_fork_slot: *fulu_fork_slot,
297+
},
257298
};
258299

259300
loader.serialize(serializer)
@@ -270,14 +311,29 @@ impl<'de> Deserialize<'de> for Chain {
270311
match loader {
271312
ChainLoader::Known(known) => Ok(Chain::from(known)),
272313
ChainLoader::Path { genesis_time_secs, path } => {
273-
let (slot_time_secs, genesis_fork_version) =
314+
let (slot_time_secs, genesis_fork_version, fulu_fork_slot) =
274315
load_chain_from_file(path).map_err(serde::de::Error::custom)?;
275-
Ok(Chain::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version })
316+
Ok(Chain::Custom {
317+
genesis_time_secs,
318+
slot_time_secs,
319+
genesis_fork_version,
320+
fulu_fork_slot,
321+
})
276322
}
277-
ChainLoader::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version } => {
323+
ChainLoader::Custom {
324+
genesis_time_secs,
325+
slot_time_secs,
326+
genesis_fork_version,
327+
fulu_fork_slot,
328+
} => {
278329
let genesis_fork_version: ForkVersion =
279330
genesis_fork_version.as_ref().try_into().map_err(serde::de::Error::custom)?;
280-
Ok(Chain::Custom { genesis_time_secs, slot_time_secs, genesis_fork_version })
331+
Ok(Chain::Custom {
332+
genesis_time_secs,
333+
slot_time_secs,
334+
genesis_fork_version,
335+
fulu_fork_slot,
336+
})
281337
}
282338
}
283339
}
@@ -289,20 +345,25 @@ impl<'de> Deserialize<'de> for Chain {
289345
/// - JSON as return the getSpec endpoint, either with or without the `data`
290346
/// field
291347
/// - YAML as used e.g. in Kurtosis/Ethereum Package
292-
pub fn load_chain_from_file(path: PathBuf) -> eyre::Result<(u64, ForkVersion)> {
348+
pub fn load_chain_from_file(path: PathBuf) -> eyre::Result<(u64, ForkVersion, u64)> {
293349
#[derive(Deserialize)]
294350
#[serde(rename_all = "UPPERCASE")]
295351
struct QuotedSpecFile {
296352
#[serde(with = "serde_utils::quoted_u64")]
297353
seconds_per_slot: u64,
298354
genesis_fork_version: Bytes,
355+
#[serde(with = "serde_utils::quoted_u64")]
356+
slots_per_epoch: u64,
357+
#[serde(with = "serde_utils::quoted_u64")]
358+
fulu_fork_epoch: u64,
299359
}
300360

301361
impl QuotedSpecFile {
302-
fn to_chain(&self) -> eyre::Result<(u64, ForkVersion)> {
362+
fn to_chain(&self) -> eyre::Result<(u64, ForkVersion, u64)> {
303363
let genesis_fork_version: ForkVersion =
304364
self.genesis_fork_version.as_ref().try_into()?;
305-
Ok((self.seconds_per_slot, genesis_fork_version))
365+
let fulu_fork_slot = self.fulu_fork_epoch.saturating_mul(self.slots_per_epoch);
366+
Ok((self.seconds_per_slot, genesis_fork_version, fulu_fork_slot))
306367
}
307368
}
308369

@@ -316,12 +377,16 @@ pub fn load_chain_from_file(path: PathBuf) -> eyre::Result<(u64, ForkVersion)> {
316377
struct SpecFile {
317378
seconds_per_slot: u64,
318379
genesis_fork_version: u32,
380+
slots_per_epoch: Option<u64>,
381+
fulu_fork_epoch: u64,
319382
}
320383

321384
impl SpecFile {
322-
fn to_chain(&self) -> (u64, ForkVersion) {
385+
fn to_chain(&self) -> (u64, ForkVersion, u64) {
323386
let genesis_fork_version: ForkVersion = self.genesis_fork_version.to_be_bytes();
324-
(self.seconds_per_slot, genesis_fork_version)
387+
let fulu_fork_slot =
388+
self.fulu_fork_epoch.saturating_mul(self.slots_per_epoch.unwrap_or(32));
389+
(self.seconds_per_slot, genesis_fork_version, fulu_fork_slot)
325390
}
326391
}
327392

@@ -357,12 +422,13 @@ mod tests {
357422

358423
#[test]
359424
fn test_load_custom() {
360-
let s = r#"chain = { genesis_time_secs = 1, slot_time_secs = 2, genesis_fork_version = "0x01000000" }"#;
425+
let s = r#"chain = { genesis_time_secs = 1, slot_time_secs = 2, genesis_fork_version = "0x01000000", fulu_fork_slot = 1 }"#;
361426
let decoded: MockConfig = toml::from_str(s).unwrap();
362427
assert_eq!(decoded.chain, Chain::Custom {
363428
genesis_time_secs: 1,
364429
slot_time_secs: 2,
365-
genesis_fork_version: [1, 0, 0, 0]
430+
genesis_fork_version: [1, 0, 0, 0],
431+
fulu_fork_slot: 1
366432
})
367433
}
368434

@@ -403,7 +469,8 @@ mod tests {
403469
assert_eq!(decoded.chain, Chain::Custom {
404470
genesis_time_secs: 1,
405471
slot_time_secs: KnownChain::Holesky.slot_time_sec(),
406-
genesis_fork_version: KnownChain::Holesky.genesis_fork_version()
472+
genesis_fork_version: KnownChain::Holesky.genesis_fork_version(),
473+
fulu_fork_slot: KnownChain::Holesky.fulu_fork_slot(),
407474
})
408475
}
409476

@@ -423,7 +490,8 @@ mod tests {
423490
assert_eq!(decoded.chain, Chain::Custom {
424491
genesis_time_secs: 1,
425492
slot_time_secs: KnownChain::Sepolia.slot_time_sec(),
426-
genesis_fork_version: KnownChain::Sepolia.genesis_fork_version()
493+
genesis_fork_version: KnownChain::Sepolia.genesis_fork_version(),
494+
fulu_fork_slot: KnownChain::Sepolia.fulu_fork_slot(),
427495
})
428496
}
429497

@@ -443,7 +511,8 @@ mod tests {
443511
assert_eq!(decoded.chain, Chain::Custom {
444512
genesis_time_secs: 1,
445513
slot_time_secs: KnownChain::Hoodi.slot_time_sec(),
446-
genesis_fork_version: KnownChain::Hoodi.genesis_fork_version()
514+
genesis_fork_version: KnownChain::Hoodi.genesis_fork_version(),
515+
fulu_fork_slot: KnownChain::Hoodi.fulu_fork_slot(),
447516
})
448517
}
449518

@@ -463,7 +532,8 @@ mod tests {
463532
assert_eq!(decoded.chain, Chain::Custom {
464533
genesis_time_secs: 1,
465534
slot_time_secs: KnownChain::Helder.slot_time_sec(),
466-
genesis_fork_version: KnownChain::Helder.genesis_fork_version()
535+
genesis_fork_version: KnownChain::Helder.genesis_fork_version(),
536+
fulu_fork_slot: KnownChain::Helder.fulu_fork_slot(),
467537
})
468538
}
469539
}

crates/pbs/src/mev_boost/submit_block.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use cb_common::{
66
pbs::{
77
BlindedBeaconBlock, BlindedBeaconBlockElectra, BlindedBeaconBlockFulu, BlobsBundle,
88
BuilderApiVersion, ExecutionPayload, ExecutionPayloadElectra, ExecutionPayloadFulu,
9-
HEADER_START_TIME_UNIX_MS, RelayClient, SignedBlindedBeaconBlock,
9+
HEADER_CONSENSUS_VERSION, HEADER_START_TIME_UNIX_MS, RelayClient, SignedBlindedBeaconBlock,
1010
SubmitBlindedBlockResponse,
1111
error::{PbsError, ValidationError},
1212
},
@@ -34,10 +34,20 @@ pub async fn submit_block<S: BuilderApiState>(
3434
state: PbsState<S>,
3535
api_version: &BuilderApiVersion,
3636
) -> eyre::Result<Option<SubmitBlindedBlockResponse>> {
37+
debug!(?req_headers, "received headers");
38+
let consensus_version =
39+
req_headers.get(HEADER_CONSENSUS_VERSION).cloned().unwrap_or_else(|| {
40+
let slot = signed_blinded_block.slot().as_u64();
41+
let fork = state.config.chain.fork_by_slot(slot);
42+
// safe because ForkName is visible ASCII chars
43+
HeaderValue::from_str(&fork.to_string()).unwrap()
44+
});
45+
3746
// prepare headers
3847
let mut send_headers = HeaderMap::new();
3948
send_headers.insert(HEADER_START_TIME_UNIX_MS, HeaderValue::from(utcnow_ms()));
4049
send_headers.insert(USER_AGENT, get_user_agent_with_version(&req_headers)?);
50+
send_headers.insert(HEADER_CONSENSUS_VERSION, consensus_version);
4151

4252
let relays = state.all_relays();
4353
let mut handles = Vec::with_capacity(relays.len());

tests/data/helder_spec.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ DENEB_FORK_EPOCH: 0
4040
ELECTRA_FORK_VERSION: 0x60132736
4141
ELECTRA_FORK_EPOCH: 999999
4242

43+
FULU_FORK_EPOCH: 18446744073709551615
44+
4345
# EIP7594 - Peerdas
4446
EIP7594_FORK_VERSION: 0x70132736
4547
EIP7594_FORK_EPOCH: 999999

0 commit comments

Comments
 (0)