Add Gloas boilerplate (#7728)

Adds the required boilerplate code for the Gloas (Glamsterdam) hard fork. This allows PRs testing Gloas-candidate features to test fork transition.

This also includes de-duplication of post-Bellatrix readiness notifiers from #6797 (credit to @dapplion)
This commit is contained in:
Mac L
2025-08-26 12:49:48 +10:00
committed by GitHub
parent daf1c7c3af
commit e438691683
75 changed files with 1801 additions and 917 deletions

View File

@@ -20,8 +20,8 @@ use tree_hash_derive::TreeHash;
use types::{
Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadBellatrix,
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu,
ExecutionPayloadHeader, FixedBytesExtended, ForkName, Hash256, KzgProofs, Transaction,
Transactions, Uint256,
ExecutionPayloadGloas, ExecutionPayloadHeader, FixedBytesExtended, ForkName, Hash256,
KzgProofs, Transaction, Transactions, Uint256,
};
use super::DEFAULT_TERMINAL_BLOCK;
@@ -146,10 +146,11 @@ pub struct ExecutionBlockGenerator<E: EthSpec> {
/*
* Post-merge fork triggers
*/
pub shanghai_time: Option<u64>, // capella
pub cancun_time: Option<u64>, // deneb
pub prague_time: Option<u64>, // electra
pub osaka_time: Option<u64>, // fulu
pub shanghai_time: Option<u64>, // capella
pub cancun_time: Option<u64>, // deneb
pub prague_time: Option<u64>, // electra
pub osaka_time: Option<u64>, // fulu
pub amsterdam_time: Option<u64>, // gloas
/*
* deneb stuff
*/
@@ -175,6 +176,7 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
cancun_time: Option<u64>,
prague_time: Option<u64>,
osaka_time: Option<u64>,
amsterdam_time: Option<u64>,
spec: Arc<ChainSpec>,
kzg: Option<Arc<Kzg>>,
) -> Self {
@@ -194,6 +196,7 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
cancun_time,
prague_time,
osaka_time,
amsterdam_time,
blobs_bundles: <_>::default(),
kzg,
rng: make_rng(),
@@ -243,19 +246,23 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
}
pub fn get_fork_at_timestamp(&self, timestamp: u64) -> ForkName {
match self.osaka_time {
Some(fork_time) if timestamp >= fork_time => ForkName::Fulu,
_ => match self.prague_time {
Some(fork_time) if timestamp >= fork_time => ForkName::Electra,
_ => match self.cancun_time {
Some(fork_time) if timestamp >= fork_time => ForkName::Deneb,
_ => match self.shanghai_time {
Some(fork_time) if timestamp >= fork_time => ForkName::Capella,
_ => ForkName::Bellatrix,
},
},
},
let forks = [
(self.amsterdam_time, ForkName::Gloas),
(self.osaka_time, ForkName::Fulu),
(self.prague_time, ForkName::Electra),
(self.cancun_time, ForkName::Deneb),
(self.shanghai_time, ForkName::Capella),
];
for (fork_time, fork_name) in forks {
if let Some(time) = fork_time
&& timestamp >= time
{
return fork_name;
}
}
ForkName::Bellatrix
}
pub fn execution_block_by_number(&self, number: u64) -> Option<ExecutionBlock> {
@@ -700,6 +707,25 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
blob_gas_used: 0,
excess_blob_gas: 0,
}),
ForkName::Gloas => ExecutionPayload::Gloas(ExecutionPayloadGloas {
parent_hash: head_block_hash,
fee_recipient: pa.suggested_fee_recipient,
receipts_root: Hash256::repeat_byte(42),
state_root: Hash256::repeat_byte(43),
logs_bloom: vec![0; 256].into(),
prev_randao: pa.prev_randao,
block_number: parent.block_number() + 1,
gas_limit: DEFAULT_GAS_LIMIT,
gas_used: GAS_USED,
timestamp: pa.timestamp,
extra_data: "block gen was here".as_bytes().to_vec().into(),
base_fee_per_gas: Uint256::from(1u64),
block_hash: ExecutionBlockHash::zero(),
transactions: vec![].into(),
withdrawals: pa.withdrawals.clone().into(),
blob_gas_used: 0,
excess_blob_gas: 0,
}),
_ => unreachable!(),
},
};
@@ -882,6 +908,12 @@ pub fn generate_genesis_header<E: EthSpec>(
*header.transactions_root_mut() = empty_transactions_root;
Some(header)
}
ForkName::Gloas => {
let mut header = ExecutionPayloadHeader::Gloas(<_>::default());
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
*header.transactions_root_mut() = empty_transactions_root;
Some(header)
}
}
}
@@ -955,6 +987,7 @@ mod test {
None,
None,
None,
None,
spec,
None,
);

View File

