Add Electra fork boilerplate (#5122)

* Add Electra fork boilerplate

* Remove electra from spec tests

* Fix tests

* Remove sneaky log file

* Fix more tests

* Fix even more tests and add suggestions

* Remove unrelated lcli addition

* Update more tests

* Merge branch 'unstable' into electra

* Add comment for test-suite lcli override

* Merge branch 'unstable' into electra

* Cleanup

* Merge branch 'unstable' into electra

* Apply suggestions

* Merge branch 'unstable' into electra

* Merge sigp/unstable into electra

* Merge branch 'unstable' into electra
This commit is contained in:
Mac L
2024-04-02 23:35:02 +11:00
committed by GitHub
parent 3058b96f25
commit f8fdb71f50
105 changed files with 2079 additions and 405 deletions

View File

@@ -236,4 +236,34 @@ mod test {
.unwrap();
test_rlp_encoding(&header, None, expected_hash);
}
#[test]
fn test_rlp_encode_block_electra() {
let header = ExecutionBlockHeader {
parent_hash: Hash256::from_str("172864416698b842f4c92f7b476be294b4ef720202779df194cd225f531053ab").unwrap(),
ommers_hash: Hash256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(),
beneficiary: Address::from_str("878705ba3f8bc32fcf7f4caa1a35e72af65cf766").unwrap(),
state_root: Hash256::from_str("c6457d0df85c84c62d1c68f68138b6e796e8a44fb44de221386fb2d5611c41e0").unwrap(),
transactions_root: Hash256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap(),
receipts_root: Hash256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap(),
logs_bloom:<[u8; 256]>::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().into(),
difficulty: 0.into(),
number: 97.into(),
gas_limit: 27482534.into(),
gas_used: 0.into(),
timestamp: 1692132829u64,
extra_data: hex::decode("d883010d00846765746888676f312e32302e37856c696e7578").unwrap(),
mix_hash: Hash256::from_str("0b493c22d2ad4ca76c77ae6ad916af429b42b1dc98fdcb8e5ddbd049bbc5d623").unwrap(),
nonce: Hash64::zero(),
base_fee_per_gas: 2374u64.into(),
withdrawals_root: Some(Hash256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap()),
blob_gas_used: Some(0x0u64),
excess_blob_gas: Some(0x0u64),
parent_beacon_block_root: Some(Hash256::from_str("f7d327d2c04e4f12e9cdd492e53d39a1d390f8b1571e3b2a22ac6e1e170e5b1a").unwrap()),
};
let expected_hash =
Hash256::from_str("a7448e600ead0a23d16f96aa46e8dea9eef8a7c5669a5f0a5ff32709afe9c408")
.unwrap();
test_rlp_encoding(&header, None, expected_hash);
}
}

View File

