resolve merge conflicts

This commit is contained in:
Eitan Seri-Levi
2026-04-30 01:51:26 +02:00
544 changed files with 48684 additions and 18351 deletions

View File

@@ -1,7 +1,8 @@
use crate::engine_api::{
ExecutionBlock, PayloadAttributes, PayloadId, PayloadStatusV1, PayloadStatusV1Status,
json_structures::{
JsonForkchoiceUpdatedV1Response, JsonPayloadStatusV1, JsonPayloadStatusV1Status,
BlobAndProof, BlobAndProofV1, BlobAndProofV2, JsonForkchoiceUpdatedV1Response,
JsonPayloadStatusV1, JsonPayloadStatusV1Status,
},
};
use crate::engines::ForkchoiceState;
@@ -15,20 +16,20 @@ use rand::{Rng, SeedableRng, rngs::StdRng};
use serde::{Deserialize, Serialize};
use ssz::Decode;
use ssz_types::VariableList;
use state_processing::per_block_processing::deneb::kzg_commitment_to_versioned_hash;
use std::cmp::max;
use std::collections::HashMap;
use std::sync::Arc;
use tracing::warn;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
use types::{
Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadBellatrix,
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadEip7805,
ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionPayloadHeader,
ForkName, Hash256, KzgProofs, Transaction, Transactions, Uint256,
ExecutionRequests, ForkName, Hash256, KzgProofs, Transaction, Transactions, Uint256,
};
use super::DEFAULT_TERMINAL_BLOCK;
const TEST_BLOB_BUNDLE: &[u8] = include_bytes!("fixtures/mainnet/test_blobs_bundle.ssz");
const TEST_BLOB_BUNDLE_V2: &[u8] = include_bytes!("fixtures/mainnet/test_blobs_bundle_v2.ssz");
@@ -161,6 +162,14 @@ pub struct ExecutionBlockGenerator<E: EthSpec> {
pub blobs_bundles: HashMap<PayloadId, BlobsBundle<E>>,
pub kzg: Option<Arc<Kzg>>,
rng: Arc<Mutex<StdRng>>,
/*
* Execution requests (electra+)
*/
/// Per-payload execution requests returned by `getPayload`.
execution_requests: HashMap<PayloadId, ExecutionRequests<E>>,
/// If set, the next call to `build_new_execution_payload` will associate these
/// execution requests with the generated payload ID.
next_execution_requests: Option<ExecutionRequests<E>>,
}
fn make_rng() -> Arc<Mutex<StdRng>> {
@@ -172,9 +181,6 @@ fn make_rng() -> Arc<Mutex<StdRng>> {
impl<E: EthSpec> ExecutionBlockGenerator<E> {
#[allow(clippy::too_many_arguments)]
pub fn new(
terminal_total_difficulty: Uint256,
terminal_block_number: u64,
terminal_block_hash: ExecutionBlockHash,
shanghai_time: Option<u64>,
cancun_time: Option<u64>,
prague_time: Option<u64>,
@@ -188,9 +194,9 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
finalized_block_hash: <_>::default(),
blocks: <_>::default(),
block_hashes: <_>::default(),
terminal_total_difficulty,
terminal_block_number,
terminal_block_hash,
terminal_total_difficulty: Default::default(),
terminal_block_number: 0,
terminal_block_hash: Default::default(),
pending_payloads: <_>::default(),
next_payload_id: 0,
payload_ids: <_>::default(),
@@ -204,6 +210,8 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
blobs_bundles: <_>::default(),
kzg,
rng: make_rng(),
execution_requests: <_>::default(),
next_execution_requests: None,
};
generator.insert_pow_block(0).unwrap();
@@ -296,25 +304,6 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
.and_then(|block| block.as_execution_payload())
}
pub fn move_to_block_prior_to_terminal_block(&mut self) -> Result<(), String> {
let target_block = self
.terminal_block_number
.checked_sub(1)
.ok_or("terminal pow block is 0")?;
self.move_to_pow_block(target_block)
}
pub fn move_to_terminal_block(&mut self) -> Result<(), String> {
self.move_to_pow_block(self.terminal_block_number)
}
pub fn move_to_pow_block(&mut self, target_block: u64) -> Result<(), String> {
let next_block = self.latest_block().unwrap().block_number() + 1;
assert!(target_block >= next_block);
self.insert_pow_blocks(next_block..=target_block)
}
pub fn drop_all_blocks(&mut self) {
self.blocks = <_>::default();
self.block_hashes = <_>::default();
@@ -483,6 +472,49 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
self.blobs_bundles.get(id).cloned()
}
pub fn get_execution_requests(&self, id: &PayloadId) -> Option<ExecutionRequests<E>> {
self.execution_requests.get(id).cloned()
}
/// Set execution requests to be returned alongside the next generated payload.
pub fn set_next_execution_requests(&mut self, requests: ExecutionRequests<E>) {
self.next_execution_requests = Some(requests);
}
/// Look up a blob and proof by versioned hash across all stored bundles.
pub fn get_blob_and_proof(&self, versioned_hash: &Hash256) -> Option<BlobAndProof<E>> {
self.blobs_bundles
.iter()
.find_map(|(payload_id, blobs_bundle)| {
let (blob_idx, _) =
blobs_bundle
.commitments
.iter()
.enumerate()
.find(|(_, commitment)| {
&kzg_commitment_to_versioned_hash(commitment) == versioned_hash
})?;
let is_fulu = self.payload_ids.get(payload_id)?.fork_name().fulu_enabled();
let blob = blobs_bundle.blobs.get(blob_idx)?.clone();
if is_fulu {
let start = blob_idx * E::cells_per_ext_blob();
let end = start + E::cells_per_ext_blob();
let proofs = blobs_bundle
.proofs
.get(start..end)?
.to_vec()
.try_into()
.ok()?;
Some(BlobAndProof::V2(BlobAndProofV2 { blob, proofs }))
} else {
Some(BlobAndProof::V1(BlobAndProofV1 {
blob,
proof: *blobs_bundle.proofs.get(blob_idx)?,
}))
}
})
}
pub fn new_payload(&mut self, payload: ExecutionPayload<E>) -> PayloadStatusV1 {
let Some(parent) = self.blocks.get(&payload.parent_hash()) else {
return PayloadStatusV1 {
@@ -541,6 +573,21 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
.contains_key(&forkchoice_state.finalized_block_hash);
if unknown_head_block_hash || unknown_safe_block_hash || unknown_finalized_block_hash {
if unknown_head_block_hash {
warn!(?head_block_hash, "Received unknown head block hash");
}
if unknown_safe_block_hash {
warn!(
safe_block_hash = ?forkchoice_state.safe_block_hash,
"Received unknown safe block hash"
);
}
if unknown_finalized_block_hash {
warn!(
finalized_block_hash = ?forkchoice_state.finalized_block_hash,
"Received unknown finalized block hash"
)
}
return Ok(JsonForkchoiceUpdatedV1Response {
payload_status: JsonPayloadStatusV1 {
status: JsonPayloadStatusV1Status::Syncing,
@@ -730,6 +777,9 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
blob_gas_used: 0,
excess_blob_gas: 0,
}),
_ => unreachable!(),
},
PayloadAttributes::V4(pa) => match self.get_fork_at_timestamp(pa.timestamp) {
ForkName::Gloas => ExecutionPayload::Gloas(ExecutionPayloadGloas {
parent_hash: head_block_hash,
fee_recipient: pa.suggested_fee_recipient,
@@ -748,11 +798,18 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
withdrawals: pa.withdrawals.clone().try_into().unwrap(),
blob_gas_used: 0,
excess_blob_gas: 0,
block_access_list: VariableList::empty(),
slot_number: pa.slot_number.into(),
}),
_ => unreachable!(),
},
};
// Store execution requests for this payload if configured.
if let Some(requests) = self.next_execution_requests.take() {
self.execution_requests.insert(id, requests);
}
let fork_name = execution_payload.fork_name();
if fork_name.deneb_enabled() {
// get random number between 0 and 1 blobs by default
@@ -886,27 +943,22 @@ fn payload_id_from_u64(n: u64) -> PayloadId {
n.to_le_bytes()
}
pub fn generate_genesis_header<E: EthSpec>(
spec: &ChainSpec,
post_transition_merge: bool,
) -> Option<ExecutionPayloadHeader<E>> {
pub fn generate_genesis_header<E: EthSpec>(spec: &ChainSpec) -> Option<ExecutionPayloadHeader<E>> {
let genesis_fork = spec.fork_name_at_slot::<E>(spec.genesis_slot);
let genesis_block_hash =
generate_genesis_block(spec.terminal_total_difficulty, DEFAULT_TERMINAL_BLOCK)
.ok()
.map(|block| block.block_hash);
let genesis_block_hash = generate_genesis_block(Default::default(), 0)
.ok()
.map(|block| block.block_hash);
let empty_transactions_root = Transactions::<E>::empty().tree_hash_root();
match genesis_fork {
ForkName::Base | ForkName::Altair => None,
ForkName::Base | ForkName::Altair => {
// Pre-Bellatrix forks have no execution payload
None
}
ForkName::Bellatrix => {
if post_transition_merge {
let mut header = ExecutionPayloadHeader::Bellatrix(<_>::default());
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
*header.transactions_root_mut() = empty_transactions_root;
Some(header)
} else {
Some(ExecutionPayloadHeader::<E>::Bellatrix(<_>::default()))
}
let mut header = ExecutionPayloadHeader::Bellatrix(<_>::default());
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
*header.transactions_root_mut() = empty_transactions_root;
Some(header)
}
ForkName::Capella => {
let mut header = ExecutionPayloadHeader::Capella(<_>::default());
@@ -938,8 +990,14 @@ pub fn generate_genesis_header<E: EthSpec>(
*header.transactions_root_mut() = empty_transactions_root;
Some(header)
}
// TODO(EIP-7732): need to look into this
ForkName::Gloas => None,
ForkName::Gloas => {
// TODO(gloas): we are using a Fulu header for now, but this gets fixed up by the
// genesis builder anyway which translates it to bid/latest_block_hash.
let mut header = ExecutionPayloadHeader::Fulu(<_>::default());
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
*header.transactions_root_mut() = empty_transactions_root;
Some(header)
}
}
}
@@ -995,7 +1053,7 @@ pub fn generate_pow_block(
#[cfg(test)]
mod test {
use super::*;
use kzg::{Bytes48, CellRef, KzgBlobRef, trusted_setup::get_trusted_setup};
use kzg::{CellRef, KzgBlobRef, trusted_setup::get_trusted_setup};
use types::{MainnetEthSpec, MinimalEthSpec};
#[test]
@@ -1086,10 +1144,11 @@ mod test {
fn validate_blob_bundle_v1<E: EthSpec>() -> Result<(), String> {
let kzg = load_kzg()?;
let (kzg_commitment, kzg_proof, blob) = load_test_blobs_bundle_v1::<E>()?;
let kzg_blob = kzg::Blob::from_bytes(blob.as_ref())
.map(Box::new)
.map_err(|e| format!("Error converting blob to kzg blob: {e:?}"))?;
kzg.verify_blob_kzg_proof(&kzg_blob, kzg_commitment, kzg_proof)
let kzg_blob: KzgBlobRef = blob
.as_ref()
.try_into()
.map_err(|e| format!("Error converting blob to kzg blob ref: {e:?}"))?;
kzg.verify_blob_kzg_proof(kzg_blob, kzg_commitment, kzg_proof)
.map_err(|e| format!("Invalid blobs bundle: {e:?}"))
}
@@ -1099,8 +1158,8 @@ mod test {
load_test_blobs_bundle_v2::<E>().map(|(commitment, proofs, blob)| {
let kzg_blob: KzgBlobRef = blob.as_ref().try_into().unwrap();
(
vec![Bytes48::from(commitment); proofs.len()],
proofs.into_iter().map(|p| p.into()).collect::<Vec<_>>(),
vec![commitment.0; proofs.len()],
proofs.into_iter().map(|p| p.0).collect::<Vec<_>>(),
kzg.compute_cells(kzg_blob).unwrap(),
)
})?;

View File

@@ -5,6 +5,7 @@ use crate::test_utils::{DEFAULT_CLIENT_VERSION, DEFAULT_MOCK_EL_PAYLOAD_VALUE_WE
use serde::{Deserialize, de::DeserializeOwned};
use serde_json::Value as JsonValue;
use std::sync::Arc;
use tracing::debug;
use types::Transaction;
pub const GENERIC_ERROR_CODE: i64 = -1234;
@@ -29,6 +30,8 @@ pub async fn handle_rpc<E: EthSpec>(
.ok_or_else(|| "missing/invalid params field".to_string())
.map_err(|s| (s, GENERIC_ERROR_CODE))?;
debug!(method, "Mock execution engine");
match method {
ETH_SYNCING => ctx
.syncing_response
@@ -100,7 +103,8 @@ pub async fn handle_rpc<E: EthSpec>(
ENGINE_NEW_PAYLOAD_V1
| ENGINE_NEW_PAYLOAD_V2
| ENGINE_NEW_PAYLOAD_V3
| ENGINE_NEW_PAYLOAD_V4 => {
| ENGINE_NEW_PAYLOAD_V4
| ENGINE_NEW_PAYLOAD_V5 => {
let request = match method {
ENGINE_NEW_PAYLOAD_V1 => JsonExecutionPayload::Bellatrix(
get_param::<JsonExecutionPayloadBellatrix<E>>(params, 0)
@@ -116,21 +120,16 @@ 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::<JsonExecutionPayloadGloas<E>>(params, 0)
.map(|jep| JsonExecutionPayload::Gloas(jep))
.or_else(|_| {
get_param::<JsonExecutionPayloadFulu<E>>(params, 0)
.map(|jep| JsonExecutionPayload::Fulu(jep))
})
ENGINE_NEW_PAYLOAD_V4 => get_param::<JsonExecutionPayloadFulu<E>>(params, 0)
.map(|jep| JsonExecutionPayload::Fulu(jep))
.or_else(|_| {
get_param::<JsonExecutionPayloadElectra<E>>(params, 0)
.map(|jep| JsonExecutionPayload::Electra(jep))
})
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
// TODO(EIP7805) fix
// ENGINE_NEW_PAYLOAD_V5 => get_param::<JsonExecutionPayloadV5<E>>(params, 0)
// .map(|jep| JsonExecutionPayload::V5(jep))
// .map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
ENGINE_NEW_PAYLOAD_V5 => get_param::<JsonExecutionPayloadGloas<E>>(params, 0)
.map(|jep| JsonExecutionPayload::Gloas(jep))
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
_ => unreachable!(),
};
@@ -232,6 +231,14 @@ pub async fn handle_rpc<E: EthSpec>(
));
}
}
ForkName::Gloas => {
if method != ENGINE_NEW_PAYLOAD_V5 {
return Err((
format!("{} called after Gloas fork!", method),
GENERIC_ERROR_CODE,
));
}
}
_ => unreachable!(),
};
@@ -270,8 +277,9 @@ pub async fn handle_rpc<E: EthSpec>(
ENGINE_GET_PAYLOAD_V1
| ENGINE_GET_PAYLOAD_V2
| ENGINE_GET_PAYLOAD_V3
| ENGINE_GET_PAYLOAD_V4
| ENGINE_GET_PAYLOAD_V5
| ENGINE_GET_PAYLOAD_V4 => {
| ENGINE_GET_PAYLOAD_V6 => {
let request: JsonPayloadIdRequest =
get_param(params, 0).map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?;
let id = request.into();
@@ -288,6 +296,10 @@ pub async fn handle_rpc<E: EthSpec>(
})?;
let maybe_blobs = ctx.execution_block_generator.write().get_blobs_bundle(&id);
let maybe_execution_requests = ctx
.execution_block_generator
.read()
.get_execution_requests(&id);
// validate method called correctly according to shanghai fork time
if ctx
@@ -356,7 +368,8 @@ pub async fn handle_rpc<E: EthSpec>(
&& (method == ENGINE_GET_PAYLOAD_V1
|| method == ENGINE_GET_PAYLOAD_V2
|| method == ENGINE_GET_PAYLOAD_V3
|| method == ENGINE_GET_PAYLOAD_V4)
|| method == ENGINE_GET_PAYLOAD_V4
|| method == ENGINE_GET_PAYLOAD_V5)
{
return Err((
format!("{} called after Gloas fork!", method),
@@ -423,8 +436,10 @@ pub async fn handle_rpc<E: EthSpec>(
))?
.into(),
should_override_builder: false,
// TODO(electra): add EL requests in mock el
execution_requests: Default::default(),
execution_requests: maybe_execution_requests
.clone()
.unwrap_or_default()
.into(),
})
.unwrap()
}
@@ -444,7 +459,10 @@ pub async fn handle_rpc<E: EthSpec>(
))?
.into(),
should_override_builder: false,
execution_requests: Default::default(),
execution_requests: maybe_execution_requests
.clone()
.unwrap_or_default()
.into(),
})
.unwrap()
}
@@ -463,18 +481,25 @@ pub async fn handle_rpc<E: EthSpec>(
})
.unwrap()
}
_ => unreachable!(),
})
}
ENGINE_GET_PAYLOAD_V6 => {
Ok(match JsonExecutionPayload::try_from(response).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(),
"No blobs returned despite V6 Payload".to_string(),
GENERIC_ERROR_CODE,
))?
.into(),
should_override_builder: false,
execution_requests: Default::default(),
execution_requests: maybe_execution_requests
.unwrap_or_default()
.into(),
})
.unwrap()
}
@@ -484,9 +509,39 @@ pub async fn handle_rpc<E: EthSpec>(
_ => unreachable!(),
}
}
ENGINE_GET_BLOBS_V1 => {
let versioned_hashes =
get_param::<Vec<Hash256>>(params, 0).map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?;
let generator = ctx.execution_block_generator.read();
// V1: per-element nullable array, positionally matching the request.
let response: Vec<Option<BlobAndProofV1<E>>> = versioned_hashes
.iter()
.map(|hash| match generator.get_blob_and_proof(hash) {
Some(BlobAndProof::V1(v1)) => Some(v1),
_ => None,
})
.collect();
Ok(serde_json::to_value(response).unwrap())
}
ENGINE_GET_BLOBS_V2 => {
let versioned_hashes =
get_param::<Vec<Hash256>>(params, 0).map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?;
let generator = ctx.execution_block_generator.read();
// V2: all-or-nothing — null if any blob is missing.
let results: Vec<Option<BlobAndProofV2<E>>> = versioned_hashes
.iter()
.map(|hash| match generator.get_blob_and_proof(hash) {
Some(BlobAndProof::V2(v2)) => Some(v2),
_ => None,
})
.collect();
let response: Option<Vec<BlobAndProofV2<E>>> = results.into_iter().collect();
Ok(serde_json::to_value(response).unwrap())
}
ENGINE_FORKCHOICE_UPDATED_V1
| ENGINE_FORKCHOICE_UPDATED_V2
| ENGINE_FORKCHOICE_UPDATED_V3 => {
| ENGINE_FORKCHOICE_UPDATED_V3
| ENGINE_FORKCHOICE_UPDATED_V4 => {
let forkchoice_state: JsonForkchoiceStateV1 =
get_param(params, 0).map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?;
let payload_attributes = match method {
@@ -533,9 +588,20 @@ pub async fn handle_rpc<E: EthSpec>(
.map(|opt| opt.map(JsonPayloadAttributes::V3))
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?
}
ENGINE_FORKCHOICE_UPDATED_V4 => {
get_param::<Option<JsonPayloadAttributesV4>>(params, 1)
.map(|opt| opt.map(JsonPayloadAttributes::V4))
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?
}
_ => unreachable!(),
};
debug!(
?payload_attributes,
?forkchoice_state,
"ENGINE_FORKCHOICE_UPDATED"
);
// validate method called correctly according to fork time
if let Some(pa) = payload_attributes.as_ref() {
match ctx
@@ -598,6 +664,14 @@ pub async fn handle_rpc<E: EthSpec>(
));
}
}
ForkName::Gloas => {
if method != ENGINE_FORKCHOICE_UPDATED_V4 {
return Err((
format!("{} called after Gloas fork! Use V4.", method),
FORK_REQUEST_MISMATCH_ERROR_CODE,
));
}
}
_ => unreachable!(),
};
}

View File

@@ -29,7 +29,7 @@ use tokio_stream::StreamExt;
use tracing::{debug, error, info, warn};
use tree_hash::TreeHash;
use types::ExecutionBlockHash;
use types::builder_bid::{
use types::builder::{
BuilderBid, BuilderBidBellatrix, BuilderBidCapella, BuilderBidDeneb, BuilderBidEip7805,
BuilderBidElectra, BuilderBidFulu, SignedBuilderBid,
};
@@ -266,7 +266,7 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
}
fn sign_builder_message(&mut self, sk: &SecretKey, spec: &ChainSpec) -> Signature {
let domain = spec.get_builder_domain();
let domain = spec.get_builder_application_domain();
let message = self.signing_root(domain);
sk.sign(message)
}
@@ -840,6 +840,10 @@ impl<E: EthSpec> MockBuilder<E> {
let head_block_root = head_block_root.unwrap_or(head.canonical_root());
// TODO(gloas): Currently the tests are pre-Gloas and we are not considering
// other payload statuses. This codepath may not be relevant for Gloas.
let head_payload_status = fork_choice::PayloadStatus::Pending;
let head_execution_payload = head
.message()
.body()
@@ -900,7 +904,8 @@ impl<E: EthSpec> MockBuilder<E> {
.data
.genesis_time
};
let timestamp = (slots_since_genesis * self.spec.seconds_per_slot) + genesis_time;
let timestamp =
(slots_since_genesis * self.spec.get_slot_duration().as_secs()) + genesis_time;
let head_state: BeaconState<E> = self
.beacon_client
@@ -937,17 +942,25 @@ impl<E: EthSpec> MockBuilder<E> {
fee_recipient,
expected_withdrawals,
None,
None,
),
ForkName::Deneb
| ForkName::Electra
| ForkName::Fulu
| ForkName::Eip7805
| ForkName::Gloas => PayloadAttributes::new(
ForkName::Deneb | ForkName::Electra | ForkName::Fulu | ForkName::Eip7805 => {
PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
expected_withdrawals,
Some(head_block_root),
None,
)
}
ForkName::Gloas => PayloadAttributes::new(
timestamp,
*prev_randao,
fee_recipient,
expected_withdrawals,
Some(head_block_root),
Some(slot.as_u64()),
),
ForkName::Base | ForkName::Altair => {
return Err("invalid fork".to_string());
@@ -967,7 +980,13 @@ impl<E: EthSpec> MockBuilder<E> {
);
self.el
.insert_proposer(slot, head_block_root, val_index, payload_attributes.clone())
.insert_proposer(
slot,
head_block_root,
head_payload_status,
val_index,
payload_attributes.clone(),
)
.await;
let forkchoice_update_params = ForkchoiceUpdateParameters {
@@ -985,6 +1004,7 @@ impl<E: EthSpec> MockBuilder<E> {
finalized_execution_hash,
slot - 1,
head_block_root,
head_payload_status,
)
.await
.map_err(|e| format!("fcu call failed : {:?}", e))?;

View File

@@ -1,9 +1,4 @@
use crate::{
test_utils::{
DEFAULT_JWT_SECRET, DEFAULT_TERMINAL_BLOCK, DEFAULT_TERMINAL_DIFFICULTY, MockServer,
},
*,
};
use crate::{test_utils::DEFAULT_JWT_SECRET, test_utils::MockServer, *};
use alloy_primitives::B256 as H256;
use fixed_bytes::FixedBytesExtended;
use kzg::Kzg;
@@ -20,12 +15,10 @@ pub struct MockExecutionLayer<E: EthSpec> {
impl<E: EthSpec> MockExecutionLayer<E> {
pub fn default_params(executor: TaskExecutor) -> Self {
let mut spec = MainnetEthSpec::default_spec();
spec.terminal_total_difficulty = Uint256::from(DEFAULT_TERMINAL_DIFFICULTY);
spec.terminal_block_hash = ExecutionBlockHash::zero();
spec.terminal_block_hash_activation_epoch = Epoch::new(0);
Self::new(
executor,
DEFAULT_TERMINAL_BLOCK,
None,
None,
None,
@@ -41,7 +34,6 @@ impl<E: EthSpec> MockExecutionLayer<E> {
#[allow(clippy::too_many_arguments)]
pub fn new(
executor: TaskExecutor,
terminal_block: u64,
shanghai_time: Option<u64>,
cancun_time: Option<u64>,
prague_time: Option<u64>,
@@ -58,9 +50,6 @@ impl<E: EthSpec> MockExecutionLayer<E> {
let server = MockServer::new(
&handle,
jwt_key,
spec.terminal_total_difficulty,
terminal_block,
spec.terminal_block_hash,
shanghai_time,
cancun_time,
prague_time,
@@ -104,20 +93,34 @@ impl<E: EthSpec> MockExecutionLayer<E> {
let timestamp = block_number;
let prev_randao = Hash256::from_low_u64_be(block_number);
let head_block_root = Hash256::repeat_byte(42);
// TODO(gloas): allow statuses other than Pending?
let head_payload_status = fork_choice::PayloadStatus::Pending;
let forkchoice_update_params = ForkchoiceUpdateParameters {
head_root: head_block_root,
head_hash: Some(parent_hash),
justified_hash: None,
finalized_hash: None,
};
let payload_attributes =
PayloadAttributes::new(timestamp, prev_randao, Address::repeat_byte(42), None, None);
let payload_attributes = PayloadAttributes::new(
timestamp,
prev_randao,
Address::repeat_byte(42),
None,
None,
None,
);
// Insert a proposer to ensure the fork choice updated command works.
let slot = Slot::new(0);
let validator_index = 0;
self.el
.insert_proposer(slot, head_block_root, validator_index, payload_attributes)
.insert_proposer(
slot,
head_block_root,
head_payload_status,
validator_index,
payload_attributes,
)
.await;
self.el
@@ -127,6 +130,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
ExecutionBlockHash::zero(),
slot,
head_block_root,
head_payload_status,
)
.await
.unwrap();
@@ -138,8 +142,14 @@ impl<E: EthSpec> MockExecutionLayer<E> {
chain_health: ChainHealth::Healthy,
};
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
let payload_attributes =
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
let payload_attributes = PayloadAttributes::new(
timestamp,
prev_randao,
suggested_fee_recipient,
None,
None,
None,
);
let payload_parameters = PayloadParameters {
parent_hash,
@@ -185,8 +195,14 @@ impl<E: EthSpec> MockExecutionLayer<E> {
chain_health: ChainHealth::Healthy,
};
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
let payload_attributes =
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
let payload_attributes = PayloadAttributes::new(
timestamp,
prev_randao,
suggested_fee_recipient,
None,
None,
None,
);
let payload_parameters = PayloadParameters {
parent_hash,
@@ -276,6 +292,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
// Use junk values for slot/head-root to ensure there is no payload supplied.
let slot = Slot::new(0);
let head_block_root = Hash256::repeat_byte(13);
// TODO(gloas): reconsider the state_payload_status
self.el
.notify_forkchoice_updated(
block_hash,
@@ -283,6 +300,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
ExecutionBlockHash::zero(),
slot,
head_block_root,
fork_choice::PayloadStatus::Pending,
)
.await
.unwrap();
@@ -296,53 +314,4 @@ impl<E: EthSpec> MockExecutionLayer<E> {
assert_eq!(head_execution_block.block_hash(), block_hash);
assert_eq!(head_execution_block.parent_hash(), parent_hash);
}
pub fn move_to_block_prior_to_terminal_block(self) -> Self {
self.server
.execution_block_generator()
.move_to_block_prior_to_terminal_block()
.unwrap();
self
}
pub fn move_to_terminal_block(self) -> Self {
self.server
.execution_block_generator()
.move_to_terminal_block()
.unwrap();
self
}
pub fn produce_forked_pow_block(self) -> (Self, ExecutionBlockHash) {
let head_block = self
.server
.execution_block_generator()
.latest_block()
.unwrap();
let block_hash = self
.server
.execution_block_generator()
.insert_pow_block_by_hash(head_block.parent_hash(), 1)
.unwrap();
(self, block_hash)
}
pub async fn with_terminal_block<U, V>(self, func: U) -> Self
where
U: Fn(Arc<ChainSpec>, ExecutionLayer<E>, Option<ExecutionBlock>) -> V,
V: Future<Output = ()>,
{
let terminal_block_number = self
.server
.execution_block_generator()
.terminal_block_number;
let terminal_block = self
.server
.execution_block_generator()
.execution_block_by_number(terminal_block_number);
func(self.spec.clone(), self.el.clone(), terminal_block).await;
self
}
}

View File

@@ -35,8 +35,6 @@ pub use hook::Hook;
pub use mock_builder::{MockBuilder, Operation, mock_builder_extra_data};
pub use mock_execution_layer::MockExecutionLayer;
pub const DEFAULT_TERMINAL_DIFFICULTY: u64 = 6400;
pub const DEFAULT_TERMINAL_BLOCK: u64 = 64;
pub const DEFAULT_JWT_SECRET: [u8; 32] = [42; 32];
pub const DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI: u128 = 10_000_000_000_000_000;
pub const DEFAULT_BUILDER_PAYLOAD_VALUE_WEI: u128 = 20_000_000_000_000_000;
@@ -45,9 +43,11 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
new_payload_v2: true,
new_payload_v3: true,
new_payload_v4: true,
new_payload_v5: true,
forkchoice_updated_v1: true,
forkchoice_updated_v2: true,
forkchoice_updated_v3: true,
forkchoice_updated_v4: true,
get_payload_bodies_by_hash_v1: true,
get_payload_bodies_by_range_v1: true,
get_payload_v1: true,
@@ -55,10 +55,12 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
get_payload_v3: true,
get_payload_v4: true,
get_payload_v5: true,
get_payload_v6: true,
get_client_version_v1: true,
get_blobs_v1: true,
get_blobs_v2: true,
get_inclusion_list_v1: true,
get_blobs_v3: true,
};
pub static DEFAULT_CLIENT_VERSION: LazyLock<JsonClientVersionV1> =
@@ -80,9 +82,6 @@ mod mock_execution_layer;
pub struct MockExecutionConfig {
pub server_config: Config,
pub jwt_key: JwtKey,
pub terminal_difficulty: Uint256,
pub terminal_block: u64,
pub terminal_block_hash: ExecutionBlockHash,
pub shanghai_time: Option<u64>,
pub cancun_time: Option<u64>,
pub prague_time: Option<u64>,
@@ -95,9 +94,6 @@ impl Default for MockExecutionConfig {
fn default() -> Self {
Self {
jwt_key: JwtKey::random(),
terminal_difficulty: Uint256::from(DEFAULT_TERMINAL_DIFFICULTY),
terminal_block: DEFAULT_TERMINAL_BLOCK,
terminal_block_hash: ExecutionBlockHash::zero(),
server_config: Config::default(),
shanghai_time: None,
cancun_time: None,
@@ -121,9 +117,6 @@ impl<E: EthSpec> MockServer<E> {
Self::new(
&runtime::Handle::current(),
JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap(),
Uint256::from(DEFAULT_TERMINAL_DIFFICULTY),
DEFAULT_TERMINAL_BLOCK,
ExecutionBlockHash::zero(),
None, // FIXME(capella): should this be the default?
None, // FIXME(deneb): should this be the default?
None, // FIXME(electra): should this be the default?
@@ -142,9 +135,6 @@ impl<E: EthSpec> MockServer<E> {
create_test_tracing_subscriber();
let MockExecutionConfig {
jwt_key,
terminal_difficulty,
terminal_block,
terminal_block_hash,
server_config,
shanghai_time,
cancun_time,
@@ -156,9 +146,6 @@ impl<E: EthSpec> MockServer<E> {
let last_echo_request = Arc::new(RwLock::new(None));
let preloaded_responses = Arc::new(Mutex::new(vec![]));
let execution_block_generator = ExecutionBlockGenerator::new(
terminal_difficulty,
terminal_block,
terminal_block_hash,
shanghai_time,
cancun_time,
prague_time,
@@ -221,9 +208,6 @@ impl<E: EthSpec> MockServer<E> {
pub fn new(
handle: &runtime::Handle,
jwt_key: JwtKey,
terminal_difficulty: Uint256,
terminal_block: u64,
terminal_block_hash: ExecutionBlockHash,
shanghai_time: Option<u64>,
cancun_time: Option<u64>,
prague_time: Option<u64>,
@@ -237,9 +221,6 @@ impl<E: EthSpec> MockServer<E> {
MockExecutionConfig {
server_config: Config::default(),
jwt_key,
terminal_difficulty,
terminal_block,
terminal_block_hash,
shanghai_time,
cancun_time,
prague_time,
@@ -762,7 +743,7 @@ pub fn serve<E: EthSpec>(
info!(
listen_address = listening_socket.to_string(),
"Metrics HTTP server started"
"Mock execution client started"
);
Ok((listening_socket, server))