@@ -115,8 +115,12 @@ pub async fn handle_rpc<E: EthSpec>(
ENGINE_NEW_PAYLOAD_V3 => get_param::<JsonExecutionPayloadDeneb<E>>(params, 0)
.map(|jep| JsonExecutionPayload::Deneb(jep))
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
ENGINE_NEW_PAYLOAD_V4 => get_param::<JsonExecutionPayloadFulu<E>>(params, 0)
.map(|jep| JsonExecutionPayload::Fulu(jep))
ENGINE_NEW_PAYLOAD_V4 => get_param::<JsonExecutionPayloadGloas<E>>(params, 0)
.map(|jep| JsonExecutionPayload::Gloas(jep))
.or_else(|_| {
get_param::<JsonExecutionPayloadFulu<E>>(params, 0)
.map(|jep| JsonExecutionPayload::Fulu(jep))
})
.or_else(|_| {
get_param::<JsonExecutionPayloadElectra<E>>(params, 0)
.map(|jep| JsonExecutionPayload::Electra(jep))
@@ -185,7 +189,7 @@ pub async fn handle_rpc<E: EthSpec>(
));
}
}
ForkName::Electra | ForkName::Fulu => {
ForkName::Electra | ForkName::Fulu | ForkName::Gloas => {
if method == ENGINE_NEW_PAYLOAD_V1
|| method == ENGINE_NEW_PAYLOAD_V2
|| method == ENGINE_NEW_PAYLOAD_V3
@@ -322,7 +326,7 @@ pub async fn handle_rpc<E: EthSpec>(
));
}
// validate method called correctly according to fulu fork time
// validate method called correctly according to osaka fork time
if ctx
.execution_block_generator
.read()
@@ -339,6 +343,23 @@ pub async fn handle_rpc<E: EthSpec>(
));
}
// validate method called correctly according to amsterdam fork time
if ctx
.execution_block_generator
.read()
.get_fork_at_timestamp(response.timestamp())
== ForkName::Gloas
&& (method == ENGINE_GET_PAYLOAD_V1
|| method == ENGINE_GET_PAYLOAD_V2
|| method == ENGINE_GET_PAYLOAD_V3
|| method == ENGINE_GET_PAYLOAD_V4)
{
return Err((
format!("{} called after Gloas fork!", method),
FORK_REQUEST_MISMATCH_ERROR_CODE,
));
}
match method {
ENGINE_GET_PAYLOAD_V1 => {
Ok(serde_json::to_value(JsonExecutionPayload::from(response)).unwrap())
@@ -415,6 +436,21 @@ pub async fn handle_rpc<E: EthSpec>(
})
.unwrap()
}
JsonExecutionPayload::Gloas(execution_payload) => {
serde_json::to_value(JsonGetPayloadResponseGloas {
execution_payload,
block_value: Uint256::from(DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI),
blobs_bundle: maybe_blobs
.ok_or((
"No blobs returned despite V5 Payload".to_string(),
GENERIC_ERROR_CODE,
))?
.into(),
should_override_builder: false,
execution_requests: Default::default(),
})
.unwrap()
}
_ => unreachable!(),
}),
_ => unreachable!(),
@@ -451,7 +487,8 @@ pub async fn handle_rpc<E: EthSpec>(
ForkName::Capella
| ForkName::Deneb
| ForkName::Electra
| ForkName::Fulu => {
| ForkName::Fulu
| ForkName::Gloas => {
get_param::<Option<JsonPayloadAttributesV2>>(params, 1)
.map(|opt| opt.map(JsonPayloadAttributes::V2))
.transpose()
@@ -515,7 +552,7 @@ pub async fn handle_rpc<E: EthSpec>(
));
}
}
ForkName::Deneb | ForkName::Electra | ForkName::Fulu => {
ForkName::Deneb | ForkName::Electra | ForkName::Fulu | ForkName::Gloas => {
if method == ENGINE_FORKCHOICE_UPDATED_V1 {
return Err((
format!("{} called after Deneb fork!", method),

View File

@@ -27,7 +27,7 @@ use tracing::{debug, error, info, warn};
use tree_hash::TreeHash;
use types::builder_bid::{
BuilderBid, BuilderBidBellatrix, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra,
BuilderBidFulu, SignedBuilderBid,
BuilderBidFulu, BuilderBidGloas, SignedBuilderBid,
};
use types::{
Address, BeaconState, ChainSpec, Epoch, EthSpec, ExecPayload, ExecutionPayload,
@@ -115,6 +115,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.fee_recipient = fee_recipient;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.fee_recipient = fee_recipient;
}
}
}
@@ -135,6 +138,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.gas_limit = gas_limit;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.gas_limit = gas_limit;
}
}
}
@@ -159,6 +165,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
}
}
}
@@ -179,6 +188,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.prev_randao = prev_randao;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.prev_randao = prev_randao;
}
}
}
@@ -199,6 +211,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.block_number = block_number;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.block_number = block_number;
}
}
}
@@ -219,6 +234,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.timestamp = timestamp;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.timestamp = timestamp;
}
}
}
@@ -239,6 +257,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Fulu(header) => {
header.withdrawals_root = withdrawals_root;
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.withdrawals_root = withdrawals_root;
}
}
}
@@ -272,6 +293,10 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
header.extra_data = extra_data;
header.block_hash = ExecutionBlockHash::from_root(header.tree_hash_root());
}
ExecutionPayloadHeaderRefMut::Gloas(header) => {
header.extra_data = extra_data;
header.block_hash = ExecutionBlockHash::from_root(header.tree_hash_root());
}
}
}
}
@@ -457,6 +482,9 @@ impl<E: EthSpec> MockBuilder<E> {
SignedBlindedBeaconBlock::Fulu(block) => {
block.message.body.execution_payload.tree_hash_root()
}
SignedBlindedBeaconBlock::Gloas(block) => {
block.message.body.execution_payload.tree_hash_root()
}
};
info!(
block_hash = %root,
@@ -540,6 +568,18 @@ impl<E: EthSpec> MockBuilder<E> {
) = payload_response.into();
match fork {
ForkName::Gloas => BuilderBid::Gloas(BuilderBidGloas {
header: payload
.as_gloas()
.map_err(|_| "incorrect payload variant".to_string())?
.into(),
blob_kzg_commitments: maybe_blobs_bundle
.map(|b| b.commitments.clone())
.unwrap_or_default(),
value: self.get_bid_value(value),
pubkey: self.builder_sk.public_key().compress(),
execution_requests: maybe_requests.unwrap_or_default(),
}),
ForkName::Fulu => BuilderBid::Fulu(BuilderBidFulu {
header: payload
.as_fulu()
@@ -845,13 +885,15 @@ impl<E: EthSpec> MockBuilder<E> {
expected_withdrawals,
None,
),
ForkName::Deneb | ForkName::Electra | ForkName::Fulu => PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
expected_withdrawals,
Some(head_block_root),
),
ForkName::Deneb | ForkName::Electra | ForkName::Fulu | ForkName::Gloas => {
PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
expected_withdrawals,
Some(head_block_root),
)
}
ForkName::Base | ForkName::Altair => {
return Err("invalid fork".to_string());
}

View File

@@ -29,6 +29,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
None,
None,
None,
None,
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
Arc::new(spec),
None,
@@ -43,6 +44,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
cancun_time: Option<u64>,
prague_time: Option<u64>,
osaka_time: Option<u64>,
amsterdam_time: Option<u64>,
jwt_key: Option<JwtKey>,
spec: Arc<ChainSpec>,
kzg: Option<Arc<Kzg>>,
@@ -60,6 +62,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
cancun_time,
prague_time,
osaka_time,
amsterdam_time,
spec.clone(),
kzg,
);

View File

@@ -86,6 +86,7 @@ pub struct MockExecutionConfig {
pub cancun_time: Option<u64>,
pub prague_time: Option<u64>,
pub osaka_time: Option<u64>,
pub amsterdam_time: Option<u64>,
}
impl Default for MockExecutionConfig {
@@ -100,6 +101,7 @@ impl Default for MockExecutionConfig {
cancun_time: None,
prague_time: None,
osaka_time: None,
amsterdam_time: None,
}
}
}
@@ -123,6 +125,7 @@ impl<E: EthSpec> MockServer<E> {
None, // FIXME(deneb): should this be the default?
None, // FIXME(electra): should this be the default?
None, // FIXME(fulu): should this be the default?
None, // FIXME(gloas): should this be the default?
chain_spec,
None,
)
@@ -145,6 +148,7 @@ impl<E: EthSpec> MockServer<E> {
cancun_time,
prague_time,
osaka_time,
amsterdam_time,
} = config;
let last_echo_request = Arc::new(RwLock::new(None));
let preloaded_responses = Arc::new(Mutex::new(vec![]));
@@ -156,6 +160,7 @@ impl<E: EthSpec> MockServer<E> {
cancun_time,
prague_time,
osaka_time,
amsterdam_time,
spec,
kzg,
);
@@ -220,6 +225,7 @@ impl<E: EthSpec> MockServer<E> {
cancun_time: Option<u64>,
prague_time: Option<u64>,
osaka_time: Option<u64>,
amsterdam_time: Option<u64>,
spec: Arc<ChainSpec>,
kzg: Option<Arc<Kzg>>,
) -> Self {
@@ -235,6 +241,7 @@ impl<E: EthSpec> MockServer<E> {
cancun_time,
prague_time,
osaka_time,
amsterdam_time,
},
spec,
kzg,