mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-10 20:22:02 +00:00
Block v3 endpoint (#4629)
## Issue Addressed #4582 ## Proposed Changes Add a new v3 block fetching flow that can decide to return a Full OR Blinded payload ## Additional Info Co-authored-by: Michael Sproul <micsproul@gmail.com>
This commit is contained in:
@@ -42,13 +42,13 @@ use tokio_stream::wrappers::WatchStream;
|
||||
use tree_hash::TreeHash;
|
||||
use types::beacon_block_body::KzgCommitments;
|
||||
use types::builder_bid::BuilderBid;
|
||||
use types::payload::BlockProductionVersion;
|
||||
use types::sidecar::{BlobItems, Sidecar};
|
||||
use types::KzgProofs;
|
||||
use types::{AbstractExecPayload, ExecutionPayloadDeneb, KzgProofs};
|
||||
use types::{
|
||||
AbstractExecPayload, BeaconStateError, BlindedPayload, BlockType, ChainSpec, Epoch,
|
||||
ExecPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge,
|
||||
BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadCapella,
|
||||
ExecutionPayloadMerge, FullPayload, ProposerPreparationData, PublicKeyBytes, Signature, Slot,
|
||||
};
|
||||
use types::{ProposerPreparationData, PublicKeyBytes, Signature, Slot};
|
||||
|
||||
mod block_hash;
|
||||
mod engine_api;
|
||||
@@ -87,9 +87,7 @@ pub enum ProvenancedPayload<P> {
|
||||
Builder(P),
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<BuilderBid<E>>
|
||||
for ProvenancedPayload<BlockProposalContents<E, Payload>>
|
||||
{
|
||||
impl<E: EthSpec> TryFrom<BuilderBid<E>> for ProvenancedPayload<BlockProposalContentsType<E>> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: BuilderBid<E>) -> Result<Self, Error> {
|
||||
@@ -112,12 +110,16 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<BuilderBid<E>>
|
||||
.map_err(|_| Error::InvalidPayloadConversion)?,
|
||||
block_value: builder_bid.value,
|
||||
kzg_commitments: builder_bid.blinded_blobs_bundle.commitments,
|
||||
blobs: BlobItems::try_from_blob_roots(builder_bid.blinded_blobs_bundle.blob_roots)
|
||||
.map_err(Error::InvalidBlobConversion)?,
|
||||
blobs: BlobItems::<E>::try_from_blob_roots(
|
||||
builder_bid.blinded_blobs_bundle.blob_roots,
|
||||
)
|
||||
.map_err(Error::InvalidBlobConversion)?,
|
||||
proofs: builder_bid.blinded_blobs_bundle.proofs,
|
||||
},
|
||||
};
|
||||
Ok(ProvenancedPayload::Builder(block_proposal_contents))
|
||||
Ok(ProvenancedPayload::Builder(
|
||||
BlockProposalContentsType::Blinded(block_proposal_contents),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +147,7 @@ pub enum Error {
|
||||
InvalidPayloadConversion,
|
||||
InvalidBlobConversion(String),
|
||||
BeaconStateError(BeaconStateError),
|
||||
PayloadTypeMismatch,
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for Error {
|
||||
@@ -159,6 +162,11 @@ impl From<ApiError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum BlockProposalContentsType<E: EthSpec> {
|
||||
Full(BlockProposalContents<E, FullPayload<E>>),
|
||||
Blinded(BlockProposalContents<E, BlindedPayload<E>>),
|
||||
}
|
||||
|
||||
pub enum BlockProposalContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
||||
Payload {
|
||||
payload: Payload,
|
||||
@@ -173,6 +181,22 @@ pub enum BlockProposalContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
||||
},
|
||||
}
|
||||
|
||||
impl<T: EthSpec> From<BlockProposalContents<T, FullPayload<T>>>
|
||||
for BlockProposalContents<T, BlindedPayload<T>>
|
||||
{
|
||||
fn from(item: BlockProposalContents<T, FullPayload<T>>) -> Self {
|
||||
let block_value = item.block_value().to_owned();
|
||||
|
||||
let blinded_payload: BlockProposalContents<T, BlindedPayload<T>> =
|
||||
BlockProposalContents::Payload {
|
||||
payload: item.to_payload().execution_payload().into(),
|
||||
block_value,
|
||||
};
|
||||
|
||||
blinded_payload
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<GetPayloadResponse<E>>
|
||||
for BlockProposalContents<E, Payload>
|
||||
{
|
||||
@@ -197,6 +221,17 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<GetPayloadResponse<E>>
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> TryFrom<GetPayloadResponseType<E>> for BlockProposalContentsType<E> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(response_type: GetPayloadResponseType<E>) -> Result<Self, Error> {
|
||||
match response_type {
|
||||
GetPayloadResponseType::Full(response) => Ok(Self::Full(response.try_into()?)),
|
||||
GetPayloadResponseType::Blinded(response) => Ok(Self::Blinded(response.try_into()?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Payload> {
|
||||
pub fn deconstruct(
|
||||
@@ -206,19 +241,26 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Paylo
|
||||
Option<KzgCommitments<T>>,
|
||||
Option<<Payload::Sidecar as Sidecar<T>>::BlobItems>,
|
||||
Option<KzgProofs<T>>,
|
||||
Uint256,
|
||||
) {
|
||||
match self {
|
||||
Self::Payload {
|
||||
payload,
|
||||
block_value: _,
|
||||
} => (payload, None, None, None),
|
||||
block_value,
|
||||
} => (payload, None, None, None, block_value),
|
||||
Self::PayloadAndBlobs {
|
||||
payload,
|
||||
block_value: _,
|
||||
block_value,
|
||||
kzg_commitments,
|
||||
blobs,
|
||||
proofs,
|
||||
} => (payload, Some(kzg_commitments), Some(blobs), Some(proofs)),
|
||||
} => (
|
||||
payload,
|
||||
Some(kzg_commitments),
|
||||
Some(blobs),
|
||||
Some(proofs),
|
||||
block_value,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,7 +837,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
///
|
||||
/// The result will be returned from the first node that returns successfully. No more nodes
|
||||
/// will be contacted.
|
||||
pub async fn get_payload<Payload: AbstractExecPayload<T>>(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn get_payload(
|
||||
&self,
|
||||
parent_hash: ExecutionBlockHash,
|
||||
payload_attributes: &PayloadAttributes,
|
||||
@@ -803,14 +846,11 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
builder_params: BuilderParams,
|
||||
current_fork: ForkName,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<BlockProposalContents<T, Payload>, Error> {
|
||||
let payload_result = match Payload::block_type() {
|
||||
BlockType::Blinded => {
|
||||
let _timer = metrics::start_timer_vec(
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
&[metrics::GET_BLINDED_PAYLOAD],
|
||||
);
|
||||
self.get_blinded_payload(
|
||||
block_production_version: BlockProductionVersion,
|
||||
) -> Result<BlockProposalContentsType<T>, Error> {
|
||||
let payload_result_type = match block_production_version {
|
||||
BlockProductionVersion::V3 => match self
|
||||
.determine_and_fetch_payload(
|
||||
parent_hash,
|
||||
payload_attributes,
|
||||
forkchoice_update_params,
|
||||
@@ -819,27 +859,51 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
spec,
|
||||
)
|
||||
.await
|
||||
}
|
||||
BlockType::Full => {
|
||||
{
|
||||
Ok(payload) => payload,
|
||||
Err(e) => {
|
||||
metrics::inc_counter_vec(
|
||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
|
||||
&[metrics::FAILURE],
|
||||
);
|
||||
return Err(e);
|
||||
}
|
||||
},
|
||||
BlockProductionVersion::BlindedV2 => {
|
||||
let _timer = metrics::start_timer_vec(
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
&[metrics::GET_PAYLOAD],
|
||||
&[metrics::GET_BLINDED_PAYLOAD],
|
||||
);
|
||||
self.get_full_payload(
|
||||
self.determine_and_fetch_payload(
|
||||
parent_hash,
|
||||
payload_attributes,
|
||||
forkchoice_update_params,
|
||||
builder_params,
|
||||
current_fork,
|
||||
spec,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
BlockProductionVersion::FullV2 => self
|
||||
.get_full_payload_with(
|
||||
parent_hash,
|
||||
payload_attributes,
|
||||
forkchoice_update_params,
|
||||
current_fork,
|
||||
noop,
|
||||
)
|
||||
.await
|
||||
.and_then(GetPayloadResponse::try_into)
|
||||
.map(ProvenancedPayload::Local)
|
||||
}
|
||||
.and_then(GetPayloadResponseType::try_into)
|
||||
.map(ProvenancedPayload::Local)?,
|
||||
};
|
||||
|
||||
// Track some metrics and return the result.
|
||||
match payload_result {
|
||||
Ok(ProvenancedPayload::Local(block_proposal_contents)) => {
|
||||
let block_proposal_content_type = match payload_result_type {
|
||||
ProvenancedPayload::Local(local_payload) => local_payload,
|
||||
ProvenancedPayload::Builder(builder_payload) => builder_payload,
|
||||
};
|
||||
|
||||
match block_proposal_content_type {
|
||||
BlockProposalContentsType::Full(block_proposal_contents) => {
|
||||
metrics::inc_counter_vec(
|
||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
|
||||
&[metrics::SUCCESS],
|
||||
@@ -848,9 +912,15 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_SOURCE,
|
||||
&[metrics::LOCAL],
|
||||
);
|
||||
Ok(block_proposal_contents)
|
||||
if matches!(block_production_version, BlockProductionVersion::BlindedV2) {
|
||||
Ok(BlockProposalContentsType::Blinded(
|
||||
block_proposal_contents.into(),
|
||||
))
|
||||
} else {
|
||||
Ok(BlockProposalContentsType::Full(block_proposal_contents))
|
||||
}
|
||||
}
|
||||
Ok(ProvenancedPayload::Builder(block_proposal_contents)) => {
|
||||
BlockProposalContentsType::Blinded(block_proposal_contents) => {
|
||||
metrics::inc_counter_vec(
|
||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
|
||||
&[metrics::SUCCESS],
|
||||
@@ -859,19 +929,12 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_SOURCE,
|
||||
&[metrics::BUILDER],
|
||||
);
|
||||
Ok(block_proposal_contents)
|
||||
}
|
||||
Err(e) => {
|
||||
metrics::inc_counter_vec(
|
||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
|
||||
&[metrics::FAILURE],
|
||||
);
|
||||
Err(e)
|
||||
Ok(BlockProposalContentsType::Blinded(block_proposal_contents))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_blinded_payload<Payload: AbstractExecPayload<T>>(
|
||||
async fn determine_and_fetch_payload(
|
||||
&self,
|
||||
parent_hash: ExecutionBlockHash,
|
||||
payload_attributes: &PayloadAttributes,
|
||||
@@ -879,11 +942,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
builder_params: BuilderParams,
|
||||
current_fork: ForkName,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<ProvenancedPayload<BlockProposalContents<T, Payload>>, Error> {
|
||||
) -> Result<ProvenancedPayload<BlockProposalContentsType<T>>, Error> {
|
||||
if let Some(builder) = self.builder() {
|
||||
let slot = builder_params.slot;
|
||||
let pubkey = builder_params.pubkey;
|
||||
|
||||
match builder_params.chain_health {
|
||||
ChainHealth::Healthy => {
|
||||
info!(
|
||||
@@ -895,7 +957,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
);
|
||||
|
||||
// Wait for the builder *and* local EL to produce a payload (or return an error).
|
||||
let ((relay_result, relay_duration), (local_result, local_duration)) = tokio::join!(
|
||||
let ((relay_result, relay_duration), (local_result_type, local_duration)) = tokio::join!(
|
||||
timed_future(metrics::GET_BLINDED_PAYLOAD_BUILDER, async {
|
||||
builder
|
||||
.get_builder_header::<T>(slot, parent_hash, &pubkey)
|
||||
@@ -912,6 +974,11 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
})
|
||||
);
|
||||
|
||||
let local_result = match local_result_type? {
|
||||
GetPayloadResponseType::Full(payload) => Ok(payload),
|
||||
GetPayloadResponseType::Blinded(_) => Err(Error::PayloadTypeMismatch),
|
||||
};
|
||||
|
||||
info!(
|
||||
self.log(),
|
||||
"Requested blinded execution payload";
|
||||
@@ -939,7 +1006,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"local_block_hash" => ?local.block_hash(),
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
||||
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Full(
|
||||
local.try_into()?,
|
||||
)))
|
||||
}
|
||||
(Ok(None), Ok(local)) => {
|
||||
info!(
|
||||
@@ -949,7 +1018,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"local_block_hash" => ?local.block_hash(),
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
||||
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Full(
|
||||
local.try_into()?,
|
||||
)))
|
||||
}
|
||||
(Ok(Some(relay)), Ok(local)) => {
|
||||
let header = &relay.data.message.header();
|
||||
@@ -973,7 +1044,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"local_block_value" => %local_value,
|
||||
"relay_value" => %relay_value
|
||||
);
|
||||
return Ok(ProvenancedPayload::Local(local.try_into()?));
|
||||
return Ok(ProvenancedPayload::Local(
|
||||
BlockProposalContentsType::Full(local.try_into()?),
|
||||
));
|
||||
} else if local.should_override_builder().unwrap_or(false) {
|
||||
let percentage_difference =
|
||||
percentage_difference_u256(local_value, *relay_value);
|
||||
@@ -989,7 +1062,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"local_block_value" => %local_value,
|
||||
"relay_value" => %relay_value
|
||||
);
|
||||
return Ok(ProvenancedPayload::Local(local.try_into()?));
|
||||
return Ok(ProvenancedPayload::Local(
|
||||
BlockProposalContentsType::Full(local.try_into()?),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
info!(
|
||||
@@ -1020,7 +1095,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"relay_block_hash" => ?header.block_hash(),
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
||||
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Full(
|
||||
local.try_into()?,
|
||||
)))
|
||||
}
|
||||
Err(reason) => {
|
||||
metrics::inc_counter_vec(
|
||||
@@ -1035,7 +1112,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"relay_block_hash" => ?header.block_hash(),
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
||||
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Full(
|
||||
local.try_into()?,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1132,28 +1211,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
current_fork,
|
||||
)
|
||||
.await
|
||||
.and_then(GetPayloadResponse::try_into)
|
||||
.and_then(GetPayloadResponseType::try_into)
|
||||
.map(ProvenancedPayload::Local)
|
||||
}
|
||||
|
||||
/// Get a full payload without caching its result in the execution layer's payload cache.
|
||||
async fn get_full_payload(
|
||||
&self,
|
||||
parent_hash: ExecutionBlockHash,
|
||||
payload_attributes: &PayloadAttributes,
|
||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
||||
current_fork: ForkName,
|
||||
) -> Result<GetPayloadResponse<T>, Error> {
|
||||
self.get_full_payload_with(
|
||||
parent_hash,
|
||||
payload_attributes,
|
||||
forkchoice_update_params,
|
||||
current_fork,
|
||||
noop,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Get a full payload and cache its result in the execution layer's payload cache.
|
||||
async fn get_full_payload_caching(
|
||||
&self,
|
||||
@@ -1161,7 +1222,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
payload_attributes: &PayloadAttributes,
|
||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
||||
current_fork: ForkName,
|
||||
) -> Result<GetPayloadResponse<T>, Error> {
|
||||
) -> Result<GetPayloadResponseType<T>, Error> {
|
||||
self.get_full_payload_with(
|
||||
parent_hash,
|
||||
payload_attributes,
|
||||
@@ -1182,7 +1243,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
&ExecutionLayer<T>,
|
||||
PayloadContentsRefTuple<T>,
|
||||
) -> Option<FullPayloadContents<T>>,
|
||||
) -> Result<GetPayloadResponse<T>, Error> {
|
||||
) -> Result<GetPayloadResponseType<T>, Error> {
|
||||
self.engine()
|
||||
.request(move |engine| async move {
|
||||
let payload_id = if let Some(id) = engine
|
||||
@@ -1244,6 +1305,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
"timestamp" => payload_attributes.timestamp(),
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
let _timer = metrics::start_timer_vec(
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
&[metrics::GET_PAYLOAD],
|
||||
);
|
||||
engine.api.get_payload::<T>(current_fork, payload_id).await
|
||||
}.await?;
|
||||
|
||||
@@ -1268,7 +1333,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
||||
);
|
||||
}
|
||||
|
||||
Ok(payload_response)
|
||||
Ok(GetPayloadResponseType::Full(payload_response))
|
||||
})
|
||||
.await
|
||||
.map_err(Box::new)
|
||||
|
||||
Reference in New Issue
Block a user