@@ -24,7 +24,11 @@ pub use types::{
ExecutionPayloadRef, FixedVector, ForkName, Hash256, Transactions, Uint256, VariableList,
Withdrawal, Withdrawals,
};
use types::{ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge, KzgProofs};
use types::{
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadMerge,
KzgProofs,
};
pub mod auth;
pub mod http;
@@ -32,7 +36,8 @@ pub mod json_structures;
mod new_payload_request;
pub use new_payload_request::{
NewPayloadRequest, NewPayloadRequestCapella, NewPayloadRequestDeneb, NewPayloadRequestMerge,
NewPayloadRequest, NewPayloadRequestCapella, NewPayloadRequestDeneb, NewPayloadRequestElectra,
NewPayloadRequestMerge,
};
pub const LATEST_TAG: &str = "latest";
@@ -150,7 +155,7 @@ pub struct ExecutionBlock {
/// Representation of an execution block with enough detail to reconstruct a payload.
#[superstruct(
variants(Merge, Capella, Deneb),
variants(Merge, Capella, Deneb, Electra),
variant_attributes(
derive(Clone, Debug, PartialEq, Serialize, Deserialize,),
serde(bound = "T: EthSpec", rename_all = "camelCase"),
@@ -184,12 +189,12 @@ pub struct ExecutionBlockWithTransactions<T: EthSpec> {
#[serde(rename = "hash")]
pub block_hash: ExecutionBlockHash,
pub transactions: Vec<Transaction>,
#[superstruct(only(Capella, Deneb))]
#[superstruct(only(Capella, Deneb, Electra))]
pub withdrawals: Vec<JsonWithdrawal>,
#[superstruct(only(Deneb))]
#[superstruct(only(Deneb, Electra))]
#[serde(with = "serde_utils::u64_hex_be")]
pub blob_gas_used: u64,
#[superstruct(only(Deneb))]
#[superstruct(only(Deneb, Electra))]
#[serde(with = "serde_utils::u64_hex_be")]
pub excess_blob_gas: u64,
}
@@ -271,6 +276,34 @@ impl<T: EthSpec> TryFrom<ExecutionPayload<T>> for ExecutionBlockWithTransactions
blob_gas_used: block.blob_gas_used,
excess_blob_gas: block.excess_blob_gas,
}),
ExecutionPayload::Electra(block) => {
Self::Electra(ExecutionBlockWithTransactionsElectra {
parent_hash: block.parent_hash,
fee_recipient: block.fee_recipient,
state_root: block.state_root,
receipts_root: block.receipts_root,
logs_bloom: block.logs_bloom,
prev_randao: block.prev_randao,
block_number: block.block_number,
gas_limit: block.gas_limit,
gas_used: block.gas_used,
timestamp: block.timestamp,
extra_data: block.extra_data,
base_fee_per_gas: block.base_fee_per_gas,
block_hash: block.block_hash,
transactions: block
.transactions
.iter()
.map(|tx| Transaction::decode(&Rlp::new(tx)))
.collect::<Result<Vec<_>, _>>()?,
withdrawals: Vec::from(block.withdrawals)
.into_iter()
.map(|withdrawal| withdrawal.into())
.collect(),
blob_gas_used: block.blob_gas_used,
excess_blob_gas: block.excess_blob_gas,
})
}
};
Ok(json_payload)
}
@@ -390,7 +423,7 @@ pub struct ProposeBlindedBlockResponse {
}
#[superstruct(
variants(Merge, Capella, Deneb),
variants(Merge, Capella, Deneb, Electra),
variant_attributes(derive(Clone, Debug, PartialEq),),
map_into(ExecutionPayload),
map_ref_into(ExecutionPayloadRef),
@@ -405,10 +438,12 @@ pub struct GetPayloadResponse<T: EthSpec> {
pub execution_payload: ExecutionPayloadCapella<T>,
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))]
pub execution_payload: ExecutionPayloadDeneb<T>,
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
pub execution_payload: ExecutionPayloadElectra<T>,
pub block_value: Uint256,
#[superstruct(only(Deneb))]
#[superstruct(only(Deneb, Electra))]
pub blobs_bundle: BlobsBundle<T>,
#[superstruct(only(Deneb), partial_getter(copy))]
#[superstruct(only(Deneb, Electra), partial_getter(copy))]
pub should_override_builder: bool,
}
@@ -462,6 +497,11 @@ impl<T: EthSpec> From<GetPayloadResponse<T>>
inner.block_value,
Some(inner.blobs_bundle),
),
GetPayloadResponse::Electra(inner) => (
ExecutionPayload::Electra(inner.execution_payload),
inner.block_value,
Some(inner.blobs_bundle),
),
}
}
}
@@ -562,7 +602,35 @@ impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
}))
} else {
Err(format!(
"block {} is post capella but payload body doesn't have withdrawals",
"block {} is post-capella but payload body doesn't have withdrawals",
header.block_hash
))
}
}
ExecutionPayloadHeader::Electra(header) => {
if let Some(withdrawals) = self.withdrawals {
Ok(ExecutionPayload::Electra(ExecutionPayloadElectra {
parent_hash: header.parent_hash,
fee_recipient: header.fee_recipient,
state_root: header.state_root,
receipts_root: header.receipts_root,
logs_bloom: header.logs_bloom,
prev_randao: header.prev_randao,
block_number: header.block_number,
gas_limit: header.gas_limit,
gas_used: header.gas_used,
timestamp: header.timestamp,
extra_data: header.extra_data,
base_fee_per_gas: header.base_fee_per_gas,
block_hash: header.block_hash,
transactions: self.transactions,
withdrawals,
blob_gas_used: header.blob_gas_used,
excess_blob_gas: header.excess_blob_gas,
}))
} else {
Err(format!(
"block {} is post-capella but payload body doesn't have withdrawals",
header.block_hash
))
}

View File

@@ -740,6 +740,14 @@ impl HttpJsonRpc {
)
.await?,
),
ForkName::Electra => ExecutionBlockWithTransactions::Electra(
self.rpc_request(
ETH_GET_BLOCK_BY_HASH,
params,
ETH_GET_BLOCK_BY_HASH_TIMEOUT * self.execution_timeout_multiplier,
)
.await?,
),
ForkName::Base | ForkName::Altair => {
return Err(Error::UnsupportedForkVariant(format!(
"called get_block_by_hash_with_txns with fork {:?}",
@@ -783,7 +791,7 @@ impl HttpJsonRpc {
Ok(response.into())
}
pub async fn new_payload_v3<T: EthSpec>(
pub async fn new_payload_v3_deneb<T: EthSpec>(
&self,
new_payload_request_deneb: NewPayloadRequestDeneb<'_, T>,
) -> Result<PayloadStatusV1, Error> {
@@ -804,6 +812,27 @@ impl HttpJsonRpc {
Ok(response.into())
}
pub async fn new_payload_v3_electra<T: EthSpec>(
&self,
new_payload_request_electra: NewPayloadRequestElectra<'_, T>,
) -> Result<PayloadStatusV1, Error> {
let params = json!([
JsonExecutionPayload::V4(new_payload_request_electra.execution_payload.clone().into()),
new_payload_request_electra.versioned_hashes,
new_payload_request_electra.parent_beacon_block_root,
]);
let response: JsonPayloadStatusV1 = self
.rpc_request(
ENGINE_NEW_PAYLOAD_V3,
params,
ENGINE_NEW_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier,
)
.await?;
Ok(response.into())
}
pub async fn get_payload_v1<T: EthSpec>(
&self,
payload_id: PayloadId,
@@ -855,7 +884,7 @@ impl HttpJsonRpc {
.await?;
Ok(JsonGetPayloadResponse::V2(response).into())
}
ForkName::Base | ForkName::Altair | ForkName::Deneb => Err(
ForkName::Base | ForkName::Altair | ForkName::Deneb | ForkName::Electra => Err(
Error::UnsupportedForkVariant(format!("called get_payload_v2 with {}", fork_name)),
),
}
@@ -879,6 +908,16 @@ impl HttpJsonRpc {
.await?;
Ok(JsonGetPayloadResponse::V3(response).into())
}
ForkName::Electra => {
let response: JsonGetPayloadResponseV4<T> = self
.rpc_request(
ENGINE_GET_PAYLOAD_V3,
params,
ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier,
)
.await?;
Ok(JsonGetPayloadResponse::V4(response).into())
}
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => Err(
Error::UnsupportedForkVariant(format!("called get_payload_v3 with {}", fork_name)),
),
@@ -1069,7 +1108,15 @@ impl HttpJsonRpc {
}
NewPayloadRequest::Deneb(new_payload_request_deneb) => {
if engine_capabilities.new_payload_v3 {
self.new_payload_v3(new_payload_request_deneb).await
self.new_payload_v3_deneb(new_payload_request_deneb).await
} else {
Err(Error::RequiredMethodUnsupported("engine_newPayloadV3"))
}
}
NewPayloadRequest::Electra(new_payload_request_electra) => {
if engine_capabilities.new_payload_v3 {
self.new_payload_v3_electra(new_payload_request_electra)
.await
} else {
Err(Error::RequiredMethodUnsupported("engine_newPayloadV3"))
}
@@ -1095,7 +1142,7 @@ impl HttpJsonRpc {
Err(Error::RequiredMethodUnsupported("engine_getPayload"))
}
}
ForkName::Deneb => {
ForkName::Deneb | ForkName::Electra => {
if engine_capabilities.get_payload_v3 {
self.get_payload_v3(fork_name, payload_id).await
} else {
@@ -1775,7 +1822,7 @@ mod test {
"extraData":"0x",
"baseFeePerGas":"0x7",
"blockHash":"0x6359b8381a370e2f54072a5784ddd78b6ed024991558c511d4452eb4f6ac898c",
"transactions":[]
"transactions":[],
}
})],
|client| async move {
@@ -1799,7 +1846,7 @@ mod test {
extra_data: vec![].into(),
base_fee_per_gas: Uint256::from(7),
block_hash: ExecutionBlockHash::from_str("0x6359b8381a370e2f54072a5784ddd78b6ed024991558c511d4452eb4f6ac898c").unwrap(),
transactions: vec![].into(),
transactions: vec![].into(),
});
assert_eq!(payload, expected);
@@ -1846,7 +1893,7 @@ mod test {
"extraData":"0x",
"baseFeePerGas":"0x7",
"blockHash":"0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858",
"transactions":[]
"transactions":[],
}],
})
)

View File

@@ -60,7 +60,7 @@ pub struct JsonPayloadIdResponse {
}
#[superstruct(
variants(V1, V2, V3),
variants(V1, V2, V3, V4),
variant_attributes(
derive(Debug, PartialEq, Default, Serialize, Deserialize,),
serde(bound = "T: EthSpec", rename_all = "camelCase"),
@@ -93,12 +93,12 @@ pub struct JsonExecutionPayload<T: EthSpec> {
pub block_hash: ExecutionBlockHash,
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
pub transactions: Transactions<T>,
#[superstruct(only(V2, V3))]
#[superstruct(only(V2, V3, V4))]
pub withdrawals: VariableList<JsonWithdrawal, T::MaxWithdrawalsPerPayload>,
#[superstruct(only(V3))]
#[superstruct(only(V3, V4))]
#[serde(with = "serde_utils::u64_hex_be")]
pub blob_gas_used: u64,
#[superstruct(only(V3))]
#[superstruct(only(V3, V4))]
#[serde(with = "serde_utils::u64_hex_be")]
pub excess_blob_gas: u64,
}
@@ -178,12 +178,42 @@ impl<T: EthSpec> From<ExecutionPayloadDeneb<T>> for JsonExecutionPayloadV3<T> {
}
}
impl<T: EthSpec> From<ExecutionPayloadElectra<T>> for JsonExecutionPayloadV4<T> {
fn from(payload: ExecutionPayloadElectra<T>) -> Self {
JsonExecutionPayloadV4 {
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom,
prev_randao: payload.prev_randao,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data,
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions: payload.transactions,
withdrawals: payload
.withdrawals
.into_iter()
.map(Into::into)
.collect::<Vec<_>>()
.into(),
blob_gas_used: payload.blob_gas_used,
excess_blob_gas: payload.excess_blob_gas,
}
}
}
impl<T: EthSpec> From<ExecutionPayload<T>> for JsonExecutionPayload<T> {
fn from(execution_payload: ExecutionPayload<T>) -> Self {
match execution_payload {
ExecutionPayload::Merge(payload) => JsonExecutionPayload::V1(payload.into()),
ExecutionPayload::Capella(payload) => JsonExecutionPayload::V2(payload.into()),
ExecutionPayload::Deneb(payload) => JsonExecutionPayload::V3(payload.into()),
ExecutionPayload::Electra(payload) => JsonExecutionPayload::V4(payload.into()),
}
}
}
@@ -234,6 +264,7 @@ impl<T: EthSpec> From<JsonExecutionPayloadV2<T>> for ExecutionPayloadCapella<T>
}
}
}
impl<T: EthSpec> From<JsonExecutionPayloadV3<T>> for ExecutionPayloadDeneb<T> {
fn from(payload: JsonExecutionPayloadV3<T>) -> Self {
ExecutionPayloadDeneb {
@@ -263,18 +294,48 @@ impl<T: EthSpec> From<JsonExecutionPayloadV3<T>> for ExecutionPayloadDeneb<T> {
}
}
impl<T: EthSpec> From<JsonExecutionPayloadV4<T>> for ExecutionPayloadElectra<T> {
fn from(payload: JsonExecutionPayloadV4<T>) -> Self {
ExecutionPayloadElectra {
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom,
prev_randao: payload.prev_randao,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data,
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions: payload.transactions,
withdrawals: payload
.withdrawals
.into_iter()
.map(Into::into)
.collect::<Vec<_>>()
.into(),
blob_gas_used: payload.blob_gas_used,
excess_blob_gas: payload.excess_blob_gas,
}
}
}
impl<T: EthSpec> From<JsonExecutionPayload<T>> for ExecutionPayload<T> {
fn from(json_execution_payload: JsonExecutionPayload<T>) -> Self {
match json_execution_payload {
JsonExecutionPayload::V1(payload) => ExecutionPayload::Merge(payload.into()),
JsonExecutionPayload::V2(payload) => ExecutionPayload::Capella(payload.into()),
JsonExecutionPayload::V3(payload) => ExecutionPayload::Deneb(payload.into()),
JsonExecutionPayload::V4(payload) => ExecutionPayload::Electra(payload.into()),
}
}
}
#[superstruct(
variants(V1, V2, V3),
variants(V1, V2, V3, V4),
variant_attributes(
derive(Debug, PartialEq, Serialize, Deserialize),
serde(bound = "T: EthSpec", rename_all = "camelCase")
@@ -291,11 +352,13 @@ pub struct JsonGetPayloadResponse<T: EthSpec> {
pub execution_payload: JsonExecutionPayloadV2<T>,
#[superstruct(only(V3), partial_getter(rename = "execution_payload_v3"))]
pub execution_payload: JsonExecutionPayloadV3<T>,
#[superstruct(only(V4), partial_getter(rename = "execution_payload_v4"))]
pub execution_payload: JsonExecutionPayloadV4<T>,
#[serde(with = "serde_utils::u256_hex_be")]
pub block_value: Uint256,
#[superstruct(only(V3))]
#[superstruct(only(V3, V4))]
pub blobs_bundle: JsonBlobsBundleV1<T>,
#[superstruct(only(V3))]
#[superstruct(only(V3, V4))]
pub should_override_builder: bool,
}
@@ -322,6 +385,14 @@ impl<T: EthSpec> From<JsonGetPayloadResponse<T>> for GetPayloadResponse<T> {
should_override_builder: response.should_override_builder,
})
}
JsonGetPayloadResponse::V4(response) => {
GetPayloadResponse::Electra(GetPayloadResponseElectra {
execution_payload: response.execution_payload.into(),
block_value: response.block_value,
blobs_bundle: response.blobs_bundle.into(),
should_override_builder: response.should_override_builder,
})
}
}
}
}

