mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-07 00:42:42 +00:00
Add getPayload v2 methods
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
use crate::{BeaconChain, BeaconChainError, BeaconChainTypes, BlockProcessStatus, metrics};
|
use crate::{BeaconChain, BeaconChainError, BeaconChainTypes, BlockProcessStatus, metrics};
|
||||||
use execution_layer::{ExecutionLayer, ExecutionPayloadBodyV1};
|
use execution_layer::{ExecutionLayer, ExecutionPayloadBody};
|
||||||
use logging::crit;
|
use logging::crit;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -58,7 +58,7 @@ struct BodiesByRange<E: EthSpec> {
|
|||||||
struct BlockParts<E: EthSpec> {
|
struct BlockParts<E: EthSpec> {
|
||||||
blinded_block: Box<SignedBlindedBeaconBlock<E>>,
|
blinded_block: Box<SignedBlindedBeaconBlock<E>>,
|
||||||
header: Box<ExecutionPayloadHeader<E>>,
|
header: Box<ExecutionPayloadHeader<E>>,
|
||||||
body: Option<Box<ExecutionPayloadBodyV1<E>>>,
|
body: Option<Box<ExecutionPayloadBody<E>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: EthSpec> BlockParts<E> {
|
impl<E: EthSpec> BlockParts<E> {
|
||||||
@@ -634,7 +634,9 @@ impl<T: BeaconChainTypes> BeaconBlockStreamer<T> {
|
|||||||
.map_err(BeaconChainError::EngineGetCapabilititesFailed)
|
.map_err(BeaconChainError::EngineGetCapabilititesFailed)
|
||||||
{
|
{
|
||||||
Ok(engine_capabilities) => {
|
Ok(engine_capabilities) => {
|
||||||
if engine_capabilities.get_payload_bodies_by_range_v1 {
|
if engine_capabilities.get_payload_bodies_by_range_v2
|
||||||
|
|| engine_capabilities.get_payload_bodies_by_range_v1
|
||||||
|
{
|
||||||
self.stream_blocks(block_roots, sender).await;
|
self.stream_blocks(block_roots, sender).await;
|
||||||
} else {
|
} else {
|
||||||
// use the fallback method
|
// use the fallback method
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ use crate::http::{
|
|||||||
ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_FORKCHOICE_UPDATED_V3,
|
ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_FORKCHOICE_UPDATED_V3,
|
||||||
ENGINE_FORKCHOICE_UPDATED_V4, ENGINE_GET_BLOBS_V1, ENGINE_GET_BLOBS_V2,
|
ENGINE_FORKCHOICE_UPDATED_V4, ENGINE_GET_BLOBS_V1, ENGINE_GET_BLOBS_V2,
|
||||||
ENGINE_GET_CLIENT_VERSION_V1, ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1,
|
ENGINE_GET_CLIENT_VERSION_V1, ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1,
|
||||||
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1, ENGINE_GET_PAYLOAD_V1, ENGINE_GET_PAYLOAD_V2,
|
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2, ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1,
|
||||||
|
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2, ENGINE_GET_PAYLOAD_V1, ENGINE_GET_PAYLOAD_V2,
|
||||||
ENGINE_GET_PAYLOAD_V3, ENGINE_GET_PAYLOAD_V4, ENGINE_GET_PAYLOAD_V5, ENGINE_GET_PAYLOAD_V6,
|
ENGINE_GET_PAYLOAD_V3, ENGINE_GET_PAYLOAD_V4, ENGINE_GET_PAYLOAD_V5, ENGINE_GET_PAYLOAD_V6,
|
||||||
ENGINE_NEW_PAYLOAD_V1, ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V4,
|
ENGINE_NEW_PAYLOAD_V1, ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V4,
|
||||||
ENGINE_NEW_PAYLOAD_V5,
|
ENGINE_NEW_PAYLOAD_V5,
|
||||||
@@ -425,10 +426,18 @@ impl<E: EthSpec> GetPayloadResponse<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[superstruct(
|
||||||
|
variants(V1, V2),
|
||||||
|
variant_attributes(derive(Clone, Debug)),
|
||||||
|
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
|
||||||
|
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
|
||||||
|
)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ExecutionPayloadBodyV1<E: EthSpec> {
|
pub struct ExecutionPayloadBody<E: EthSpec> {
|
||||||
pub transactions: Transactions<E>,
|
pub transactions: Transactions<E>,
|
||||||
pub withdrawals: Option<Withdrawals<E>>,
|
pub withdrawals: Option<Withdrawals<E>>,
|
||||||
|
#[superstruct(only(V2))]
|
||||||
|
pub block_access_list: Option<VariableList<u8, E::MaxBytesPerTransaction>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
|
impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
|
||||||
@@ -608,6 +617,195 @@ impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> ExecutionPayloadBodyV2<E> {
|
||||||
|
pub fn to_payload(
|
||||||
|
self,
|
||||||
|
header: ExecutionPayloadHeader<E>,
|
||||||
|
) -> Result<ExecutionPayload<E>, String> {
|
||||||
|
match header {
|
||||||
|
ExecutionPayloadHeader::Bellatrix(header) => {
|
||||||
|
if self.withdrawals.is_some() {
|
||||||
|
return Err(format!(
|
||||||
|
"block {} is bellatrix but payload body has withdrawals",
|
||||||
|
header.block_hash
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(ExecutionPayload::Bellatrix(ExecutionPayloadBellatrix {
|
||||||
|
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,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
ExecutionPayloadHeader::Capella(header) => {
|
||||||
|
if let Some(withdrawals) = self.withdrawals {
|
||||||
|
Ok(ExecutionPayload::Capella(ExecutionPayloadCapella {
|
||||||
|
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,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"block {} is capella but payload body doesn't have withdrawals",
|
||||||
|
header.block_hash
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExecutionPayloadHeader::Deneb(header) => {
|
||||||
|
if let Some(withdrawals) = self.withdrawals {
|
||||||
|
Ok(ExecutionPayload::Deneb(ExecutionPayloadDeneb {
|
||||||
|
parent_hash: header.parent_hash,
|
||||||
|
fee_recipient: header.fee_recipient,
|
||||||
|
state_root: header.state_root,
|
||||||
|
receipts_root: header.receipts_root,
|
||||||
|
logs_bloom: header.logs_bloom,
|
||||||
|
prev_randao: header.prev_randao,
|
||||||
|
block_number: header.block_number,
|
||||||
|
gas_limit: header.gas_limit,
|
||||||
|
gas_used: header.gas_used,
|
||||||
|
timestamp: header.timestamp,
|
||||||
|
extra_data: header.extra_data,
|
||||||
|
base_fee_per_gas: header.base_fee_per_gas,
|
||||||
|
block_hash: header.block_hash,
|
||||||
|
transactions: self.transactions,
|
||||||
|
withdrawals,
|
||||||
|
blob_gas_used: header.blob_gas_used,
|
||||||
|
excess_blob_gas: header.excess_blob_gas,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"block {} is post capella but payload body doesn't have withdrawals",
|
||||||
|
header.block_hash
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExecutionPayloadHeader::Electra(header) => {
|
||||||
|
if let Some(withdrawals) = self.withdrawals {
|
||||||
|
Ok(ExecutionPayload::Electra(ExecutionPayloadElectra {
|
||||||
|
parent_hash: header.parent_hash,
|
||||||
|
fee_recipient: header.fee_recipient,
|
||||||
|
state_root: header.state_root,
|
||||||
|
receipts_root: header.receipts_root,
|
||||||
|
logs_bloom: header.logs_bloom,
|
||||||
|
prev_randao: header.prev_randao,
|
||||||
|
block_number: header.block_number,
|
||||||
|
gas_limit: header.gas_limit,
|
||||||
|
gas_used: header.gas_used,
|
||||||
|
timestamp: header.timestamp,
|
||||||
|
extra_data: header.extra_data,
|
||||||
|
base_fee_per_gas: header.base_fee_per_gas,
|
||||||
|
block_hash: header.block_hash,
|
||||||
|
transactions: self.transactions,
|
||||||
|
withdrawals,
|
||||||
|
blob_gas_used: header.blob_gas_used,
|
||||||
|
excess_blob_gas: header.excess_blob_gas,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"block {} is post capella but payload body doesn't have withdrawals",
|
||||||
|
header.block_hash
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExecutionPayloadHeader::Fulu(header) => {
|
||||||
|
if let Some(withdrawals) = self.withdrawals {
|
||||||
|
Ok(ExecutionPayload::Fulu(ExecutionPayloadFulu {
|
||||||
|
parent_hash: header.parent_hash,
|
||||||
|
fee_recipient: header.fee_recipient,
|
||||||
|
state_root: header.state_root,
|
||||||
|
receipts_root: header.receipts_root,
|
||||||
|
logs_bloom: header.logs_bloom,
|
||||||
|
prev_randao: header.prev_randao,
|
||||||
|
block_number: header.block_number,
|
||||||
|
gas_limit: header.gas_limit,
|
||||||
|
gas_used: header.gas_used,
|
||||||
|
timestamp: header.timestamp,
|
||||||
|
extra_data: header.extra_data,
|
||||||
|
base_fee_per_gas: header.base_fee_per_gas,
|
||||||
|
block_hash: header.block_hash,
|
||||||
|
transactions: self.transactions,
|
||||||
|
withdrawals,
|
||||||
|
blob_gas_used: header.blob_gas_used,
|
||||||
|
excess_blob_gas: header.excess_blob_gas,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"block {} is post capella but payload body doesn't have withdrawals",
|
||||||
|
header.block_hash
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExecutionPayloadHeader::Gloas(header) => {
|
||||||
|
if let Some(withdrawals) = self.withdrawals {
|
||||||
|
Ok(ExecutionPayload::Gloas(ExecutionPayloadGloas {
|
||||||
|
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,
|
||||||
|
// V2 provides block_access_list from EL; use empty if not present
|
||||||
|
block_access_list: self
|
||||||
|
.block_access_list
|
||||||
|
.unwrap_or_else(VariableList::empty),
|
||||||
|
slot_number: header.slot_number,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"block {} is post capella but payload body doesn't have withdrawals",
|
||||||
|
header.block_hash
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> ExecutionPayloadBody<E> {
|
||||||
|
pub fn to_payload(
|
||||||
|
self,
|
||||||
|
header: ExecutionPayloadHeader<E>,
|
||||||
|
) -> Result<ExecutionPayload<E>, String> {
|
||||||
|
match self {
|
||||||
|
ExecutionPayloadBody::V1(body) => body.to_payload(header),
|
||||||
|
ExecutionPayloadBody::V2(body) => body.to_payload(header),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct EngineCapabilities {
|
pub struct EngineCapabilities {
|
||||||
pub new_payload_v1: bool,
|
pub new_payload_v1: bool,
|
||||||
@@ -620,7 +818,9 @@ pub struct EngineCapabilities {
|
|||||||
pub forkchoice_updated_v3: bool,
|
pub forkchoice_updated_v3: bool,
|
||||||
pub forkchoice_updated_v4: bool,
|
pub forkchoice_updated_v4: bool,
|
||||||
pub get_payload_bodies_by_hash_v1: bool,
|
pub get_payload_bodies_by_hash_v1: bool,
|
||||||
|
pub get_payload_bodies_by_hash_v2: bool,
|
||||||
pub get_payload_bodies_by_range_v1: bool,
|
pub get_payload_bodies_by_range_v1: bool,
|
||||||
|
pub get_payload_bodies_by_range_v2: bool,
|
||||||
pub get_payload_v1: bool,
|
pub get_payload_v1: bool,
|
||||||
pub get_payload_v2: bool,
|
pub get_payload_v2: bool,
|
||||||
pub get_payload_v3: bool,
|
pub get_payload_v3: bool,
|
||||||
@@ -665,9 +865,15 @@ impl EngineCapabilities {
|
|||||||
if self.get_payload_bodies_by_hash_v1 {
|
if self.get_payload_bodies_by_hash_v1 {
|
||||||
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1);
|
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1);
|
||||||
}
|
}
|
||||||
|
if self.get_payload_bodies_by_hash_v2 {
|
||||||
|
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2);
|
||||||
|
}
|
||||||
if self.get_payload_bodies_by_range_v1 {
|
if self.get_payload_bodies_by_range_v1 {
|
||||||
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1);
|
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1);
|
||||||
}
|
}
|
||||||
|
if self.get_payload_bodies_by_range_v2 {
|
||||||
|
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2);
|
||||||
|
}
|
||||||
if self.get_payload_v1 {
|
if self.get_payload_v1 {
|
||||||
response.push(ENGINE_GET_PAYLOAD_V1);
|
response.push(ENGINE_GET_PAYLOAD_V1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,9 @@ pub const ENGINE_FORKCHOICE_UPDATED_V4: &str = "engine_forkchoiceUpdatedV4";
|
|||||||
pub const ENGINE_FORKCHOICE_UPDATED_TIMEOUT: Duration = Duration::from_secs(8);
|
pub const ENGINE_FORKCHOICE_UPDATED_TIMEOUT: Duration = Duration::from_secs(8);
|
||||||
|
|
||||||
pub const ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1: &str = "engine_getPayloadBodiesByHashV1";
|
pub const ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1: &str = "engine_getPayloadBodiesByHashV1";
|
||||||
|
pub const ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2: &str = "engine_getPayloadBodiesByHashV2";
|
||||||
pub const ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1: &str = "engine_getPayloadBodiesByRangeV1";
|
pub const ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1: &str = "engine_getPayloadBodiesByRangeV1";
|
||||||
|
pub const ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2: &str = "engine_getPayloadBodiesByRangeV2";
|
||||||
pub const ENGINE_GET_PAYLOAD_BODIES_TIMEOUT: Duration = Duration::from_secs(10);
|
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: &str = "engine_exchangeCapabilities";
|
||||||
@@ -90,7 +92,9 @@ pub static LIGHTHOUSE_CAPABILITIES: &[&str] = &[
|
|||||||
ENGINE_FORKCHOICE_UPDATED_V3,
|
ENGINE_FORKCHOICE_UPDATED_V3,
|
||||||
ENGINE_FORKCHOICE_UPDATED_V4,
|
ENGINE_FORKCHOICE_UPDATED_V4,
|
||||||
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1,
|
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1,
|
||||||
|
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2,
|
||||||
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1,
|
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1,
|
||||||
|
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2,
|
||||||
ENGINE_GET_CLIENT_VERSION_V1,
|
ENGINE_GET_CLIENT_VERSION_V1,
|
||||||
ENGINE_GET_BLOBS_V1,
|
ENGINE_GET_BLOBS_V1,
|
||||||
ENGINE_GET_BLOBS_V2,
|
ENGINE_GET_BLOBS_V2,
|
||||||
@@ -1364,6 +1368,58 @@ impl HttpJsonRpc {
|
|||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_payload_bodies_by_hash_v2<E: EthSpec>(
|
||||||
|
&self,
|
||||||
|
block_hashes: Vec<ExecutionBlockHash>,
|
||||||
|
) -> Result<Vec<Option<ExecutionPayloadBodyV2<E>>>, Error> {
|
||||||
|
let params = json!([block_hashes]);
|
||||||
|
|
||||||
|
let response: Vec<Option<JsonExecutionPayloadBodyV2<E>>> = self
|
||||||
|
.rpc_request(
|
||||||
|
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2,
|
||||||
|
params,
|
||||||
|
ENGINE_GET_PAYLOAD_BODIES_TIMEOUT * self.execution_timeout_multiplier,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
response
|
||||||
|
.into_iter()
|
||||||
|
.map(|opt_json| {
|
||||||
|
opt_json
|
||||||
|
.map(|json| json.try_into().map_err(Error::from))
|
||||||
|
.transpose()
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_payload_bodies_by_range_v2<E: EthSpec>(
|
||||||
|
&self,
|
||||||
|
start: u64,
|
||||||
|
count: u64,
|
||||||
|
) -> Result<Vec<Option<ExecutionPayloadBodyV2<E>>>, Error> {
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct Quantity(#[serde(with = "serde_utils::u64_hex_be")] u64);
|
||||||
|
|
||||||
|
let params = json!([Quantity(start), Quantity(count)]);
|
||||||
|
let response: Vec<Option<JsonExecutionPayloadBodyV2<E>>> = self
|
||||||
|
.rpc_request(
|
||||||
|
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2,
|
||||||
|
params,
|
||||||
|
ENGINE_GET_PAYLOAD_BODIES_TIMEOUT * self.execution_timeout_multiplier,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
response
|
||||||
|
.into_iter()
|
||||||
|
.map(|opt_json| {
|
||||||
|
opt_json
|
||||||
|
.map(|json| json.try_into().map_err(Error::from))
|
||||||
|
.transpose()
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn exchange_capabilities(&self) -> Result<EngineCapabilities, Error> {
|
pub async fn exchange_capabilities(&self) -> Result<EngineCapabilities, Error> {
|
||||||
let params = json!([LIGHTHOUSE_CAPABILITIES]);
|
let params = json!([LIGHTHOUSE_CAPABILITIES]);
|
||||||
|
|
||||||
@@ -1387,8 +1443,12 @@ impl HttpJsonRpc {
|
|||||||
forkchoice_updated_v4: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V4),
|
forkchoice_updated_v4: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V4),
|
||||||
get_payload_bodies_by_hash_v1: capabilities
|
get_payload_bodies_by_hash_v1: capabilities
|
||||||
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1),
|
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1),
|
||||||
|
get_payload_bodies_by_hash_v2: capabilities
|
||||||
|
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V2),
|
||||||
get_payload_bodies_by_range_v1: capabilities
|
get_payload_bodies_by_range_v1: capabilities
|
||||||
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1),
|
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1),
|
||||||
|
get_payload_bodies_by_range_v2: capabilities
|
||||||
|
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V2),
|
||||||
get_payload_v1: capabilities.contains(ENGINE_GET_PAYLOAD_V1),
|
get_payload_v1: capabilities.contains(ENGINE_GET_PAYLOAD_V1),
|
||||||
get_payload_v2: capabilities.contains(ENGINE_GET_PAYLOAD_V2),
|
get_payload_v2: capabilities.contains(ENGINE_GET_PAYLOAD_V2),
|
||||||
get_payload_v3: capabilities.contains(ENGINE_GET_PAYLOAD_V3),
|
get_payload_v3: capabilities.contains(ENGINE_GET_PAYLOAD_V3),
|
||||||
|
|||||||
@@ -1020,12 +1020,24 @@ impl From<ForkchoiceUpdatedResponse> for JsonForkchoiceUpdatedV1Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[superstruct(
|
||||||
|
variants(V1, V2),
|
||||||
|
variant_attributes(
|
||||||
|
derive(Clone, Debug, Serialize, Deserialize),
|
||||||
|
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, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[serde(bound = "E: EthSpec")]
|
#[serde(bound = "E: EthSpec", rename_all = "camelCase")]
|
||||||
pub struct JsonExecutionPayloadBodyV1<E: EthSpec> {
|
pub struct JsonExecutionPayloadBody<E: EthSpec> {
|
||||||
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
|
#[serde(with = "ssz_types::serde_utils::list_of_hex_var_list")]
|
||||||
pub transactions: Transactions<E>,
|
pub transactions: Transactions<E>,
|
||||||
pub withdrawals: Option<VariableList<JsonWithdrawal, E::MaxWithdrawalsPerPayload>>,
|
pub withdrawals: Option<VariableList<JsonWithdrawal, E::MaxWithdrawalsPerPayload>>,
|
||||||
|
#[superstruct(only(V2))]
|
||||||
|
#[serde(with = "optional_hex_var_list")]
|
||||||
|
pub block_access_list: Option<VariableList<u8, E::MaxBytesPerTransaction>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: EthSpec> TryFrom<JsonExecutionPayloadBodyV1<E>> for ExecutionPayloadBodyV1<E> {
|
impl<E: EthSpec> TryFrom<JsonExecutionPayloadBodyV1<E>> for ExecutionPayloadBodyV1<E> {
|
||||||
@@ -1050,6 +1062,30 @@ impl<E: EthSpec> TryFrom<ExecutionPayloadBodyV1<E>> for JsonExecutionPayloadBody
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> TryFrom<JsonExecutionPayloadBodyV2<E>> for ExecutionPayloadBodyV2<E> {
|
||||||
|
type Error = ssz_types::Error;
|
||||||
|
|
||||||
|
fn try_from(value: JsonExecutionPayloadBodyV2<E>) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
transactions: value.transactions,
|
||||||
|
withdrawals: value.withdrawals.map(withdrawals_from_json).transpose()?,
|
||||||
|
block_access_list: value.block_access_list,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> TryFrom<ExecutionPayloadBodyV2<E>> for JsonExecutionPayloadBodyV2<E> {
|
||||||
|
type Error = ssz_types::Error;
|
||||||
|
|
||||||
|
fn try_from(value: ExecutionPayloadBodyV2<E>) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
transactions: value.transactions,
|
||||||
|
withdrawals: value.withdrawals.map(withdrawals_to_json).transpose()?,
|
||||||
|
block_access_list: value.block_access_list,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct TransitionConfigurationV1 {
|
pub struct TransitionConfigurationV1 {
|
||||||
@@ -1090,6 +1126,50 @@ pub mod serde_logs_bloom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Serializes an optional hex variable list field (e.g., blockAccessList in EIP-7928).
|
||||||
|
/// JSON `null` maps to `None`, hex string maps to `Some(VariableList<u8, N>)`.
|
||||||
|
pub mod optional_hex_var_list {
|
||||||
|
use super::*;
|
||||||
|
use serde::{Deserialize, Deserializer, Serializer};
|
||||||
|
|
||||||
|
pub fn serialize<S, N>(
|
||||||
|
opt_bytes: &Option<VariableList<u8, N>>,
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
N: Unsigned,
|
||||||
|
{
|
||||||
|
match opt_bytes {
|
||||||
|
Some(bytes) => {
|
||||||
|
let mut hex_string: String = "0x".to_string();
|
||||||
|
hex_string.push_str(&hex::encode(&bytes[..]));
|
||||||
|
serializer.serialize_str(&hex_string)
|
||||||
|
}
|
||||||
|
None => serializer.serialize_none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D, N>(deserializer: D) -> Result<Option<VariableList<u8, N>>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
N: Unsigned,
|
||||||
|
{
|
||||||
|
let opt: Option<String> = Option::deserialize(deserializer)?;
|
||||||
|
match opt {
|
||||||
|
Some(hex_str) => {
|
||||||
|
let hex_str = hex_str.strip_prefix("0x").unwrap_or(&hex_str);
|
||||||
|
let bytes = hex::decode(hex_str)
|
||||||
|
.map_err(|e| serde::de::Error::custom(format!("invalid hex: {:?}", e)))?;
|
||||||
|
VariableList::new(bytes)
|
||||||
|
.map(Some)
|
||||||
|
.map_err(|e| serde::de::Error::custom(format!("invalid var list: {:?}", e)))
|
||||||
|
}
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct JsonClientVersionV1 {
|
pub struct JsonClientVersionV1 {
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ pub enum Error {
|
|||||||
},
|
},
|
||||||
ZeroLengthTransaction,
|
ZeroLengthTransaction,
|
||||||
PayloadBodiesByRangeNotSupported,
|
PayloadBodiesByRangeNotSupported,
|
||||||
|
PayloadBodiesByHashNotSupported,
|
||||||
GetBlobsNotSupported,
|
GetBlobsNotSupported,
|
||||||
InvalidJWTSecret(String),
|
InvalidJWTSecret(String),
|
||||||
InvalidForkForPayload,
|
InvalidForkForPayload,
|
||||||
@@ -1787,32 +1788,87 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
|||||||
pub async fn get_payload_bodies_by_hash(
|
pub async fn get_payload_bodies_by_hash(
|
||||||
&self,
|
&self,
|
||||||
hashes: Vec<ExecutionBlockHash>,
|
hashes: Vec<ExecutionBlockHash>,
|
||||||
) -> Result<Vec<Option<ExecutionPayloadBodyV1<E>>>, Error> {
|
) -> Result<Vec<Option<ExecutionPayloadBody<E>>>, Error> {
|
||||||
self.engine()
|
let capabilities = self.get_engine_capabilities(None).await?;
|
||||||
.request(|engine: &Engine| async move {
|
|
||||||
engine.api.get_payload_bodies_by_hash_v1(hashes).await
|
if capabilities.get_payload_bodies_by_hash_v2 {
|
||||||
})
|
self.engine()
|
||||||
.await
|
.request(|engine: &Engine| async move {
|
||||||
.map_err(Box::new)
|
engine.api.get_payload_bodies_by_hash_v2(hashes).await
|
||||||
.map_err(Error::EngineError)
|
})
|
||||||
|
.await
|
||||||
|
.map(|bodies| {
|
||||||
|
bodies
|
||||||
|
.into_iter()
|
||||||
|
.map(|opt| opt.map(ExecutionPayloadBody::V2))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.map_err(Box::new)
|
||||||
|
.map_err(Error::EngineError)
|
||||||
|
} else if capabilities.get_payload_bodies_by_hash_v1 {
|
||||||
|
self.engine()
|
||||||
|
.request(|engine: &Engine| async move {
|
||||||
|
engine.api.get_payload_bodies_by_hash_v1::<E>(hashes).await
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(|bodies| {
|
||||||
|
bodies
|
||||||
|
.into_iter()
|
||||||
|
.map(|opt| opt.map(ExecutionPayloadBody::V1))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.map_err(Box::new)
|
||||||
|
.map_err(Error::EngineError)
|
||||||
|
} else {
|
||||||
|
Err(Error::PayloadBodiesByHashNotSupported)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_payload_bodies_by_range(
|
pub async fn get_payload_bodies_by_range(
|
||||||
&self,
|
&self,
|
||||||
start: u64,
|
start: u64,
|
||||||
count: u64,
|
count: u64,
|
||||||
) -> Result<Vec<Option<ExecutionPayloadBodyV1<E>>>, Error> {
|
) -> Result<Vec<Option<ExecutionPayloadBody<E>>>, Error> {
|
||||||
let _timer = metrics::start_timer(&metrics::EXECUTION_LAYER_GET_PAYLOAD_BODIES_BY_RANGE);
|
let _timer = metrics::start_timer(&metrics::EXECUTION_LAYER_GET_PAYLOAD_BODIES_BY_RANGE);
|
||||||
self.engine()
|
let capabilities = self.get_engine_capabilities(None).await?;
|
||||||
.request(|engine: &Engine| async move {
|
|
||||||
engine
|
if capabilities.get_payload_bodies_by_range_v2 {
|
||||||
.api
|
self.engine()
|
||||||
.get_payload_bodies_by_range_v1(start, count)
|
.request(|engine: &Engine| async move {
|
||||||
.await
|
engine
|
||||||
})
|
.api
|
||||||
.await
|
.get_payload_bodies_by_range_v2(start, count)
|
||||||
.map_err(Box::new)
|
.await
|
||||||
.map_err(Error::EngineError)
|
})
|
||||||
|
.await
|
||||||
|
.map(|bodies| {
|
||||||
|
bodies
|
||||||
|
.into_iter()
|
||||||
|
.map(|opt| opt.map(ExecutionPayloadBody::V2))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.map_err(Box::new)
|
||||||
|
.map_err(Error::EngineError)
|
||||||
|
} else if capabilities.get_payload_bodies_by_range_v1 {
|
||||||
|
self.engine()
|
||||||
|
.request(|engine: &Engine| async move {
|
||||||
|
engine
|
||||||
|
.api
|
||||||
|
.get_payload_bodies_by_range_v1::<E>(start, count)
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(|bodies| {
|
||||||
|
bodies
|
||||||
|
.into_iter()
|
||||||
|
.map(|opt| opt.map(ExecutionPayloadBody::V1))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.map_err(Box::new)
|
||||||
|
.map_err(Error::EngineError)
|
||||||
|
} else {
|
||||||
|
Err(Error::PayloadBodiesByRangeNotSupported)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch a full payload from the execution node.
|
/// Fetch a full payload from the execution node.
|
||||||
@@ -1845,7 +1901,9 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
|||||||
|
|
||||||
// Use efficient payload bodies by range method if supported.
|
// Use efficient payload bodies by range method if supported.
|
||||||
let capabilities = self.get_engine_capabilities(None).await?;
|
let capabilities = self.get_engine_capabilities(None).await?;
|
||||||
if capabilities.get_payload_bodies_by_range_v1 {
|
if capabilities.get_payload_bodies_by_range_v2
|
||||||
|
|| capabilities.get_payload_bodies_by_range_v1
|
||||||
|
{
|
||||||
let mut payload_bodies = self.get_payload_bodies_by_range(block_number, 1).await?;
|
let mut payload_bodies = self.get_payload_bodies_by_range(block_number, 1).await?;
|
||||||
|
|
||||||
if payload_bodies.len() != 1 {
|
if payload_bodies.len() != 1 {
|
||||||
|
|||||||
@@ -51,7 +51,9 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
|
|||||||
forkchoice_updated_v3: true,
|
forkchoice_updated_v3: true,
|
||||||
forkchoice_updated_v4: true,
|
forkchoice_updated_v4: true,
|
||||||
get_payload_bodies_by_hash_v1: true,
|
get_payload_bodies_by_hash_v1: true,
|
||||||
|
get_payload_bodies_by_hash_v2: true,
|
||||||
get_payload_bodies_by_range_v1: true,
|
get_payload_bodies_by_range_v1: true,
|
||||||
|
get_payload_bodies_by_range_v2: true,
|
||||||
get_payload_v1: true,
|
get_payload_v1: true,
|
||||||
get_payload_v2: true,
|
get_payload_v2: true,
|
||||||
get_payload_v3: true,
|
get_payload_v3: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user