mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-01 05:37:05 +00:00
Merge branch 'unstable' of https://github.com/ethDreamer/lighthouse into remove_use_of_ethers_core
This commit is contained in:
@@ -14,8 +14,8 @@ use types::{
|
||||
///
|
||||
/// Return `(block_hash, transactions_root)`, where `transactions_root` is the root of the RLP
|
||||
/// transactions.
|
||||
pub fn calculate_execution_block_hash<T: EthSpec>(
|
||||
payload: ExecutionPayloadRef<T>,
|
||||
pub fn calculate_execution_block_hash<E: EthSpec>(
|
||||
payload: ExecutionPayloadRef<E>,
|
||||
parent_beacon_block_root: Option<Hash256>,
|
||||
) -> (ExecutionBlockHash, Hash256) {
|
||||
// Calculate the transactions root.
|
||||
@@ -127,7 +127,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rlp_encode_merge_block() {
|
||||
fn test_rlp_encode_bellatrix_block() {
|
||||
let header = ExecutionBlockHeader {
|
||||
parent_hash: Hash256::from_str("927ca537f06c783a3a2635b8805eef1c8c2124f7444ad4a3389898dd832f2dbe").unwrap(),
|
||||
ommers_hash: Hash256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(),
|
||||
@@ -217,4 +217,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::engines::ForkchoiceState;
|
||||
use crate::http::{
|
||||
ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_FORKCHOICE_UPDATED_V3,
|
||||
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_NEW_PAYLOAD_V1,
|
||||
ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3,
|
||||
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_NEW_PAYLOAD_V1, ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3,
|
||||
};
|
||||
use eth2::types::{
|
||||
BlobsBundle, SsePayloadAttributes, SsePayloadAttributesV1, SsePayloadAttributesV2,
|
||||
@@ -17,7 +17,6 @@ pub use json_structures::{JsonWithdrawal, TransitionConfigurationV1};
|
||||
use pretty_reqwest_error::PrettyReqwestError;
|
||||
use reqwest::StatusCode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
use strum::IntoStaticStr;
|
||||
use superstruct::superstruct;
|
||||
pub use types::{
|
||||
@@ -25,7 +24,11 @@ pub use types::{
|
||||
ExecutionPayloadRef, FixedVector, ForkName, Hash256, Transactions, Uint256, VariableList,
|
||||
Withdrawal, Withdrawals,
|
||||
};
|
||||
use types::{ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge, KzgProofs};
|
||||
use types::{
|
||||
ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
|
||||
ExecutionPayloadElectra, KzgProofs,
|
||||
};
|
||||
use types::{Graffiti, GRAFFITI_BYTES_LEN};
|
||||
|
||||
pub mod auth;
|
||||
pub mod http;
|
||||
@@ -33,7 +36,8 @@ pub mod json_structures;
|
||||
mod new_payload_request;
|
||||
|
||||
pub use new_payload_request::{
|
||||
NewPayloadRequest, NewPayloadRequestCapella, NewPayloadRequestDeneb, NewPayloadRequestMerge,
|
||||
NewPayloadRequest, NewPayloadRequestBellatrix, NewPayloadRequestCapella,
|
||||
NewPayloadRequestDeneb, NewPayloadRequestElectra,
|
||||
};
|
||||
|
||||
pub const LATEST_TAG: &str = "latest";
|
||||
@@ -57,13 +61,13 @@ pub enum Error {
|
||||
ParentHashEqualsBlockHash(ExecutionBlockHash),
|
||||
PayloadIdUnavailable,
|
||||
TransitionConfigurationMismatch,
|
||||
PayloadConversionLogicFlaw,
|
||||
SszError(ssz_types::Error),
|
||||
DeserializeWithdrawals(ssz_types::Error),
|
||||
BuilderApi(builder_client::Error),
|
||||
IncorrectStateVariant,
|
||||
RequiredMethodUnsupported(&'static str),
|
||||
UnsupportedForkVariant(String),
|
||||
InvalidClientVersion(String),
|
||||
RlpDecoderError(rlp::DecoderError),
|
||||
}
|
||||
|
||||
@@ -151,24 +155,24 @@ pub struct ExecutionBlock {
|
||||
|
||||
/// Representation of an execution block with enough detail to reconstruct a payload.
|
||||
#[superstruct(
|
||||
variants(Merge, Capella, Deneb),
|
||||
variants(Bellatrix, Capella, Deneb, Electra),
|
||||
variant_attributes(
|
||||
derive(Clone, Debug, PartialEq, Serialize, Deserialize,),
|
||||
serde(bound = "T: EthSpec", rename_all = "camelCase"),
|
||||
serde(bound = "E: EthSpec", rename_all = "camelCase"),
|
||||
),
|
||||
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
|
||||
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
|
||||
)]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(bound = "T: EthSpec", rename_all = "camelCase", untagged)]
|
||||
pub struct ExecutionBlockWithTransactions<T: EthSpec> {
|
||||
#[serde(bound = "E: EthSpec", rename_all = "camelCase", untagged)]
|
||||
pub struct ExecutionBlockWithTransactions<E: EthSpec> {
|
||||
pub parent_hash: ExecutionBlockHash,
|
||||
#[serde(alias = "miner")]
|
||||
pub fee_recipient: Address,
|
||||
pub state_root: Hash256,
|
||||
pub receipts_root: Hash256,
|
||||
#[serde(with = "ssz_types::serde_utils::hex_fixed_vec")]
|
||||
pub logs_bloom: FixedVector<u8, T::BytesPerLogsBloom>,
|
||||
pub logs_bloom: FixedVector<u8, E::BytesPerLogsBloom>,
|
||||
#[serde(alias = "mixHash")]
|
||||
pub prev_randao: Hash256,
|
||||
#[serde(rename = "number", with = "serde_utils::u64_hex_be")]
|
||||
@@ -180,46 +184,48 @@ pub struct ExecutionBlockWithTransactions<T: EthSpec> {
|
||||
#[serde(with = "serde_utils::u64_hex_be")]
|
||||
pub timestamp: u64,
|
||||
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
|
||||
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
|
||||
pub extra_data: VariableList<u8, E::MaxExtraDataBytes>,
|
||||
pub base_fee_per_gas: Uint256,
|
||||
#[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,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> TryFrom<ExecutionPayload<T>> for ExecutionBlockWithTransactions<T> {
|
||||
impl<E: EthSpec> TryFrom<ExecutionPayload<E>> for ExecutionBlockWithTransactions<E> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(payload: ExecutionPayload<T>) -> Result<Self, Error> {
|
||||
fn try_from(payload: ExecutionPayload<E>) -> Result<Self, Error> {
|
||||
let json_payload = match payload {
|
||||
ExecutionPayload::Merge(block) => Self::Merge(ExecutionBlockWithTransactionsMerge {
|
||||
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<_>, _>>()?,
|
||||
}),
|
||||
ExecutionPayload::Bellatrix(block) => {
|
||||
Self::Bellatrix(ExecutionBlockWithTransactionsBellatrix {
|
||||
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<_>, _>>()?,
|
||||
})
|
||||
}
|
||||
ExecutionPayload::Capella(block) => {
|
||||
Self::Capella(ExecutionBlockWithTransactionsCapella {
|
||||
parent_hash: block.parent_hash,
|
||||
@@ -272,6 +278,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)
|
||||
}
|
||||
@@ -391,7 +425,7 @@ pub struct ProposeBlindedBlockResponse {
|
||||
}
|
||||
|
||||
#[superstruct(
|
||||
variants(Merge, Capella, Deneb),
|
||||
variants(Bellatrix, Capella, Deneb, Electra),
|
||||
variant_attributes(derive(Clone, Debug, PartialEq),),
|
||||
map_into(ExecutionPayload),
|
||||
map_ref_into(ExecutionPayloadRef),
|
||||
@@ -399,17 +433,22 @@ pub struct ProposeBlindedBlockResponse {
|
||||
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
|
||||
)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct GetPayloadResponse<T: EthSpec> {
|
||||
#[superstruct(only(Merge), partial_getter(rename = "execution_payload_merge"))]
|
||||
pub execution_payload: ExecutionPayloadMerge<T>,
|
||||
pub struct GetPayloadResponse<E: EthSpec> {
|
||||
#[superstruct(
|
||||
only(Bellatrix),
|
||||
partial_getter(rename = "execution_payload_bellatrix")
|
||||
)]
|
||||
pub execution_payload: ExecutionPayloadBellatrix<E>,
|
||||
#[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))]
|
||||
pub execution_payload: ExecutionPayloadCapella<T>,
|
||||
pub execution_payload: ExecutionPayloadCapella<E>,
|
||||
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))]
|
||||
pub execution_payload: ExecutionPayloadDeneb<T>,
|
||||
pub execution_payload: ExecutionPayloadDeneb<E>,
|
||||
#[superstruct(only(Electra), partial_getter(rename = "execution_payload_electra"))]
|
||||
pub execution_payload: ExecutionPayloadElectra<E>,
|
||||
pub block_value: Uint256,
|
||||
#[superstruct(only(Deneb))]
|
||||
pub blobs_bundle: BlobsBundle<T>,
|
||||
#[superstruct(only(Deneb), partial_getter(copy))]
|
||||
#[superstruct(only(Deneb, Electra))]
|
||||
pub blobs_bundle: BlobsBundle<E>,
|
||||
#[superstruct(only(Deneb, Electra), partial_getter(copy))]
|
||||
pub should_override_builder: bool,
|
||||
}
|
||||
|
||||
@@ -427,29 +466,29 @@ impl<E: EthSpec> GetPayloadResponse<E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: EthSpec> From<GetPayloadResponseRef<'a, T>> for ExecutionPayloadRef<'a, T> {
|
||||
fn from(response: GetPayloadResponseRef<'a, T>) -> Self {
|
||||
impl<'a, E: EthSpec> From<GetPayloadResponseRef<'a, E>> for ExecutionPayloadRef<'a, E> {
|
||||
fn from(response: GetPayloadResponseRef<'a, E>) -> Self {
|
||||
map_get_payload_response_ref_into_execution_payload_ref!(&'a _, response, |inner, cons| {
|
||||
cons(&inner.execution_payload)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<GetPayloadResponse<T>> for ExecutionPayload<T> {
|
||||
fn from(response: GetPayloadResponse<T>) -> Self {
|
||||
impl<E: EthSpec> From<GetPayloadResponse<E>> for ExecutionPayload<E> {
|
||||
fn from(response: GetPayloadResponse<E>) -> Self {
|
||||
map_get_payload_response_into_execution_payload!(response, |inner, cons| {
|
||||
cons(inner.execution_payload)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<GetPayloadResponse<T>>
|
||||
for (ExecutionPayload<T>, Uint256, Option<BlobsBundle<T>>)
|
||||
impl<E: EthSpec> From<GetPayloadResponse<E>>
|
||||
for (ExecutionPayload<E>, Uint256, Option<BlobsBundle<E>>)
|
||||
{
|
||||
fn from(response: GetPayloadResponse<T>) -> Self {
|
||||
fn from(response: GetPayloadResponse<E>) -> Self {
|
||||
match response {
|
||||
GetPayloadResponse::Merge(inner) => (
|
||||
ExecutionPayload::Merge(inner.execution_payload),
|
||||
GetPayloadResponse::Bellatrix(inner) => (
|
||||
ExecutionPayload::Bellatrix(inner.execution_payload),
|
||||
inner.block_value,
|
||||
None,
|
||||
),
|
||||
@@ -463,6 +502,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),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -472,8 +516,8 @@ pub enum GetPayloadResponseType<E: EthSpec> {
|
||||
Blinded(GetPayloadResponse<E>),
|
||||
}
|
||||
|
||||
impl<T: EthSpec> GetPayloadResponse<T> {
|
||||
pub fn execution_payload_ref(&self) -> ExecutionPayloadRef<T> {
|
||||
impl<E: EthSpec> GetPayloadResponse<E> {
|
||||
pub fn execution_payload_ref(&self) -> ExecutionPayloadRef<E> {
|
||||
self.to_ref().into()
|
||||
}
|
||||
}
|
||||
@@ -490,14 +534,14 @@ impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
|
||||
header: ExecutionPayloadHeader<E>,
|
||||
) -> Result<ExecutionPayload<E>, String> {
|
||||
match header {
|
||||
ExecutionPayloadHeader::Merge(header) => {
|
||||
ExecutionPayloadHeader::Bellatrix(header) => {
|
||||
if self.withdrawals.is_some() {
|
||||
return Err(format!(
|
||||
"block {} is merge but payload body has withdrawals",
|
||||
header.block_hash
|
||||
));
|
||||
}
|
||||
Ok(ExecutionPayload::Merge(ExecutionPayloadMerge {
|
||||
Ok(ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
|
||||
parent_hash: header.parent_hash,
|
||||
fee_recipient: header.fee_recipient,
|
||||
state_root: header.state_root,
|
||||
@@ -563,7 +607,38 @@ 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,
|
||||
// TODO(electra)
|
||||
deposit_receipts: <_>::default(),
|
||||
withdrawal_requests: <_>::default(),
|
||||
}))
|
||||
} else {
|
||||
Err(format!(
|
||||
"block {} is post-capella but payload body doesn't have withdrawals",
|
||||
header.block_hash
|
||||
))
|
||||
}
|
||||
@@ -585,6 +660,7 @@ pub struct EngineCapabilities {
|
||||
pub get_payload_v1: bool,
|
||||
pub get_payload_v2: bool,
|
||||
pub get_payload_v3: bool,
|
||||
pub get_client_version_v1: bool,
|
||||
}
|
||||
|
||||
impl EngineCapabilities {
|
||||
@@ -623,7 +699,141 @@ impl EngineCapabilities {
|
||||
if self.get_payload_v3 {
|
||||
response.push(ENGINE_GET_PAYLOAD_V3);
|
||||
}
|
||||
if self.get_client_version_v1 {
|
||||
response.push(ENGINE_GET_CLIENT_VERSION_V1);
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ClientCode {
|
||||
Besu,
|
||||
EtherumJS,
|
||||
Erigon,
|
||||
GoEthereum,
|
||||
Grandine,
|
||||
Lighthouse,
|
||||
Lodestar,
|
||||
Nethermind,
|
||||
Nimbus,
|
||||
Teku,
|
||||
Prysm,
|
||||
Reth,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ClientCode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let s = match self {
|
||||
ClientCode::Besu => "BU",
|
||||
ClientCode::EtherumJS => "EJ",
|
||||
ClientCode::Erigon => "EG",
|
||||
ClientCode::GoEthereum => "GE",
|
||||
ClientCode::Grandine => "GR",
|
||||
ClientCode::Lighthouse => "LH",
|
||||
ClientCode::Lodestar => "LS",
|
||||
ClientCode::Nethermind => "NM",
|
||||
ClientCode::Nimbus => "NB",
|
||||
ClientCode::Teku => "TK",
|
||||
ClientCode::Prysm => "PM",
|
||||
ClientCode::Reth => "RH",
|
||||
ClientCode::Unknown(code) => code,
|
||||
};
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for ClientCode {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(code: String) -> Result<Self, Self::Error> {
|
||||
match code.as_str() {
|
||||
"BU" => Ok(Self::Besu),
|
||||
"EJ" => Ok(Self::EtherumJS),
|
||||
"EG" => Ok(Self::Erigon),
|
||||
"GE" => Ok(Self::GoEthereum),
|
||||
"GR" => Ok(Self::Grandine),
|
||||
"LH" => Ok(Self::Lighthouse),
|
||||
"LS" => Ok(Self::Lodestar),
|
||||
"NM" => Ok(Self::Nethermind),
|
||||
"NB" => Ok(Self::Nimbus),
|
||||
"TK" => Ok(Self::Teku),
|
||||
"PM" => Ok(Self::Prysm),
|
||||
"RH" => Ok(Self::Reth),
|
||||
string => {
|
||||
if string.len() == 2 {
|
||||
Ok(Self::Unknown(code))
|
||||
} else {
|
||||
Err(format!("Invalid client code: {}", code))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CommitPrefix(pub String);
|
||||
|
||||
impl TryFrom<String> for CommitPrefix {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
// Check if the input starts with '0x' and strip it if it does
|
||||
let commit_prefix = value.strip_prefix("0x").unwrap_or(&value);
|
||||
|
||||
// Ensure length is exactly 8 characters after '0x' removal
|
||||
if commit_prefix.len() != 8 {
|
||||
return Err(
|
||||
"Input must be exactly 8 characters long (excluding any '0x' prefix)".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure all characters are valid hex digits
|
||||
if commit_prefix.chars().all(|c| c.is_ascii_hexdigit()) {
|
||||
Ok(CommitPrefix(commit_prefix.to_lowercase()))
|
||||
} else {
|
||||
Err("Input must contain only hexadecimal characters".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CommitPrefix {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClientVersionV1 {
|
||||
pub code: ClientCode,
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub commit: CommitPrefix,
|
||||
}
|
||||
|
||||
impl ClientVersionV1 {
|
||||
pub fn calculate_graffiti(&self, lighthouse_commit_prefix: CommitPrefix) -> Graffiti {
|
||||
let graffiti_string = format!(
|
||||
"{}{}LH{}",
|
||||
self.code,
|
||||
self.commit
|
||||
.0
|
||||
.get(..4)
|
||||
.map_or_else(|| self.commit.0.as_str(), |s| s)
|
||||
.to_lowercase(),
|
||||
lighthouse_commit_prefix
|
||||
.0
|
||||
.get(..4)
|
||||
.unwrap_or("0000")
|
||||
.to_lowercase(),
|
||||
);
|
||||
let mut graffiti_bytes = [0u8; GRAFFITI_BYTES_LEN];
|
||||
let bytes_to_copy = std::cmp::min(graffiti_string.len(), GRAFFITI_BYTES_LEN);
|
||||
graffiti_bytes[..bytes_to_copy]
|
||||
.copy_from_slice(&graffiti_string.as_bytes()[..bytes_to_copy]);
|
||||
|
||||
Graffiti::from(graffiti_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
use super::*;
|
||||
use crate::auth::Auth;
|
||||
use crate::json_structures::*;
|
||||
use lazy_static::lazy_static;
|
||||
use lighthouse_version::{COMMIT_PREFIX, VERSION};
|
||||
use reqwest::header::CONTENT_TYPE;
|
||||
use sensitive_url::SensitiveUrl;
|
||||
use serde::de::DeserializeOwned;
|
||||
@@ -11,7 +13,6 @@ use std::collections::HashSet;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
use types::EthSpec;
|
||||
|
||||
pub use deposit_log::{DepositLog, Log};
|
||||
pub use reqwest::Client;
|
||||
@@ -52,6 +53,9 @@ pub const ENGINE_GET_PAYLOAD_BODIES_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
pub const ENGINE_EXCHANGE_CAPABILITIES: &str = "engine_exchangeCapabilities";
|
||||
pub const ENGINE_EXCHANGE_CAPABILITIES_TIMEOUT: Duration = Duration::from_secs(1);
|
||||
|
||||
pub const ENGINE_GET_CLIENT_VERSION_V1: &str = "engine_getClientVersionV1";
|
||||
pub const ENGINE_GET_CLIENT_VERSION_TIMEOUT: Duration = Duration::from_secs(1);
|
||||
|
||||
/// This error is returned during a `chainId` call by Geth.
|
||||
pub const EIP155_ERROR_STR: &str = "chain not synced beyond EIP-155 replay-protection fork block";
|
||||
/// This code is returned by all clients when a method is not supported
|
||||
@@ -70,24 +74,21 @@ pub static LIGHTHOUSE_CAPABILITIES: &[&str] = &[
|
||||
ENGINE_FORKCHOICE_UPDATED_V3,
|
||||
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1,
|
||||
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1,
|
||||
ENGINE_GET_CLIENT_VERSION_V1,
|
||||
];
|
||||
|
||||
/// This is necessary because a user might run a capella-enabled version of
|
||||
/// lighthouse before they update to a capella-enabled execution engine.
|
||||
// TODO (mark): rip this out once we are post-capella on mainnet
|
||||
pub static PRE_CAPELLA_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
|
||||
new_payload_v1: true,
|
||||
new_payload_v2: false,
|
||||
new_payload_v3: false,
|
||||
forkchoice_updated_v1: true,
|
||||
forkchoice_updated_v2: false,
|
||||
forkchoice_updated_v3: false,
|
||||
get_payload_bodies_by_hash_v1: false,
|
||||
get_payload_bodies_by_range_v1: false,
|
||||
get_payload_v1: true,
|
||||
get_payload_v2: false,
|
||||
get_payload_v3: false,
|
||||
};
|
||||
lazy_static! {
|
||||
/// We opt to initialize the JsonClientVersionV1 rather than the ClientVersionV1
|
||||
/// for two reasons:
|
||||
/// 1. This saves the overhead of converting into Json for every engine call
|
||||
/// 2. The Json version lacks error checking so we can avoid calling `unwrap()`
|
||||
pub static ref LIGHTHOUSE_JSON_CLIENT_VERSION: JsonClientVersionV1 = JsonClientVersionV1 {
|
||||
code: ClientCode::Lighthouse.to_string(),
|
||||
name: "Lighthouse".to_string(),
|
||||
version: VERSION.replace("Lighthouse/", ""),
|
||||
commit: COMMIT_PREFIX.to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Contains methods to convert arbitrary bytes to an ETH2 deposit contract object.
|
||||
pub mod deposit_log {
|
||||
@@ -237,7 +238,6 @@ pub mod deposit_methods {
|
||||
/// Represents an eth1 chain/network id.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum Eth1Id {
|
||||
Goerli,
|
||||
Mainnet,
|
||||
Custom(u64),
|
||||
}
|
||||
@@ -261,7 +261,6 @@ pub mod deposit_methods {
|
||||
fn into(self) -> u64 {
|
||||
match self {
|
||||
Eth1Id::Mainnet => 1,
|
||||
Eth1Id::Goerli => 5,
|
||||
Eth1Id::Custom(id) => id,
|
||||
}
|
||||
}
|
||||
@@ -272,7 +271,6 @@ pub mod deposit_methods {
|
||||
let into = |x: Eth1Id| -> u64 { x.into() };
|
||||
match id {
|
||||
id if id == into(Eth1Id::Mainnet) => Eth1Id::Mainnet,
|
||||
id if id == into(Eth1Id::Goerli) => Eth1Id::Goerli,
|
||||
id => Eth1Id::Custom(id),
|
||||
}
|
||||
}
|
||||
@@ -414,7 +412,7 @@ pub mod deposit_methods {
|
||||
.ok_or("Block number was not string")?,
|
||||
)?;
|
||||
|
||||
if number <= usize::max_value() as u64 {
|
||||
if number <= usize::MAX as u64 {
|
||||
Ok(Block {
|
||||
hash,
|
||||
timestamp,
|
||||
@@ -564,22 +562,21 @@ pub mod deposit_methods {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CapabilitiesCacheEntry {
|
||||
engine_capabilities: EngineCapabilities,
|
||||
fetch_time: Instant,
|
||||
pub struct CachedResponse<T: Clone> {
|
||||
pub data: T,
|
||||
pub fetch_time: Instant,
|
||||
}
|
||||
|
||||
impl CapabilitiesCacheEntry {
|
||||
pub fn new(engine_capabilities: EngineCapabilities) -> Self {
|
||||
impl<T: Clone> CachedResponse<T> {
|
||||
pub fn new(data: T) -> Self {
|
||||
Self {
|
||||
engine_capabilities,
|
||||
data,
|
||||
fetch_time: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn engine_capabilities(&self) -> EngineCapabilities {
|
||||
self.engine_capabilities
|
||||
pub fn data(&self) -> T {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
pub fn age(&self) -> Duration {
|
||||
@@ -596,7 +593,8 @@ pub struct HttpJsonRpc {
|
||||
pub client: Client,
|
||||
pub url: SensitiveUrl,
|
||||
pub execution_timeout_multiplier: u32,
|
||||
pub engine_capabilities_cache: Mutex<Option<CapabilitiesCacheEntry>>,
|
||||
pub engine_capabilities_cache: Mutex<Option<CachedResponse<EngineCapabilities>>>,
|
||||
pub engine_version_cache: Mutex<Option<CachedResponse<Vec<ClientVersionV1>>>>,
|
||||
auth: Option<Auth>,
|
||||
}
|
||||
|
||||
@@ -610,6 +608,7 @@ impl HttpJsonRpc {
|
||||
url,
|
||||
execution_timeout_multiplier: execution_timeout_multiplier.unwrap_or(1),
|
||||
engine_capabilities_cache: Mutex::new(None),
|
||||
engine_version_cache: Mutex::new(None),
|
||||
auth: None,
|
||||
})
|
||||
}
|
||||
@@ -624,6 +623,7 @@ impl HttpJsonRpc {
|
||||
url,
|
||||
execution_timeout_multiplier: execution_timeout_multiplier.unwrap_or(1),
|
||||
engine_capabilities_cache: Mutex::new(None),
|
||||
engine_version_cache: Mutex::new(None),
|
||||
auth: Some(auth),
|
||||
})
|
||||
}
|
||||
@@ -727,14 +727,14 @@ impl HttpJsonRpc {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_block_by_hash_with_txns<T: EthSpec>(
|
||||
pub async fn get_block_by_hash_with_txns<E: EthSpec>(
|
||||
&self,
|
||||
block_hash: ExecutionBlockHash,
|
||||
fork: ForkName,
|
||||
) -> Result<Option<ExecutionBlockWithTransactions<T>>, Error> {
|
||||
) -> Result<Option<ExecutionBlockWithTransactions<E>>, Error> {
|
||||
let params = json!([block_hash, true]);
|
||||
Ok(Some(match fork {
|
||||
ForkName::Merge => ExecutionBlockWithTransactions::Merge(
|
||||
ForkName::Bellatrix => ExecutionBlockWithTransactions::Bellatrix(
|
||||
self.rpc_request(
|
||||
ETH_GET_BLOCK_BY_HASH,
|
||||
params,
|
||||
@@ -758,6 +758,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 {:?}",
|
||||
@@ -767,9 +775,9 @@ impl HttpJsonRpc {
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn new_payload_v1<T: EthSpec>(
|
||||
pub async fn new_payload_v1<E: EthSpec>(
|
||||
&self,
|
||||
execution_payload: ExecutionPayload<T>,
|
||||
execution_payload: ExecutionPayload<E>,
|
||||
) -> Result<PayloadStatusV1, Error> {
|
||||
let params = json!([JsonExecutionPayload::from(execution_payload)]);
|
||||
|
||||
@@ -784,9 +792,9 @@ impl HttpJsonRpc {
|
||||
Ok(response.into())
|
||||
}
|
||||
|
||||
pub async fn new_payload_v2<T: EthSpec>(
|
||||
pub async fn new_payload_v2<E: EthSpec>(
|
||||
&self,
|
||||
execution_payload: ExecutionPayload<T>,
|
||||
execution_payload: ExecutionPayload<E>,
|
||||
) -> Result<PayloadStatusV1, Error> {
|
||||
let params = json!([JsonExecutionPayload::from(execution_payload)]);
|
||||
|
||||
@@ -801,9 +809,9 @@ impl HttpJsonRpc {
|
||||
Ok(response.into())
|
||||
}
|
||||
|
||||
pub async fn new_payload_v3<T: EthSpec>(
|
||||
pub async fn new_payload_v3_deneb<E: EthSpec>(
|
||||
&self,
|
||||
new_payload_request_deneb: NewPayloadRequestDeneb<'_, T>,
|
||||
new_payload_request_deneb: NewPayloadRequestDeneb<'_, E>,
|
||||
) -> Result<PayloadStatusV1, Error> {
|
||||
let params = json!([
|
||||
JsonExecutionPayload::V3(new_payload_request_deneb.execution_payload.clone().into()),
|
||||
@@ -822,13 +830,34 @@ impl HttpJsonRpc {
|
||||
Ok(response.into())
|
||||
}
|
||||
|
||||
pub async fn get_payload_v1<T: EthSpec>(
|
||||
pub async fn new_payload_v3_electra<E: EthSpec>(
|
||||
&self,
|
||||
new_payload_request_electra: NewPayloadRequestElectra<'_, E>,
|
||||
) -> 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<E: EthSpec>(
|
||||
&self,
|
||||
payload_id: PayloadId,
|
||||
) -> Result<GetPayloadResponse<T>, Error> {
|
||||
) -> Result<GetPayloadResponse<E>, Error> {
|
||||
let params = json!([JsonPayloadIdRequest::from(payload_id)]);
|
||||
|
||||
let payload_v1: JsonExecutionPayloadV1<T> = self
|
||||
let payload_v1: JsonExecutionPayloadV1<E> = self
|
||||
.rpc_request(
|
||||
ENGINE_GET_PAYLOAD_V1,
|
||||
params,
|
||||
@@ -836,7 +865,7 @@ impl HttpJsonRpc {
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(GetPayloadResponse::Merge(GetPayloadResponseMerge {
|
||||
Ok(GetPayloadResponse::Bellatrix(GetPayloadResponseBellatrix {
|
||||
execution_payload: payload_v1.into(),
|
||||
// Set the V1 payload values from the EE to be zero. This simulates
|
||||
// the pre-block-value functionality of always choosing the builder
|
||||
@@ -845,16 +874,16 @@ impl HttpJsonRpc {
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn get_payload_v2<T: EthSpec>(
|
||||
pub async fn get_payload_v2<E: EthSpec>(
|
||||
&self,
|
||||
fork_name: ForkName,
|
||||
payload_id: PayloadId,
|
||||
) -> Result<GetPayloadResponse<T>, Error> {
|
||||
) -> Result<GetPayloadResponse<E>, Error> {
|
||||
let params = json!([JsonPayloadIdRequest::from(payload_id)]);
|
||||
|
||||
match fork_name {
|
||||
ForkName::Merge => {
|
||||
let response: JsonGetPayloadResponseV1<T> = self
|
||||
ForkName::Bellatrix => {
|
||||
let response: JsonGetPayloadResponseV1<E> = self
|
||||
.rpc_request(
|
||||
ENGINE_GET_PAYLOAD_V2,
|
||||
params,
|
||||
@@ -864,7 +893,7 @@ impl HttpJsonRpc {
|
||||
Ok(JsonGetPayloadResponse::V1(response).into())
|
||||
}
|
||||
ForkName::Capella => {
|
||||
let response: JsonGetPayloadResponseV2<T> = self
|
||||
let response: JsonGetPayloadResponseV2<E> = self
|
||||
.rpc_request(
|
||||
ENGINE_GET_PAYLOAD_V2,
|
||||
params,
|
||||
@@ -873,22 +902,22 @@ 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)),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_payload_v3<T: EthSpec>(
|
||||
pub async fn get_payload_v3<E: EthSpec>(
|
||||
&self,
|
||||
fork_name: ForkName,
|
||||
payload_id: PayloadId,
|
||||
) -> Result<GetPayloadResponse<T>, Error> {
|
||||
) -> Result<GetPayloadResponse<E>, Error> {
|
||||
let params = json!([JsonPayloadIdRequest::from(payload_id)]);
|
||||
|
||||
match fork_name {
|
||||
ForkName::Deneb => {
|
||||
let response: JsonGetPayloadResponseV3<T> = self
|
||||
let response: JsonGetPayloadResponseV3<E> = self
|
||||
.rpc_request(
|
||||
ENGINE_GET_PAYLOAD_V3,
|
||||
params,
|
||||
@@ -897,7 +926,17 @@ impl HttpJsonRpc {
|
||||
.await?;
|
||||
Ok(JsonGetPayloadResponse::V3(response).into())
|
||||
}
|
||||
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => Err(
|
||||
ForkName::Electra => {
|
||||
let response: JsonGetPayloadResponseV4<E> = 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::Bellatrix | ForkName::Capella => Err(
|
||||
Error::UnsupportedForkVariant(format!("called get_payload_v3 with {}", fork_name)),
|
||||
),
|
||||
}
|
||||
@@ -1013,38 +1052,30 @@ impl HttpJsonRpc {
|
||||
pub async fn exchange_capabilities(&self) -> Result<EngineCapabilities, Error> {
|
||||
let params = json!([LIGHTHOUSE_CAPABILITIES]);
|
||||
|
||||
let response: Result<HashSet<String>, _> = self
|
||||
let capabilities: HashSet<String> = self
|
||||
.rpc_request(
|
||||
ENGINE_EXCHANGE_CAPABILITIES,
|
||||
params,
|
||||
ENGINE_EXCHANGE_CAPABILITIES_TIMEOUT * self.execution_timeout_multiplier,
|
||||
)
|
||||
.await;
|
||||
.await?;
|
||||
|
||||
match response {
|
||||
// TODO (mark): rip this out once we are post capella on mainnet
|
||||
Err(error) => match error {
|
||||
Error::ServerMessage { code, message: _ } if code == METHOD_NOT_FOUND_CODE => {
|
||||
Ok(PRE_CAPELLA_ENGINE_CAPABILITIES)
|
||||
}
|
||||
_ => Err(error),
|
||||
},
|
||||
Ok(capabilities) => Ok(EngineCapabilities {
|
||||
new_payload_v1: capabilities.contains(ENGINE_NEW_PAYLOAD_V1),
|
||||
new_payload_v2: capabilities.contains(ENGINE_NEW_PAYLOAD_V2),
|
||||
new_payload_v3: capabilities.contains(ENGINE_NEW_PAYLOAD_V3),
|
||||
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),
|
||||
get_payload_bodies_by_hash_v1: capabilities
|
||||
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1),
|
||||
get_payload_bodies_by_range_v1: capabilities
|
||||
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1),
|
||||
get_payload_v1: capabilities.contains(ENGINE_GET_PAYLOAD_V1),
|
||||
get_payload_v2: capabilities.contains(ENGINE_GET_PAYLOAD_V2),
|
||||
get_payload_v3: capabilities.contains(ENGINE_GET_PAYLOAD_V3),
|
||||
}),
|
||||
}
|
||||
Ok(EngineCapabilities {
|
||||
new_payload_v1: capabilities.contains(ENGINE_NEW_PAYLOAD_V1),
|
||||
new_payload_v2: capabilities.contains(ENGINE_NEW_PAYLOAD_V2),
|
||||
new_payload_v3: capabilities.contains(ENGINE_NEW_PAYLOAD_V3),
|
||||
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),
|
||||
get_payload_bodies_by_hash_v1: capabilities
|
||||
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1),
|
||||
get_payload_bodies_by_range_v1: capabilities
|
||||
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1),
|
||||
get_payload_v1: capabilities.contains(ENGINE_GET_PAYLOAD_V1),
|
||||
get_payload_v2: capabilities.contains(ENGINE_GET_PAYLOAD_V2),
|
||||
get_payload_v3: capabilities.contains(ENGINE_GET_PAYLOAD_V3),
|
||||
get_client_version_v1: capabilities.contains(ENGINE_GET_CLIENT_VERSION_V1),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn clear_exchange_capabilties_cache(&self) {
|
||||
@@ -1066,24 +1097,87 @@ impl HttpJsonRpc {
|
||||
) -> Result<EngineCapabilities, Error> {
|
||||
let mut lock = self.engine_capabilities_cache.lock().await;
|
||||
|
||||
if let Some(lock) = lock.as_ref().filter(|entry| !entry.older_than(age_limit)) {
|
||||
Ok(lock.engine_capabilities())
|
||||
if let Some(lock) = lock
|
||||
.as_ref()
|
||||
.filter(|cached_response| !cached_response.older_than(age_limit))
|
||||
{
|
||||
Ok(lock.data())
|
||||
} else {
|
||||
let engine_capabilities = self.exchange_capabilities().await?;
|
||||
*lock = Some(CapabilitiesCacheEntry::new(engine_capabilities));
|
||||
*lock = Some(CachedResponse::new(engine_capabilities));
|
||||
Ok(engine_capabilities)
|
||||
}
|
||||
}
|
||||
|
||||
/// This method fetches the response from the engine without checking
|
||||
/// any caches or storing the result in the cache. It is better to use
|
||||
/// `get_engine_version(Some(Duration::ZERO))` if you want to force
|
||||
/// fetching from the EE as this will cache the result.
|
||||
pub async fn get_client_version_v1(&self) -> Result<Vec<ClientVersionV1>, Error> {
|
||||
let params = json!([*LIGHTHOUSE_JSON_CLIENT_VERSION]);
|
||||
|
||||
let response: Vec<JsonClientVersionV1> = self
|
||||
.rpc_request(
|
||||
ENGINE_GET_CLIENT_VERSION_V1,
|
||||
params,
|
||||
ENGINE_GET_CLIENT_VERSION_TIMEOUT * self.execution_timeout_multiplier,
|
||||
)
|
||||
.await?;
|
||||
|
||||
response
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(Error::InvalidClientVersion)
|
||||
}
|
||||
|
||||
pub async fn clear_engine_version_cache(&self) {
|
||||
*self.engine_version_cache.lock().await = None;
|
||||
}
|
||||
|
||||
/// Returns the execution engine version resulting from a call to
|
||||
/// engine_getClientVersionV1. If the version cache is not populated, or if it
|
||||
/// is populated with a cached result of age >= `age_limit`, this method will
|
||||
/// fetch the result from the execution engine and populate the cache before
|
||||
/// returning it. Otherwise it will return the cached result from an earlier
|
||||
/// call.
|
||||
///
|
||||
/// Set `age_limit` to `None` to always return the cached result
|
||||
/// Set `age_limit` to `Some(Duration::ZERO)` to force fetching from EE
|
||||
pub async fn get_engine_version(
|
||||
&self,
|
||||
age_limit: Option<Duration>,
|
||||
) -> Result<Vec<ClientVersionV1>, Error> {
|
||||
// check engine capabilities first (avoids holding two locks at once)
|
||||
let engine_capabilities = self.get_engine_capabilities(None).await?;
|
||||
if !engine_capabilities.get_client_version_v1 {
|
||||
// We choose an empty vec to denote that this method is not
|
||||
// supported instead of an error since this method is optional
|
||||
// & we don't want to log a warning and concern the user
|
||||
return Ok(vec![]);
|
||||
}
|
||||
let mut lock = self.engine_version_cache.lock().await;
|
||||
if let Some(lock) = lock
|
||||
.as_ref()
|
||||
.filter(|cached_response| !cached_response.older_than(age_limit))
|
||||
{
|
||||
Ok(lock.data())
|
||||
} else {
|
||||
let engine_version = self.get_client_version_v1().await?;
|
||||
*lock = Some(CachedResponse::new(engine_version.clone()));
|
||||
Ok(engine_version)
|
||||
}
|
||||
}
|
||||
|
||||
// automatically selects the latest version of
|
||||
// new_payload that the execution engine supports
|
||||
pub async fn new_payload<T: EthSpec>(
|
||||
pub async fn new_payload<E: EthSpec>(
|
||||
&self,
|
||||
new_payload_request: NewPayloadRequest<'_, T>,
|
||||
new_payload_request: NewPayloadRequest<'_, E>,
|
||||
) -> Result<PayloadStatusV1, Error> {
|
||||
let engine_capabilities = self.get_engine_capabilities(None).await?;
|
||||
match new_payload_request {
|
||||
NewPayloadRequest::Merge(_) | NewPayloadRequest::Capella(_) => {
|
||||
NewPayloadRequest::Bellatrix(_) | NewPayloadRequest::Capella(_) => {
|
||||
if engine_capabilities.new_payload_v2 {
|
||||
self.new_payload_v2(new_payload_request.into_execution_payload())
|
||||
.await
|
||||
@@ -1096,7 +1190,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"))
|
||||
}
|
||||
@@ -1106,14 +1208,14 @@ impl HttpJsonRpc {
|
||||
|
||||
// automatically selects the latest version of
|
||||
// get_payload that the execution engine supports
|
||||
pub async fn get_payload<T: EthSpec>(
|
||||
pub async fn get_payload<E: EthSpec>(
|
||||
&self,
|
||||
fork_name: ForkName,
|
||||
payload_id: PayloadId,
|
||||
) -> Result<GetPayloadResponse<T>, Error> {
|
||||
) -> Result<GetPayloadResponse<E>, Error> {
|
||||
let engine_capabilities = self.get_engine_capabilities(None).await?;
|
||||
match fork_name {
|
||||
ForkName::Merge | ForkName::Capella => {
|
||||
ForkName::Bellatrix | ForkName::Capella => {
|
||||
if engine_capabilities.get_payload_v2 {
|
||||
self.get_payload_v2(fork_name, payload_id).await
|
||||
} else if engine_capabilities.new_payload_v1 {
|
||||
@@ -1122,7 +1224,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 {
|
||||
@@ -1191,7 +1293,7 @@ mod test {
|
||||
use std::future::Future;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use types::{ExecutionPayloadMerge, MainnetEthSpec, Transactions, Unsigned, VariableList};
|
||||
use types::{MainnetEthSpec, Unsigned};
|
||||
|
||||
struct Tester {
|
||||
server: MockServer<MainnetEthSpec>,
|
||||
@@ -1554,8 +1656,8 @@ mod test {
|
||||
.assert_request_equals(
|
||||
|client| async move {
|
||||
let _ = client
|
||||
.new_payload_v1::<MainnetEthSpec>(ExecutionPayload::Merge(
|
||||
ExecutionPayloadMerge {
|
||||
.new_payload_v1::<MainnetEthSpec>(ExecutionPayload::Bellatrix(
|
||||
ExecutionPayloadBellatrix {
|
||||
parent_hash: ExecutionBlockHash::repeat_byte(0),
|
||||
fee_recipient: Address::repeat_byte(1),
|
||||
state_root: Hash256::repeat_byte(1),
|
||||
@@ -1601,8 +1703,8 @@ mod test {
|
||||
Tester::new(false)
|
||||
.assert_auth_failure(|client| async move {
|
||||
client
|
||||
.new_payload_v1::<MainnetEthSpec>(ExecutionPayload::Merge(
|
||||
ExecutionPayloadMerge {
|
||||
.new_payload_v1::<MainnetEthSpec>(ExecutionPayload::Bellatrix(
|
||||
ExecutionPayloadBellatrix {
|
||||
parent_hash: ExecutionBlockHash::repeat_byte(0),
|
||||
fee_recipient: Address::repeat_byte(1),
|
||||
state_root: Hash256::repeat_byte(1),
|
||||
@@ -1802,7 +1904,7 @@ mod test {
|
||||
"extraData":"0x",
|
||||
"baseFeePerGas":"0x7",
|
||||
"blockHash":"0x6359b8381a370e2f54072a5784ddd78b6ed024991558c511d4452eb4f6ac898c",
|
||||
"transactions":[]
|
||||
"transactions":[],
|
||||
}
|
||||
})],
|
||||
|client| async move {
|
||||
@@ -1812,7 +1914,7 @@ mod test {
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
let expected = ExecutionPayload::Merge(ExecutionPayloadMerge {
|
||||
let expected = ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
|
||||
parent_hash: ExecutionBlockHash::from_str("0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a").unwrap(),
|
||||
fee_recipient: Address::from_str("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(),
|
||||
state_root: Hash256::from_str("0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45").unwrap(),
|
||||
@@ -1826,7 +1928,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);
|
||||
@@ -1837,7 +1939,7 @@ mod test {
|
||||
// engine_newPayloadV1 REQUEST validation
|
||||
|client| async move {
|
||||
let _ = client
|
||||
.new_payload_v1::<MainnetEthSpec>(ExecutionPayload::Merge(ExecutionPayloadMerge{
|
||||
.new_payload_v1::<MainnetEthSpec>(ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix{
|
||||
parent_hash: ExecutionBlockHash::from_str("0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a").unwrap(),
|
||||
fee_recipient: Address::from_str("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(),
|
||||
state_root: Hash256::from_str("0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45").unwrap(),
|
||||
@@ -1873,7 +1975,7 @@ mod test {
|
||||
"extraData":"0x",
|
||||
"baseFeePerGas":"0x7",
|
||||
"blockHash":"0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858",
|
||||
"transactions":[]
|
||||
"transactions":[],
|
||||
}],
|
||||
})
|
||||
)
|
||||
@@ -1891,7 +1993,7 @@ mod test {
|
||||
})],
|
||||
|client| async move {
|
||||
let response = client
|
||||
.new_payload_v1::<MainnetEthSpec>(ExecutionPayload::Merge(ExecutionPayloadMerge::default()))
|
||||
.new_payload_v1::<MainnetEthSpec>(ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix::default()))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -5,10 +5,7 @@ use strum::EnumString;
|
||||
use superstruct::superstruct;
|
||||
use types::beacon_block_body::KzgCommitments;
|
||||
use types::blob_sidecar::BlobsList;
|
||||
use types::{
|
||||
EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb,
|
||||
ExecutionPayloadMerge, FixedVector, Transactions, Unsigned, VariableList, Withdrawal,
|
||||
};
|
||||
use types::{FixedVector, Unsigned};
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -64,23 +61,23 @@ 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"),
|
||||
serde(bound = "E: EthSpec", rename_all = "camelCase"),
|
||||
),
|
||||
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
|
||||
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
|
||||
)]
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(bound = "T: EthSpec", rename_all = "camelCase", untagged)]
|
||||
pub struct JsonExecutionPayload<T: EthSpec> {
|
||||
#[serde(bound = "E: EthSpec", rename_all = "camelCase", untagged)]
|
||||
pub struct JsonExecutionPayload<E: EthSpec> {
|
||||
pub parent_hash: ExecutionBlockHash,
|
||||
pub fee_recipient: Address,
|
||||
pub state_root: Hash256,
|
||||
pub receipts_root: Hash256,
|
||||
#[serde(with = "serde_logs_bloom")]
|
||||
pub logs_bloom: FixedVector<u8, T::BytesPerLogsBloom>,
|
||||
pub logs_bloom: FixedVector<u8, E::BytesPerLogsBloom>,
|
||||
pub prev_randao: Hash256,
|
||||
#[serde(with = "serde_utils::u64_hex_be")]
|
||||
pub block_number: u64,
|
||||
@@ -91,24 +88,24 @@ pub struct JsonExecutionPayload<T: EthSpec> {
|
||||
#[serde(with = "serde_utils::u64_hex_be")]
|
||||
pub timestamp: u64,
|
||||
#[serde(with = "ssz_types::serde_utils::hex_var_list")]
|
||||
pub extra_data: VariableList<u8, T::MaxExtraDataBytes>,
|
||||
pub extra_data: VariableList<u8, E::MaxExtraDataBytes>,
|
||||
#[serde(with = "serde_utils::u256_hex_be")]
|
||||
pub base_fee_per_gas: Uint256,
|
||||
pub block_hash: ExecutionBlockHash,
|
||||
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
|
||||
pub transactions: Transactions<T>,
|
||||
#[superstruct(only(V2, V3))]
|
||||
pub withdrawals: VariableList<JsonWithdrawal, T::MaxWithdrawalsPerPayload>,
|
||||
#[superstruct(only(V3))]
|
||||
pub transactions: Transactions<E>,
|
||||
#[superstruct(only(V2, V3, V4))]
|
||||
pub withdrawals: VariableList<JsonWithdrawal, E::MaxWithdrawalsPerPayload>,
|
||||
#[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,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<ExecutionPayloadMerge<T>> for JsonExecutionPayloadV1<T> {
|
||||
fn from(payload: ExecutionPayloadMerge<T>) -> Self {
|
||||
impl<E: EthSpec> From<ExecutionPayloadBellatrix<E>> for JsonExecutionPayloadV1<E> {
|
||||
fn from(payload: ExecutionPayloadBellatrix<E>) -> Self {
|
||||
JsonExecutionPayloadV1 {
|
||||
parent_hash: payload.parent_hash,
|
||||
fee_recipient: payload.fee_recipient,
|
||||
@@ -127,8 +124,8 @@ impl<T: EthSpec> From<ExecutionPayloadMerge<T>> for JsonExecutionPayloadV1<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: EthSpec> From<ExecutionPayloadCapella<T>> for JsonExecutionPayloadV2<T> {
|
||||
fn from(payload: ExecutionPayloadCapella<T>) -> Self {
|
||||
impl<E: EthSpec> From<ExecutionPayloadCapella<E>> for JsonExecutionPayloadV2<E> {
|
||||
fn from(payload: ExecutionPayloadCapella<E>) -> Self {
|
||||
JsonExecutionPayloadV2 {
|
||||
parent_hash: payload.parent_hash,
|
||||
fee_recipient: payload.fee_recipient,
|
||||
@@ -153,8 +150,8 @@ impl<T: EthSpec> From<ExecutionPayloadCapella<T>> for JsonExecutionPayloadV2<T>
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: EthSpec> From<ExecutionPayloadDeneb<T>> for JsonExecutionPayloadV3<T> {
|
||||
fn from(payload: ExecutionPayloadDeneb<T>) -> Self {
|
||||
impl<E: EthSpec> From<ExecutionPayloadDeneb<E>> for JsonExecutionPayloadV3<E> {
|
||||
fn from(payload: ExecutionPayloadDeneb<E>) -> Self {
|
||||
JsonExecutionPayloadV3 {
|
||||
parent_hash: payload.parent_hash,
|
||||
fee_recipient: payload.fee_recipient,
|
||||
@@ -182,19 +179,49 @@ impl<T: EthSpec> From<ExecutionPayloadDeneb<T>> for JsonExecutionPayloadV3<T> {
|
||||
}
|
||||
}
|
||||
|
||||
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()),
|
||||
impl<E: EthSpec> From<ExecutionPayloadElectra<E>> for JsonExecutionPayloadV4<E> {
|
||||
fn from(payload: ExecutionPayloadElectra<E>) -> 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<JsonExecutionPayloadV1<T>> for ExecutionPayloadMerge<T> {
|
||||
fn from(payload: JsonExecutionPayloadV1<T>) -> Self {
|
||||
ExecutionPayloadMerge {
|
||||
impl<E: EthSpec> From<ExecutionPayload<E>> for JsonExecutionPayload<E> {
|
||||
fn from(execution_payload: ExecutionPayload<E>) -> Self {
|
||||
match execution_payload {
|
||||
ExecutionPayload::Bellatrix(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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> From<JsonExecutionPayloadV1<E>> for ExecutionPayloadBellatrix<E> {
|
||||
fn from(payload: JsonExecutionPayloadV1<E>) -> Self {
|
||||
ExecutionPayloadBellatrix {
|
||||
parent_hash: payload.parent_hash,
|
||||
fee_recipient: payload.fee_recipient,
|
||||
state_root: payload.state_root,
|
||||
@@ -212,8 +239,8 @@ impl<T: EthSpec> From<JsonExecutionPayloadV1<T>> for ExecutionPayloadMerge<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: EthSpec> From<JsonExecutionPayloadV2<T>> for ExecutionPayloadCapella<T> {
|
||||
fn from(payload: JsonExecutionPayloadV2<T>) -> Self {
|
||||
impl<E: EthSpec> From<JsonExecutionPayloadV2<E>> for ExecutionPayloadCapella<E> {
|
||||
fn from(payload: JsonExecutionPayloadV2<E>) -> Self {
|
||||
ExecutionPayloadCapella {
|
||||
parent_hash: payload.parent_hash,
|
||||
fee_recipient: payload.fee_recipient,
|
||||
@@ -238,8 +265,9 @@ impl<T: EthSpec> From<JsonExecutionPayloadV2<T>> for ExecutionPayloadCapella<T>
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: EthSpec> From<JsonExecutionPayloadV3<T>> for ExecutionPayloadDeneb<T> {
|
||||
fn from(payload: JsonExecutionPayloadV3<T>) -> Self {
|
||||
|
||||
impl<E: EthSpec> From<JsonExecutionPayloadV3<E>> for ExecutionPayloadDeneb<E> {
|
||||
fn from(payload: JsonExecutionPayloadV3<E>) -> Self {
|
||||
ExecutionPayloadDeneb {
|
||||
parent_hash: payload.parent_hash,
|
||||
fee_recipient: payload.fee_recipient,
|
||||
@@ -267,47 +295,82 @@ impl<T: EthSpec> From<JsonExecutionPayloadV3<T>> for ExecutionPayloadDeneb<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<JsonExecutionPayload<T>> for ExecutionPayload<T> {
|
||||
fn from(json_execution_payload: JsonExecutionPayload<T>) -> Self {
|
||||
impl<E: EthSpec> From<JsonExecutionPayloadV4<E>> for ExecutionPayloadElectra<E> {
|
||||
fn from(payload: JsonExecutionPayloadV4<E>) -> 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,
|
||||
// TODO(electra)
|
||||
deposit_receipts: Default::default(),
|
||||
withdrawal_requests: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> From<JsonExecutionPayload<E>> for ExecutionPayload<E> {
|
||||
fn from(json_execution_payload: JsonExecutionPayload<E>) -> Self {
|
||||
match json_execution_payload {
|
||||
JsonExecutionPayload::V1(payload) => ExecutionPayload::Merge(payload.into()),
|
||||
JsonExecutionPayload::V1(payload) => ExecutionPayload::Bellatrix(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")
|
||||
serde(bound = "E: EthSpec", rename_all = "camelCase")
|
||||
),
|
||||
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
|
||||
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
|
||||
)]
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub struct JsonGetPayloadResponse<T: EthSpec> {
|
||||
pub struct JsonGetPayloadResponse<E: EthSpec> {
|
||||
#[superstruct(only(V1), partial_getter(rename = "execution_payload_v1"))]
|
||||
pub execution_payload: JsonExecutionPayloadV1<T>,
|
||||
pub execution_payload: JsonExecutionPayloadV1<E>,
|
||||
#[superstruct(only(V2), partial_getter(rename = "execution_payload_v2"))]
|
||||
pub execution_payload: JsonExecutionPayloadV2<T>,
|
||||
pub execution_payload: JsonExecutionPayloadV2<E>,
|
||||
#[superstruct(only(V3), partial_getter(rename = "execution_payload_v3"))]
|
||||
pub execution_payload: JsonExecutionPayloadV3<T>,
|
||||
pub execution_payload: JsonExecutionPayloadV3<E>,
|
||||
#[superstruct(only(V4), partial_getter(rename = "execution_payload_v4"))]
|
||||
pub execution_payload: JsonExecutionPayloadV4<E>,
|
||||
#[serde(with = "serde_utils::u256_hex_be")]
|
||||
pub block_value: Uint256,
|
||||
#[superstruct(only(V3))]
|
||||
pub blobs_bundle: JsonBlobsBundleV1<T>,
|
||||
#[superstruct(only(V3))]
|
||||
#[superstruct(only(V3, V4))]
|
||||
pub blobs_bundle: JsonBlobsBundleV1<E>,
|
||||
#[superstruct(only(V3, V4))]
|
||||
pub should_override_builder: bool,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<JsonGetPayloadResponse<T>> for GetPayloadResponse<T> {
|
||||
fn from(json_get_payload_response: JsonGetPayloadResponse<T>) -> Self {
|
||||
impl<E: EthSpec> From<JsonGetPayloadResponse<E>> for GetPayloadResponse<E> {
|
||||
fn from(json_get_payload_response: JsonGetPayloadResponse<E>) -> Self {
|
||||
match json_get_payload_response {
|
||||
JsonGetPayloadResponse::V1(response) => {
|
||||
GetPayloadResponse::Merge(GetPayloadResponseMerge {
|
||||
GetPayloadResponse::Bellatrix(GetPayloadResponseBellatrix {
|
||||
execution_payload: response.execution_payload.into(),
|
||||
block_value: response.block_value,
|
||||
})
|
||||
@@ -326,6 +389,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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -698,3 +769,39 @@ pub mod serde_logs_bloom {
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid logs bloom: {:?}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct JsonClientVersionV1 {
|
||||
pub code: String,
|
||||
// This `default` is required until Geth v1.13.x is no longer supported on mainnet.
|
||||
// See: https://github.com/ethereum/go-ethereum/pull/29351
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub commit: String,
|
||||
}
|
||||
|
||||
impl From<ClientVersionV1> for JsonClientVersionV1 {
|
||||
fn from(client_version: ClientVersionV1) -> Self {
|
||||
Self {
|
||||
code: client_version.code.to_string(),
|
||||
name: client_version.name,
|
||||
version: client_version.version,
|
||||
commit: client_version.commit.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<JsonClientVersionV1> for ClientVersionV1 {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(json: JsonClientVersionV1) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
code: json.code.try_into()?,
|
||||
name: json.name,
|
||||
version: json.version,
|
||||
commit: json.commit.try_into()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,13 @@ use types::{
|
||||
BeaconBlockRef, BeaconStateError, EthSpec, ExecutionBlockHash, ExecutionPayload,
|
||||
ExecutionPayloadRef, Hash256, VersionedHash,
|
||||
};
|
||||
use types::{ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge};
|
||||
use types::{
|
||||
ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb,
|
||||
ExecutionPayloadElectra,
|
||||
};
|
||||
|
||||
#[superstruct(
|
||||
variants(Merge, Capella, Deneb),
|
||||
variants(Bellatrix, Capella, Deneb, Electra),
|
||||
variant_attributes(derive(Clone, Debug, PartialEq),),
|
||||
map_into(ExecutionPayload),
|
||||
map_ref_into(ExecutionPayloadRef),
|
||||
@@ -25,56 +28,68 @@ use types::{ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerg
|
||||
)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct NewPayloadRequest<'block, E: EthSpec> {
|
||||
#[superstruct(only(Merge), partial_getter(rename = "execution_payload_merge"))]
|
||||
pub execution_payload: &'block ExecutionPayloadMerge<E>,
|
||||
#[superstruct(
|
||||
only(Bellatrix),
|
||||
partial_getter(rename = "execution_payload_bellatrix")
|
||||
)]
|
||||
pub execution_payload: &'block ExecutionPayloadBellatrix<E>,
|
||||
#[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))]
|
||||
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,
|
||||
}
|
||||
|
||||
impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
pub fn parent_hash(&self) -> ExecutionBlockHash {
|
||||
match self {
|
||||
Self::Merge(payload) => payload.execution_payload.parent_hash,
|
||||
Self::Bellatrix(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,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block_hash(&self) -> ExecutionBlockHash {
|
||||
match self {
|
||||
Self::Merge(payload) => payload.execution_payload.block_hash,
|
||||
Self::Bellatrix(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,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block_number(&self) -> u64 {
|
||||
match self {
|
||||
Self::Merge(payload) => payload.execution_payload.block_number,
|
||||
Self::Bellatrix(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,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execution_payload_ref(&self) -> ExecutionPayloadRef<'block, E> {
|
||||
match self {
|
||||
Self::Merge(request) => ExecutionPayloadRef::Merge(request.execution_payload),
|
||||
Self::Bellatrix(request) => ExecutionPayloadRef::Bellatrix(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),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_execution_payload(self) -> ExecutionPayload<E> {
|
||||
match self {
|
||||
Self::Merge(request) => ExecutionPayload::Merge(request.execution_payload.clone()),
|
||||
Self::Bellatrix(request) => {
|
||||
ExecutionPayload::Bellatrix(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()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +101,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
///
|
||||
/// https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.2/specs/deneb/beacon-chain.md#modified-verify_and_notify_new_payload
|
||||
pub fn perform_optimistic_sync_verifications(&self) -> Result<(), Error> {
|
||||
self.verfiy_payload_block_hash()?;
|
||||
self.verify_payload_block_hash()?;
|
||||
self.verify_versioned_hashes()?;
|
||||
|
||||
Ok(())
|
||||
@@ -98,7 +113,7 @@ impl<'block, E: EthSpec> NewPayloadRequest<'block, E> {
|
||||
///
|
||||
/// Equivalent to `is_valid_block_hash` in the spec:
|
||||
/// https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.2/specs/deneb/beacon-chain.md#is_valid_block_hash
|
||||
pub fn verfiy_payload_block_hash(&self) -> Result<(), Error> {
|
||||
pub fn verify_payload_block_hash(&self) -> Result<(), Error> {
|
||||
let payload = self.execution_payload_ref();
|
||||
let parent_beacon_block_root = self.parent_beacon_block_root().ok().cloned();
|
||||
|
||||
@@ -141,9 +156,11 @@ impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<'a, E>
|
||||
BeaconBlockRef::Base(_) | BeaconBlockRef::Altair(_) => {
|
||||
Err(Self::Error::IncorrectStateVariant)
|
||||
}
|
||||
BeaconBlockRef::Merge(block_ref) => Ok(Self::Merge(NewPayloadRequestMerge {
|
||||
execution_payload: &block_ref.body.execution_payload.execution_payload,
|
||||
})),
|
||||
BeaconBlockRef::Bellatrix(block_ref) => {
|
||||
Ok(Self::Bellatrix(NewPayloadRequestBellatrix {
|
||||
execution_payload: &block_ref.body.execution_payload.execution_payload,
|
||||
}))
|
||||
}
|
||||
BeaconBlockRef::Capella(block_ref) => Ok(Self::Capella(NewPayloadRequestCapella {
|
||||
execution_payload: &block_ref.body.execution_payload.execution_payload,
|
||||
})),
|
||||
@@ -157,6 +174,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,
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,13 +193,16 @@ impl<'a, E: EthSpec> TryFrom<ExecutionPayloadRef<'a, E>> for NewPayloadRequest<'
|
||||
|
||||
fn try_from(payload: ExecutionPayloadRef<'a, E>) -> Result<Self, Self::Error> {
|
||||
match payload {
|
||||
ExecutionPayloadRef::Merge(payload) => Ok(Self::Merge(NewPayloadRequestMerge {
|
||||
execution_payload: payload,
|
||||
})),
|
||||
ExecutionPayloadRef::Bellatrix(payload) => {
|
||||
Ok(Self::Bellatrix(NewPayloadRequestBellatrix {
|
||||
execution_payload: payload,
|
||||
}))
|
||||
}
|
||||
ExecutionPayloadRef::Capella(payload) => Ok(Self::Capella(NewPayloadRequestCapella {
|
||||
execution_payload: payload,
|
||||
})),
|
||||
ExecutionPayloadRef::Deneb(_) => Err(Self::Error::IncorrectStateVariant),
|
||||
ExecutionPayloadRef::Electra(_) => Err(Self::Error::IncorrectStateVariant),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::engine_api::{
|
||||
EngineCapabilities, Error as EngineApiError, ForkchoiceUpdatedResponse, PayloadAttributes,
|
||||
PayloadId,
|
||||
};
|
||||
use crate::HttpJsonRpc;
|
||||
use crate::{ClientVersionV1, HttpJsonRpc};
|
||||
use lru::LruCache;
|
||||
use slog::{debug, error, info, warn, Logger};
|
||||
use std::future::Future;
|
||||
@@ -21,7 +21,7 @@ use types::ExecutionBlockHash;
|
||||
///
|
||||
/// Since the size of each value is small (~800 bytes) a large number is used for safety.
|
||||
const PAYLOAD_ID_LRU_CACHE_SIZE: NonZeroUsize = new_non_zero_usize(512);
|
||||
const CACHED_ENGINE_CAPABILITIES_AGE_LIMIT: Duration = Duration::from_secs(900); // 15 minutes
|
||||
const CACHED_RESPONSE_AGE_LIMIT: Duration = Duration::from_secs(900); // 15 minutes
|
||||
|
||||
/// Stores the remembered state of a engine.
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Eq, Default)]
|
||||
@@ -34,11 +34,11 @@ enum EngineStateInternal {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
enum CapabilitiesCacheAction {
|
||||
enum ResponseCacheAction {
|
||||
#[default]
|
||||
None,
|
||||
Update,
|
||||
Clear,
|
||||
Update, // Update cached responses
|
||||
Clear, // Clear cached responses
|
||||
}
|
||||
|
||||
/// A subset of the engine state to inform other services if the engine is online or offline.
|
||||
@@ -266,12 +266,12 @@ impl Engine {
|
||||
);
|
||||
}
|
||||
state.update(EngineStateInternal::Synced);
|
||||
(**state, CapabilitiesCacheAction::Update)
|
||||
(**state, ResponseCacheAction::Update)
|
||||
}
|
||||
Err(EngineApiError::IsSyncing) => {
|
||||
let mut state = self.state.write().await;
|
||||
state.update(EngineStateInternal::Syncing);
|
||||
(**state, CapabilitiesCacheAction::Update)
|
||||
(**state, ResponseCacheAction::Update)
|
||||
}
|
||||
Err(EngineApiError::Auth(err)) => {
|
||||
error!(
|
||||
@@ -282,7 +282,7 @@ impl Engine {
|
||||
|
||||
let mut state = self.state.write().await;
|
||||
state.update(EngineStateInternal::AuthFailed);
|
||||
(**state, CapabilitiesCacheAction::Clear)
|
||||
(**state, ResponseCacheAction::Clear)
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
@@ -293,28 +293,37 @@ impl Engine {
|
||||
|
||||
let mut state = self.state.write().await;
|
||||
state.update(EngineStateInternal::Offline);
|
||||
// need to clear the engine capabilities cache if we detect the
|
||||
// execution engine is offline as it is likely the engine is being
|
||||
// updated to a newer version with new capabilities
|
||||
(**state, CapabilitiesCacheAction::Clear)
|
||||
// need to clear cached responses if we detect the execution engine
|
||||
// is offline as it is likely the engine is being updated to a newer
|
||||
// version which might also have new capabilities
|
||||
(**state, ResponseCacheAction::Clear)
|
||||
}
|
||||
};
|
||||
|
||||
// do this after dropping state lock guard to avoid holding two locks at once
|
||||
match cache_action {
|
||||
CapabilitiesCacheAction::None => {}
|
||||
CapabilitiesCacheAction::Update => {
|
||||
ResponseCacheAction::None => {}
|
||||
ResponseCacheAction::Update => {
|
||||
if let Err(e) = self
|
||||
.get_engine_capabilities(Some(CACHED_ENGINE_CAPABILITIES_AGE_LIMIT))
|
||||
.get_engine_capabilities(Some(CACHED_RESPONSE_AGE_LIMIT))
|
||||
.await
|
||||
{
|
||||
warn!(self.log,
|
||||
"Error during exchange capabilities";
|
||||
"error" => ?e,
|
||||
)
|
||||
} else {
|
||||
// no point in running this if there was an error fetching the capabilities
|
||||
// as it will just result in an error again
|
||||
let _ = self
|
||||
.get_engine_version(Some(CACHED_RESPONSE_AGE_LIMIT))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
CapabilitiesCacheAction::Clear => self.api.clear_exchange_capabilties_cache().await,
|
||||
ResponseCacheAction::Clear => {
|
||||
self.api.clear_exchange_capabilties_cache().await;
|
||||
self.api.clear_engine_version_cache().await;
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
@@ -340,6 +349,22 @@ impl Engine {
|
||||
self.api.get_engine_capabilities(age_limit).await
|
||||
}
|
||||
|
||||
/// Returns the execution engine version resulting from a call to
|
||||
/// engine_clientVersionV1. If the version cache is not populated, or if it
|
||||
/// is populated with a cached result of age >= `age_limit`, this method will
|
||||
/// fetch the result from the execution engine and populate the cache before
|
||||
/// returning it. Otherwise it will return the cached result from an earlier
|
||||
/// call.
|
||||
///
|
||||
/// Set `age_limit` to `None` to always return the cached result
|
||||
/// Set `age_limit` to `Some(Duration::ZERO)` to force fetching from EE
|
||||
pub async fn get_engine_version(
|
||||
&self,
|
||||
age_limit: Option<Duration>,
|
||||
) -> Result<Vec<ClientVersionV1>, EngineApiError> {
|
||||
self.api.get_engine_version(age_limit).await
|
||||
}
|
||||
|
||||
/// Run `func` on the node regardless of the node's current state.
|
||||
///
|
||||
/// ## Note
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
use crate::payload_cache::PayloadCache;
|
||||
use arc_swap::ArcSwapOption;
|
||||
use auth::{strip_prefix, Auth, JwtKey};
|
||||
pub use block_hash::calculate_execution_block_hash;
|
||||
use builder_client::BuilderHttpClient;
|
||||
pub use engine_api::EngineCapabilities;
|
||||
use engine_api::Error as ApiError;
|
||||
@@ -23,7 +24,7 @@ use payload_status::process_payload_status;
|
||||
pub use payload_status::PayloadStatus;
|
||||
use sensitive_url::SensitiveUrl;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use slog::{crit, debug, error, info, trace, warn, Logger};
|
||||
use slog::{crit, debug, error, info, warn, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
@@ -49,8 +50,9 @@ use types::{
|
||||
AbstractExecPayload, BlobsList, ExecutionPayloadDeneb, KzgProofs, SignedBlindedBeaconBlock,
|
||||
};
|
||||
use types::{
|
||||
BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadCapella,
|
||||
ExecutionPayloadMerge, FullPayload, ProposerPreparationData, PublicKeyBytes, Signature, Slot,
|
||||
BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadBellatrix,
|
||||
ExecutionPayloadCapella, ExecutionPayloadElectra, FullPayload, ProposerPreparationData,
|
||||
PublicKeyBytes, Signature, Slot,
|
||||
};
|
||||
|
||||
mod block_hash;
|
||||
@@ -96,8 +98,8 @@ impl<E: EthSpec> TryFrom<BuilderBid<E>> for ProvenancedPayload<BlockProposalCont
|
||||
|
||||
fn try_from(value: BuilderBid<E>) -> Result<Self, Error> {
|
||||
let block_proposal_contents = match value {
|
||||
BuilderBid::Merge(builder_bid) => BlockProposalContents::Payload {
|
||||
payload: ExecutionPayloadHeader::Merge(builder_bid.header).into(),
|
||||
BuilderBid::Bellatrix(builder_bid) => BlockProposalContents::Payload {
|
||||
payload: ExecutionPayloadHeader::Bellatrix(builder_bid.header).into(),
|
||||
block_value: builder_bid.value,
|
||||
},
|
||||
BuilderBid::Capella(builder_bid) => BlockProposalContents::Payload {
|
||||
@@ -110,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),
|
||||
@@ -157,12 +165,23 @@ impl From<ApiError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EngineError> for Error {
|
||||
fn from(e: EngineError) -> Self {
|
||||
match e {
|
||||
// This removes an unnecessary layer of indirection.
|
||||
// TODO (mark): consider refactoring these error enums
|
||||
EngineError::Api { error } => Error::ApiError(error),
|
||||
_ => Error::EngineError(Box::new(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum BlockProposalContentsType<E: EthSpec> {
|
||||
Full(BlockProposalContents<E, FullPayload<E>>),
|
||||
Blinded(BlockProposalContents<E, BlindedPayload<E>>),
|
||||
}
|
||||
|
||||
pub enum BlockProposalContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
||||
pub enum BlockProposalContents<E: EthSpec, Payload: AbstractExecPayload<E>> {
|
||||
Payload {
|
||||
payload: Payload,
|
||||
block_value: Uint256,
|
||||
@@ -170,16 +189,16 @@ pub enum BlockProposalContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
||||
PayloadAndBlobs {
|
||||
payload: Payload,
|
||||
block_value: Uint256,
|
||||
kzg_commitments: KzgCommitments<T>,
|
||||
kzg_commitments: KzgCommitments<E>,
|
||||
/// `None` for blinded `PayloadAndBlobs`.
|
||||
blobs_and_proofs: Option<(BlobsList<T>, KzgProofs<T>)>,
|
||||
blobs_and_proofs: Option<(BlobsList<E>, KzgProofs<E>)>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<BlockProposalContents<T, FullPayload<T>>>
|
||||
for BlockProposalContents<T, BlindedPayload<T>>
|
||||
impl<E: EthSpec> From<BlockProposalContents<E, FullPayload<E>>>
|
||||
for BlockProposalContents<E, BlindedPayload<E>>
|
||||
{
|
||||
fn from(item: BlockProposalContents<T, FullPayload<T>>) -> Self {
|
||||
fn from(item: BlockProposalContents<E, FullPayload<E>>) -> Self {
|
||||
match item {
|
||||
BlockProposalContents::Payload {
|
||||
payload,
|
||||
@@ -237,13 +256,13 @@ impl<E: EthSpec> TryFrom<GetPayloadResponseType<E>> for BlockProposalContentsTyp
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Payload> {
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> BlockProposalContents<E, Payload> {
|
||||
pub fn deconstruct(
|
||||
self,
|
||||
) -> (
|
||||
Payload,
|
||||
Option<KzgCommitments<T>>,
|
||||
Option<(BlobsList<T>, KzgProofs<T>)>,
|
||||
Option<KzgCommitments<E>>,
|
||||
Option<(BlobsList<E>, KzgProofs<E>)>,
|
||||
Uint256,
|
||||
) {
|
||||
match self {
|
||||
@@ -325,7 +344,7 @@ pub enum FailedCondition {
|
||||
EpochsSinceFinalization,
|
||||
}
|
||||
|
||||
type PayloadContentsRefTuple<'a, T> = (ExecutionPayloadRef<'a, T>, Option<&'a BlobsBundle<T>>);
|
||||
type PayloadContentsRefTuple<'a, E> = (ExecutionPayloadRef<'a, E>, Option<&'a BlobsBundle<E>>);
|
||||
|
||||
struct Inner<E: EthSpec> {
|
||||
engine: Arc<Engine>,
|
||||
@@ -347,14 +366,17 @@ struct Inner<E: EthSpec> {
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
/// Endpoint urls for EL nodes that are running the engine api.
|
||||
pub execution_endpoints: Vec<SensitiveUrl>,
|
||||
/// Endpoint url for EL nodes that are running the engine api.
|
||||
pub execution_endpoint: Option<SensitiveUrl>,
|
||||
/// Endpoint urls for services providing the builder api.
|
||||
pub builder_url: Option<SensitiveUrl>,
|
||||
/// The timeout value used when making a request to fetch a block header
|
||||
/// from the builder api.
|
||||
pub builder_header_timeout: Option<Duration>,
|
||||
/// User agent to send with requests to the builder API.
|
||||
pub builder_user_agent: Option<String>,
|
||||
/// JWT secrets for the above endpoints running the engine api.
|
||||
pub secret_files: Vec<PathBuf>,
|
||||
/// JWT secret for the above endpoint running the engine api.
|
||||
pub secret_file: Option<PathBuf>,
|
||||
/// The default fee recipient to use on the beacon node if none if provided from
|
||||
/// the validator client during block preparation.
|
||||
pub suggested_fee_recipient: Option<Address>,
|
||||
@@ -370,18 +392,19 @@ pub struct Config {
|
||||
/// Provides access to one execution engine and provides a neat interface for consumption by the
|
||||
/// `BeaconChain`.
|
||||
#[derive(Clone)]
|
||||
pub struct ExecutionLayer<T: EthSpec> {
|
||||
inner: Arc<Inner<T>>,
|
||||
pub struct ExecutionLayer<E: EthSpec> {
|
||||
inner: Arc<Inner<E>>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> ExecutionLayer<T> {
|
||||
impl<E: EthSpec> ExecutionLayer<E> {
|
||||
/// Instantiate `Self` with an Execution engine specified in `Config`, using JSON-RPC via HTTP.
|
||||
pub fn from_config(config: Config, executor: TaskExecutor, log: Logger) -> Result<Self, Error> {
|
||||
let Config {
|
||||
execution_endpoints: urls,
|
||||
execution_endpoint: url,
|
||||
builder_url,
|
||||
builder_user_agent,
|
||||
secret_files,
|
||||
builder_header_timeout,
|
||||
secret_file,
|
||||
suggested_fee_recipient,
|
||||
jwt_id,
|
||||
jwt_version,
|
||||
@@ -389,16 +412,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
execution_timeout_multiplier,
|
||||
} = config;
|
||||
|
||||
if urls.len() > 1 {
|
||||
warn!(log, "Only the first execution engine url will be used");
|
||||
}
|
||||
let execution_url = urls.into_iter().next().ok_or(Error::NoEngine)?;
|
||||
let execution_url = url.ok_or(Error::NoEngine)?;
|
||||
|
||||
// Use the default jwt secret path if not provided via cli.
|
||||
let secret_file = secret_files
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or_else(|| default_datadir.join(DEFAULT_JWT_FILE));
|
||||
let secret_file = secret_file.unwrap_or_else(|| default_datadir.join(DEFAULT_JWT_FILE));
|
||||
|
||||
let jwt_key = if secret_file.exists() {
|
||||
// Read secret from file if it already exists
|
||||
@@ -456,7 +473,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
};
|
||||
|
||||
if let Some(builder_url) = builder_url {
|
||||
el.set_builder_url(builder_url, builder_user_agent)?;
|
||||
el.set_builder_url(builder_url, builder_user_agent, builder_header_timeout)?;
|
||||
}
|
||||
|
||||
Ok(el)
|
||||
@@ -478,9 +495,14 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
&self,
|
||||
builder_url: SensitiveUrl,
|
||||
builder_user_agent: Option<String>,
|
||||
builder_header_timeout: Option<Duration>,
|
||||
) -> Result<(), Error> {
|
||||
let builder_client = BuilderHttpClient::new(builder_url.clone(), builder_user_agent)
|
||||
.map_err(Error::Builder)?;
|
||||
let builder_client = BuilderHttpClient::new(
|
||||
builder_url.clone(),
|
||||
builder_user_agent,
|
||||
builder_header_timeout,
|
||||
)
|
||||
.map_err(Error::Builder)?;
|
||||
info!(
|
||||
self.log(),
|
||||
"Using external block builder";
|
||||
@@ -494,8 +516,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
/// Cache a full payload, keyed on the `tree_hash_root` of the payload
|
||||
fn cache_payload(
|
||||
&self,
|
||||
payload_and_blobs: PayloadContentsRefTuple<T>,
|
||||
) -> Option<FullPayloadContents<T>> {
|
||||
payload_and_blobs: PayloadContentsRefTuple<E>,
|
||||
) -> Option<FullPayloadContents<E>> {
|
||||
let (payload_ref, maybe_json_blobs_bundle) = payload_and_blobs;
|
||||
|
||||
let payload = payload_ref.clone_from_ref();
|
||||
@@ -513,7 +535,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
}
|
||||
|
||||
/// Attempt to retrieve a full payload from the payload cache by the payload root
|
||||
pub fn get_payload_by_root(&self, root: &Hash256) -> Option<FullPayloadContents<T>> {
|
||||
pub fn get_payload_by_root(&self, root: &Hash256) -> Option<FullPayloadContents<E>> {
|
||||
self.inner.payload_cache.get(root)
|
||||
}
|
||||
|
||||
@@ -575,7 +597,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
|
||||
/// Spawns a routine which attempts to keep the execution engine online.
|
||||
pub fn spawn_watchdog_routine<S: SlotClock + 'static>(&self, slot_clock: S) {
|
||||
let watchdog = |el: ExecutionLayer<T>| async move {
|
||||
let watchdog = |el: ExecutionLayer<E>| async move {
|
||||
// Run one task immediately.
|
||||
el.watchdog_task().await;
|
||||
|
||||
@@ -599,18 +621,18 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
|
||||
/// Spawns a routine which cleans the cached proposer data periodically.
|
||||
pub fn spawn_clean_proposer_caches_routine<S: SlotClock + 'static>(&self, slot_clock: S) {
|
||||
let preparation_cleaner = |el: ExecutionLayer<T>| async move {
|
||||
let preparation_cleaner = |el: ExecutionLayer<E>| async move {
|
||||
// Start the loop to periodically clean proposer preparation cache.
|
||||
loop {
|
||||
if let Some(duration_to_next_epoch) =
|
||||
slot_clock.duration_to_next_epoch(T::slots_per_epoch())
|
||||
slot_clock.duration_to_next_epoch(E::slots_per_epoch())
|
||||
{
|
||||
// Wait for next epoch
|
||||
sleep(duration_to_next_epoch).await;
|
||||
|
||||
match slot_clock
|
||||
.now()
|
||||
.map(|slot| slot.epoch(T::slots_per_epoch()))
|
||||
.map(|slot| slot.epoch(E::slots_per_epoch()))
|
||||
{
|
||||
Some(current_epoch) => el
|
||||
.clean_proposer_caches(current_epoch)
|
||||
@@ -713,7 +735,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
});
|
||||
drop(proposer_preparation_data);
|
||||
|
||||
let retain_slot = retain_epoch.start_slot(T::slots_per_epoch());
|
||||
let retain_slot = retain_epoch.start_slot(E::slots_per_epoch());
|
||||
self.proposers()
|
||||
.write()
|
||||
.await
|
||||
@@ -792,7 +814,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
spec: &ChainSpec,
|
||||
builder_boost_factor: Option<u64>,
|
||||
block_production_version: BlockProductionVersion,
|
||||
) -> Result<BlockProposalContentsType<T>, Error> {
|
||||
) -> Result<BlockProposalContentsType<E>, Error> {
|
||||
let payload_result_type = match block_production_version {
|
||||
BlockProductionVersion::V3 => match self
|
||||
.determine_and_fetch_payload(
|
||||
@@ -891,8 +913,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
||||
current_fork: ForkName,
|
||||
) -> (
|
||||
Result<Option<ForkVersionedResponse<SignedBuilderBid<T>>>, builder_client::Error>,
|
||||
Result<GetPayloadResponse<T>, Error>,
|
||||
Result<Option<ForkVersionedResponse<SignedBuilderBid<E>>>, builder_client::Error>,
|
||||
Result<GetPayloadResponse<E>, Error>,
|
||||
) {
|
||||
let slot = builder_params.slot;
|
||||
let pubkey = &builder_params.pubkey;
|
||||
@@ -909,7 +931,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
let ((relay_result, relay_duration), (local_result, local_duration)) = tokio::join!(
|
||||
timed_future(metrics::GET_BLINDED_PAYLOAD_BUILDER, async {
|
||||
builder
|
||||
.get_builder_header::<T>(slot, parent_hash, pubkey)
|
||||
.get_builder_header::<E>(slot, parent_hash, pubkey)
|
||||
.await
|
||||
}),
|
||||
timed_future(metrics::GET_BLINDED_PAYLOAD_LOCAL, async {
|
||||
@@ -957,7 +979,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
current_fork: ForkName,
|
||||
builder_boost_factor: Option<u64>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<ProvenancedPayload<BlockProposalContentsType<T>>, Error> {
|
||||
) -> Result<ProvenancedPayload<BlockProposalContentsType<E>>, Error> {
|
||||
let Some(builder) = self.builder() else {
|
||||
// no builder.. return local payload
|
||||
return self
|
||||
@@ -1202,7 +1224,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
payload_attributes: &PayloadAttributes,
|
||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
||||
current_fork: ForkName,
|
||||
) -> Result<GetPayloadResponseType<T>, Error> {
|
||||
) -> Result<GetPayloadResponseType<E>, Error> {
|
||||
self.get_full_payload_with(
|
||||
parent_hash,
|
||||
payload_attributes,
|
||||
@@ -1220,10 +1242,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
||||
current_fork: ForkName,
|
||||
cache_fn: fn(
|
||||
&ExecutionLayer<T>,
|
||||
PayloadContentsRefTuple<T>,
|
||||
) -> Option<FullPayloadContents<T>>,
|
||||
) -> Result<GetPayloadResponseType<T>, Error> {
|
||||
&ExecutionLayer<E>,
|
||||
PayloadContentsRefTuple<E>,
|
||||
) -> Option<FullPayloadContents<E>>,
|
||||
) -> Result<GetPayloadResponseType<E>, Error> {
|
||||
self.engine()
|
||||
.request(move |engine| async move {
|
||||
let payload_id = if let Some(id) = engine
|
||||
@@ -1289,7 +1311,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
&[metrics::GET_PAYLOAD],
|
||||
);
|
||||
engine.api.get_payload::<T>(current_fork, payload_id).await
|
||||
engine.api.get_payload::<E>(current_fork, payload_id).await
|
||||
}.await?;
|
||||
|
||||
if payload_response.execution_payload_ref().fee_recipient() != payload_attributes.suggested_fee_recipient() {
|
||||
@@ -1323,21 +1345,17 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
/// Maps to the `engine_newPayload` JSON-RPC call.
|
||||
pub async fn notify_new_payload(
|
||||
&self,
|
||||
new_payload_request: NewPayloadRequest<'_, T>,
|
||||
new_payload_request: NewPayloadRequest<'_, E>,
|
||||
) -> Result<PayloadStatus, Error> {
|
||||
let _timer = metrics::start_timer_vec(
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
&[metrics::NEW_PAYLOAD],
|
||||
);
|
||||
let timer = std::time::Instant::now();
|
||||
|
||||
let block_number = new_payload_request.block_number();
|
||||
let block_hash = new_payload_request.block_hash();
|
||||
trace!(
|
||||
self.log(),
|
||||
"Issuing engine_newPayload";
|
||||
"parent_hash" => ?new_payload_request.parent_hash(),
|
||||
"block_hash" => ?block_hash,
|
||||
"block_number" => ?new_payload_request.block_number(),
|
||||
);
|
||||
let parent_hash = new_payload_request.parent_hash();
|
||||
|
||||
let result = self
|
||||
.engine()
|
||||
@@ -1345,9 +1363,19 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
.await;
|
||||
|
||||
if let Ok(status) = &result {
|
||||
let status_str = <&'static str>::from(status.status);
|
||||
metrics::inc_counter_vec(
|
||||
&metrics::EXECUTION_LAYER_PAYLOAD_STATUS,
|
||||
&["new_payload", status.status.into()],
|
||||
&["new_payload", status_str],
|
||||
);
|
||||
debug!(
|
||||
self.log(),
|
||||
"Processed engine_newPayload";
|
||||
"status" => status_str,
|
||||
"parent_hash" => ?parent_hash,
|
||||
"block_hash" => ?block_hash,
|
||||
"block_number" => block_number,
|
||||
"response_time_ms" => timer.elapsed().as_millis()
|
||||
);
|
||||
}
|
||||
*self.inner.last_new_payload_errored.write().await = result.is_err();
|
||||
@@ -1518,8 +1546,26 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
self.engine()
|
||||
.request(|engine| engine.get_engine_capabilities(age_limit))
|
||||
.await
|
||||
.map_err(Box::new)
|
||||
.map_err(Error::EngineError)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Returns the execution engine version resulting from a call to
|
||||
/// engine_clientVersionV1. If the version cache is not populated, or if it
|
||||
/// is populated with a cached result of age >= `age_limit`, this method will
|
||||
/// fetch the result from the execution engine and populate the cache before
|
||||
/// returning it. Otherwise it will return the cached result from an earlier
|
||||
/// call.
|
||||
///
|
||||
/// Set `age_limit` to `None` to always return the cached result
|
||||
/// Set `age_limit` to `Some(Duration::ZERO)` to force fetching from EE
|
||||
pub async fn get_engine_version(
|
||||
&self,
|
||||
age_limit: Option<Duration>,
|
||||
) -> Result<Vec<ClientVersionV1>, Error> {
|
||||
self.engine()
|
||||
.request(|engine| engine.get_engine_version(age_limit))
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Used during block production to determine if the merge has been triggered.
|
||||
@@ -1725,7 +1771,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
pub async fn get_payload_bodies_by_hash(
|
||||
&self,
|
||||
hashes: Vec<ExecutionBlockHash>,
|
||||
) -> Result<Vec<Option<ExecutionPayloadBodyV1<T>>>, Error> {
|
||||
) -> Result<Vec<Option<ExecutionPayloadBodyV1<E>>>, Error> {
|
||||
self.engine()
|
||||
.request(|engine: &Engine| async move {
|
||||
engine.api.get_payload_bodies_by_hash_v1(hashes).await
|
||||
@@ -1739,7 +1785,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
&self,
|
||||
start: u64,
|
||||
count: u64,
|
||||
) -> Result<Vec<Option<ExecutionPayloadBodyV1<T>>>, Error> {
|
||||
) -> Result<Vec<Option<ExecutionPayloadBodyV1<E>>>, Error> {
|
||||
let _timer = metrics::start_timer(&metrics::EXECUTION_LAYER_GET_PAYLOAD_BODIES_BY_RANGE);
|
||||
self.engine()
|
||||
.request(|engine: &Engine| async move {
|
||||
@@ -1758,18 +1804,19 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
/// This will fail if the payload is not from the finalized portion of the chain.
|
||||
pub async fn get_payload_for_header(
|
||||
&self,
|
||||
header: &ExecutionPayloadHeader<T>,
|
||||
header: &ExecutionPayloadHeader<E>,
|
||||
fork: ForkName,
|
||||
) -> Result<Option<ExecutionPayload<T>>, Error> {
|
||||
) -> Result<Option<ExecutionPayload<E>>, Error> {
|
||||
let hash = header.block_hash();
|
||||
let block_number = header.block_number();
|
||||
|
||||
// Handle default payload body.
|
||||
if header.block_hash() == ExecutionBlockHash::zero() {
|
||||
let payload = match fork {
|
||||
ForkName::Merge => ExecutionPayloadMerge::default().into(),
|
||||
ForkName::Bellatrix => ExecutionPayloadBellatrix::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);
|
||||
}
|
||||
@@ -1814,7 +1861,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
&self,
|
||||
hash: ExecutionBlockHash,
|
||||
fork: ForkName,
|
||||
) -> Result<Option<ExecutionPayload<T>>, Error> {
|
||||
) -> Result<Option<ExecutionPayload<E>>, Error> {
|
||||
self.engine()
|
||||
.request(|engine| async move {
|
||||
self.get_payload_by_hash_from_engine(engine, hash, fork)
|
||||
@@ -1830,14 +1877,15 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
engine: &Engine,
|
||||
hash: ExecutionBlockHash,
|
||||
fork: ForkName,
|
||||
) -> Result<Option<ExecutionPayload<T>>, ApiError> {
|
||||
) -> Result<Option<ExecutionPayload<E>>, ApiError> {
|
||||
let _timer = metrics::start_timer(&metrics::EXECUTION_LAYER_GET_PAYLOAD_BY_BLOCK_HASH);
|
||||
|
||||
if hash == ExecutionBlockHash::zero() {
|
||||
return match fork {
|
||||
ForkName::Merge => Ok(Some(ExecutionPayloadMerge::default().into())),
|
||||
ForkName::Bellatrix => Ok(Some(ExecutionPayloadBellatrix::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),
|
||||
)),
|
||||
@@ -1846,7 +1894,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
|
||||
let Some(block) = engine
|
||||
.api
|
||||
.get_block_by_hash_with_txns::<T>(hash, fork)
|
||||
.get_block_by_hash_with_txns::<E>(hash, fork)
|
||||
.await?
|
||||
else {
|
||||
return Ok(None);
|
||||
@@ -1863,22 +1911,22 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
};
|
||||
|
||||
let payload = match block {
|
||||
ExecutionBlockWithTransactions::Merge(merge_block) => {
|
||||
ExecutionPayload::Merge(ExecutionPayloadMerge {
|
||||
parent_hash: merge_block.parent_hash,
|
||||
fee_recipient: merge_block.fee_recipient,
|
||||
state_root: merge_block.state_root,
|
||||
receipts_root: merge_block.receipts_root,
|
||||
logs_bloom: merge_block.logs_bloom,
|
||||
prev_randao: merge_block.prev_randao,
|
||||
block_number: merge_block.block_number,
|
||||
gas_limit: merge_block.gas_limit,
|
||||
gas_used: merge_block.gas_used,
|
||||
timestamp: merge_block.timestamp,
|
||||
extra_data: merge_block.extra_data,
|
||||
base_fee_per_gas: merge_block.base_fee_per_gas,
|
||||
block_hash: merge_block.block_hash,
|
||||
transactions: convert_transactions(merge_block.transactions)?,
|
||||
ExecutionBlockWithTransactions::Bellatrix(bellatrix_block) => {
|
||||
ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
|
||||
parent_hash: bellatrix_block.parent_hash,
|
||||
fee_recipient: bellatrix_block.fee_recipient,
|
||||
state_root: bellatrix_block.state_root,
|
||||
receipts_root: bellatrix_block.receipts_root,
|
||||
logs_bloom: bellatrix_block.logs_bloom,
|
||||
prev_randao: bellatrix_block.prev_randao,
|
||||
block_number: bellatrix_block.block_number,
|
||||
gas_limit: bellatrix_block.gas_limit,
|
||||
gas_used: bellatrix_block.gas_used,
|
||||
timestamp: bellatrix_block.timestamp,
|
||||
extra_data: bellatrix_block.extra_data,
|
||||
base_fee_per_gas: bellatrix_block.base_fee_per_gas,
|
||||
block_hash: bellatrix_block.block_hash,
|
||||
transactions: convert_transactions(bellatrix_block.transactions)?,
|
||||
})
|
||||
}
|
||||
ExecutionBlockWithTransactions::Capella(capella_block) => {
|
||||
@@ -1937,6 +1985,11 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
excess_blob_gas: deneb_block.excess_blob_gas,
|
||||
})
|
||||
}
|
||||
ExecutionBlockWithTransactions::Electra(_) => {
|
||||
return Err(ApiError::UnsupportedForkVariant(format!(
|
||||
"legacy payload construction for {fork} is not implemented"
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Some(payload))
|
||||
@@ -1945,8 +1998,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
pub async fn propose_blinded_beacon_block(
|
||||
&self,
|
||||
block_root: Hash256,
|
||||
block: &SignedBlindedBeaconBlock<T>,
|
||||
) -> Result<FullPayloadContents<T>, Error> {
|
||||
block: &SignedBlindedBeaconBlock<E>,
|
||||
) -> Result<FullPayloadContents<E>, Error> {
|
||||
debug!(
|
||||
self.log(),
|
||||
"Sending block to builder";
|
||||
@@ -2083,8 +2136,8 @@ impl fmt::Display for InvalidBuilderPayload {
|
||||
}
|
||||
|
||||
/// Perform some cursory, non-exhaustive validation of the bid returned from the builder.
|
||||
fn verify_builder_bid<T: EthSpec>(
|
||||
bid: &ForkVersionedResponse<SignedBuilderBid<T>>,
|
||||
fn verify_builder_bid<E: EthSpec>(
|
||||
bid: &ForkVersionedResponse<SignedBuilderBid<E>>,
|
||||
parent_hash: ExecutionBlockHash,
|
||||
payload_attributes: &PayloadAttributes,
|
||||
block_number: Option<u64>,
|
||||
@@ -2096,7 +2149,7 @@ fn verify_builder_bid<T: EthSpec>(
|
||||
|
||||
// Avoid logging values that we can't represent with our Prometheus library.
|
||||
let payload_value_gwei = bid.data.message.value() / 1_000_000_000;
|
||||
if payload_value_gwei <= Uint256::from(i64::max_value()) {
|
||||
if payload_value_gwei <= Uint256::from(i64::MAX) {
|
||||
metrics::set_gauge_vec(
|
||||
&metrics::EXECUTION_LAYER_PAYLOAD_BIDS,
|
||||
&[metrics::BUILDER],
|
||||
@@ -2108,7 +2161,7 @@ fn verify_builder_bid<T: EthSpec>(
|
||||
.withdrawals()
|
||||
.ok()
|
||||
.cloned()
|
||||
.map(|withdrawals| Withdrawals::<T>::from(withdrawals).tree_hash_root());
|
||||
.map(|withdrawals| Withdrawals::<E>::from(withdrawals).tree_hash_root());
|
||||
let payload_withdrawals_root = header.withdrawals_root().ok().copied();
|
||||
|
||||
if header.parent_hash() != parent_hash {
|
||||
@@ -2169,10 +2222,10 @@ fn timestamp_now() -> u64 {
|
||||
.as_secs()
|
||||
}
|
||||
|
||||
fn noop<T: EthSpec>(
|
||||
_: &ExecutionLayer<T>,
|
||||
_: PayloadContentsRefTuple<T>,
|
||||
) -> Option<FullPayloadContents<T>> {
|
||||
fn noop<E: EthSpec>(
|
||||
_: &ExecutionLayer<E>,
|
||||
_: PayloadContentsRefTuple<E>,
|
||||
) -> Option<FullPayloadContents<E>> {
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ lazy_static::lazy_static! {
|
||||
);
|
||||
pub static ref EXECUTION_LAYER_PAYLOAD_BIDS: Result<IntGaugeVec> = try_create_int_gauge_vec(
|
||||
"execution_layer_payload_bids",
|
||||
"The gwei bid value of payloads received by local EEs or builders. Only shows values up to i64::max_value.",
|
||||
"The gwei bid value of payloads received by local EEs or builders. Only shows values up to i64::MAX.",
|
||||
&["source"]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ use types::{EthSpec, Hash256};
|
||||
pub const DEFAULT_PAYLOAD_CACHE_SIZE: NonZeroUsize = new_non_zero_usize(10);
|
||||
|
||||
/// A cache mapping execution payloads by tree hash roots.
|
||||
pub struct PayloadCache<T: EthSpec> {
|
||||
payloads: Mutex<LruCache<PayloadCacheId, FullPayloadContents<T>>>,
|
||||
pub struct PayloadCache<E: EthSpec> {
|
||||
payloads: Mutex<LruCache<PayloadCacheId, FullPayloadContents<E>>>,
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
struct PayloadCacheId(Hash256);
|
||||
|
||||
impl<T: EthSpec> Default for PayloadCache<T> {
|
||||
impl<E: EthSpec> Default for PayloadCache<E> {
|
||||
fn default() -> Self {
|
||||
PayloadCache {
|
||||
payloads: Mutex::new(LruCache::new(DEFAULT_PAYLOAD_CACHE_SIZE)),
|
||||
@@ -24,17 +24,17 @@ impl<T: EthSpec> Default for PayloadCache<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> PayloadCache<T> {
|
||||
pub fn put(&self, payload: FullPayloadContents<T>) -> Option<FullPayloadContents<T>> {
|
||||
impl<E: EthSpec> PayloadCache<E> {
|
||||
pub fn put(&self, payload: FullPayloadContents<E>) -> Option<FullPayloadContents<E>> {
|
||||
let root = payload.payload_ref().tree_hash_root();
|
||||
self.payloads.lock().put(PayloadCacheId(root), payload)
|
||||
}
|
||||
|
||||
pub fn pop(&self, root: &Hash256) -> Option<FullPayloadContents<T>> {
|
||||
pub fn pop(&self, root: &Hash256) -> Option<FullPayloadContents<E>> {
|
||||
self.payloads.lock().pop(&PayloadCacheId(*root))
|
||||
}
|
||||
|
||||
pub fn get(&self, hash: &Hash256) -> Option<FullPayloadContents<T>> {
|
||||
pub fn get(&self, hash: &Hash256) -> Option<FullPayloadContents<E>> {
|
||||
self.payloads.lock().get(&PayloadCacheId(*hash)).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ use std::sync::Arc;
|
||||
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,
|
||||
Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadBellatrix,
|
||||
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra,
|
||||
ExecutionPayloadHeader, ForkName, Hash256, Transaction, Transactions, Uint256,
|
||||
};
|
||||
|
||||
use super::DEFAULT_TERMINAL_BLOCK;
|
||||
@@ -35,12 +35,12 @@ const GAS_USED: u64 = GAS_LIMIT - 1;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[allow(clippy::large_enum_variant)] // This struct is only for testing.
|
||||
pub enum Block<T: EthSpec> {
|
||||
pub enum Block<E: EthSpec> {
|
||||
PoW(PoWBlock),
|
||||
PoS(ExecutionPayload<T>),
|
||||
PoS(ExecutionPayload<E>),
|
||||
}
|
||||
|
||||
impl<T: EthSpec> Block<T> {
|
||||
impl<E: EthSpec> Block<E> {
|
||||
pub fn block_number(&self) -> u64 {
|
||||
match self {
|
||||
Block::PoW(block) => block.block_number,
|
||||
@@ -88,10 +88,17 @@ impl<T: EthSpec> Block<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_execution_block_with_tx(&self) -> Option<ExecutionBlockWithTransactions<T>> {
|
||||
pub fn as_execution_block_with_tx(&self) -> Option<ExecutionBlockWithTransactions<E>> {
|
||||
match self {
|
||||
Block::PoS(payload) => Some(payload.clone().try_into().unwrap()),
|
||||
Block::PoW(_) => None,
|
||||
Block::PoW(block) => Some(
|
||||
ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
|
||||
block_hash: block.block_hash,
|
||||
..Default::default()
|
||||
})
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,13 +114,13 @@ pub struct PoWBlock {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExecutionBlockGenerator<T: EthSpec> {
|
||||
pub struct ExecutionBlockGenerator<E: EthSpec> {
|
||||
/*
|
||||
* Common database
|
||||
*/
|
||||
head_block: Option<Block<T>>,
|
||||
head_block: Option<Block<E>>,
|
||||
finalized_block_hash: Option<ExecutionBlockHash>,
|
||||
blocks: HashMap<ExecutionBlockHash, Block<T>>,
|
||||
blocks: HashMap<ExecutionBlockHash, Block<E>>,
|
||||
block_hashes: HashMap<u64, Vec<ExecutionBlockHash>>,
|
||||
/*
|
||||
* PoW block parameters
|
||||
@@ -124,18 +131,19 @@ pub struct ExecutionBlockGenerator<T: EthSpec> {
|
||||
/*
|
||||
* PoS block parameters
|
||||
*/
|
||||
pub pending_payloads: HashMap<ExecutionBlockHash, ExecutionPayload<T>>,
|
||||
pub pending_payloads: HashMap<ExecutionBlockHash, ExecutionPayload<E>>,
|
||||
pub next_payload_id: u64,
|
||||
pub payload_ids: HashMap<PayloadId, ExecutionPayload<T>>,
|
||||
pub payload_ids: HashMap<PayloadId, ExecutionPayload<E>>,
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
pub blobs_bundles: HashMap<PayloadId, BlobsBundle<T>>,
|
||||
pub blobs_bundles: HashMap<PayloadId, BlobsBundle<E>>,
|
||||
pub kzg: Option<Arc<Kzg>>,
|
||||
rng: Arc<Mutex<StdRng>>,
|
||||
}
|
||||
@@ -146,14 +154,15 @@ fn make_rng() -> Arc<Mutex<StdRng>> {
|
||||
Arc::new(Mutex::new(StdRng::seed_from_u64(0xDEADBEEF0BAD5EEDu64)))
|
||||
}
|
||||
|
||||
impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
pub fn new(
|
||||
terminal_total_difficulty: Uint256,
|
||||
terminal_block_number: u64,
|
||||
terminal_block_hash: ExecutionBlockHash,
|
||||
shanghai_time: Option<u64>,
|
||||
cancun_time: Option<u64>,
|
||||
kzg: Option<Kzg>,
|
||||
prague_time: Option<u64>,
|
||||
kzg: Option<Arc<Kzg>>,
|
||||
) -> Self {
|
||||
let mut gen = Self {
|
||||
head_block: <_>::default(),
|
||||
@@ -168,8 +177,9 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
payload_ids: <_>::default(),
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
blobs_bundles: <_>::default(),
|
||||
kzg: kzg.map(Arc::new),
|
||||
kzg,
|
||||
rng: make_rng(),
|
||||
};
|
||||
|
||||
@@ -178,7 +188,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
gen
|
||||
}
|
||||
|
||||
pub fn latest_block(&self) -> Option<Block<T>> {
|
||||
pub fn latest_block(&self) -> Option<Block<E>> {
|
||||
self.head_block.clone()
|
||||
}
|
||||
|
||||
@@ -187,7 +197,20 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
.map(|block| block.as_execution_block(self.terminal_total_difficulty))
|
||||
}
|
||||
|
||||
pub fn block_by_number(&self, number: u64) -> Option<Block<T>> {
|
||||
pub fn genesis_block(&self) -> Option<Block<E>> {
|
||||
if let Some(genesis_block_hash) = self.block_hashes.get(&0) {
|
||||
self.blocks.get(genesis_block_hash.first()?).cloned()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn genesis_execution_block(&self) -> Option<ExecutionBlock> {
|
||||
self.genesis_block()
|
||||
.map(|block| block.as_execution_block(self.terminal_total_difficulty))
|
||||
}
|
||||
|
||||
pub fn block_by_number(&self, number: u64) -> Option<Block<E>> {
|
||||
// Get the latest canonical head block
|
||||
let mut latest_block = self.latest_block()?;
|
||||
loop {
|
||||
@@ -203,11 +226,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::Bellatrix,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -217,7 +243,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
.map(|block| block.as_execution_block(self.terminal_total_difficulty))
|
||||
}
|
||||
|
||||
pub fn block_by_hash(&self, hash: ExecutionBlockHash) -> Option<Block<T>> {
|
||||
pub fn block_by_hash(&self, hash: ExecutionBlockHash) -> Option<Block<E>> {
|
||||
self.blocks.get(&hash).cloned()
|
||||
}
|
||||
|
||||
@@ -229,7 +255,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
pub fn execution_block_with_txs_by_hash(
|
||||
&self,
|
||||
hash: ExecutionBlockHash,
|
||||
) -> Option<ExecutionBlockWithTransactions<T>> {
|
||||
) -> Option<ExecutionBlockWithTransactions<E>> {
|
||||
self.block_by_hash(hash)
|
||||
.and_then(|block| block.as_execution_block_with_tx())
|
||||
}
|
||||
@@ -237,7 +263,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
pub fn execution_block_with_txs_by_number(
|
||||
&self,
|
||||
number: u64,
|
||||
) -> Option<ExecutionBlockWithTransactions<T>> {
|
||||
) -> Option<ExecutionBlockWithTransactions<E>> {
|
||||
self.block_by_number(number)
|
||||
.and_then(|block| block.as_execution_block_with_tx())
|
||||
}
|
||||
@@ -362,7 +388,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
|
||||
// This does not reject duplicate blocks inserted. This lets us re-use the same execution
|
||||
// block generator for multiple beacon chains which is useful in testing.
|
||||
pub fn insert_block(&mut self, block: Block<T>) -> Result<ExecutionBlockHash, String> {
|
||||
pub fn insert_block(&mut self, block: Block<E>) -> Result<ExecutionBlockHash, String> {
|
||||
if block.parent_hash() != ExecutionBlockHash::zero()
|
||||
&& !self.blocks.contains_key(&block.parent_hash())
|
||||
{
|
||||
@@ -372,7 +398,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
Ok(self.insert_block_without_checks(block))
|
||||
}
|
||||
|
||||
pub fn insert_block_without_checks(&mut self, block: Block<T>) -> ExecutionBlockHash {
|
||||
pub fn insert_block_without_checks(&mut self, block: Block<E>) -> ExecutionBlockHash {
|
||||
let block_hash = block.block_hash();
|
||||
self.block_hashes
|
||||
.entry(block.block_number())
|
||||
@@ -383,7 +409,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
block_hash
|
||||
}
|
||||
|
||||
pub fn modify_last_block(&mut self, block_modifier: impl FnOnce(&mut Block<T>)) {
|
||||
pub fn modify_last_block(&mut self, block_modifier: impl FnOnce(&mut Block<E>)) {
|
||||
if let Some(last_block_hash) = self
|
||||
.block_hashes
|
||||
.iter_mut()
|
||||
@@ -417,15 +443,15 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_payload(&mut self, id: &PayloadId) -> Option<ExecutionPayload<T>> {
|
||||
pub fn get_payload(&mut self, id: &PayloadId) -> Option<ExecutionPayload<E>> {
|
||||
self.payload_ids.get(id).cloned()
|
||||
}
|
||||
|
||||
pub fn get_blobs_bundle(&mut self, id: &PayloadId) -> Option<BlobsBundle<T>> {
|
||||
pub fn get_blobs_bundle(&mut self, id: &PayloadId) -> Option<BlobsBundle<E>> {
|
||||
self.blobs_bundles.get(id).cloned()
|
||||
}
|
||||
|
||||
pub fn new_payload(&mut self, payload: ExecutionPayload<T>) -> PayloadStatusV1 {
|
||||
pub fn new_payload(&mut self, payload: ExecutionPayload<E>) -> PayloadStatusV1 {
|
||||
let Some(parent) = self.blocks.get(&payload.parent_hash()) else {
|
||||
return PayloadStatusV1 {
|
||||
status: PayloadStatusV1Status::Syncing,
|
||||
@@ -496,13 +522,6 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
let id = match payload_attributes {
|
||||
None => None,
|
||||
Some(attributes) => {
|
||||
if !self.blocks.iter().any(|(_, block)| {
|
||||
block.block_hash() == self.terminal_block_hash
|
||||
|| block.block_number() == self.terminal_block_number
|
||||
}) {
|
||||
return Err("refusing to create payload id before terminal block".to_string());
|
||||
}
|
||||
|
||||
let parent = self
|
||||
.blocks
|
||||
.get(&head_block_hash)
|
||||
@@ -514,6 +533,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)
|
||||
@@ -544,12 +564,12 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
pub fn build_new_execution_payload(
|
||||
&mut self,
|
||||
head_block_hash: ExecutionBlockHash,
|
||||
parent: &Block<T>,
|
||||
parent: &Block<E>,
|
||||
id: PayloadId,
|
||||
attributes: &PayloadAttributes,
|
||||
) -> Result<ExecutionPayload<T>, String> {
|
||||
) -> Result<ExecutionPayload<E>, String> {
|
||||
let mut execution_payload = match attributes {
|
||||
PayloadAttributes::V1(pa) => ExecutionPayload::Merge(ExecutionPayloadMerge {
|
||||
PayloadAttributes::V1(pa) => ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
|
||||
parent_hash: head_block_hash,
|
||||
fee_recipient: pa.suggested_fee_recipient,
|
||||
receipts_root: Hash256::repeat_byte(42),
|
||||
@@ -566,7 +586,7 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
||||
transactions: vec![].into(),
|
||||
}),
|
||||
PayloadAttributes::V2(pa) => match self.get_fork_at_timestamp(pa.timestamp) {
|
||||
ForkName::Merge => ExecutionPayload::Merge(ExecutionPayloadMerge {
|
||||
ForkName::Bellatrix => ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
|
||||
parent_hash: head_block_hash,
|
||||
fee_recipient: pa.suggested_fee_recipient,
|
||||
receipts_root: Hash256::repeat_byte(42),
|
||||
@@ -601,33 +621,57 @@ 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,
|
||||
deposit_receipts: vec![].into(),
|
||||
withdrawal_requests: vec![].into(),
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
|
||||
match execution_payload.fork_name() {
|
||||
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {}
|
||||
ForkName::Deneb => {
|
||||
ForkName::Base | ForkName::Altair | ForkName::Bellatrix | ForkName::Capella => {}
|
||||
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);
|
||||
let num_blobs = rng.gen::<usize>() % (E::max_blobs_per_block() + 1);
|
||||
let (bundle, transactions) = generate_blobs(num_blobs)?;
|
||||
for tx in Vec::from(transactions) {
|
||||
execution_payload
|
||||
@@ -699,7 +743,7 @@ pub fn generate_blobs<E: EthSpec>(
|
||||
Ok((bundle, transactions.into()))
|
||||
}
|
||||
|
||||
pub fn static_valid_tx<T: EthSpec>() -> Result<Transaction<T::MaxBytesPerTransaction>, String> {
|
||||
pub fn static_valid_tx<E: EthSpec>() -> Result<Transaction<E::MaxBytesPerTransaction>, String> {
|
||||
// This is a real transaction hex encoded, but we don't care about the contents of the transaction.
|
||||
let transaction: EthersTransaction = serde_json::from_str(
|
||||
r#"{
|
||||
@@ -728,34 +772,44 @@ fn payload_id_from_u64(n: u64) -> PayloadId {
|
||||
n.to_le_bytes()
|
||||
}
|
||||
|
||||
pub fn generate_genesis_header<T: EthSpec>(
|
||||
pub fn generate_genesis_header<E: EthSpec>(
|
||||
spec: &ChainSpec,
|
||||
post_transition_merge: bool,
|
||||
) -> Option<ExecutionPayloadHeader<T>> {
|
||||
let genesis_fork = spec.fork_name_at_slot::<T>(spec.genesis_slot);
|
||||
) -> 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 empty_transactions_root = Transactions::<E>::empty().tree_hash_root();
|
||||
match genesis_fork {
|
||||
ForkName::Base | ForkName::Altair => None,
|
||||
ForkName::Merge => {
|
||||
ForkName::Bellatrix => {
|
||||
if post_transition_merge {
|
||||
let mut header = ExecutionPayloadHeader::Merge(<_>::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)
|
||||
} else {
|
||||
Some(ExecutionPayloadHeader::<T>::Merge(<_>::default()))
|
||||
Some(ExecutionPayloadHeader::<E>::Bellatrix(<_>::default()))
|
||||
}
|
||||
}
|
||||
ForkName::Capella => {
|
||||
let mut header = ExecutionPayloadHeader::Capella(<_>::default());
|
||||
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
|
||||
*header.transactions_root_mut() = empty_transactions_root;
|
||||
Some(header)
|
||||
}
|
||||
ForkName::Deneb => {
|
||||
let mut header = ExecutionPayloadHeader::Deneb(<_>::default());
|
||||
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
|
||||
*header.transactions_root_mut() = empty_transactions_root;
|
||||
Some(header)
|
||||
}
|
||||
ForkName::Electra => {
|
||||
let mut header = ExecutionPayloadHeader::Electra(<_>::default());
|
||||
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
|
||||
*header.transactions_root_mut() = empty_transactions_root;
|
||||
Some(header)
|
||||
}
|
||||
}
|
||||
@@ -830,6 +884,7 @@ mod test {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
for i in 0..=TERMINAL_BLOCK {
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
use super::Context;
|
||||
use crate::engine_api::{http::*, *};
|
||||
use crate::json_structures::*;
|
||||
use crate::test_utils::DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI;
|
||||
use crate::test_utils::{DEFAULT_CLIENT_VERSION, DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI};
|
||||
use serde::{de::DeserializeOwned, Deserialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::sync::Arc;
|
||||
use types::{EthSpec, ForkName};
|
||||
|
||||
pub const GENERIC_ERROR_CODE: i64 = -1234;
|
||||
pub const BAD_PARAMS_ERROR_CODE: i64 = -32602;
|
||||
pub const UNKNOWN_PAYLOAD_ERROR_CODE: i64 = -38001;
|
||||
pub const FORK_REQUEST_MISMATCH_ERROR_CODE: i64 = -32000;
|
||||
|
||||
pub async fn handle_rpc<T: EthSpec>(
|
||||
pub async fn handle_rpc<E: EthSpec>(
|
||||
body: JsonValue,
|
||||
ctx: Arc<Context<T>>,
|
||||
ctx: Arc<Context<E>>,
|
||||
) -> Result<JsonValue, (String, i64)> {
|
||||
*ctx.previous_request.lock() = Some(body.clone());
|
||||
|
||||
@@ -50,6 +49,12 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
.latest_execution_block(),
|
||||
)
|
||||
.unwrap()),
|
||||
"0x0" => Ok(serde_json::to_value(
|
||||
ctx.execution_block_generator
|
||||
.read()
|
||||
.genesis_execution_block(),
|
||||
)
|
||||
.unwrap()),
|
||||
other => Err((
|
||||
format!("The tag {} is not supported", other),
|
||||
BAD_PARAMS_ERROR_CODE,
|
||||
@@ -96,24 +101,28 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
ENGINE_NEW_PAYLOAD_V1 | ENGINE_NEW_PAYLOAD_V2 | ENGINE_NEW_PAYLOAD_V3 => {
|
||||
let request = match method {
|
||||
ENGINE_NEW_PAYLOAD_V1 => JsonExecutionPayload::V1(
|
||||
get_param::<JsonExecutionPayloadV1<T>>(params, 0)
|
||||
get_param::<JsonExecutionPayloadV1<E>>(params, 0)
|
||||
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
|
||||
),
|
||||
ENGINE_NEW_PAYLOAD_V2 => get_param::<JsonExecutionPayloadV2<T>>(params, 0)
|
||||
ENGINE_NEW_PAYLOAD_V2 => get_param::<JsonExecutionPayloadV2<E>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V2(jep))
|
||||
.or_else(|_| {
|
||||
get_param::<JsonExecutionPayloadV1<T>>(params, 0)
|
||||
get_param::<JsonExecutionPayloadV1<E>>(params, 0)
|
||||
.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<E>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V4(jep))
|
||||
.or_else(|_| {
|
||||
get_param::<JsonExecutionPayloadV2<T>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V2(jep))
|
||||
get_param::<JsonExecutionPayloadV3<E>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V3(jep))
|
||||
.or_else(|_| {
|
||||
get_param::<JsonExecutionPayloadV1<T>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V1(jep))
|
||||
get_param::<JsonExecutionPayloadV2<E>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V2(jep))
|
||||
.or_else(|_| {
|
||||
get_param::<JsonExecutionPayloadV1<E>>(params, 0)
|
||||
.map(|jep| JsonExecutionPayload::V1(jep))
|
||||
})
|
||||
})
|
||||
})
|
||||
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?,
|
||||
@@ -124,9 +133,9 @@ 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 => {
|
||||
ForkName::Bellatrix => {
|
||||
if matches!(request, JsonExecutionPayload::V2(_)) {
|
||||
return Err((
|
||||
format!(
|
||||
@@ -157,14 +166,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,
|
||||
@@ -173,7 +182,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,
|
||||
@@ -246,7 +290,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()
|
||||
@@ -255,7 +299,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,
|
||||
));
|
||||
}
|
||||
@@ -296,6 +353,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!(),
|
||||
@@ -324,12 +395,12 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
.read()
|
||||
.get_fork_at_timestamp(*pa.timestamp())
|
||||
{
|
||||
ForkName::Merge => {
|
||||
ForkName::Bellatrix => {
|
||||
get_param::<Option<JsonPayloadAttributesV1>>(params, 1)
|
||||
.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()
|
||||
@@ -356,7 +427,7 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
.read()
|
||||
.get_fork_at_timestamp(*pa.timestamp())
|
||||
{
|
||||
ForkName::Merge => {
|
||||
ForkName::Bellatrix => {
|
||||
if matches!(pa, JsonPayloadAttributes::V2(_)) {
|
||||
return Err((
|
||||
format!(
|
||||
@@ -393,7 +464,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),
|
||||
@@ -457,6 +528,9 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
let engine_capabilities = ctx.engine_capabilities.read();
|
||||
Ok(serde_json::to_value(engine_capabilities.to_response()).unwrap())
|
||||
}
|
||||
ENGINE_GET_CLIENT_VERSION_V1 => {
|
||||
Ok(serde_json::to_value([DEFAULT_CLIENT_VERSION.clone()]).unwrap())
|
||||
}
|
||||
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1 => {
|
||||
#[derive(Deserialize)]
|
||||
#[serde(transparent)]
|
||||
@@ -478,7 +552,7 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
|
||||
match maybe_block {
|
||||
Some(block) => {
|
||||
let transactions = Transactions::<T>::new(
|
||||
let transactions = Transactions::<E>::new(
|
||||
block
|
||||
.transactions()
|
||||
.iter()
|
||||
@@ -498,7 +572,7 @@ pub async fn handle_rpc<T: EthSpec>(
|
||||
)
|
||||
})?;
|
||||
|
||||
response.push(Some(JsonExecutionPayloadBodyV1::<T> {
|
||||
response.push(Some(JsonExecutionPayloadBodyV1::<E> {
|
||||
transactions,
|
||||
withdrawals: block
|
||||
.withdrawals()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::test_utils::{DEFAULT_BUILDER_PAYLOAD_VALUE_WEI, DEFAULT_JWT_SECRET};
|
||||
use crate::{Config, ExecutionLayer, PayloadAttributes};
|
||||
use eth2::types::{BlobsBundle, BlockId, StateId, ValidatorId};
|
||||
use eth2::{BeaconNodeHttpClient, Timeouts};
|
||||
use eth2::{BeaconNodeHttpClient, Timeouts, CONSENSUS_VERSION_HEADER};
|
||||
use fork_choice::ForkchoiceUpdateParameters;
|
||||
use parking_lot::RwLock;
|
||||
use sensitive_url::SensitiveUrl;
|
||||
@@ -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, BuilderBidBellatrix, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra,
|
||||
SignedBuilderBid,
|
||||
};
|
||||
use types::{
|
||||
Address, BeaconState, ChainSpec, EthSpec, ExecPayload, ExecutionPayload,
|
||||
@@ -71,14 +72,12 @@ pub trait BidStuff<E: EthSpec> {
|
||||
fn set_withdrawals_root(&mut self, withdrawals_root: Hash256);
|
||||
|
||||
fn sign_builder_message(&mut self, sk: &SecretKey, spec: &ChainSpec) -> Signature;
|
||||
|
||||
fn to_signed_bid(self, signature: Signature) -> SignedBuilderBid<E>;
|
||||
}
|
||||
|
||||
impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
fn set_fee_recipient(&mut self, fee_recipient: Address) {
|
||||
match self.to_mut().header_mut() {
|
||||
ExecutionPayloadHeaderRefMut::Merge(header) => {
|
||||
ExecutionPayloadHeaderRefMut::Bellatrix(header) => {
|
||||
header.fee_recipient = fee_recipient;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Capella(header) => {
|
||||
@@ -87,12 +86,15 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
ExecutionPayloadHeaderRefMut::Deneb(header) => {
|
||||
header.fee_recipient = fee_recipient;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Electra(header) => {
|
||||
header.fee_recipient = fee_recipient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_gas_limit(&mut self, gas_limit: u64) {
|
||||
match self.to_mut().header_mut() {
|
||||
ExecutionPayloadHeaderRefMut::Merge(header) => {
|
||||
ExecutionPayloadHeaderRefMut::Bellatrix(header) => {
|
||||
header.gas_limit = gas_limit;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Capella(header) => {
|
||||
@@ -101,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +115,7 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
|
||||
fn set_parent_hash(&mut self, parent_hash: Hash256) {
|
||||
match self.to_mut().header_mut() {
|
||||
ExecutionPayloadHeaderRefMut::Merge(header) => {
|
||||
ExecutionPayloadHeaderRefMut::Bellatrix(header) => {
|
||||
header.parent_hash = ExecutionBlockHash::from_root(parent_hash);
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Capella(header) => {
|
||||
@@ -119,12 +124,15 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_prev_randao(&mut self, prev_randao: Hash256) {
|
||||
match self.to_mut().header_mut() {
|
||||
ExecutionPayloadHeaderRefMut::Merge(header) => {
|
||||
ExecutionPayloadHeaderRefMut::Bellatrix(header) => {
|
||||
header.prev_randao = prev_randao;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Capella(header) => {
|
||||
@@ -133,12 +141,15 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
ExecutionPayloadHeaderRefMut::Deneb(header) => {
|
||||
header.prev_randao = prev_randao;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Electra(header) => {
|
||||
header.prev_randao = prev_randao;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_block_number(&mut self, block_number: u64) {
|
||||
match self.to_mut().header_mut() {
|
||||
ExecutionPayloadHeaderRefMut::Merge(header) => {
|
||||
ExecutionPayloadHeaderRefMut::Bellatrix(header) => {
|
||||
header.block_number = block_number;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Capella(header) => {
|
||||
@@ -147,12 +158,15 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
ExecutionPayloadHeaderRefMut::Deneb(header) => {
|
||||
header.block_number = block_number;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Electra(header) => {
|
||||
header.block_number = block_number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_timestamp(&mut self, timestamp: u64) {
|
||||
match self.to_mut().header_mut() {
|
||||
ExecutionPayloadHeaderRefMut::Merge(header) => {
|
||||
ExecutionPayloadHeaderRefMut::Bellatrix(header) => {
|
||||
header.timestamp = timestamp;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Capella(header) => {
|
||||
@@ -161,12 +175,15 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
ExecutionPayloadHeaderRefMut::Deneb(header) => {
|
||||
header.timestamp = timestamp;
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Electra(header) => {
|
||||
header.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_withdrawals_root(&mut self, withdrawals_root: Hash256) {
|
||||
match self.to_mut().header_mut() {
|
||||
ExecutionPayloadHeaderRefMut::Merge(_) => {
|
||||
ExecutionPayloadHeaderRefMut::Bellatrix(_) => {
|
||||
panic!("no withdrawals before capella")
|
||||
}
|
||||
ExecutionPayloadHeaderRefMut::Capella(header) => {
|
||||
@@ -175,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,13 +203,6 @@ impl<E: EthSpec> BidStuff<E> for BuilderBid<E> {
|
||||
let message = self.signing_root(domain);
|
||||
sk.sign(message)
|
||||
}
|
||||
|
||||
fn to_signed_bid(self, signature: Signature) -> SignedBuilderBid<E> {
|
||||
SignedBuilderBid {
|
||||
message: self,
|
||||
signature,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -216,8 +229,8 @@ impl<E: EthSpec> MockBuilder<E> {
|
||||
|
||||
// This EL should not talk to a builder
|
||||
let config = Config {
|
||||
execution_endpoints: vec![mock_el_url],
|
||||
secret_files: vec![path],
|
||||
execution_endpoint: Some(mock_el_url),
|
||||
secret_file: Some(path),
|
||||
suggested_fee_recipient: None,
|
||||
..Default::default()
|
||||
};
|
||||
@@ -308,53 +321,57 @@ pub fn serve<E: EthSpec>(
|
||||
},
|
||||
);
|
||||
|
||||
let blinded_block = prefix
|
||||
.and(warp::path("blinded_blocks"))
|
||||
.and(warp::body::json())
|
||||
.and(warp::path::end())
|
||||
.and(ctx_filter.clone())
|
||||
.and_then(
|
||||
|block: SignedBlindedBeaconBlock<E>, builder: MockBuilder<E>| async move {
|
||||
let slot = block.slot();
|
||||
let root = match block {
|
||||
SignedBlindedBeaconBlock::Base(_) | types::SignedBeaconBlock::Altair(_) => {
|
||||
return Err(reject("invalid fork"));
|
||||
}
|
||||
SignedBlindedBeaconBlock::Merge(block) => {
|
||||
block.message.body.execution_payload.tree_hash_root()
|
||||
}
|
||||
SignedBlindedBeaconBlock::Capella(block) => {
|
||||
block.message.body.execution_payload.tree_hash_root()
|
||||
}
|
||||
SignedBlindedBeaconBlock::Deneb(block) => {
|
||||
block.message.body.execution_payload.tree_hash_root()
|
||||
}
|
||||
};
|
||||
let blinded_block =
|
||||
prefix
|
||||
.and(warp::path("blinded_blocks"))
|
||||
.and(warp::body::json())
|
||||
.and(warp::header::header::<ForkName>(CONSENSUS_VERSION_HEADER))
|
||||
.and(warp::path::end())
|
||||
.and(ctx_filter.clone())
|
||||
.and_then(
|
||||
|block: SignedBlindedBeaconBlock<E>,
|
||||
fork_name: ForkName,
|
||||
builder: MockBuilder<E>| async move {
|
||||
let root = match block {
|
||||
SignedBlindedBeaconBlock::Base(_) | types::SignedBeaconBlock::Altair(_) => {
|
||||
return Err(reject("invalid fork"));
|
||||
}
|
||||
SignedBlindedBeaconBlock::Bellatrix(block) => {
|
||||
block.message.body.execution_payload.tree_hash_root()
|
||||
}
|
||||
SignedBlindedBeaconBlock::Capella(block) => {
|
||||
block.message.body.execution_payload.tree_hash_root()
|
||||
}
|
||||
SignedBlindedBeaconBlock::Deneb(block) => {
|
||||
block.message.body.execution_payload.tree_hash_root()
|
||||
}
|
||||
SignedBlindedBeaconBlock::Electra(block) => {
|
||||
block.message.body.execution_payload.tree_hash_root()
|
||||
}
|
||||
};
|
||||
let payload = builder
|
||||
.el
|
||||
.get_payload_by_root(&root)
|
||||
.ok_or_else(|| reject("missing payload for tx root"))?;
|
||||
let resp: ForkVersionedResponse<_> = ForkVersionedResponse {
|
||||
version: Some(fork_name),
|
||||
metadata: Default::default(),
|
||||
data: payload,
|
||||
};
|
||||
|
||||
let fork_name = builder.spec.fork_name_at_slot::<E>(slot);
|
||||
let payload = builder
|
||||
.el
|
||||
.get_payload_by_root(&root)
|
||||
.ok_or_else(|| reject("missing payload for tx root"))?;
|
||||
let resp: ForkVersionedResponse<_> = ForkVersionedResponse {
|
||||
version: Some(fork_name),
|
||||
metadata: Default::default(),
|
||||
data: payload,
|
||||
};
|
||||
|
||||
let json_payload = serde_json::to_string(&resp)
|
||||
.map_err(|_| reject("coudn't serialize response"))?;
|
||||
Ok::<_, warp::reject::Rejection>(
|
||||
warp::http::Response::builder()
|
||||
.status(200)
|
||||
.body(
|
||||
serde_json::to_string(&json_payload)
|
||||
.map_err(|_| reject("nvalid JSON"))?,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
},
|
||||
);
|
||||
let json_payload = serde_json::to_string(&resp)
|
||||
.map_err(|_| reject("coudn't serialize response"))?;
|
||||
Ok::<_, warp::reject::Rejection>(
|
||||
warp::http::Response::builder()
|
||||
.status(200)
|
||||
.body(
|
||||
serde_json::to_string(&json_payload)
|
||||
.map_err(|_| reject("invalid JSON"))?,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
let status = prefix
|
||||
.and(warp::path("status"))
|
||||
@@ -463,8 +480,8 @@ pub fn serve<E: EthSpec>(
|
||||
.get_randao_mix(head_state.current_epoch())
|
||||
.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::Base | ForkName::Altair | ForkName::Bellatrix => None,
|
||||
ForkName::Capella | ForkName::Deneb | ForkName::Electra => Some(
|
||||
builder
|
||||
.beacon_client
|
||||
.get_expected_withdrawals(&StateId::Head)
|
||||
@@ -479,14 +496,14 @@ pub fn serve<E: EthSpec>(
|
||||
// first to avoid polluting the execution block generator with invalid payload attributes
|
||||
// NOTE: this was part of an effort to add payload attribute uniqueness checks,
|
||||
// which was abandoned because it broke too many tests in subtle ways.
|
||||
ForkName::Merge | ForkName::Capella => PayloadAttributes::new(
|
||||
ForkName::Bellatrix | ForkName::Capella => PayloadAttributes::new(
|
||||
timestamp,
|
||||
*prev_randao,
|
||||
fee_recipient,
|
||||
expected_withdrawals,
|
||||
None,
|
||||
),
|
||||
ForkName::Deneb => PayloadAttributes::new(
|
||||
ForkName::Deneb | ForkName::Electra => PayloadAttributes::new(
|
||||
timestamp,
|
||||
*prev_randao,
|
||||
fee_recipient,
|
||||
@@ -530,6 +547,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()
|
||||
@@ -549,9 +577,9 @@ pub fn serve<E: EthSpec>(
|
||||
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
|
||||
pubkey: builder.builder_sk.public_key().compress(),
|
||||
}),
|
||||
ForkName::Merge => BuilderBid::Merge(BuilderBidMerge {
|
||||
ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix {
|
||||
header: payload
|
||||
.as_merge()
|
||||
.as_bellatrix()
|
||||
.map_err(|_| reject("incorrect payload variant"))?
|
||||
.into(),
|
||||
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
|
||||
@@ -569,6 +597,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()
|
||||
@@ -588,9 +627,9 @@ pub fn serve<E: EthSpec>(
|
||||
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
|
||||
pubkey: builder.builder_sk.public_key().compress(),
|
||||
}),
|
||||
ForkName::Merge => BuilderBid::Merge(BuilderBidMerge {
|
||||
ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix {
|
||||
header: payload
|
||||
.as_merge()
|
||||
.as_bellatrix()
|
||||
.map_err(|_| reject("incorrect payload variant"))?
|
||||
.into(),
|
||||
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
|
||||
|
||||
@@ -2,23 +2,21 @@ use crate::{
|
||||
test_utils::{
|
||||
MockServer, DEFAULT_JWT_SECRET, DEFAULT_TERMINAL_BLOCK, DEFAULT_TERMINAL_DIFFICULTY,
|
||||
},
|
||||
Config, *,
|
||||
*,
|
||||
};
|
||||
use keccak_hash::H256;
|
||||
use kzg::Kzg;
|
||||
use sensitive_url::SensitiveUrl;
|
||||
use task_executor::TaskExecutor;
|
||||
use tempfile::NamedTempFile;
|
||||
use types::{Address, ChainSpec, Epoch, EthSpec, Hash256, MainnetEthSpec};
|
||||
use types::MainnetEthSpec;
|
||||
|
||||
pub struct MockExecutionLayer<T: EthSpec> {
|
||||
pub server: MockServer<T>,
|
||||
pub el: ExecutionLayer<T>,
|
||||
pub struct MockExecutionLayer<E: EthSpec> {
|
||||
pub server: MockServer<E>,
|
||||
pub el: ExecutionLayer<E>,
|
||||
pub executor: TaskExecutor,
|
||||
pub spec: ChainSpec,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
pub fn default_params(executor: TaskExecutor) -> Self {
|
||||
let mut spec = MainnetEthSpec::default_spec();
|
||||
spec.terminal_total_difficulty = DEFAULT_TERMINAL_DIFFICULTY.into();
|
||||
@@ -29,6 +27,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
DEFAULT_TERMINAL_BLOCK,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
|
||||
spec,
|
||||
None,
|
||||
@@ -41,9 +40,10 @@ 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>,
|
||||
kzg: Option<Arc<Kzg>>,
|
||||
) -> Self {
|
||||
let handle = executor.handle().unwrap();
|
||||
|
||||
@@ -56,6 +56,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
spec.terminal_block_hash,
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
kzg,
|
||||
);
|
||||
|
||||
@@ -66,8 +67,8 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
std::fs::write(&path, hex::encode(DEFAULT_JWT_SECRET)).unwrap();
|
||||
|
||||
let config = Config {
|
||||
execution_endpoints: vec![url],
|
||||
secret_files: vec![path],
|
||||
execution_endpoint: Some(url),
|
||||
secret_file: Some(path),
|
||||
suggested_fee_recipient: Some(Address::repeat_byte(42)),
|
||||
..Default::default()
|
||||
};
|
||||
@@ -137,7 +138,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
&payload_attributes,
|
||||
forkchoice_update_params,
|
||||
builder_params,
|
||||
ForkName::Merge,
|
||||
ForkName::Bellatrix,
|
||||
&self.spec,
|
||||
None,
|
||||
BlockProductionVersion::FullV2,
|
||||
@@ -145,7 +146,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let payload: ExecutionPayload<T> = match block_proposal_content_type {
|
||||
let payload: ExecutionPayload<E> = match block_proposal_content_type {
|
||||
BlockProposalContentsType::Full(block) => block.to_payload().into(),
|
||||
BlockProposalContentsType::Blinded(_) => panic!("Should always be a full payload"),
|
||||
};
|
||||
@@ -177,7 +178,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
&payload_attributes,
|
||||
forkchoice_update_params,
|
||||
builder_params,
|
||||
ForkName::Merge,
|
||||
ForkName::Bellatrix,
|
||||
&self.spec,
|
||||
None,
|
||||
BlockProductionVersion::BlindedV2,
|
||||
@@ -218,9 +219,9 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn assert_valid_execution_payload_on_head<Payload: AbstractExecPayload<T>>(
|
||||
pub async fn assert_valid_execution_payload_on_head<Payload: AbstractExecPayload<E>>(
|
||||
&self,
|
||||
payload: ExecutionPayload<T>,
|
||||
payload: ExecutionPayload<E>,
|
||||
payload_header: Payload,
|
||||
block_hash: ExecutionBlockHash,
|
||||
parent_hash: ExecutionBlockHash,
|
||||
@@ -306,7 +307,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
||||
|
||||
pub async fn with_terminal_block<'a, U, V>(self, func: U) -> Self
|
||||
where
|
||||
U: Fn(ChainSpec, ExecutionLayer<T>, Option<ExecutionBlock>) -> V,
|
||||
U: Fn(ChainSpec, ExecutionLayer<E>, Option<ExecutionBlock>) -> V,
|
||||
V: Future<Output = ()>,
|
||||
{
|
||||
let terminal_block_number = self
|
||||
|
||||
@@ -4,11 +4,13 @@ use crate::engine_api::auth::JwtKey;
|
||||
use crate::engine_api::{
|
||||
auth::Auth, http::JSONRPC_VERSION, ExecutionBlock, PayloadStatusV1, PayloadStatusV1Status,
|
||||
};
|
||||
use crate::json_structures::JsonClientVersionV1;
|
||||
use bytes::Bytes;
|
||||
use environment::null_logger;
|
||||
use execution_block_generator::PoWBlock;
|
||||
use handle_rpc::handle_rpc;
|
||||
use kzg::Kzg;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
@@ -49,8 +51,18 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
|
||||
get_payload_v1: true,
|
||||
get_payload_v2: true,
|
||||
get_payload_v3: true,
|
||||
get_client_version_v1: true,
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref DEFAULT_CLIENT_VERSION: JsonClientVersionV1 = JsonClientVersionV1 {
|
||||
code: "MC".to_string(), // "mock client"
|
||||
name: "Mock Execution Client".to_string(),
|
||||
version: "0.1.0".to_string(),
|
||||
commit: "0xabcdef01".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
mod execution_block_generator;
|
||||
mod handle_rpc;
|
||||
mod hook;
|
||||
@@ -58,6 +70,7 @@ mod mock_builder;
|
||||
mod mock_execution_layer;
|
||||
|
||||
/// Configuration for the MockExecutionLayer.
|
||||
#[derive(Clone)]
|
||||
pub struct MockExecutionConfig {
|
||||
pub server_config: Config,
|
||||
pub jwt_key: JwtKey,
|
||||
@@ -66,6 +79,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,18 +92,19 @@ impl Default for MockExecutionConfig {
|
||||
server_config: Config::default(),
|
||||
shanghai_time: None,
|
||||
cancun_time: None,
|
||||
prague_time: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MockServer<T: EthSpec> {
|
||||
pub struct MockServer<E: EthSpec> {
|
||||
_shutdown_tx: oneshot::Sender<()>,
|
||||
listen_socket_addr: SocketAddr,
|
||||
last_echo_request: Arc<RwLock<Option<Bytes>>>,
|
||||
pub ctx: Arc<Context<T>>,
|
||||
pub ctx: Arc<Context<E>>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> MockServer<T> {
|
||||
impl<E: EthSpec> MockServer<E> {
|
||||
pub fn unit_testing() -> Self {
|
||||
Self::new(
|
||||
&runtime::Handle::current(),
|
||||
@@ -99,14 +114,15 @@ 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,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_with_config(
|
||||
handle: &runtime::Handle,
|
||||
config: MockExecutionConfig,
|
||||
kzg: Option<Kzg>,
|
||||
kzg: Option<Arc<Kzg>>,
|
||||
) -> Self {
|
||||
let MockExecutionConfig {
|
||||
jwt_key,
|
||||
@@ -116,6 +132,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,10 +142,11 @@ impl<T: EthSpec> MockServer<T> {
|
||||
terminal_block_hash,
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
kzg,
|
||||
);
|
||||
|
||||
let ctx: Arc<Context<T>> = Arc::new(Context {
|
||||
let ctx: Arc<Context<E>> = Arc::new(Context {
|
||||
config: server_config,
|
||||
jwt_key,
|
||||
log: null_logger().unwrap(),
|
||||
@@ -187,7 +205,8 @@ impl<T: EthSpec> MockServer<T> {
|
||||
terminal_block_hash: ExecutionBlockHash,
|
||||
shanghai_time: Option<u64>,
|
||||
cancun_time: Option<u64>,
|
||||
kzg: Option<Kzg>,
|
||||
prague_time: Option<u64>,
|
||||
kzg: Option<Arc<Kzg>>,
|
||||
) -> Self {
|
||||
Self::new_with_config(
|
||||
handle,
|
||||
@@ -199,12 +218,13 @@ impl<T: EthSpec> MockServer<T> {
|
||||
terminal_block_hash,
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
},
|
||||
kzg,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn execution_block_generator(&self) -> RwLockWriteGuard<'_, ExecutionBlockGenerator<T>> {
|
||||
pub fn execution_block_generator(&self) -> RwLockWriteGuard<'_, ExecutionBlockGenerator<E>> {
|
||||
self.ctx.execution_block_generator.write()
|
||||
}
|
||||
|
||||
@@ -416,7 +436,7 @@ impl<T: EthSpec> MockServer<T> {
|
||||
.insert_block_without_checks(block);
|
||||
}
|
||||
|
||||
pub fn get_block(&self, block_hash: ExecutionBlockHash) -> Option<Block<T>> {
|
||||
pub fn get_block(&self, block_hash: ExecutionBlockHash) -> Option<Block<E>> {
|
||||
self.ctx
|
||||
.execution_block_generator
|
||||
.read()
|
||||
@@ -494,12 +514,12 @@ impl warp::reject::Reject for AuthError {}
|
||||
/// A wrapper around all the items required to spawn the HTTP server.
|
||||
///
|
||||
/// The server will gracefully handle the case where any fields are `None`.
|
||||
pub struct Context<T: EthSpec> {
|
||||
pub struct Context<E: EthSpec> {
|
||||
pub config: Config,
|
||||
pub jwt_key: JwtKey,
|
||||
pub log: Logger,
|
||||
pub last_echo_request: Arc<RwLock<Option<Bytes>>>,
|
||||
pub execution_block_generator: RwLock<ExecutionBlockGenerator<T>>,
|
||||
pub execution_block_generator: RwLock<ExecutionBlockGenerator<E>>,
|
||||
pub preloaded_responses: Arc<Mutex<Vec<serde_json::Value>>>,
|
||||
pub previous_request: Arc<Mutex<Option<serde_json::Value>>>,
|
||||
pub static_new_payload_response: Arc<Mutex<Option<StaticNewPayloadResponse>>>,
|
||||
@@ -518,10 +538,10 @@ pub struct Context<T: EthSpec> {
|
||||
pub syncing_response: Arc<Mutex<Result<bool, String>>>,
|
||||
|
||||
pub engine_capabilities: Arc<RwLock<EngineCapabilities>>,
|
||||
pub _phantom: PhantomData<T>,
|
||||
pub _phantom: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> Context<T> {
|
||||
impl<E: EthSpec> Context<E> {
|
||||
pub fn get_new_payload_status(
|
||||
&self,
|
||||
block_hash: &ExecutionBlockHash,
|
||||
@@ -630,8 +650,8 @@ async fn handle_rejection(err: Rejection) -> Result<impl warp::Reply, Infallible
|
||||
///
|
||||
/// Returns an error if the server is unable to bind or there is another error during
|
||||
/// configuration.
|
||||
pub fn serve<T: EthSpec>(
|
||||
ctx: Arc<Context<T>>,
|
||||
pub fn serve<E: EthSpec>(
|
||||
ctx: Arc<Context<E>>,
|
||||
shutdown: impl Future<Output = ()> + Send + Sync + 'static,
|
||||
) -> Result<(SocketAddr, impl Future<Output = ()>), Error> {
|
||||
let config = &ctx.config;
|
||||
@@ -646,7 +666,7 @@ pub fn serve<T: EthSpec>(
|
||||
let root = warp::path::end()
|
||||
.and(warp::body::json())
|
||||
.and(ctx_filter.clone())
|
||||
.and_then(|body: serde_json::Value, ctx: Arc<Context<T>>| async move {
|
||||
.and_then(|body: serde_json::Value, ctx: Arc<Context<E>>| async move {
|
||||
let id = body
|
||||
.get("id")
|
||||
.and_then(serde_json::Value::as_u64)
|
||||
@@ -693,7 +713,7 @@ pub fn serve<T: EthSpec>(
|
||||
let echo = warp::path("echo")
|
||||
.and(warp::body::bytes())
|
||||
.and(ctx_filter)
|
||||
.and_then(|bytes: Bytes, ctx: Arc<Context<T>>| async move {
|
||||
.and_then(|bytes: Bytes, ctx: Arc<Context<E>>| async move {
|
||||
*ctx.last_echo_request.write() = Some(bytes.clone());
|
||||
Ok::<_, warp::reject::Rejection>(
|
||||
warp::http::Response::builder().status(200).body(bytes),
|
||||
|
||||
Reference in New Issue
Block a user