View File

@@ -7,10 +7,12 @@ use types::{
BeaconBlockRef, BeaconStateError, EthSpec, ExecutionBlockHash, ExecutionPayload,
ExecutionPayloadRef, Hash256, VersionedHash,
};
use types::{ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge};
use types::{
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadMerge,
};
#[superstruct(
variants(Merge, Capella, Deneb),
variants(Merge, Capella, Deneb, Electra),
variant_attributes(derive(Clone, Debug, PartialEq),),
map_into(ExecutionPayload),
map_ref_into(ExecutionPayloadRef),
@@ -31,9 +33,11 @@ pub struct NewPayloadRequest<'block, E: EthSpec> {
pub execution_payload: &'block ExecutionPayloadCapella<E>,
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))]
pub execution_payload: &'block ExecutionPayloadDeneb<E>,
#[superstruct(only(Deneb))]
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
pub execution_payload: &'block ExecutionPayloadElectra<E>,
#[superstruct(only(Deneb, Electra))]
pub versioned_hashes: Vec<VersionedHash>,
#[superstruct(only(Deneb))]
#[superstruct(only(Deneb, Electra))]
pub parent_beacon_block_root: Hash256,
}
@@ -43,6 +47,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
Self::Merge(payload) => payload.execution_payload.parent_hash,
Self::Capella(payload) => payload.execution_payload.parent_hash,
Self::Deneb(payload) => payload.execution_payload.parent_hash,
Self::Electra(payload) => payload.execution_payload.parent_hash,
}
}
@@ -51,6 +56,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
Self::Merge(payload) => payload.execution_payload.block_hash,
Self::Capella(payload) => payload.execution_payload.block_hash,
Self::Deneb(payload) => payload.execution_payload.block_hash,
Self::Electra(payload) => payload.execution_payload.block_hash,
}
}
@@ -59,6 +65,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
Self::Merge(payload) => payload.execution_payload.block_number,
Self::Capella(payload) => payload.execution_payload.block_number,
Self::Deneb(payload) => payload.execution_payload.block_number,
Self::Electra(payload) => payload.execution_payload.block_number,
}
}
@@ -67,6 +74,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
Self::Merge(request) => ExecutionPayloadRef::Merge(request.execution_payload),
Self::Capella(request) => ExecutionPayloadRef::Capella(request.execution_payload),
Self::Deneb(request) => ExecutionPayloadRef::Deneb(request.execution_payload),
Self::Electra(request) => ExecutionPayloadRef::Electra(request.execution_payload),
}
}
@@ -75,6 +83,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
Self::Merge(request) => ExecutionPayload::Merge(request.execution_payload.clone()),
Self::Capella(request) => ExecutionPayload::Capella(request.execution_payload.clone()),
Self::Deneb(request) => ExecutionPayload::Deneb(request.execution_payload.clone()),
Self::Electra(request) => ExecutionPayload::Electra(request.execution_payload.clone()),
}
}
@@ -157,6 +166,16 @@ impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<'a, E>
.collect(),
parent_beacon_block_root: block_ref.parent_root,
})),
BeaconBlockRef::Electra(block_ref) => Ok(Self::Electra(NewPayloadRequestElectra {
execution_payload: &block_ref.body.execution_payload.execution_payload,
versioned_hashes: block_ref
.body
.blob_kzg_commitments
.iter()
.map(kzg_commitment_to_versioned_hash)
.collect(),
parent_beacon_block_root: block_ref.parent_root,
})),
}
}
}
@@ -173,6 +192,7 @@ impl<'a, E: EthSpec> TryFrom<ExecutionPayloadRef<'a, E>> for NewPayloadRequest<'
execution_payload: payload,
})),
ExecutionPayloadRef::Deneb(_) => Err(Self::Error::IncorrectStateVariant),
ExecutionPayloadRef::Electra(_) => Err(Self::Error::IncorrectStateVariant),
}
}
}

