mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 12:47:05 +00:00
Merge unstable
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
use crate::engines::ForkchoiceState;
|
||||
use crate::http::{
|
||||
ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_FORKCHOICE_UPDATED_V3,
|
||||
ENGINE_GET_BLOBS_V1, ENGINE_GET_CLIENT_VERSION_V1, ENGINE_GET_INCLUSION_LIST_V1,
|
||||
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1, ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1,
|
||||
ENGINE_GET_PAYLOAD_V1, ENGINE_GET_PAYLOAD_V2, ENGINE_GET_PAYLOAD_V3, ENGINE_GET_PAYLOAD_V4,
|
||||
ENGINE_NEW_PAYLOAD_V1, ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V4,
|
||||
ENGINE_GET_BLOBS_V1, ENGINE_GET_CLIENT_VERSION_V1, ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1,
|
||||
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1, ENGINE_GET_PAYLOAD_V1, ENGINE_GET_PAYLOAD_V2,
|
||||
ENGINE_GET_PAYLOAD_V3, ENGINE_GET_PAYLOAD_V4, ENGINE_GET_PAYLOAD_V5, ENGINE_NEW_PAYLOAD_V1,
|
||||
ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V4, ENGINE_NEW_PAYLOAD_V5, ENGINE_GET_INCLUSION_LIST_V1
|
||||
};
|
||||
use eth2::types::{
|
||||
BlobsBundle, SsePayloadAttributes, SsePayloadAttributesV1, SsePayloadAttributesV2,
|
||||
@@ -24,7 +24,7 @@ pub use types::{
|
||||
};
|
||||
use types::{
|
||||
ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
|
||||
ExecutionPayloadElectra, ExecutionRequests, KzgProofs,
|
||||
ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionRequests, KzgProofs,
|
||||
};
|
||||
use types::{Graffiti, GRAFFITI_BYTES_LEN};
|
||||
|
||||
@@ -35,7 +35,7 @@ mod new_payload_request;
|
||||
|
||||
pub use new_payload_request::{
|
||||
NewPayloadRequest, NewPayloadRequestBellatrix, NewPayloadRequestCapella,
|
||||
NewPayloadRequestDeneb, NewPayloadRequestElectra,
|
||||
NewPayloadRequestDeneb, NewPayloadRequestElectra, NewPayloadRequestFulu,
|
||||
};
|
||||
|
||||
pub const LATEST_TAG: &str = "latest";
|
||||
@@ -261,7 +261,7 @@ pub struct ProposeBlindedBlockResponse {
|
||||
}
|
||||
|
||||
#[superstruct(
|
||||
variants(Bellatrix, Capella, Deneb, Electra),
|
||||
variants(Bellatrix, Capella, Deneb, Electra, Fulu),
|
||||
variant_attributes(derive(Clone, Debug, PartialEq),),
|
||||
map_into(ExecutionPayload),
|
||||
map_ref_into(ExecutionPayloadRef),
|
||||
@@ -281,12 +281,14 @@ pub struct GetPayloadResponse<E: EthSpec> {
|
||||
pub execution_payload: ExecutionPayloadDeneb<E>,
|
||||
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
|
||||
pub execution_payload: ExecutionPayloadElectra<E>,
|
||||
#[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))]
|
||||
pub execution_payload: ExecutionPayloadFulu<E>,
|
||||
pub block_value: Uint256,
|
||||
#[superstruct(only(Deneb, Electra))]
|
||||
#[superstruct(only(Deneb, Electra, Fulu))]
|
||||
pub blobs_bundle: BlobsBundle<E>,
|
||||
#[superstruct(only(Deneb, Electra), partial_getter(copy))]
|
||||
#[superstruct(only(Deneb, Electra, Fulu), partial_getter(copy))]
|
||||
pub should_override_builder: bool,
|
||||
#[superstruct(only(Electra))]
|
||||
#[superstruct(only(Electra, Fulu))]
|
||||
pub requests: ExecutionRequests<E>,
|
||||
}
|
||||
|
||||
@@ -354,6 +356,12 @@ impl<E: EthSpec> From<GetPayloadResponse<E>>
|
||||
Some(inner.blobs_bundle),
|
||||
Some(inner.requests),
|
||||
),
|
||||
GetPayloadResponse::Fulu(inner) => (
|
||||
ExecutionPayload::Fulu(inner.execution_payload),
|
||||
inner.block_value,
|
||||
Some(inner.blobs_bundle),
|
||||
Some(inner.requests),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -487,6 +495,34 @@ impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
|
||||
))
|
||||
}
|
||||
}
|
||||
ExecutionPayloadHeader::Fulu(header) => {
|
||||
if let Some(withdrawals) = self.withdrawals {
|
||||
Ok(ExecutionPayload::Fulu(ExecutionPayloadFulu {
|
||||
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
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -497,6 +533,7 @@ pub struct EngineCapabilities {
|
||||
pub new_payload_v2: bool,
|
||||
pub new_payload_v3: bool,
|
||||
pub new_payload_v4: bool,
|
||||
pub new_payload_v5: bool,
|
||||
pub forkchoice_updated_v1: bool,
|
||||
pub forkchoice_updated_v2: bool,
|
||||
pub forkchoice_updated_v3: bool,
|
||||
@@ -506,6 +543,7 @@ pub struct EngineCapabilities {
|
||||
pub get_payload_v2: bool,
|
||||
pub get_payload_v3: bool,
|
||||
pub get_payload_v4: bool,
|
||||
pub get_payload_v5: bool,
|
||||
pub get_client_version_v1: bool,
|
||||
pub get_blobs_v1: bool,
|
||||
pub get_inclusion_list_v1: bool,
|
||||
@@ -526,6 +564,9 @@ impl EngineCapabilities {
|
||||
if self.new_payload_v4 {
|
||||
response.push(ENGINE_NEW_PAYLOAD_V4);
|
||||
}
|
||||
if self.new_payload_v5 {
|
||||
response.push(ENGINE_NEW_PAYLOAD_V5);
|
||||
}
|
||||
if self.forkchoice_updated_v1 {
|
||||
response.push(ENGINE_FORKCHOICE_UPDATED_V1);
|
||||
}
|
||||
@@ -553,6 +594,9 @@ impl EngineCapabilities {
|
||||
if self.get_payload_v4 {
|
||||
response.push(ENGINE_GET_PAYLOAD_V4);
|
||||
}
|
||||
if self.get_payload_v5 {
|
||||
response.push(ENGINE_GET_PAYLOAD_V5);
|
||||
}
|
||||
if self.get_client_version_v1 {
|
||||
response.push(ENGINE_GET_CLIENT_VERSION_V1);
|
||||
}
|
||||
|
||||
@@ -35,12 +35,14 @@ pub const ENGINE_NEW_PAYLOAD_V1: &str = "engine_newPayloadV1";
|
||||
pub const ENGINE_NEW_PAYLOAD_V2: &str = "engine_newPayloadV2";
|
||||
pub const ENGINE_NEW_PAYLOAD_V3: &str = "engine_newPayloadV3";
|
||||
pub const ENGINE_NEW_PAYLOAD_V4: &str = "engine_newPayloadV4";
|
||||
pub const ENGINE_NEW_PAYLOAD_V5: &str = "engine_newPayloadV5";
|
||||
pub const ENGINE_NEW_PAYLOAD_TIMEOUT: Duration = Duration::from_secs(8);
|
||||
|
||||
pub const ENGINE_GET_PAYLOAD_V1: &str = "engine_getPayloadV1";
|
||||
pub const ENGINE_GET_PAYLOAD_V2: &str = "engine_getPayloadV2";
|
||||
pub const ENGINE_GET_PAYLOAD_V3: &str = "engine_getPayloadV3";
|
||||
pub const ENGINE_GET_PAYLOAD_V4: &str = "engine_getPayloadV4";
|
||||
pub const ENGINE_GET_PAYLOAD_V5: &str = "engine_getPayloadV5";
|
||||
pub const ENGINE_GET_PAYLOAD_TIMEOUT: Duration = Duration::from_secs(2);
|
||||
|
||||
pub const ENGINE_FORKCHOICE_UPDATED_V1: &str = "engine_forkchoiceUpdatedV1";
|
||||
@@ -75,10 +77,12 @@ pub static LIGHTHOUSE_CAPABILITIES: &[&str] = &[
|
||||
ENGINE_NEW_PAYLOAD_V2,
|
||||
ENGINE_NEW_PAYLOAD_V3,
|
||||
ENGINE_NEW_PAYLOAD_V4,
|
||||
ENGINE_NEW_PAYLOAD_V5,
|
||||
ENGINE_GET_PAYLOAD_V1,
|
||||
ENGINE_GET_PAYLOAD_V2,
|
||||
ENGINE_GET_PAYLOAD_V3,
|
||||
ENGINE_GET_PAYLOAD_V4,
|
||||
ENGINE_GET_PAYLOAD_V5,
|
||||
ENGINE_FORKCHOICE_UPDATED_V1,
|
||||
ENGINE_FORKCHOICE_UPDATED_V2,
|
||||
ENGINE_FORKCHOICE_UPDATED_V3,
|
||||
@@ -162,9 +166,7 @@ pub mod deposit_log {
|
||||
};
|
||||
|
||||
let signature_is_valid = deposit_pubkey_signature_message(&deposit_data, spec)
|
||||
.map_or(false, |(public_key, signature, msg)| {
|
||||
signature.verify(&public_key, msg)
|
||||
});
|
||||
.is_some_and(|(public_key, signature, msg)| signature.verify(&public_key, msg));
|
||||
|
||||
Ok(DepositLog {
|
||||
deposit_data,
|
||||
@@ -596,7 +598,7 @@ impl<T: Clone> CachedResponse<T> {
|
||||
|
||||
/// returns `true` if the entry's age is >= age_limit
|
||||
pub fn older_than(&self, age_limit: Option<Duration>) -> bool {
|
||||
age_limit.map_or(false, |limit| self.age() >= limit)
|
||||
age_limit.is_some_and(|limit| self.age() >= limit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -743,9 +745,9 @@ impl HttpJsonRpc {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_block_by_number<'a>(
|
||||
pub async fn get_block_by_number(
|
||||
&self,
|
||||
query: BlockByNumberQuery<'a>,
|
||||
query: BlockByNumberQuery<'_>,
|
||||
) -> Result<Option<ExecutionBlock>, Error> {
|
||||
let params = json!([query, RETURN_FULL_TRANSACTION_OBJECTS]);
|
||||
|
||||
@@ -850,6 +852,31 @@ impl HttpJsonRpc {
|
||||
Ok(response.into())
|
||||
}
|
||||
|
||||
// TODO(fulu): switch to v5 endpoint when the EL is ready for Fulu
|
||||
pub async fn new_payload_v4_fulu<E: EthSpec>(
|
||||
&self,
|
||||
new_payload_request_fulu: NewPayloadRequestFulu<'_, E>,
|
||||
) -> Result<PayloadStatusV1, Error> {
|
||||
let params = json!([
|
||||
JsonExecutionPayload::V5(new_payload_request_fulu.execution_payload.clone().into()),
|
||||
new_payload_request_fulu.versioned_hashes,
|
||||
new_payload_request_fulu.parent_beacon_block_root,
|
||||
new_payload_request_fulu
|
||||
.execution_requests
|
||||
.get_execution_requests_list(),
|
||||
]);
|
||||
|
||||
let response: JsonPayloadStatusV1 = self
|
||||
.rpc_request(
|
||||
ENGINE_NEW_PAYLOAD_V4,
|
||||
params,
|
||||
ENGINE_NEW_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(response.into())
|
||||
}
|
||||
|
||||
pub async fn get_payload_v1<E: EthSpec>(
|
||||
&self,
|
||||
payload_id: PayloadId,
|
||||
@@ -905,9 +932,10 @@ impl HttpJsonRpc {
|
||||
.try_into()
|
||||
.map_err(Error::BadResponse)
|
||||
}
|
||||
ForkName::Base | ForkName::Altair | ForkName::Deneb | ForkName::Electra => Err(
|
||||
Error::UnsupportedForkVariant(format!("called get_payload_v2 with {}", fork_name)),
|
||||
),
|
||||
_ => Err(Error::UnsupportedForkVariant(format!(
|
||||
"called get_payload_v2 with {}",
|
||||
fork_name
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -931,11 +959,7 @@ impl HttpJsonRpc {
|
||||
.try_into()
|
||||
.map_err(Error::BadResponse)
|
||||
}
|
||||
ForkName::Base
|
||||
| ForkName::Altair
|
||||
| ForkName::Bellatrix
|
||||
| ForkName::Capella
|
||||
| ForkName::Electra => Err(Error::UnsupportedForkVariant(format!(
|
||||
_ => Err(Error::UnsupportedForkVariant(format!(
|
||||
"called get_payload_v3 with {}",
|
||||
fork_name
|
||||
))),
|
||||
@@ -962,17 +986,53 @@ impl HttpJsonRpc {
|
||||
.try_into()
|
||||
.map_err(Error::BadResponse)
|
||||
}
|
||||
ForkName::Base
|
||||
| ForkName::Altair
|
||||
| ForkName::Bellatrix
|
||||
| ForkName::Capella
|
||||
| ForkName::Deneb => Err(Error::UnsupportedForkVariant(format!(
|
||||
// TODO(fulu): remove when v5 method is ready.
|
||||
ForkName::Fulu => {
|
||||
let response: JsonGetPayloadResponseV5<E> = self
|
||||
.rpc_request(
|
||||
ENGINE_GET_PAYLOAD_V4,
|
||||
params,
|
||||
ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier,
|
||||
)
|
||||
.await?;
|
||||
JsonGetPayloadResponse::V5(response)
|
||||
.try_into()
|
||||
.map_err(Error::BadResponse)
|
||||
}
|
||||
_ => Err(Error::UnsupportedForkVariant(format!(
|
||||
"called get_payload_v4 with {}",
|
||||
fork_name
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_payload_v5<E: EthSpec>(
|
||||
&self,
|
||||
fork_name: ForkName,
|
||||
payload_id: PayloadId,
|
||||
) -> Result<GetPayloadResponse<E>, Error> {
|
||||
let params = json!([JsonPayloadIdRequest::from(payload_id)]);
|
||||
|
||||
match fork_name {
|
||||
ForkName::Fulu => {
|
||||
let response: JsonGetPayloadResponseV5<E> = self
|
||||
.rpc_request(
|
||||
ENGINE_GET_PAYLOAD_V5,
|
||||
params,
|
||||
ENGINE_GET_PAYLOAD_TIMEOUT * self.execution_timeout_multiplier,
|
||||
)
|
||||
.await?;
|
||||
JsonGetPayloadResponse::V5(response)
|
||||
.try_into()
|
||||
.map_err(Error::BadResponse)
|
||||
}
|
||||
_ => Err(Error::UnsupportedForkVariant(format!(
|
||||
"called get_payload_v5 with {}",
|
||||
fork_name
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn forkchoice_updated_v1(
|
||||
&self,
|
||||
forkchoice_state: ForkchoiceState,
|
||||
@@ -1096,6 +1156,7 @@ impl HttpJsonRpc {
|
||||
new_payload_v2: capabilities.contains(ENGINE_NEW_PAYLOAD_V2),
|
||||
new_payload_v3: capabilities.contains(ENGINE_NEW_PAYLOAD_V3),
|
||||
new_payload_v4: capabilities.contains(ENGINE_NEW_PAYLOAD_V4),
|
||||
new_payload_v5: capabilities.contains(ENGINE_NEW_PAYLOAD_V5),
|
||||
forkchoice_updated_v1: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V1),
|
||||
forkchoice_updated_v2: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V2),
|
||||
forkchoice_updated_v3: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V3),
|
||||
@@ -1107,6 +1168,7 @@ impl HttpJsonRpc {
|
||||
get_payload_v2: capabilities.contains(ENGINE_GET_PAYLOAD_V2),
|
||||
get_payload_v3: capabilities.contains(ENGINE_GET_PAYLOAD_V3),
|
||||
get_payload_v4: capabilities.contains(ENGINE_GET_PAYLOAD_V4),
|
||||
get_payload_v5: capabilities.contains(ENGINE_GET_PAYLOAD_V5),
|
||||
get_client_version_v1: capabilities.contains(ENGINE_GET_CLIENT_VERSION_V1),
|
||||
get_blobs_v1: capabilities.contains(ENGINE_GET_BLOBS_V1),
|
||||
get_inclusion_list_v1: capabilities.contains(ENGINE_GET_INCLUSION_LIST_V1),
|
||||
@@ -1238,6 +1300,14 @@ impl HttpJsonRpc {
|
||||
Err(Error::RequiredMethodUnsupported("engine_newPayloadV4"))
|
||||
}
|
||||
}
|
||||
NewPayloadRequest::Fulu(new_payload_request_fulu) => {
|
||||
// TODO(fulu): switch to v5 endpoint when the EL is ready for Fulu
|
||||
if engine_capabilities.new_payload_v4 {
|
||||
self.new_payload_v4_fulu(new_payload_request_fulu).await
|
||||
} else {
|
||||
Err(Error::RequiredMethodUnsupported("engine_newPayloadV4"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1273,6 +1343,14 @@ impl HttpJsonRpc {
|
||||
Err(Error::RequiredMethodUnsupported("engine_getPayloadv4"))
|
||||
}
|
||||
}
|
||||
ForkName::Fulu => {
|
||||
// TODO(fulu): switch to v5 when the EL is ready
|
||||
if engine_capabilities.get_payload_v4 {
|
||||
self.get_payload_v4(fork_name, payload_id).await
|
||||
} else {
|
||||
Err(Error::RequiredMethodUnsupported("engine_getPayloadv5"))
|
||||
}
|
||||
}
|
||||
ForkName::Base | ForkName::Altair => Err(Error::UnsupportedForkVariant(format!(
|
||||
"called get_payload with {}",
|
||||
fork_name
|
||||
@@ -1345,7 +1423,8 @@ mod test {
|
||||
|
||||
impl Tester {
|
||||
pub fn new(with_auth: bool) -> Self {
|
||||
let server = MockServer::unit_testing();
|
||||
let spec = Arc::new(MainnetEthSpec::default_spec());
|
||||
let server = MockServer::unit_testing(spec);
|
||||
|
||||
let rpc_url = SensitiveUrl::parse(&server.url()).unwrap();
|
||||
let echo_url = SensitiveUrl::parse(&format!("{}/echo", server.url())).unwrap();
|
||||
|
||||
@@ -7,7 +7,7 @@ use superstruct::superstruct;
|
||||
use types::beacon_block_body::KzgCommitments;
|
||||
use types::blob_sidecar::BlobsList;
|
||||
use types::execution_requests::{
|
||||
ConsolidationRequests, DepositRequests, RequestPrefix, WithdrawalRequests,
|
||||
ConsolidationRequests, DepositRequests, RequestType, WithdrawalRequests,
|
||||
};
|
||||
use types::{Blob, FixedVector, KzgProof, Unsigned};
|
||||
|
||||
@@ -65,7 +65,7 @@ pub struct JsonPayloadIdResponse {
|
||||
}
|
||||
|
||||
#[superstruct(
|
||||
variants(V1, V2, V3, V4),
|
||||
variants(V1, V2, V3, V4, V5),
|
||||
variant_attributes(
|
||||
derive(Debug, PartialEq, Default, Serialize, Deserialize,),
|
||||
serde(bound = "E: EthSpec", rename_all = "camelCase"),
|
||||
@@ -100,12 +100,12 @@ pub struct JsonExecutionPayload<E: EthSpec> {
|
||||
pub block_hash: ExecutionBlockHash,
|
||||
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
|
||||
pub transactions: Transactions<E>,
|
||||
#[superstruct(only(V2, V3, V4))]
|
||||
#[superstruct(only(V2, V3, V4, V5))]
|
||||
pub withdrawals: VariableList<JsonWithdrawal, E::MaxWithdrawalsPerPayload>,
|
||||
#[superstruct(only(V3, V4))]
|
||||
#[superstruct(only(V3, V4, V5))]
|
||||
#[serde(with = "serde_utils::u64_hex_be")]
|
||||
pub blob_gas_used: u64,
|
||||
#[superstruct(only(V3, V4))]
|
||||
#[superstruct(only(V3, V4, V5))]
|
||||
#[serde(with = "serde_utils::u64_hex_be")]
|
||||
pub excess_blob_gas: u64,
|
||||
}
|
||||
@@ -214,6 +214,35 @@ impl<E: EthSpec> From<ExecutionPayloadElectra<E>> for JsonExecutionPayloadV4<E>
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> From<ExecutionPayloadFulu<E>> for JsonExecutionPayloadV5<E> {
|
||||
fn from(payload: ExecutionPayloadFulu<E>) -> Self {
|
||||
JsonExecutionPayloadV5 {
|
||||
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<E: EthSpec> From<ExecutionPayload<E>> for JsonExecutionPayload<E> {
|
||||
fn from(execution_payload: ExecutionPayload<E>) -> Self {
|
||||
match execution_payload {
|
||||
@@ -221,6 +250,7 @@ impl<E: EthSpec> From<ExecutionPayload<E>> for JsonExecutionPayload<E> {
|
||||
ExecutionPayload::Capella(payload) => JsonExecutionPayload::V2(payload.into()),
|
||||
ExecutionPayload::Deneb(payload) => JsonExecutionPayload::V3(payload.into()),
|
||||
ExecutionPayload::Electra(payload) => JsonExecutionPayload::V4(payload.into()),
|
||||
ExecutionPayload::Fulu(payload) => JsonExecutionPayload::V5(payload.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,6 +360,35 @@ impl<E: EthSpec> From<JsonExecutionPayloadV4<E>> for ExecutionPayloadElectra<E>
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> From<JsonExecutionPayloadV5<E>> for ExecutionPayloadFulu<E> {
|
||||
fn from(payload: JsonExecutionPayloadV5<E>) -> Self {
|
||||
ExecutionPayloadFulu {
|
||||
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<E: EthSpec> From<JsonExecutionPayload<E>> for ExecutionPayload<E> {
|
||||
fn from(json_execution_payload: JsonExecutionPayload<E>) -> Self {
|
||||
match json_execution_payload {
|
||||
@@ -337,51 +396,85 @@ impl<E: EthSpec> From<JsonExecutionPayload<E>> for ExecutionPayload<E> {
|
||||
JsonExecutionPayload::V2(payload) => ExecutionPayload::Capella(payload.into()),
|
||||
JsonExecutionPayload::V3(payload) => ExecutionPayload::Deneb(payload.into()),
|
||||
JsonExecutionPayload::V4(payload) => ExecutionPayload::Electra(payload.into()),
|
||||
JsonExecutionPayload::V5(payload) => ExecutionPayload::Fulu(payload.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RequestsError {
|
||||
InvalidHex(hex::FromHexError),
|
||||
EmptyRequest(usize),
|
||||
InvalidOrdering,
|
||||
InvalidPrefix(u8),
|
||||
DecodeError(String),
|
||||
}
|
||||
|
||||
/// Format of `ExecutionRequests` received over the engine api.
|
||||
///
|
||||
/// Array of ssz-encoded requests list encoded as hex bytes.
|
||||
/// The prefix of the request type is used to index into the array.
|
||||
///
|
||||
/// For e.g. [0xab, 0xcd, 0xef]
|
||||
/// Here, 0xab are the deposits bytes (prefix and index == 0)
|
||||
/// 0xcd are the withdrawals bytes (prefix and index == 1)
|
||||
/// 0xef are the consolidations bytes (prefix and index == 2)
|
||||
/// Array of ssz-encoded requests list encoded as hex bytes prefixed
|
||||
/// with a `RequestType`
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct JsonExecutionRequests(pub Vec<String>);
|
||||
|
||||
impl<E: EthSpec> TryFrom<JsonExecutionRequests> for ExecutionRequests<E> {
|
||||
type Error = String;
|
||||
type Error = RequestsError;
|
||||
|
||||
fn try_from(value: JsonExecutionRequests) -> Result<Self, Self::Error> {
|
||||
let mut requests = ExecutionRequests::default();
|
||||
|
||||
let mut prev_prefix: Option<RequestType> = None;
|
||||
for (i, request) in value.0.into_iter().enumerate() {
|
||||
// hex string
|
||||
let decoded_bytes = hex::decode(request.strip_prefix("0x").unwrap_or(&request))
|
||||
.map_err(|e| format!("Invalid hex {:?}", e))?;
|
||||
match RequestPrefix::from_prefix(i as u8) {
|
||||
Some(RequestPrefix::Deposit) => {
|
||||
requests.deposits = DepositRequests::<E>::from_ssz_bytes(&decoded_bytes)
|
||||
.map_err(|e| format!("Failed to decode DepositRequest from EL: {:?}", e))?;
|
||||
.map_err(RequestsError::InvalidHex)?;
|
||||
|
||||
// The first byte of each element is the `request_type` and the remaining bytes are the `request_data`.
|
||||
// Elements with empty `request_data` **MUST** be excluded from the list.
|
||||
let Some((prefix_byte, request_bytes)) = decoded_bytes.split_first() else {
|
||||
return Err(RequestsError::EmptyRequest(i));
|
||||
};
|
||||
if request_bytes.is_empty() {
|
||||
return Err(RequestsError::EmptyRequest(i));
|
||||
}
|
||||
// Elements of the list **MUST** be ordered by `request_type` in ascending order
|
||||
let current_prefix = RequestType::from_u8(*prefix_byte)
|
||||
.ok_or(RequestsError::InvalidPrefix(*prefix_byte))?;
|
||||
if let Some(prev) = prev_prefix {
|
||||
if prev.to_u8() >= current_prefix.to_u8() {
|
||||
return Err(RequestsError::InvalidOrdering);
|
||||
}
|
||||
Some(RequestPrefix::Withdrawal) => {
|
||||
requests.withdrawals = WithdrawalRequests::<E>::from_ssz_bytes(&decoded_bytes)
|
||||
}
|
||||
prev_prefix = Some(current_prefix);
|
||||
|
||||
match current_prefix {
|
||||
RequestType::Deposit => {
|
||||
requests.deposits = DepositRequests::<E>::from_ssz_bytes(request_bytes)
|
||||
.map_err(|e| {
|
||||
format!("Failed to decode WithdrawalRequest from EL: {:?}", e)
|
||||
RequestsError::DecodeError(format!(
|
||||
"Failed to decode DepositRequest from EL: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
Some(RequestPrefix::Consolidation) => {
|
||||
requests.consolidations =
|
||||
ConsolidationRequests::<E>::from_ssz_bytes(&decoded_bytes).map_err(
|
||||
|e| format!("Failed to decode ConsolidationRequest from EL: {:?}", e),
|
||||
)?;
|
||||
RequestType::Withdrawal => {
|
||||
requests.withdrawals = WithdrawalRequests::<E>::from_ssz_bytes(request_bytes)
|
||||
.map_err(|e| {
|
||||
RequestsError::DecodeError(format!(
|
||||
"Failed to decode WithdrawalRequest from EL: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
RequestType::Consolidation => {
|
||||
requests.consolidations =
|
||||
ConsolidationRequests::<E>::from_ssz_bytes(request_bytes).map_err(|e| {
|
||||
RequestsError::DecodeError(format!(
|
||||
"Failed to decode ConsolidationRequest from EL: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
None => return Err("Empty requests string".to_string()),
|
||||
}
|
||||
}
|
||||
Ok(requests)
|
||||
@@ -389,7 +482,7 @@ impl<E: EthSpec> TryFrom<JsonExecutionRequests> for ExecutionRequests<E> {
|
||||
}
|
||||
|
||||
#[superstruct(
|
||||
variants(V1, V2, V3, V4),
|
||||
variants(V1, V2, V3, V4, V5),
|
||||
variant_attributes(
|
||||
derive(Debug, PartialEq, Serialize, Deserialize),
|
||||
serde(bound = "E: EthSpec", rename_all = "camelCase")
|
||||
@@ -408,13 +501,15 @@ pub struct JsonGetPayloadResponse<E: EthSpec> {
|
||||
pub execution_payload: JsonExecutionPayloadV3<E>,
|
||||
#[superstruct(only(V4), partial_getter(rename = "execution_payload_v4"))]
|
||||
pub execution_payload: JsonExecutionPayloadV4<E>,
|
||||
#[superstruct(only(V5), partial_getter(rename = "execution_payload_v5"))]
|
||||
pub execution_payload: JsonExecutionPayloadV5<E>,
|
||||
#[serde(with = "serde_utils::u256_hex_be")]
|
||||
pub block_value: Uint256,
|
||||
#[superstruct(only(V3, V4))]
|
||||
#[superstruct(only(V3, V4, V5))]
|
||||
pub blobs_bundle: JsonBlobsBundleV1<E>,
|
||||
#[superstruct(only(V3, V4))]
|
||||
#[superstruct(only(V3, V4, V5))]
|
||||
pub should_override_builder: bool,
|
||||
#[superstruct(only(V4))]
|
||||
#[superstruct(only(V4, V5))]
|
||||
pub execution_requests: JsonExecutionRequests,
|
||||
}
|
||||
|
||||
@@ -448,7 +543,20 @@ impl<E: EthSpec> TryFrom<JsonGetPayloadResponse<E>> for GetPayloadResponse<E> {
|
||||
block_value: response.block_value,
|
||||
blobs_bundle: response.blobs_bundle.into(),
|
||||
should_override_builder: response.should_override_builder,
|
||||
requests: response.execution_requests.try_into()?,
|
||||
requests: response.execution_requests.try_into().map_err(|e| {
|
||||
format!("Failed to convert json to execution requests : {:?}", e)
|
||||
})?,
|
||||
}))
|
||||
}
|
||||
JsonGetPayloadResponse::V5(response) => {
|
||||
Ok(GetPayloadResponse::Fulu(GetPayloadResponseFulu {
|
||||
execution_payload: response.execution_payload.into(),
|
||||
block_value: response.block_value,
|
||||
blobs_bundle: response.blobs_bundle.into(),
|
||||
should_override_builder: response.should_override_builder,
|
||||
requests: response.execution_requests.try_into().map_err(|e| {
|
||||
format!("Failed to convert json to execution requests {:?}", e)
|
||||
})?,
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -883,3 +991,154 @@ impl TryFrom<JsonClientVersionV1> for ClientVersionV1 {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ssz::Encode;
|
||||
use types::{
|
||||
ConsolidationRequest, DepositRequest, MainnetEthSpec, PublicKeyBytes, RequestType,
|
||||
SignatureBytes, WithdrawalRequest,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn create_request_string<T: Encode>(prefix: u8, request_bytes: &T) -> String {
|
||||
format!(
|
||||
"0x{:02x}{}",
|
||||
prefix,
|
||||
hex::encode(request_bytes.as_ssz_bytes())
|
||||
)
|
||||
}
|
||||
|
||||
/// Tests all error conditions except ssz decoding errors
|
||||
///
|
||||
/// ***
|
||||
/// Elements of the list MUST be ordered by request_type in ascending order.
|
||||
/// Elements with empty request_data MUST be excluded from the list.
|
||||
/// If any element is out of order, has a length of 1-byte or shorter,
|
||||
/// or more than one element has the same type byte, client software MUST return -32602: Invalid params error.
|
||||
/// ***
|
||||
#[test]
|
||||
fn test_invalid_execution_requests() {
|
||||
let deposit_request = DepositRequest {
|
||||
pubkey: PublicKeyBytes::empty(),
|
||||
withdrawal_credentials: Hash256::random(),
|
||||
amount: 32,
|
||||
signature: SignatureBytes::empty(),
|
||||
index: 0,
|
||||
};
|
||||
|
||||
let consolidation_request = ConsolidationRequest {
|
||||
source_address: Address::random(),
|
||||
source_pubkey: PublicKeyBytes::empty(),
|
||||
target_pubkey: PublicKeyBytes::empty(),
|
||||
};
|
||||
|
||||
let withdrawal_request = WithdrawalRequest {
|
||||
amount: 32,
|
||||
source_address: Address::random(),
|
||||
validator_pubkey: PublicKeyBytes::empty(),
|
||||
};
|
||||
|
||||
// First check a valid request with all requests
|
||||
assert!(
|
||||
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
||||
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
||||
create_request_string(RequestType::Withdrawal.to_u8(), &withdrawal_request),
|
||||
create_request_string(RequestType::Consolidation.to_u8(), &consolidation_request),
|
||||
]))
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
// Single requests
|
||||
assert!(
|
||||
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
||||
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
||||
]))
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
assert!(
|
||||
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
||||
create_request_string(RequestType::Withdrawal.to_u8(), &withdrawal_request),
|
||||
]))
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
assert!(
|
||||
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
||||
create_request_string(RequestType::Consolidation.to_u8(), &consolidation_request),
|
||||
]))
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
// Out of order
|
||||
assert!(matches!(
|
||||
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
||||
create_request_string(RequestType::Withdrawal.to_u8(), &withdrawal_request),
|
||||
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
||||
]))
|
||||
.unwrap_err(),
|
||||
RequestsError::InvalidOrdering
|
||||
));
|
||||
|
||||
assert!(matches!(
|
||||
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
||||
create_request_string(RequestType::Consolidation.to_u8(), &consolidation_request),
|
||||
create_request_string(RequestType::Withdrawal.to_u8(), &withdrawal_request),
|
||||
]))
|
||||
.unwrap_err(),
|
||||
RequestsError::InvalidOrdering
|
||||
));
|
||||
|
||||
assert!(matches!(
|
||||
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
||||
create_request_string(RequestType::Consolidation.to_u8(), &consolidation_request),
|
||||
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
||||
]))
|
||||
.unwrap_err(),
|
||||
RequestsError::InvalidOrdering
|
||||
));
|
||||
|
||||
// Multiple requests of same type
|
||||
assert!(matches!(
|
||||
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
||||
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
||||
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
||||
]))
|
||||
.unwrap_err(),
|
||||
RequestsError::InvalidOrdering
|
||||
));
|
||||
|
||||
// Invalid prefix
|
||||
assert!(matches!(
|
||||
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
||||
create_request_string(42, &deposit_request),
|
||||
]))
|
||||
.unwrap_err(),
|
||||
RequestsError::InvalidPrefix(42)
|
||||
));
|
||||
|
||||
// Prefix followed by no data
|
||||
assert!(matches!(
|
||||
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
||||
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
||||
create_request_string(
|
||||
RequestType::Consolidation.to_u8(),
|
||||
&Vec::<ConsolidationRequest>::new()
|
||||
),
|
||||
]))
|
||||
.unwrap_err(),
|
||||
RequestsError::EmptyRequest(1)
|
||||
));
|
||||
// Empty request
|
||||
assert!(matches!(
|
||||
ExecutionRequests::<MainnetEthSpec>::try_from(JsonExecutionRequests(vec![
|
||||
create_request_string(RequestType::Deposit.to_u8(), &deposit_request),
|
||||
"0x".to_string()
|
||||
]))
|
||||
.unwrap_err(),
|
||||
RequestsError::EmptyRequest(1)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ use types::{
|
||||
};
|
||||
use types::{
|
||||
ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
|
||||
ExecutionPayloadElectra, ExecutionRequests,
|
||||
ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionRequests,
|
||||
};
|
||||
|
||||
#[superstruct(
|
||||
variants(Bellatrix, Capella, Deneb, Electra),
|
||||
variants(Bellatrix, Capella, Deneb, Electra, Fulu),
|
||||
variant_attributes(derive(Clone, Debug, PartialEq),),
|
||||
map_into(ExecutionPayload),
|
||||
map_ref_into(ExecutionPayloadRef),
|
||||
@@ -39,13 +39,15 @@ pub struct NewPayloadRequest<'block, E: EthSpec> {
|
||||
pub execution_payload: &'block ExecutionPayloadDeneb<E>,
|
||||
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
|
||||
pub execution_payload: &'block ExecutionPayloadElectra<E>,
|
||||
#[superstruct(only(Deneb, Electra))]
|
||||
#[superstruct(only(Fulu), partial_getter(rename = "execution_payload_fulu"))]
|
||||
pub execution_payload: &'block ExecutionPayloadFulu<E>,
|
||||
#[superstruct(only(Deneb, Electra, Fulu))]
|
||||
pub versioned_hashes: Vec<VersionedHash>,
|
||||
#[superstruct(only(Deneb, Electra))]
|
||||
#[superstruct(only(Deneb, Electra, Fulu))]
|
||||
pub parent_beacon_block_root: Hash256,
|
||||
#[superstruct(only(Electra))]
|
||||
#[superstruct(only(Electra, Fulu))]
|
||||
pub execution_requests: &'block ExecutionRequests<E>,
|
||||
#[superstruct(only(Electra))]
|
||||
#[superstruct(only(Electra, Fulu))]
|
||||
pub il_transactions: InclusionListTransactions<E>,
|
||||
}
|
||||
|
||||
@@ -56,6 +58,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Capella(payload) => payload.execution_payload.parent_hash,
|
||||
Self::Deneb(payload) => payload.execution_payload.parent_hash,
|
||||
Self::Electra(payload) => payload.execution_payload.parent_hash,
|
||||
Self::Fulu(payload) => payload.execution_payload.parent_hash,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +68,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Capella(payload) => payload.execution_payload.block_hash,
|
||||
Self::Deneb(payload) => payload.execution_payload.block_hash,
|
||||
Self::Electra(payload) => payload.execution_payload.block_hash,
|
||||
Self::Fulu(payload) => payload.execution_payload.block_hash,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +78,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Capella(payload) => payload.execution_payload.block_number,
|
||||
Self::Deneb(payload) => payload.execution_payload.block_number,
|
||||
Self::Electra(payload) => payload.execution_payload.block_number,
|
||||
Self::Fulu(payload) => payload.execution_payload.block_number,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +88,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
Self::Capella(request) => ExecutionPayloadRef::Capella(request.execution_payload),
|
||||
Self::Deneb(request) => ExecutionPayloadRef::Deneb(request.execution_payload),
|
||||
Self::Electra(request) => ExecutionPayloadRef::Electra(request.execution_payload),
|
||||
Self::Fulu(request) => ExecutionPayloadRef::Fulu(request.execution_payload),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +100,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
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()),
|
||||
Self::Fulu(request) => ExecutionPayload::Fulu(request.execution_payload.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +130,11 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
|
||||
let _timer = metrics::start_timer(&metrics::EXECUTION_LAYER_VERIFY_BLOCK_HASH);
|
||||
|
||||
// Check that no transactions in the payload are zero length
|
||||
if payload.transactions().iter().any(|slice| slice.is_empty()) {
|
||||
return Err(Error::ZeroLengthTransaction);
|
||||
}
|
||||
|
||||
let (header_hash, rlp_transactions_root) = calculate_execution_block_hash(
|
||||
payload,
|
||||
parent_beacon_block_root,
|
||||
@@ -194,6 +206,18 @@ impl<'a, E: EthSpec> NewPayloadRequest<'a, E> {
|
||||
execution_requests: &block_ref.body.execution_requests,
|
||||
il_transactions,
|
||||
})),
|
||||
BeaconBlockRef::Fulu(block_ref) => Ok(Self::Fulu(NewPayloadRequestFulu {
|
||||
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,
|
||||
execution_requests: &block_ref.body.execution_requests,
|
||||
il_transactions,
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,6 +261,18 @@ impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<'a, E>
|
||||
execution_requests: &block_ref.body.execution_requests,
|
||||
il_transactions: vec![].into(),
|
||||
})),
|
||||
BeaconBlockRef::Fulu(block_ref) => Ok(Self::Fulu(NewPayloadRequestFulu {
|
||||
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,
|
||||
execution_requests: &block_ref.body.execution_requests,
|
||||
il_transactions: vec![].into()
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -256,6 +292,7 @@ impl<'a, E: EthSpec> TryFrom<ExecutionPayloadRef<'a, E>> for NewPayloadRequest<'
|
||||
})),
|
||||
ExecutionPayloadRef::Deneb(_) => Err(Self::Error::IncorrectStateVariant),
|
||||
ExecutionPayloadRef::Electra(_) => Err(Self::Error::IncorrectStateVariant),
|
||||
ExecutionPayloadRef::Fulu(_) => Err(Self::Error::IncorrectStateVariant),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ use types::{
|
||||
};
|
||||
use types::{
|
||||
BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadBellatrix,
|
||||
ExecutionPayloadCapella, ExecutionPayloadElectra, FullPayload, InclusionListTransactions,
|
||||
ProposerPreparationData, PublicKeyBytes, Signature, Slot,
|
||||
ExecutionPayloadCapella, ExecutionPayloadElectra, ExecutionPayloadFulu, FullPayload,
|
||||
ProposerPreparationData, PublicKeyBytes, Signature, Slot, InclusionListTransactions
|
||||
};
|
||||
|
||||
mod block_hash;
|
||||
@@ -121,7 +121,14 @@ impl<E: EthSpec> TryFrom<BuilderBid<E>> for ProvenancedPayload<BlockProposalCont
|
||||
block_value: builder_bid.value,
|
||||
kzg_commitments: builder_bid.blob_kzg_commitments,
|
||||
blobs_and_proofs: None,
|
||||
// TODO(electra): update this with builder api returning the requests
|
||||
requests: Some(builder_bid.execution_requests),
|
||||
},
|
||||
BuilderBid::Fulu(builder_bid) => BlockProposalContents::PayloadAndBlobs {
|
||||
payload: ExecutionPayloadHeader::Fulu(builder_bid.header).into(),
|
||||
block_value: builder_bid.value,
|
||||
kzg_commitments: builder_bid.blob_kzg_commitments,
|
||||
blobs_and_proofs: None,
|
||||
// TODO(fulu): update this with builder api returning the requests
|
||||
requests: None,
|
||||
},
|
||||
};
|
||||
@@ -149,6 +156,7 @@ pub enum Error {
|
||||
payload: ExecutionBlockHash,
|
||||
transactions_root: Hash256,
|
||||
},
|
||||
ZeroLengthTransaction,
|
||||
PayloadBodiesByRangeNotSupported,
|
||||
InvalidJWTSecret(String),
|
||||
InvalidForkForPayload,
|
||||
@@ -321,7 +329,7 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> BlockProposalContents<E, Paylo
|
||||
|
||||
// This just groups together a bunch of parameters that commonly
|
||||
// get passed around together in calls to get_payload.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PayloadParameters<'a> {
|
||||
pub parent_hash: ExecutionBlockHash,
|
||||
pub parent_gas_limit: u64,
|
||||
@@ -1822,6 +1830,7 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
||||
ForkName::Capella => ExecutionPayloadCapella::default().into(),
|
||||
ForkName::Deneb => ExecutionPayloadDeneb::default().into(),
|
||||
ForkName::Electra => ExecutionPayloadElectra::default().into(),
|
||||
ForkName::Fulu => ExecutionPayloadFulu::default().into(),
|
||||
ForkName::Base | ForkName::Altair => {
|
||||
return Err(Error::InvalidForkForPayload);
|
||||
}
|
||||
@@ -2109,7 +2118,7 @@ fn verify_builder_bid<E: EthSpec>(
|
||||
payload: header.timestamp(),
|
||||
expected: payload_attributes.timestamp(),
|
||||
}))
|
||||
} else if block_number.map_or(false, |n| n != header.block_number()) {
|
||||
} else if block_number.is_some_and(|n| n != header.block_number()) {
|
||||
Err(Box::new(InvalidBuilderPayload::BlockNumber {
|
||||
payload: header.block_number(),
|
||||
expected: block_number,
|
||||
|
||||
@@ -41,7 +41,7 @@ pub fn process_payload_status(
|
||||
PayloadStatusV1Status::Valid => {
|
||||
if response
|
||||
.latest_valid_hash
|
||||
.map_or(false, |h| h == head_block_hash)
|
||||
.is_some_and(|h| h == head_block_hash)
|
||||
{
|
||||
// The response is only valid if `latest_valid_hash` is not `null` and
|
||||
// equal to the provided `block_hash`.
|
||||
|
||||
@@ -19,7 +19,7 @@ use tree_hash::TreeHash;
|
||||
use tree_hash_derive::TreeHash;
|
||||
use types::{
|
||||
Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadBellatrix,
|
||||
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra,
|
||||
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu,
|
||||
ExecutionPayloadHeader, FixedBytesExtended, ForkName, Hash256, Transaction, Transactions,
|
||||
Uint256,
|
||||
};
|
||||
@@ -147,12 +147,14 @@ pub struct ExecutionBlockGenerator<E: EthSpec> {
|
||||
pub shanghai_time: Option<u64>, // capella
|
||||
pub cancun_time: Option<u64>, // deneb
|
||||
pub prague_time: Option<u64>, // electra
|
||||
pub osaka_time: Option<u64>, // fulu
|
||||
/*
|
||||
* deneb stuff
|
||||
*/
|
||||
pub blobs_bundles: HashMap<PayloadId, BlobsBundle<E>>,
|
||||
pub kzg: Option<Arc<Kzg>>,
|
||||
rng: Arc<Mutex<StdRng>>,
|
||||
spec: Arc<ChainSpec>,
|
||||
}
|
||||
|
||||
fn make_rng() -> Arc<Mutex<StdRng>> {
|
||||
@@ -162,6 +164,7 @@ 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,
|
||||
@@ -169,6 +172,8 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
shanghai_time: Option<u64>,
|
||||
cancun_time: Option<u64>,
|
||||
prague_time: Option<u64>,
|
||||
osaka_time: Option<u64>,
|
||||
spec: Arc<ChainSpec>,
|
||||
kzg: Option<Arc<Kzg>>,
|
||||
) -> Self {
|
||||
let mut gen = Self {
|
||||
@@ -185,9 +190,11 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
osaka_time,
|
||||
blobs_bundles: <_>::default(),
|
||||
kzg,
|
||||
rng: make_rng(),
|
||||
spec,
|
||||
};
|
||||
|
||||
gen.insert_pow_block(0).unwrap();
|
||||
@@ -233,13 +240,16 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
}
|
||||
|
||||
pub fn get_fork_at_timestamp(&self, timestamp: u64) -> ForkName {
|
||||
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,
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -664,6 +674,25 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
blob_gas_used: 0,
|
||||
excess_blob_gas: 0,
|
||||
}),
|
||||
ForkName::Fulu => ExecutionPayload::Fulu(ExecutionPayloadFulu {
|
||||
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!(),
|
||||
},
|
||||
};
|
||||
@@ -671,7 +700,11 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
if execution_payload.fork_name().deneb_enabled() {
|
||||
// get random number between 0 and Max Blobs
|
||||
let mut rng = self.rng.lock();
|
||||
let num_blobs = rng.gen::<usize>() % (E::max_blobs_per_block() + 1);
|
||||
let max_blobs = self
|
||||
.spec
|
||||
.max_blobs_per_block_by_fork(execution_payload.fork_name())
|
||||
as usize;
|
||||
let num_blobs = rng.gen::<usize>() % (max_blobs + 1);
|
||||
let (bundle, transactions) = generate_blobs(num_blobs)?;
|
||||
for tx in Vec::from(transactions) {
|
||||
execution_payload
|
||||
@@ -811,6 +844,12 @@ pub fn generate_genesis_header<E: EthSpec>(
|
||||
*header.transactions_root_mut() = empty_transactions_root;
|
||||
Some(header)
|
||||
}
|
||||
ForkName::Fulu => {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,6 +913,7 @@ mod test {
|
||||
const TERMINAL_DIFFICULTY: u64 = 10;
|
||||
const TERMINAL_BLOCK: u64 = 10;
|
||||
const DIFFICULTY_INCREMENT: u64 = 1;
|
||||
let spec = Arc::new(MainnetEthSpec::default_spec());
|
||||
|
||||
let mut generator: ExecutionBlockGenerator<MainnetEthSpec> = ExecutionBlockGenerator::new(
|
||||
Uint256::from(TERMINAL_DIFFICULTY),
|
||||
@@ -883,6 +923,8 @@ mod test {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
spec,
|
||||
None,
|
||||
);
|
||||
|
||||
for i in 0..=TERMINAL_BLOCK {
|
||||
|
||||
@@ -99,7 +99,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::V1(
|
||||
get_param::<JsonExecutionPayloadV1<E>>(params, 0)
|
||||
@@ -121,6 +122,9 @@ pub async fn handle_rpc<E: EthSpec>(
|
||||
ENGINE_NEW_PAYLOAD_V4 => get_param::<JsonExecutionPayloadV4<E>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V4(jep))
|
||||
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
|
||||
ENGINE_NEW_PAYLOAD_V5 => get_param::<JsonExecutionPayloadV5<E>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V5(jep))
|
||||
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
@@ -222,6 +226,56 @@ pub async fn handle_rpc<E: EthSpec>(
|
||||
));
|
||||
}
|
||||
}
|
||||
ForkName::Fulu => {
|
||||
if method == ENGINE_NEW_PAYLOAD_V1
|
||||
|| method == ENGINE_NEW_PAYLOAD_V2
|
||||
|| method == ENGINE_NEW_PAYLOAD_V3
|
||||
// TODO(fulu): Uncomment this once v5 method is ready for Fulu
|
||||
// || method == ENGINE_NEW_PAYLOAD_V4
|
||||
{
|
||||
return Err((
|
||||
format!("{} called after Fulu fork!", method),
|
||||
GENERIC_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
if matches!(request, JsonExecutionPayload::V1(_)) {
|
||||
return Err((
|
||||
format!(
|
||||
"{} called with `ExecutionPayloadV1` after Fulu fork!",
|
||||
method
|
||||
),
|
||||
GENERIC_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
if matches!(request, JsonExecutionPayload::V2(_)) {
|
||||
return Err((
|
||||
format!(
|
||||
"{} called with `ExecutionPayloadV2` after Fulu fork!",
|
||||
method
|
||||
),
|
||||
GENERIC_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
if matches!(request, JsonExecutionPayload::V3(_)) {
|
||||
return Err((
|
||||
format!(
|
||||
"{} called with `ExecutionPayloadV3` after Fulu fork!",
|
||||
method
|
||||
),
|
||||
GENERIC_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
// TODO(fulu): remove once we switch to v5
|
||||
// if matches!(request, JsonExecutionPayload::V4(_)) {
|
||||
// return Err((
|
||||
// format!(
|
||||
// "{} called with `ExecutionPayloadV4` after Fulu fork!",
|
||||
// method
|
||||
// ),
|
||||
// GENERIC_ERROR_CODE,
|
||||
// ));
|
||||
// }
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
@@ -260,7 +314,8 @@ 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_V4
|
||||
| ENGINE_GET_PAYLOAD_V5 => {
|
||||
let request: JsonPayloadIdRequest =
|
||||
get_param(params, 0).map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?;
|
||||
let id = request.into();
|
||||
@@ -320,6 +375,24 @@ pub async fn handle_rpc<E: EthSpec>(
|
||||
));
|
||||
}
|
||||
|
||||
// validate method called correctly according to fulu fork time
|
||||
if ctx
|
||||
.execution_block_generator
|
||||
.read()
|
||||
.get_fork_at_timestamp(response.timestamp())
|
||||
== ForkName::Fulu
|
||||
&& (method == ENGINE_GET_PAYLOAD_V1
|
||||
|| method == ENGINE_GET_PAYLOAD_V2
|
||||
|| method == ENGINE_GET_PAYLOAD_V3)
|
||||
// TODO(fulu): Uncomment this once v5 method is ready for Fulu
|
||||
// || method == ENGINE_GET_PAYLOAD_V4)
|
||||
{
|
||||
return Err((
|
||||
format!("{} called after Fulu fork!", method),
|
||||
FORK_REQUEST_MISMATCH_ERROR_CODE,
|
||||
));
|
||||
}
|
||||
|
||||
match method {
|
||||
ENGINE_GET_PAYLOAD_V1 => {
|
||||
Ok(serde_json::to_value(JsonExecutionPayload::from(response)).unwrap())
|
||||
@@ -378,6 +451,40 @@ pub async fn handle_rpc<E: EthSpec>(
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
// TODO(fulu): remove this once we switch to v5 method
|
||||
JsonExecutionPayload::V5(execution_payload) => {
|
||||
serde_json::to_value(JsonGetPayloadResponseV5 {
|
||||
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!(),
|
||||
}),
|
||||
ENGINE_GET_PAYLOAD_V5 => Ok(match JsonExecutionPayload::from(response) {
|
||||
JsonExecutionPayload::V5(execution_payload) => {
|
||||
serde_json::to_value(JsonGetPayloadResponseV5 {
|
||||
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!(),
|
||||
@@ -411,7 +518,10 @@ pub async fn handle_rpc<E: EthSpec>(
|
||||
.map(|opt| opt.map(JsonPayloadAttributes::V1))
|
||||
.transpose()
|
||||
}
|
||||
ForkName::Capella | ForkName::Deneb | ForkName::Electra => {
|
||||
ForkName::Capella
|
||||
| ForkName::Deneb
|
||||
| ForkName::Electra
|
||||
| ForkName::Fulu => {
|
||||
get_param::<Option<JsonPayloadAttributesV2>>(params, 1)
|
||||
.map(|opt| opt.map(JsonPayloadAttributes::V2))
|
||||
.transpose()
|
||||
@@ -475,7 +585,7 @@ pub async fn handle_rpc<E: EthSpec>(
|
||||
));
|
||||
}
|
||||
}
|
||||
ForkName::Deneb | ForkName::Electra => {
|
||||
ForkName::Deneb | ForkName::Electra | ForkName::Fulu => {
|
||||
if method == ENGINE_FORKCHOICE_UPDATED_V1 {
|
||||
return Err((
|
||||
format!("{} called after Deneb fork!", method),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ pub struct MockExecutionLayer<E: EthSpec> {
|
||||
pub server: MockServer<E>,
|
||||
pub el: ExecutionLayer<E>,
|
||||
pub executor: TaskExecutor,
|
||||
pub spec: ChainSpec,
|
||||
pub spec: Arc<ChainSpec>,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
@@ -28,8 +28,9 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
|
||||
spec,
|
||||
Arc::new(spec),
|
||||
None,
|
||||
)
|
||||
}
|
||||
@@ -41,8 +42,9 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
shanghai_time: Option<u64>,
|
||||
cancun_time: Option<u64>,
|
||||
prague_time: Option<u64>,
|
||||
osaka_time: Option<u64>,
|
||||
jwt_key: Option<JwtKey>,
|
||||
spec: ChainSpec,
|
||||
spec: Arc<ChainSpec>,
|
||||
kzg: Option<Arc<Kzg>>,
|
||||
) -> Self {
|
||||
let handle = executor.handle().unwrap();
|
||||
@@ -57,6 +59,8 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
osaka_time,
|
||||
spec.clone(),
|
||||
kzg,
|
||||
);
|
||||
|
||||
@@ -318,9 +322,9 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
(self, block_hash)
|
||||
}
|
||||
|
||||
pub async fn with_terminal_block<'a, U, V>(self, func: U) -> Self
|
||||
pub async fn with_terminal_block<U, V>(self, func: U) -> Self
|
||||
where
|
||||
U: Fn(ChainSpec, ExecutionLayer<E>, Option<ExecutionBlock>) -> V,
|
||||
U: Fn(Arc<ChainSpec>, ExecutionLayer<E>, Option<ExecutionBlock>) -> V,
|
||||
V: Future<Output = ()>,
|
||||
{
|
||||
let terminal_block_number = self
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::marker::PhantomData;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::sync::{Arc, LazyLock};
|
||||
use tokio::{runtime, sync::oneshot};
|
||||
use types::{EthSpec, ExecutionBlockHash, Uint256};
|
||||
use types::{ChainSpec, EthSpec, ExecutionBlockHash, Uint256};
|
||||
use warp::{http::StatusCode, Filter, Rejection};
|
||||
|
||||
use crate::EngineCapabilities;
|
||||
@@ -44,6 +44,7 @@ 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,
|
||||
@@ -53,6 +54,7 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
|
||||
get_payload_v2: true,
|
||||
get_payload_v3: true,
|
||||
get_payload_v4: true,
|
||||
get_payload_v5: true,
|
||||
get_client_version_v1: true,
|
||||
get_blobs_v1: true,
|
||||
get_inclusion_list_v1: true,
|
||||
@@ -83,6 +85,7 @@ pub struct MockExecutionConfig {
|
||||
pub shanghai_time: Option<u64>,
|
||||
pub cancun_time: Option<u64>,
|
||||
pub prague_time: Option<u64>,
|
||||
pub osaka_time: Option<u64>,
|
||||
}
|
||||
|
||||
impl Default for MockExecutionConfig {
|
||||
@@ -96,6 +99,7 @@ impl Default for MockExecutionConfig {
|
||||
shanghai_time: None,
|
||||
cancun_time: None,
|
||||
prague_time: None,
|
||||
osaka_time: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +112,7 @@ pub struct MockServer<E: EthSpec> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> MockServer<E> {
|
||||
pub fn unit_testing() -> Self {
|
||||
pub fn unit_testing(chain_spec: Arc<ChainSpec>) -> Self {
|
||||
Self::new(
|
||||
&runtime::Handle::current(),
|
||||
JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap(),
|
||||
@@ -118,6 +122,8 @@ impl<E: EthSpec> MockServer<E> {
|
||||
None, // FIXME(capella): should this be the default?
|
||||
None, // FIXME(deneb): should this be the default?
|
||||
None, // FIXME(electra): should this be the default?
|
||||
None, // FIXME(fulu): should this be the default?
|
||||
chain_spec,
|
||||
None,
|
||||
)
|
||||
}
|
||||
@@ -125,6 +131,7 @@ impl<E: EthSpec> MockServer<E> {
|
||||
pub fn new_with_config(
|
||||
handle: &runtime::Handle,
|
||||
config: MockExecutionConfig,
|
||||
spec: Arc<ChainSpec>,
|
||||
kzg: Option<Arc<Kzg>>,
|
||||
) -> Self {
|
||||
let MockExecutionConfig {
|
||||
@@ -136,6 +143,7 @@ impl<E: EthSpec> MockServer<E> {
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
osaka_time,
|
||||
} = config;
|
||||
let last_echo_request = Arc::new(RwLock::new(None));
|
||||
let preloaded_responses = Arc::new(Mutex::new(vec![]));
|
||||
@@ -146,6 +154,8 @@ impl<E: EthSpec> MockServer<E> {
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
osaka_time,
|
||||
spec,
|
||||
kzg,
|
||||
);
|
||||
|
||||
@@ -209,6 +219,8 @@ impl<E: EthSpec> MockServer<E> {
|
||||
shanghai_time: Option<u64>,
|
||||
cancun_time: Option<u64>,
|
||||
prague_time: Option<u64>,
|
||||
osaka_time: Option<u64>,
|
||||
spec: Arc<ChainSpec>,
|
||||
kzg: Option<Arc<Kzg>>,
|
||||
) -> Self {
|
||||
Self::new_with_config(
|
||||
@@ -222,7 +234,9 @@ impl<E: EthSpec> MockServer<E> {
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
osaka_time,
|
||||
},
|
||||
spec,
|
||||
kzg,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user