View File

@@ -51,7 +51,8 @@ use types::{
};
use types::{
BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadCapella,
ExecutionPayloadMerge, FullPayload, ProposerPreparationData, PublicKeyBytes, Signature, Slot,
ExecutionPayloadElectra, ExecutionPayloadMerge, FullPayload, ProposerPreparationData,
PublicKeyBytes, Signature, Slot,
};
mod block_hash;
@@ -111,6 +112,12 @@ impl<E: EthSpec> TryFrom<BuilderBid<E>> for ProvenancedPayload<BlockProposalCont
kzg_commitments: builder_bid.blob_kzg_commitments,
blobs_and_proofs: None,
},
BuilderBid::Electra(builder_bid) => BlockProposalContents::PayloadAndBlobs {
payload: ExecutionPayloadHeader::Electra(builder_bid.header).into(),
block_value: builder_bid.value,
kzg_commitments: builder_bid.blob_kzg_commitments,
blobs_and_proofs: None,
},
};
Ok(ProvenancedPayload::Builder(
BlockProposalContentsType::Blinded(block_proposal_contents),
@@ -1771,6 +1778,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
ForkName::Merge => ExecutionPayloadMerge::default().into(),
ForkName::Capella => ExecutionPayloadCapella::default().into(),
ForkName::Deneb => ExecutionPayloadDeneb::default().into(),
ForkName::Electra => ExecutionPayloadElectra::default().into(),
ForkName::Base | ForkName::Altair => {
return Err(Error::InvalidForkForPayload);
}
@@ -1839,6 +1847,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
ForkName::Merge => Ok(Some(ExecutionPayloadMerge::default().into())),
ForkName::Capella => Ok(Some(ExecutionPayloadCapella::default().into())),
ForkName::Deneb => Ok(Some(ExecutionPayloadDeneb::default().into())),
ForkName::Electra => Ok(Some(ExecutionPayloadElectra::default().into())),
ForkName::Base | ForkName::Altair => Err(ApiError::UnsupportedForkVariant(
format!("called get_payload_by_hash_from_engine with {}", fork),
)),
@@ -1938,6 +1947,35 @@ impl<T: EthSpec> ExecutionLayer<T> {
excess_blob_gas: deneb_block.excess_blob_gas,
})
}
ExecutionBlockWithTransactions::Electra(electra_block) => {
let withdrawals = VariableList::new(
electra_block
.withdrawals
.into_iter()
.map(Into::into)
.collect(),
)
.map_err(ApiError::DeserializeWithdrawals)?;
ExecutionPayload::Electra(ExecutionPayloadElectra {
parent_hash: electra_block.parent_hash,
fee_recipient: electra_block.fee_recipient,
state_root: electra_block.state_root,
receipts_root: electra_block.receipts_root,
logs_bloom: electra_block.logs_bloom,
prev_randao: electra_block.prev_randao,
block_number: electra_block.block_number,
gas_limit: electra_block.gas_limit,
gas_used: electra_block.gas_used,
timestamp: electra_block.timestamp,
extra_data: electra_block.extra_data,
base_fee_per_gas: electra_block.base_fee_per_gas,
block_hash: electra_block.block_hash,
transactions: convert_transactions(electra_block.transactions)?,
withdrawals,
blob_gas_used: electra_block.blob_gas_used,
excess_blob_gas: electra_block.excess_blob_gas,
})
}
};
Ok(Some(payload))

View File

@@ -22,8 +22,8 @@ use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use types::{
Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadCapella,
ExecutionPayloadDeneb, ExecutionPayloadHeader, ExecutionPayloadMerge, ForkName, Hash256,
Transaction, Transactions, Uint256,
ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadHeader, ExecutionPayloadMerge,
ForkName, Hash256, Transaction, Transactions, Uint256,
};
use super::DEFAULT_TERMINAL_BLOCK;
@@ -130,8 +130,9 @@ pub struct ExecutionBlockGenerator<T: EthSpec> {
/*
* Post-merge fork triggers
*/
pub shanghai_time: Option<u64>, // withdrawals
pub shanghai_time: Option<u64>, // capella
pub cancun_time: Option<u64>, // deneb
pub prague_time: Option<u64>, // electra
/*
* deneb stuff
*/
@@ -153,6 +154,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
terminal_block_hash: ExecutionBlockHash,
shanghai_time: Option<u64>,
cancun_time: Option<u64>,
prague_time: Option<u64>,
kzg: Option<Kzg>,
) -> Self {
let mut gen = Self {
@@ -168,6 +170,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
payload_ids: <_>::default(),
shanghai_time,
cancun_time,
prague_time,
blobs_bundles: <_>::default(),
kzg: kzg.map(Arc::new),
rng: make_rng(),
@@ -203,11 +206,14 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
}
pub fn get_fork_at_timestamp(&self, timestamp: u64) -> ForkName {
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::Merge,
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::Merge,
},
},
}
}
@@ -514,6 +520,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
let execution_payload =
self.build_new_execution_payload(head_block_hash, &parent, id, &attributes)?;
self.payload_ids.insert(id, execution_payload);
Some(id)
@@ -601,30 +608,52 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
}),
_ => unreachable!(),
},
PayloadAttributes::V3(pa) => ExecutionPayload::Deneb(ExecutionPayloadDeneb {
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: 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::one(),
block_hash: ExecutionBlockHash::zero(),
transactions: vec![].into(),
withdrawals: pa.withdrawals.clone().into(),
blob_gas_used: 0,
excess_blob_gas: 0,
}),
PayloadAttributes::V3(pa) => match self.get_fork_at_timestamp(pa.timestamp) {
ForkName::Deneb => ExecutionPayload::Deneb(ExecutionPayloadDeneb {
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: 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::one(),
block_hash: ExecutionBlockHash::zero(),
transactions: vec![].into(),
withdrawals: pa.withdrawals.clone().into(),
blob_gas_used: 0,
excess_blob_gas: 0,
}),
ForkName::Electra => ExecutionPayload::Electra(ExecutionPayloadElectra {
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: 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::one(),
block_hash: ExecutionBlockHash::zero(),
transactions: vec![].into(),
withdrawals: pa.withdrawals.clone().into(),
blob_gas_used: 0,
excess_blob_gas: 0,
}),
_ => unreachable!(),
},
};
match execution_payload.fork_name() {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {}
ForkName::Deneb => {
ForkName::Deneb | ForkName::Electra => {
// get random number between 0 and Max Blobs
let mut rng = self.rng.lock();
let num_blobs = rng.gen::<usize>() % (T::max_blobs_per_block() + 1);
@@ -758,6 +787,11 @@ pub fn generate_genesis_header<T: EthSpec>(
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
Some(header)
}
ForkName::Electra => {
let mut header = ExecutionPayloadHeader::Electra(<_>::default());
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
Some(header)
}
}
}
@@ -830,6 +864,7 @@ mod test {
None,
None,
None,
None,
);
for i in 0..=TERMINAL_BLOCK {

View File

@@ -105,14 +105,18 @@ pub async fn handle_rpc<T: EthSpec>(
.map(|jep| JsonExecutionPayload::V1(jep))
})
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
ENGINE_NEW_PAYLOAD_V3 => get_param::<JsonExecutionPayloadV3<T>>(params, 0)
.map(|jep| JsonExecutionPayload::V3(jep))
ENGINE_NEW_PAYLOAD_V3 => get_param::<JsonExecutionPayloadV4<T>>(params, 0)
.map(|jep| JsonExecutionPayload::V4(jep))
.or_else(|_| {
get_param::<JsonExecutionPayloadV2<T>>(params, 0)
.map(|jep| JsonExecutionPayload::V2(jep))
get_param::<JsonExecutionPayloadV3<T>>(params, 0)
.map(|jep| JsonExecutionPayload::V3(jep))
.or_else(|_| {
get_param::<JsonExecutionPayloadV1<T>>(params, 0)
.map(|jep| JsonExecutionPayload::V1(jep))
get_param::<JsonExecutionPayloadV2<T>>(params, 0)
.map(|jep| JsonExecutionPayload::V2(jep))
.or_else(|_| {
get_param::<JsonExecutionPayloadV1<T>>(params, 0)
.map(|jep| JsonExecutionPayload::V1(jep))
})
})
})
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
@@ -123,7 +127,7 @@ pub async fn handle_rpc<T: EthSpec>(
.execution_block_generator
.read()
.get_fork_at_timestamp(*request.timestamp());
// validate method called correctly according to shanghai fork time
// validate method called correctly according to fork time
match fork {
ForkName::Merge => {
if matches!(request, JsonExecutionPayload::V2(_)) {
@@ -156,14 +160,14 @@ pub async fn handle_rpc<T: EthSpec>(
ForkName::Deneb => {
if method == ENGINE_NEW_PAYLOAD_V1 || method == ENGINE_NEW_PAYLOAD_V2 {
return Err((
format!("{} called after deneb fork!", method),
format!("{} called after Deneb fork!", method),
GENERIC_ERROR_CODE,
));
}
if matches!(request, JsonExecutionPayload::V1(_)) {
return Err((
format!(
"{} called with `ExecutionPayloadV1` after deneb fork!",
"{} called with `ExecutionPayloadV1` after Deneb fork!",
method
),
GENERIC_ERROR_CODE,
@@ -172,7 +176,42 @@ pub async fn handle_rpc<T: EthSpec>(
if matches!(request, JsonExecutionPayload::V2(_)) {
return Err((
format!(
"{} called with `ExecutionPayloadV2` after deneb fork!",
"{} called with `ExecutionPayloadV2` after Deneb fork!",
method
),
GENERIC_ERROR_CODE,
));
}
}
ForkName::Electra => {
if method == ENGINE_NEW_PAYLOAD_V1 || method == ENGINE_NEW_PAYLOAD_V2 {
return Err((
format!("{} called after Electra fork!", method),
GENERIC_ERROR_CODE,
));
}
if matches!(request, JsonExecutionPayload::V1(_)) {
return Err((
format!(
"{} called with `ExecutionPayloadV1` after Electra fork!",
method
),
GENERIC_ERROR_CODE,
));
}
if matches!(request, JsonExecutionPayload::V2(_)) {
return Err((
format!(
"{} called with `ExecutionPayloadV2` after Electra fork!",
method
),
GENERIC_ERROR_CODE,
));
}
if matches!(request, JsonExecutionPayload::V3(_)) {
return Err((
format!(
"{} called with `ExecutionPayloadV3` after Electra fork!",
method
),
GENERIC_ERROR_CODE,
@@ -245,7 +284,7 @@ pub async fn handle_rpc<T: EthSpec>(
FORK_REQUEST_MISMATCH_ERROR_CODE,
));
}
// validate method called correctly according to deneb fork time
// validate method called correctly according to cancun fork time
if ctx
.execution_block_generator
.read()
@@ -254,7 +293,20 @@ pub async fn handle_rpc<T: EthSpec>(
&& (method == ENGINE_GET_PAYLOAD_V1 || method == ENGINE_GET_PAYLOAD_V2)
{
return Err((
format!("{} called after deneb fork!", method),
format!("{} called after Deneb fork!", method),
FORK_REQUEST_MISMATCH_ERROR_CODE,
));
}
// validate method called correctly according to prague fork time
if ctx
.execution_block_generator
.read()
.get_fork_at_timestamp(response.timestamp())
== ForkName::Electra
&& method == ENGINE_GET_PAYLOAD_V1
{
return Err((
format!("{} called after Electra fork!", method),
FORK_REQUEST_MISMATCH_ERROR_CODE,
));
}
@@ -295,6 +347,20 @@ pub async fn handle_rpc<T: EthSpec>(
})
.unwrap()
}
JsonExecutionPayload::V4(execution_payload) => {
serde_json::to_value(JsonGetPayloadResponseV4 {
execution_payload,
block_value: DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI.into(),
blobs_bundle: maybe_blobs
.ok_or((
"No blobs returned despite V4 Payload".to_string(),
GENERIC_ERROR_CODE,
))?
.into(),
should_override_builder: false,
})
.unwrap()
}
_ => unreachable!(),
}),
_ => unreachable!(),
@@ -328,7 +394,7 @@ pub async fn handle_rpc<T: EthSpec>(
.map(|opt| opt.map(JsonPayloadAttributes::V1))
.transpose()
}
ForkName::Capella | ForkName::Deneb => {
ForkName::Capella | ForkName::Deneb | ForkName::Electra => {
get_param::<Option<JsonPayloadAttributesV2>>(params, 1)
.map(|opt| opt.map(JsonPayloadAttributes::V2))
.transpose()
@@ -392,7 +458,7 @@ pub async fn handle_rpc<T: EthSpec>(
));
}
}
ForkName::Deneb => {
ForkName::Deneb | ForkName::Electra => {
if method == ENGINE_FORKCHOICE_UPDATED_V1 {
return Err((
format!("{} called after Deneb fork!", method),

View File

@@ -15,7 +15,8 @@ use task_executor::TaskExecutor;
use tempfile::NamedTempFile;
use tree_hash::TreeHash;
use types::builder_bid::{
BuilderBid, BuilderBidCapella, BuilderBidDeneb, BuilderBidMerge, SignedBuilderBid,
BuilderBid, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra, BuilderBidMerge,
SignedBuilderBid,
};
use types::{
Address, BeaconState, ChainSpec, EthSpec, ExecPayload, ExecutionPayload,
@@ -85,6 +86,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Deneb(header) => {
header.fee_recipient = fee_recipient;
}
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.fee_recipient = fee_recipient;
}
}
}
@@ -99,6 +103,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Deneb(header) => {
header.gas_limit = gas_limit;
}
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.gas_limit = gas_limit;
}
}
}
@@ -117,6 +124,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Deneb(header) => {
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
}
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
}
}
}
@@ -131,6 +141,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Deneb(header) => {
header.prev_randao = prev_randao;
}
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.prev_randao = prev_randao;
}
}
}
@@ -145,6 +158,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Deneb(header) => {
header.block_number = block_number;
}
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.block_number = block_number;
}
}
}
@@ -159,6 +175,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Deneb(header) => {
header.timestamp = timestamp;
}
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.timestamp = timestamp;
}
}
}
@@ -173,6 +192,9 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
ExecutionPayloadHeaderRefMut::Deneb(header) => {
header.withdrawals_root = withdrawals_root;
}
ExecutionPayloadHeaderRefMut::Electra(header) => {
header.withdrawals_root = withdrawals_root;
}
}
}
@@ -320,6 +342,9 @@ pub fn serve<E: EthSpec>(
SignedBlindedBeaconBlock::Deneb(block) => {
block.message.body.execution_payload.tree_hash_root()
}
SignedBlindedBeaconBlock::Electra(block) => {
block.message.body.execution_payload.tree_hash_root()
}
};
let fork_name = builder.spec.fork_name_at_slot::<E>(slot);
@@ -455,7 +480,7 @@ pub fn serve<E: EthSpec>(
.map_err(|_| reject("couldn't get prev randao"))?;
let expected_withdrawals = match fork {
ForkName::Base | ForkName::Altair | ForkName::Merge => None,
ForkName::Capella | ForkName::Deneb => Some(
ForkName::Capella | ForkName::Deneb | ForkName::Electra => Some(
builder
.beacon_client
.get_expected_withdrawals(&StateId::Head)
@@ -477,7 +502,7 @@ pub fn serve<E: EthSpec>(
expected_withdrawals,
None,
),
ForkName::Deneb => PayloadAttributes::new(
ForkName::Deneb | ForkName::Electra => PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
@@ -521,6 +546,17 @@ pub fn serve<E: EthSpec>(
) = payload_response.into();
match fork {
ForkName::Electra => BuilderBid::Electra(BuilderBidElectra {
header: payload
.as_electra()
.map_err(|_| reject("incorrect payload variant"))?
.into(),
blob_kzg_commitments: maybe_blobs_bundle
.map(|b| b.commitments)
.unwrap_or_default(),
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
pubkey: builder.builder_sk.public_key().compress(),
}),
ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb {
header: payload
.as_deneb()
@@ -560,6 +596,17 @@ pub fn serve<E: EthSpec>(
Option<BlobsBundle<E>>,
) = payload_response.into();
match fork {
ForkName::Electra => BuilderBid::Electra(BuilderBidElectra {
header: payload
.as_electra()
.map_err(|_| reject("incorrect payload variant"))?
.into(),
blob_kzg_commitments: maybe_blobs_bundle
.map(|b| b.commitments)
.unwrap_or_default(),
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
pubkey: builder.builder_sk.public_key().compress(),
}),
ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb {
header: payload
.as_deneb()

View File

@@ -27,6 +27,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
DEFAULT_TERMINAL_BLOCK,
None,
None,
None,
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
spec,
None,
@@ -39,6 +40,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
terminal_block: u64,
shanghai_time: Option<u64>,
cancun_time: Option<u64>,
prague_time: Option<u64>,
jwt_key: Option<JwtKey>,
spec: ChainSpec,
kzg: Option<Kzg>,
@@ -54,6 +56,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
spec.terminal_block_hash,
shanghai_time,
cancun_time,
prague_time,
kzg,
);

View File

@@ -66,6 +66,7 @@ pub struct MockExecutionConfig {
pub terminal_block_hash: ExecutionBlockHash,
pub shanghai_time: Option<u64>,
pub cancun_time: Option<u64>,
pub prague_time: Option<u64>,
}
impl Default for MockExecutionConfig {
@@ -78,6 +79,7 @@ impl Default for MockExecutionConfig {
server_config: Config::default(),
shanghai_time: None,
cancun_time: None,
prague_time: None,
}
}
}
@@ -99,7 +101,8 @@ impl<T: EthSpec> MockServer<T> {
ExecutionBlockHash::zero(),
None, // FIXME(capella): should this be the default?
None, // FIXME(deneb): should this be the default?
None, // FIXME(deneb): should this be the default?
None, // FIXME(electra): should this be the default?
None,
)
}
@@ -116,6 +119,7 @@ impl<T: EthSpec> MockServer<T> {
server_config,
shanghai_time,
cancun_time,
prague_time,
} = config;
let last_echo_request = Arc::new(RwLock::new(None));
let preloaded_responses = Arc::new(Mutex::new(vec![]));
@@ -125,6 +129,7 @@ impl<T: EthSpec> MockServer<T> {
terminal_block_hash,
shanghai_time,
cancun_time,
prague_time,
kzg,
);
@@ -187,6 +192,7 @@ impl<T: EthSpec> MockServer<T> {
terminal_block_hash: ExecutionBlockHash,
shanghai_time: Option<u64>,
cancun_time: Option<u64>,
prague_time: Option<u64>,
kzg: Option<Kzg>,
) -> Self {
Self::new_with_config(
@@ -199,6 +205,7 @@ impl<T: EthSpec> MockServer<T> {
terminal_block_hash,
shanghai_time,
cancun_time,
prague_time,
},
kzg,
)