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:
Eitan Seri-Levi 2023-11-03 00:12:18 +00:00
parent 42da392edc
commit 07f53b18fc
20 changed files with 972 additions and 459 deletions

View File

@ -33,6 +33,17 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
state.build_committee_cache(RelativeEpoch::Previous, &self.spec)?; state.build_committee_cache(RelativeEpoch::Previous, &self.spec)?;
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?; state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
self.compute_beacon_block_reward_with_cache(block, block_root, state)
}
// This should only be called after a committee cache has been built
// for both the previous and current epoch
fn compute_beacon_block_reward_with_cache<Payload: AbstractExecPayload<T::EthSpec>>(
&self,
block: BeaconBlockRef<'_, T::EthSpec, Payload>,
block_root: Hash256,
state: &BeaconState<T::EthSpec>,
) -> Result<StandardBlockReward, BeaconChainError> {
let proposer_index = block.proposer_index(); let proposer_index = block.proposer_index();
let sync_aggregate_reward = let sync_aggregate_reward =
@ -178,7 +189,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
>( >(
&self, &self,
block: BeaconBlockRef<'_, T::EthSpec, Payload>, block: BeaconBlockRef<'_, T::EthSpec, Payload>,
state: &mut BeaconState<T::EthSpec>, state: &BeaconState<T::EthSpec>,
) -> Result<BeaconBlockSubRewardValue, BeaconChainError> { ) -> Result<BeaconBlockSubRewardValue, BeaconChainError> {
let total_active_balance = state.get_total_active_balance()?; let total_active_balance = state.get_total_active_balance()?;
let base_reward_per_increment = let base_reward_per_increment =
@ -191,6 +202,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.safe_mul(WEIGHT_DENOMINATOR)? .safe_mul(WEIGHT_DENOMINATOR)?
.safe_div(PROPOSER_WEIGHT)?; .safe_div(PROPOSER_WEIGHT)?;
let mut current_epoch_participation = state.current_epoch_participation()?.clone();
let mut previous_epoch_participation = state.previous_epoch_participation()?.clone();
for attestation in block.body().attestations() { for attestation in block.body().attestations() {
let data = &attestation.data; let data = &attestation.data;
let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64(); let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64();
@ -203,13 +217,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
)?; )?;
let attesting_indices = get_attesting_indices_from_state(state, attestation)?; let attesting_indices = get_attesting_indices_from_state(state, attestation)?;
let mut proposer_reward_numerator = 0; let mut proposer_reward_numerator = 0;
for index in attesting_indices { for index in attesting_indices {
let index = index as usize; let index = index as usize;
for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() { for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() {
let epoch_participation = let epoch_participation = if data.target.epoch == state.current_epoch() {
state.get_epoch_participation_mut(data.target.epoch)?; &mut current_epoch_participation
} else {
&mut previous_epoch_participation
};
let validator_participation = epoch_participation let validator_participation = epoch_participation
.get_mut(index) .get_mut(index)
.ok_or(BeaconStateError::ParticipationOutOfBounds(index))?; .ok_or(BeaconStateError::ParticipationOutOfBounds(index))?;

View File

@ -72,8 +72,8 @@ use crate::{
}; };
use eth2::types::{EventKind, SseBlobSidecar, SseBlock, SseExtendedPayloadAttributes, SyncDuty}; use eth2::types::{EventKind, SseBlobSidecar, SseBlock, SseExtendedPayloadAttributes, SyncDuty};
use execution_layer::{ use execution_layer::{
BlockProposalContents, BuilderParams, ChainHealth, ExecutionLayer, FailedCondition, BlockProposalContents, BlockProposalContentsType, BuilderParams, ChainHealth, ExecutionLayer,
PayloadAttributes, PayloadStatus, FailedCondition, PayloadAttributes, PayloadStatus,
}; };
use fork_choice::{ use fork_choice::{
AttestationFromBlock, ExecutionStatus, ForkChoice, ForkchoiceUpdateParameters, AttestationFromBlock, ExecutionStatus, ForkChoice, ForkchoiceUpdateParameters,
@ -120,6 +120,7 @@ use tokio_stream::Stream;
use tree_hash::TreeHash; use tree_hash::TreeHash;
use types::beacon_state::CloneConfig; use types::beacon_state::CloneConfig;
use types::blob_sidecar::{BlobSidecarList, FixedBlobSidecarList}; use types::blob_sidecar::{BlobSidecarList, FixedBlobSidecarList};
use types::payload::BlockProductionVersion;
use types::sidecar::BlobItems; use types::sidecar::BlobItems;
use types::*; use types::*;
@ -320,8 +321,7 @@ pub trait BeaconChainTypes: Send + Sync + 'static {
type EthSpec: types::EthSpec; type EthSpec: types::EthSpec;
} }
/// Used internally to split block production into discrete functions. struct PartialBeaconBlock<E: EthSpec> {
struct PartialBeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E>> {
state: BeaconState<E>, state: BeaconState<E>,
slot: Slot, slot: Slot,
proposer_index: u64, proposer_index: u64,
@ -335,7 +335,7 @@ struct PartialBeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E>> {
deposits: Vec<Deposit>, deposits: Vec<Deposit>,
voluntary_exits: Vec<SignedVoluntaryExit>, voluntary_exits: Vec<SignedVoluntaryExit>,
sync_aggregate: Option<SyncAggregate<E>>, sync_aggregate: Option<SyncAggregate<E>>,
prepare_payload_handle: Option<PreparePayloadHandle<E, Payload>>, prepare_payload_handle: Option<PreparePayloadHandle<E>>,
bls_to_execution_changes: Vec<SignedBlsToExecutionChange>, bls_to_execution_changes: Vec<SignedBlsToExecutionChange>,
} }
@ -484,11 +484,18 @@ pub struct BeaconChain<T: BeaconChainTypes> {
pub kzg: Option<Arc<Kzg>>, pub kzg: Option<Arc<Kzg>>,
} }
type BeaconBlockAndState<T, Payload> = ( pub enum BeaconBlockResponseType<T: EthSpec> {
BeaconBlock<T, Payload>, Full(BeaconBlockResponse<T, FullPayload<T>>),
BeaconState<T>, Blinded(BeaconBlockResponse<T, BlindedPayload<T>>),
Option<SidecarList<T, <Payload as AbstractExecPayload<T>>::Sidecar>>, }
);
pub struct BeaconBlockResponse<T: EthSpec, Payload: AbstractExecPayload<T>> {
pub block: BeaconBlock<T, Payload>,
pub state: BeaconState<T>,
pub maybe_side_car: Option<SidecarList<T, <Payload as AbstractExecPayload<T>>::Sidecar>>,
pub execution_payload_value: Option<Uint256>,
pub consensus_block_value: Option<u64>,
}
impl FinalizationAndCanonicity { impl FinalizationAndCanonicity {
pub fn is_finalized(self) -> bool { pub fn is_finalized(self) -> bool {
@ -3949,38 +3956,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
Ok(()) Ok(())
} }
/// Produce a new block at the given `slot`. pub async fn produce_block_with_verification(
///
/// The produced block will not be inherently valid, it must be signed by a block producer.
/// Block signing is out of the scope of this function and should be done by a separate program.
pub async fn produce_block<Payload: AbstractExecPayload<T::EthSpec> + 'static>(
self: &Arc<Self>,
randao_reveal: Signature,
slot: Slot,
validator_graffiti: Option<Graffiti>,
) -> Result<BeaconBlockAndState<T::EthSpec, Payload>, BlockProductionError> {
self.produce_block_with_verification(
randao_reveal,
slot,
validator_graffiti,
ProduceBlockVerification::VerifyRandao,
)
.await
}
/// Same as `produce_block` but allowing for configuration of RANDAO-verification.
pub async fn produce_block_with_verification<
Payload: AbstractExecPayload<T::EthSpec> + 'static,
>(
self: &Arc<Self>, self: &Arc<Self>,
randao_reveal: Signature, randao_reveal: Signature,
slot: Slot, slot: Slot,
validator_graffiti: Option<Graffiti>, validator_graffiti: Option<Graffiti>,
verification: ProduceBlockVerification, verification: ProduceBlockVerification,
) -> Result<BeaconBlockAndState<T::EthSpec, Payload>, BlockProductionError> { block_production_version: BlockProductionVersion,
) -> Result<BeaconBlockResponseType<T::EthSpec>, BlockProductionError> {
metrics::inc_counter(&metrics::BLOCK_PRODUCTION_REQUESTS); metrics::inc_counter(&metrics::BLOCK_PRODUCTION_REQUESTS);
let _complete_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_TIMES); let _complete_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_TIMES);
// Part 1/2 (blocking) // Part 1/2 (blocking)
// //
// Load the parent state from disk. // Load the parent state from disk.
@ -3998,13 +3983,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// Part 2/2 (async, with some blocking components) // Part 2/2 (async, with some blocking components)
// //
// Produce the block upon the state // Produce the block upon the state
self.produce_block_on_state::<Payload>( self.produce_block_on_state(
state, state,
state_root_opt, state_root_opt,
slot, slot,
randao_reveal, randao_reveal,
validator_graffiti, validator_graffiti,
verification, verification,
block_production_version,
) )
.await .await
} }
@ -4568,7 +4554,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// The provided `state_root_opt` should only ever be set to `Some` if the contained value is /// The provided `state_root_opt` should only ever be set to `Some` if the contained value is
/// equal to the root of `state`. Providing this value will serve as an optimization to avoid /// equal to the root of `state`. Providing this value will serve as an optimization to avoid
/// performing a tree hash in some scenarios. /// performing a tree hash in some scenarios.
pub async fn produce_block_on_state<Payload: AbstractExecPayload<T::EthSpec> + 'static>( #[allow(clippy::too_many_arguments)]
pub async fn produce_block_on_state(
self: &Arc<Self>, self: &Arc<Self>,
state: BeaconState<T::EthSpec>, state: BeaconState<T::EthSpec>,
state_root_opt: Option<Hash256>, state_root_opt: Option<Hash256>,
@ -4576,7 +4563,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
randao_reveal: Signature, randao_reveal: Signature,
validator_graffiti: Option<Graffiti>, validator_graffiti: Option<Graffiti>,
verification: ProduceBlockVerification, verification: ProduceBlockVerification,
) -> Result<BeaconBlockAndState<T::EthSpec, Payload>, BlockProductionError> { block_production_version: BlockProductionVersion,
) -> Result<BeaconBlockResponseType<T::EthSpec>, BlockProductionError> {
// Part 1/3 (blocking) // Part 1/3 (blocking)
// //
// Perform the state advance and block-packing functions. // Perform the state advance and block-packing functions.
@ -4591,6 +4579,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
produce_at_slot, produce_at_slot,
randao_reveal, randao_reveal,
validator_graffiti, validator_graffiti,
block_production_version,
) )
}, },
"produce_partial_beacon_block", "produce_partial_beacon_block",
@ -4598,12 +4587,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.ok_or(BlockProductionError::ShuttingDown)? .ok_or(BlockProductionError::ShuttingDown)?
.await .await
.map_err(BlockProductionError::TokioJoin)??; .map_err(BlockProductionError::TokioJoin)??;
// Part 2/3 (async) // Part 2/3 (async)
// //
// Wait for the execution layer to return an execution payload (if one is required). // Wait for the execution layer to return an execution payload (if one is required).
let prepare_payload_handle = partial_beacon_block.prepare_payload_handle.take(); let prepare_payload_handle = partial_beacon_block.prepare_payload_handle.take();
let block_contents = if let Some(prepare_payload_handle) = prepare_payload_handle { let block_contents_type_option =
if let Some(prepare_payload_handle) = prepare_payload_handle {
Some( Some(
prepare_payload_handle prepare_payload_handle
.await .await
@ -4613,17 +4602,18 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
} else { } else {
None None
}; };
// Part 3/3 (blocking) // Part 3/3 (blocking)
// if let Some(block_contents_type) = block_contents_type_option {
// Perform the final steps of combining all the parts and computing the state root. match block_contents_type {
BlockProposalContentsType::Full(block_contents) => {
let chain = self.clone(); let chain = self.clone();
self.task_executor let beacon_block_response = self
.task_executor
.spawn_blocking_handle( .spawn_blocking_handle(
move || { move || {
chain.complete_partial_beacon_block( chain.complete_partial_beacon_block(
partial_beacon_block, partial_beacon_block,
block_contents, Some(block_contents),
verification, verification,
) )
}, },
@ -4631,17 +4621,62 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
) )
.ok_or(BlockProductionError::ShuttingDown)? .ok_or(BlockProductionError::ShuttingDown)?
.await .await
.map_err(BlockProductionError::TokioJoin)? .map_err(BlockProductionError::TokioJoin)??;
Ok(BeaconBlockResponseType::Full(beacon_block_response))
}
BlockProposalContentsType::Blinded(block_contents) => {
let chain = self.clone();
let beacon_block_response = self
.task_executor
.spawn_blocking_handle(
move || {
chain.complete_partial_beacon_block(
partial_beacon_block,
Some(block_contents),
verification,
)
},
"complete_partial_beacon_block",
)
.ok_or(BlockProductionError::ShuttingDown)?
.await
.map_err(BlockProductionError::TokioJoin)??;
Ok(BeaconBlockResponseType::Blinded(beacon_block_response))
}
}
} else {
let chain = self.clone();
let beacon_block_response = self
.task_executor
.spawn_blocking_handle(
move || {
chain.complete_partial_beacon_block(
partial_beacon_block,
None,
verification,
)
},
"complete_partial_beacon_block",
)
.ok_or(BlockProductionError::ShuttingDown)?
.await
.map_err(BlockProductionError::TokioJoin)??;
Ok(BeaconBlockResponseType::Full(beacon_block_response))
}
} }
fn produce_partial_beacon_block<Payload: AbstractExecPayload<T::EthSpec> + 'static>( fn produce_partial_beacon_block(
self: &Arc<Self>, self: &Arc<Self>,
mut state: BeaconState<T::EthSpec>, mut state: BeaconState<T::EthSpec>,
state_root_opt: Option<Hash256>, state_root_opt: Option<Hash256>,
produce_at_slot: Slot, produce_at_slot: Slot,
randao_reveal: Signature, randao_reveal: Signature,
validator_graffiti: Option<Graffiti>, validator_graffiti: Option<Graffiti>,
) -> Result<PartialBeaconBlock<T::EthSpec, Payload>, BlockProductionError> { block_production_version: BlockProductionVersion,
) -> Result<PartialBeaconBlock<T::EthSpec>, BlockProductionError> {
let eth1_chain = self let eth1_chain = self
.eth1_chain .eth1_chain
.as_ref() .as_ref()
@ -4701,6 +4736,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
parent_root, parent_root,
proposer_index, proposer_index,
builder_params, builder_params,
block_production_version,
)?; )?;
Some(prepare_payload_handle) Some(prepare_payload_handle)
} }
@ -4710,6 +4746,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.op_pool.get_slashings_and_exits(&state, &self.spec); self.op_pool.get_slashings_and_exits(&state, &self.spec);
let eth1_data = eth1_chain.eth1_data_for_block_production(&state, &self.spec)?; let eth1_data = eth1_chain.eth1_data_for_block_production(&state, &self.spec)?;
let deposits = eth1_chain.deposits_for_block_inclusion(&state, &eth1_data, &self.spec)?; let deposits = eth1_chain.deposits_for_block_inclusion(&state, &eth1_data, &self.spec)?;
let bls_to_execution_changes = self let bls_to_execution_changes = self
@ -4880,10 +4917,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
fn complete_partial_beacon_block<Payload: AbstractExecPayload<T::EthSpec>>( fn complete_partial_beacon_block<Payload: AbstractExecPayload<T::EthSpec>>(
&self, &self,
partial_beacon_block: PartialBeaconBlock<T::EthSpec, Payload>, partial_beacon_block: PartialBeaconBlock<T::EthSpec>,
block_contents: Option<BlockProposalContents<T::EthSpec, Payload>>, block_contents: Option<BlockProposalContents<T::EthSpec, Payload>>,
verification: ProduceBlockVerification, verification: ProduceBlockVerification,
) -> Result<BeaconBlockAndState<T::EthSpec, Payload>, BlockProductionError> { ) -> Result<BeaconBlockResponse<T::EthSpec, Payload>, BlockProductionError> {
let PartialBeaconBlock { let PartialBeaconBlock {
mut state, mut state,
slot, slot,
@ -4905,7 +4942,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
bls_to_execution_changes, bls_to_execution_changes,
} = partial_beacon_block; } = partial_beacon_block;
let (inner_block, blobs_opt, proofs_opt) = match &state { let (inner_block, blobs_opt, proofs_opt, execution_payload_value) = match &state {
BeaconState::Base(_) => ( BeaconState::Base(_) => (
BeaconBlock::Base(BeaconBlockBase { BeaconBlock::Base(BeaconBlockBase {
slot, slot,
@ -4926,6 +4963,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}), }),
None, None,
None, None,
Uint256::zero(),
), ),
BeaconState::Altair(_) => ( BeaconState::Altair(_) => (
BeaconBlock::Altair(BeaconBlockAltair { BeaconBlock::Altair(BeaconBlockAltair {
@ -4949,11 +4987,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}), }),
None, None,
None, None,
Uint256::zero(),
), ),
BeaconState::Merge(_) => { BeaconState::Merge(_) => {
let (payload, _, _, _) = block_contents let block_proposal_contents =
.ok_or(BlockProductionError::MissingExecutionPayload)? block_contents.ok_or(BlockProductionError::MissingExecutionPayload)?;
.deconstruct(); let execution_payload_value = block_proposal_contents.block_value().to_owned();
( (
BeaconBlock::Merge(BeaconBlockMerge { BeaconBlock::Merge(BeaconBlockMerge {
slot, slot,
@ -4971,19 +5010,22 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
voluntary_exits: voluntary_exits.into(), voluntary_exits: voluntary_exits.into(),
sync_aggregate: sync_aggregate sync_aggregate: sync_aggregate
.ok_or(BlockProductionError::MissingSyncAggregate)?, .ok_or(BlockProductionError::MissingSyncAggregate)?,
execution_payload: payload execution_payload: block_proposal_contents
.to_payload()
.try_into() .try_into()
.map_err(|_| BlockProductionError::InvalidPayloadFork)?, .map_err(|_| BlockProductionError::InvalidPayloadFork)?,
}, },
}), }),
None, None,
None, None,
execution_payload_value,
) )
} }
BeaconState::Capella(_) => { BeaconState::Capella(_) => {
let (payload, _, _, _) = block_contents let block_proposal_contents =
.ok_or(BlockProductionError::MissingExecutionPayload)? block_contents.ok_or(BlockProductionError::MissingExecutionPayload)?;
.deconstruct(); let execution_payload_value = block_proposal_contents.block_value().to_owned();
( (
BeaconBlock::Capella(BeaconBlockCapella { BeaconBlock::Capella(BeaconBlockCapella {
slot, slot,
@ -5001,7 +5043,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
voluntary_exits: voluntary_exits.into(), voluntary_exits: voluntary_exits.into(),
sync_aggregate: sync_aggregate sync_aggregate: sync_aggregate
.ok_or(BlockProductionError::MissingSyncAggregate)?, .ok_or(BlockProductionError::MissingSyncAggregate)?,
execution_payload: payload execution_payload: block_proposal_contents
.to_payload()
.try_into() .try_into()
.map_err(|_| BlockProductionError::InvalidPayloadFork)?, .map_err(|_| BlockProductionError::InvalidPayloadFork)?,
bls_to_execution_changes: bls_to_execution_changes.into(), bls_to_execution_changes: bls_to_execution_changes.into(),
@ -5009,12 +5052,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}), }),
None, None,
None, None,
execution_payload_value,
) )
} }
BeaconState::Deneb(_) => { BeaconState::Deneb(_) => {
let (payload, kzg_commitments, blobs, proofs) = block_contents let (payload, kzg_commitments, blobs, proofs, execution_payload_value) =
block_contents
.ok_or(BlockProductionError::MissingExecutionPayload)? .ok_or(BlockProductionError::MissingExecutionPayload)?
.deconstruct(); .deconstruct();
( (
BeaconBlock::Deneb(BeaconBlockDeneb { BeaconBlock::Deneb(BeaconBlockDeneb {
slot, slot,
@ -5042,6 +5088,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}), }),
blobs, blobs,
proofs, proofs,
execution_payload_value,
) )
} }
}; };
@ -5057,7 +5104,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.log, self.log,
"Produced block on state"; "Produced block on state";
"block_size" => block_size, "block_size" => block_size,
"slot" => block.slot(),
); );
metrics::observe(&metrics::BLOCK_SIZE, block_size as f64); metrics::observe(&metrics::BLOCK_SIZE, block_size as f64);
@ -5075,6 +5121,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// Use a context without block root or proposer index so that both are checked. // Use a context without block root or proposer index so that both are checked.
let mut ctxt = ConsensusContext::new(block.slot()); let mut ctxt = ConsensusContext::new(block.slot());
let consensus_block_value = self
.compute_beacon_block_reward(block.message(), Hash256::zero(), &mut state)
.map(|reward| reward.total)
.unwrap_or(0);
per_block_processing( per_block_processing(
&mut state, &mut state,
&block, &block,
@ -5154,7 +5205,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
"slot" => block.slot() "slot" => block.slot()
); );
Ok((block, state, maybe_sidecar_list)) Ok(BeaconBlockResponse {
block,
state,
maybe_side_car: maybe_sidecar_list,
execution_payload_value: Some(execution_payload_value),
consensus_block_value: Some(consensus_block_value),
})
} }
/// This method must be called whenever an execution engine indicates that a payload is /// This method must be called whenever an execution engine indicates that a payload is

View File

@ -13,7 +13,8 @@ use crate::{
ExecutionPayloadError, ExecutionPayloadError,
}; };
use execution_layer::{ use execution_layer::{
BlockProposalContents, BuilderParams, NewPayloadRequest, PayloadAttributes, PayloadStatus, BlockProposalContents, BlockProposalContentsType, BuilderParams, NewPayloadRequest,
PayloadAttributes, PayloadStatus,
}; };
use fork_choice::{InvalidationOperation, PayloadVerificationStatus}; use fork_choice::{InvalidationOperation, PayloadVerificationStatus};
use proto_array::{Block as ProtoBlock, ExecutionStatus}; use proto_array::{Block as ProtoBlock, ExecutionStatus};
@ -26,11 +27,11 @@ use state_processing::per_block_processing::{
use std::sync::Arc; use std::sync::Arc;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use tree_hash::TreeHash; use tree_hash::TreeHash;
use types::payload::BlockProductionVersion;
use types::*; use types::*;
pub type PreparePayloadResult<E, Payload> = pub type PreparePayloadResult<E> = Result<BlockProposalContentsType<E>, BlockProductionError>;
Result<BlockProposalContents<E, Payload>, BlockProductionError>; pub type PreparePayloadHandle<E> = JoinHandle<Option<PreparePayloadResult<E>>>;
pub type PreparePayloadHandle<E, Payload> = JoinHandle<Option<PreparePayloadResult<E, Payload>>>;
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum AllowOptimisticImport { pub enum AllowOptimisticImport {
@ -398,16 +399,14 @@ pub fn validate_execution_payload_for_gossip<T: BeaconChainTypes>(
/// Equivalent to the `get_execution_payload` function in the Validator Guide: /// Equivalent to the `get_execution_payload` function in the Validator Guide:
/// ///
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal /// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal
pub fn get_execution_payload< pub fn get_execution_payload<T: BeaconChainTypes>(
T: BeaconChainTypes,
Payload: AbstractExecPayload<T::EthSpec> + 'static,
>(
chain: Arc<BeaconChain<T>>, chain: Arc<BeaconChain<T>>,
state: &BeaconState<T::EthSpec>, state: &BeaconState<T::EthSpec>,
parent_block_root: Hash256, parent_block_root: Hash256,
proposer_index: u64, proposer_index: u64,
builder_params: BuilderParams, builder_params: BuilderParams,
) -> Result<PreparePayloadHandle<T::EthSpec, Payload>, BlockProductionError> { block_production_version: BlockProductionVersion,
) -> Result<PreparePayloadHandle<T::EthSpec>, BlockProductionError> {
// Compute all required values from the `state` now to avoid needing to pass it into a spawned // Compute all required values from the `state` now to avoid needing to pass it into a spawned
// task. // task.
let spec = &chain.spec; let spec = &chain.spec;
@ -440,7 +439,7 @@ pub fn get_execution_payload<
.clone() .clone()
.spawn_handle( .spawn_handle(
async move { async move {
prepare_execution_payload::<T, Payload>( prepare_execution_payload::<T>(
&chain, &chain,
is_merge_transition_complete, is_merge_transition_complete,
timestamp, timestamp,
@ -450,6 +449,7 @@ pub fn get_execution_payload<
builder_params, builder_params,
withdrawals, withdrawals,
parent_beacon_block_root, parent_beacon_block_root,
block_production_version,
) )
.await .await
}, },
@ -475,7 +475,7 @@ pub fn get_execution_payload<
/// ///
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal /// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub async fn prepare_execution_payload<T, Payload>( pub async fn prepare_execution_payload<T>(
chain: &Arc<BeaconChain<T>>, chain: &Arc<BeaconChain<T>>,
is_merge_transition_complete: bool, is_merge_transition_complete: bool,
timestamp: u64, timestamp: u64,
@ -485,10 +485,10 @@ pub async fn prepare_execution_payload<T, Payload>(
builder_params: BuilderParams, builder_params: BuilderParams,
withdrawals: Option<Vec<Withdrawal>>, withdrawals: Option<Vec<Withdrawal>>,
parent_beacon_block_root: Option<Hash256>, parent_beacon_block_root: Option<Hash256>,
) -> Result<BlockProposalContents<T::EthSpec, Payload>, BlockProductionError> block_production_version: BlockProductionVersion,
) -> Result<BlockProposalContentsType<T::EthSpec>, BlockProductionError>
where where
T: BeaconChainTypes, T: BeaconChainTypes,
Payload: AbstractExecPayload<T::EthSpec>,
{ {
let current_epoch = builder_params.slot.epoch(T::EthSpec::slots_per_epoch()); let current_epoch = builder_params.slot.epoch(T::EthSpec::slots_per_epoch());
let spec = &chain.spec; let spec = &chain.spec;
@ -506,7 +506,12 @@ where
if is_terminal_block_hash_set && !is_activation_epoch_reached { if is_terminal_block_hash_set && !is_activation_epoch_reached {
// Use the "empty" payload if there's a terminal block hash, but we haven't reached the // Use the "empty" payload if there's a terminal block hash, but we haven't reached the
// terminal block epoch yet. // terminal block epoch yet.
return BlockProposalContents::default_at_fork(fork).map_err(Into::into); return Ok(BlockProposalContentsType::Full(
BlockProposalContents::Payload {
payload: FullPayload::default_at_fork(fork)?,
block_value: Uint256::zero(),
},
));
} }
let terminal_pow_block_hash = execution_layer let terminal_pow_block_hash = execution_layer
@ -519,7 +524,12 @@ where
} else { } else {
// If the merge transition hasn't occurred yet and the EL hasn't found the terminal // If the merge transition hasn't occurred yet and the EL hasn't found the terminal
// block, return an "empty" payload. // block, return an "empty" payload.
return BlockProposalContents::default_at_fork(fork).map_err(Into::into); return Ok(BlockProposalContentsType::Full(
BlockProposalContents::Payload {
payload: FullPayload::default_at_fork(fork)?,
block_value: Uint256::zero(),
},
));
} }
} else { } else {
latest_execution_payload_header_block_hash latest_execution_payload_header_block_hash
@ -558,13 +568,14 @@ where
// //
// This future is not executed here, it's up to the caller to await it. // This future is not executed here, it's up to the caller to await it.
let block_contents = execution_layer let block_contents = execution_layer
.get_payload::<Payload>( .get_payload(
parent_hash, parent_hash,
&payload_attributes, &payload_attributes,
forkchoice_update_params, forkchoice_update_params,
builder_params, builder_params,
fork, fork,
&chain.spec, &chain.spec,
block_production_version,
) )
.await .await
.map_err(BlockProductionError::GetPayloadFailed)?; .map_err(BlockProductionError::GetPayloadFailed)?;

View File

@ -56,10 +56,10 @@ pub mod validator_monitor;
pub mod validator_pubkey_cache; pub mod validator_pubkey_cache;
pub use self::beacon_chain::{ pub use self::beacon_chain::{
AttestationProcessingOutcome, AvailabilityProcessingStatus, BeaconChain, BeaconChainTypes, AttestationProcessingOutcome, AvailabilityProcessingStatus, BeaconBlockResponse,
BeaconStore, ChainSegmentResult, ForkChoiceError, OverrideForkchoiceUpdate, BeaconBlockResponseType, BeaconChain, BeaconChainTypes, BeaconStore, ChainSegmentResult,
ProduceBlockVerification, StateSkipConfig, WhenSlotSkipped, ForkChoiceError, OverrideForkchoiceUpdate, ProduceBlockVerification, StateSkipConfig,
INVALID_FINALIZED_MERGE_TRANSITION_BLOCK_SHUTDOWN_REASON, WhenSlotSkipped, INVALID_FINALIZED_MERGE_TRANSITION_BLOCK_SHUTDOWN_REASON,
INVALID_JUSTIFIED_PAYLOAD_SHUTDOWN_REASON, INVALID_JUSTIFIED_PAYLOAD_SHUTDOWN_REASON,
}; };
pub use self::beacon_snapshot::BeaconSnapshot; pub use self::beacon_snapshot::BeaconSnapshot;

View File

@ -1,6 +1,7 @@
use crate::block_verification_types::{AsBlock, RpcBlock}; use crate::block_verification_types::{AsBlock, RpcBlock};
use crate::observed_operations::ObservationOutcome; use crate::observed_operations::ObservationOutcome;
pub use crate::persisted_beacon_chain::PersistedBeaconChain; pub use crate::persisted_beacon_chain::PersistedBeaconChain;
use crate::BeaconBlockResponseType;
pub use crate::{ pub use crate::{
beacon_chain::{BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY}, beacon_chain::{BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY},
migrate::MigratorConfig, migrate::MigratorConfig,
@ -60,6 +61,7 @@ use store::{config::StoreConfig, HotColdDB, ItemStore, LevelDB, MemoryStore};
use task_executor::TaskExecutor; use task_executor::TaskExecutor;
use task_executor::{test_utils::TestRuntime, ShutdownReason}; use task_executor::{test_utils::TestRuntime, ShutdownReason};
use tree_hash::TreeHash; use tree_hash::TreeHash;
use types::payload::BlockProductionVersion;
use types::sync_selection_proof::SyncSelectionProof; use types::sync_selection_proof::SyncSelectionProof;
pub use types::test_utils::generate_deterministic_keypairs; pub use types::test_utils::generate_deterministic_keypairs;
use types::test_utils::TestRandom; use types::test_utils::TestRandom;
@ -878,7 +880,7 @@ where
let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot); let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot);
let (block, state, maybe_blob_sidecars) = self let BeaconBlockResponseType::Full(block_response) = self
.chain .chain
.produce_block_on_state( .produce_block_on_state(
state, state,
@ -887,14 +889,18 @@ where
randao_reveal, randao_reveal,
Some(graffiti), Some(graffiti),
ProduceBlockVerification::VerifyRandao, ProduceBlockVerification::VerifyRandao,
BlockProductionVersion::FullV2,
) )
.await .await
.unwrap(); .unwrap()
else {
panic!("Should always be a full payload response");
};
let signed_block = block.sign( let signed_block = block_response.block.sign(
&self.validator_keypairs[proposer_index].sk, &self.validator_keypairs[proposer_index].sk,
&state.fork(), &block_response.state.fork(),
state.genesis_validators_root(), block_response.state.genesis_validators_root(),
&self.spec, &self.spec,
); );
@ -905,11 +911,13 @@ where
| SignedBeaconBlock::Capella(_) => (signed_block, None), | SignedBeaconBlock::Capella(_) => (signed_block, None),
SignedBeaconBlock::Deneb(_) => ( SignedBeaconBlock::Deneb(_) => (
signed_block, signed_block,
maybe_blob_sidecars.map(|blobs| self.sign_blobs(blobs, &state, proposer_index)), block_response
.maybe_side_car
.map(|blobs| self.sign_blobs(blobs, &block_response.state, proposer_index)),
), ),
}; };
(block_contents, state) (block_contents, block_response.state)
} }
/// Useful for the `per_block_processing` tests. Creates a block, and returns the state after /// Useful for the `per_block_processing` tests. Creates a block, and returns the state after
@ -938,7 +946,7 @@ where
let pre_state = state.clone(); let pre_state = state.clone();
let (block, state, maybe_blob_sidecars) = self let BeaconBlockResponseType::Full(block_response) = self
.chain .chain
.produce_block_on_state( .produce_block_on_state(
state, state,
@ -947,14 +955,18 @@ where
randao_reveal, randao_reveal,
Some(graffiti), Some(graffiti),
ProduceBlockVerification::VerifyRandao, ProduceBlockVerification::VerifyRandao,
BlockProductionVersion::FullV2,
) )
.await .await
.unwrap(); .unwrap()
else {
panic!("Should always be a full payload response");
};
let signed_block = block.sign( let signed_block = block_response.block.sign(
&self.validator_keypairs[proposer_index].sk, &self.validator_keypairs[proposer_index].sk,
&state.fork(), &block_response.state.fork(),
state.genesis_validators_root(), block_response.state.genesis_validators_root(),
&self.spec, &self.spec,
); );
@ -964,14 +976,14 @@ where
| SignedBeaconBlock::Merge(_) | SignedBeaconBlock::Merge(_)
| SignedBeaconBlock::Capella(_) => (signed_block, None), | SignedBeaconBlock::Capella(_) => (signed_block, None),
SignedBeaconBlock::Deneb(_) => { SignedBeaconBlock::Deneb(_) => {
if let Some(blobs) = maybe_blob_sidecars { if let Some(blobs) = block_response.maybe_side_car {
let signed_blobs: SignedSidecarList<E, BlobSidecar<E>> = Vec::from(blobs) let signed_blobs: SignedSidecarList<E, BlobSidecar<E>> = Vec::from(blobs)
.into_iter() .into_iter()
.map(|blob| { .map(|blob| {
blob.sign( blob.sign(
&self.validator_keypairs[proposer_index].sk, &self.validator_keypairs[proposer_index].sk,
&state.fork(), &block_response.state.fork(),
state.genesis_validators_root(), block_response.state.genesis_validators_root(),
&self.spec, &self.spec,
) )
}) })
@ -990,7 +1002,6 @@ where
} }
} }
}; };
(block_contents, pre_state) (block_contents, pre_state)
} }

View File

@ -466,6 +466,11 @@ impl<T: EthSpec> From<GetPayloadResponse<T>>
} }
} }
pub enum GetPayloadResponseType<E: EthSpec> {
Full(GetPayloadResponse<E>),
Blinded(GetPayloadResponse<E>),
}
impl<T: EthSpec> GetPayloadResponse<T> { impl<T: EthSpec> GetPayloadResponse<T> {
pub fn execution_payload_ref(&self) -> ExecutionPayloadRef<T> { pub fn execution_payload_ref(&self) -> ExecutionPayloadRef<T> {
self.to_ref().into() self.to_ref().into()

View File

@ -42,13 +42,13 @@ use tokio_stream::wrappers::WatchStream;
use tree_hash::TreeHash; use tree_hash::TreeHash;
use types::beacon_block_body::KzgCommitments; use types::beacon_block_body::KzgCommitments;
use types::builder_bid::BuilderBid; use types::builder_bid::BuilderBid;
use types::payload::BlockProductionVersion;
use types::sidecar::{BlobItems, Sidecar}; use types::sidecar::{BlobItems, Sidecar};
use types::KzgProofs; use types::{AbstractExecPayload, ExecutionPayloadDeneb, KzgProofs};
use types::{ use types::{
AbstractExecPayload, BeaconStateError, BlindedPayload, BlockType, ChainSpec, Epoch, BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadCapella,
ExecPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge, ExecutionPayloadMerge, FullPayload, ProposerPreparationData, PublicKeyBytes, Signature, Slot,
}; };
use types::{ProposerPreparationData, PublicKeyBytes, Signature, Slot};
mod block_hash; mod block_hash;
mod engine_api; mod engine_api;
@ -87,9 +87,7 @@ pub enum ProvenancedPayload<P> {
Builder(P), Builder(P),
} }
impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<BuilderBid<E>> impl<E: EthSpec> TryFrom<BuilderBid<E>> for ProvenancedPayload<BlockProposalContentsType<E>> {
for ProvenancedPayload<BlockProposalContents<E, Payload>>
{
type Error = Error; type Error = Error;
fn try_from(value: BuilderBid<E>) -> Result<Self, 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)?, .map_err(|_| Error::InvalidPayloadConversion)?,
block_value: builder_bid.value, block_value: builder_bid.value,
kzg_commitments: builder_bid.blinded_blobs_bundle.commitments, kzg_commitments: builder_bid.blinded_blobs_bundle.commitments,
blobs: BlobItems::try_from_blob_roots(builder_bid.blinded_blobs_bundle.blob_roots) blobs: BlobItems::<E>::try_from_blob_roots(
builder_bid.blinded_blobs_bundle.blob_roots,
)
.map_err(Error::InvalidBlobConversion)?, .map_err(Error::InvalidBlobConversion)?,
proofs: builder_bid.blinded_blobs_bundle.proofs, 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, InvalidPayloadConversion,
InvalidBlobConversion(String), InvalidBlobConversion(String),
BeaconStateError(BeaconStateError), BeaconStateError(BeaconStateError),
PayloadTypeMismatch,
} }
impl From<BeaconStateError> for Error { 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>> { pub enum BlockProposalContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
Payload { Payload {
payload: 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>> impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<GetPayloadResponse<E>>
for BlockProposalContents<E, Payload> 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)] #[allow(clippy::type_complexity)]
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Payload> { impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Payload> {
pub fn deconstruct( pub fn deconstruct(
@ -206,19 +241,26 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Paylo
Option<KzgCommitments<T>>, Option<KzgCommitments<T>>,
Option<<Payload::Sidecar as Sidecar<T>>::BlobItems>, Option<<Payload::Sidecar as Sidecar<T>>::BlobItems>,
Option<KzgProofs<T>>, Option<KzgProofs<T>>,
Uint256,
) { ) {
match self { match self {
Self::Payload { Self::Payload {
payload, payload,
block_value: _, block_value,
} => (payload, None, None, None), } => (payload, None, None, None, block_value),
Self::PayloadAndBlobs { Self::PayloadAndBlobs {
payload, payload,
block_value: _, block_value,
kzg_commitments, kzg_commitments,
blobs, blobs,
proofs, 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 /// The result will be returned from the first node that returns successfully. No more nodes
/// will be contacted. /// will be contacted.
pub async fn get_payload<Payload: AbstractExecPayload<T>>( #[allow(clippy::too_many_arguments)]
pub async fn get_payload(
&self, &self,
parent_hash: ExecutionBlockHash, parent_hash: ExecutionBlockHash,
payload_attributes: &PayloadAttributes, payload_attributes: &PayloadAttributes,
@ -803,14 +846,11 @@ impl<T: EthSpec> ExecutionLayer<T> {
builder_params: BuilderParams, builder_params: BuilderParams,
current_fork: ForkName, current_fork: ForkName,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<BlockProposalContents<T, Payload>, Error> { block_production_version: BlockProductionVersion,
let payload_result = match Payload::block_type() { ) -> Result<BlockProposalContentsType<T>, Error> {
BlockType::Blinded => { let payload_result_type = match block_production_version {
let _timer = metrics::start_timer_vec( BlockProductionVersion::V3 => match self
&metrics::EXECUTION_LAYER_REQUEST_TIMES, .determine_and_fetch_payload(
&[metrics::GET_BLINDED_PAYLOAD],
);
self.get_blinded_payload(
parent_hash, parent_hash,
payload_attributes, payload_attributes,
forkchoice_update_params, forkchoice_update_params,
@ -819,27 +859,51 @@ impl<T: EthSpec> ExecutionLayer<T> {
spec, spec,
) )
.await .await
{
Ok(payload) => payload,
Err(e) => {
metrics::inc_counter_vec(
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
&[metrics::FAILURE],
);
return Err(e);
} }
BlockType::Full => { },
BlockProductionVersion::BlindedV2 => {
let _timer = metrics::start_timer_vec( let _timer = metrics::start_timer_vec(
&metrics::EXECUTION_LAYER_REQUEST_TIMES, &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, parent_hash,
payload_attributes, payload_attributes,
forkchoice_update_params, forkchoice_update_params,
current_fork, current_fork,
noop,
) )
.await .await
.and_then(GetPayloadResponse::try_into) .and_then(GetPayloadResponseType::try_into)
.map(ProvenancedPayload::Local) .map(ProvenancedPayload::Local)?,
}
}; };
// Track some metrics and return the result. let block_proposal_content_type = match payload_result_type {
match payload_result { ProvenancedPayload::Local(local_payload) => local_payload,
Ok(ProvenancedPayload::Local(block_proposal_contents)) => { ProvenancedPayload::Builder(builder_payload) => builder_payload,
};
match block_proposal_content_type {
BlockProposalContentsType::Full(block_proposal_contents) => {
metrics::inc_counter_vec( metrics::inc_counter_vec(
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME, &metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
&[metrics::SUCCESS], &[metrics::SUCCESS],
@ -848,9 +912,15 @@ impl<T: EthSpec> ExecutionLayer<T> {
&metrics::EXECUTION_LAYER_GET_PAYLOAD_SOURCE, &metrics::EXECUTION_LAYER_GET_PAYLOAD_SOURCE,
&[metrics::LOCAL], &[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::inc_counter_vec(
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME, &metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
&[metrics::SUCCESS], &[metrics::SUCCESS],
@ -859,19 +929,12 @@ impl<T: EthSpec> ExecutionLayer<T> {
&metrics::EXECUTION_LAYER_GET_PAYLOAD_SOURCE, &metrics::EXECUTION_LAYER_GET_PAYLOAD_SOURCE,
&[metrics::BUILDER], &[metrics::BUILDER],
); );
Ok(block_proposal_contents) Ok(BlockProposalContentsType::Blinded(block_proposal_contents))
}
Err(e) => {
metrics::inc_counter_vec(
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
&[metrics::FAILURE],
);
Err(e)
} }
} }
} }
async fn get_blinded_payload<Payload: AbstractExecPayload<T>>( async fn determine_and_fetch_payload(
&self, &self,
parent_hash: ExecutionBlockHash, parent_hash: ExecutionBlockHash,
payload_attributes: &PayloadAttributes, payload_attributes: &PayloadAttributes,
@ -879,11 +942,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
builder_params: BuilderParams, builder_params: BuilderParams,
current_fork: ForkName, current_fork: ForkName,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<ProvenancedPayload<BlockProposalContents<T, Payload>>, Error> { ) -> Result<ProvenancedPayload<BlockProposalContentsType<T>>, Error> {
if let Some(builder) = self.builder() { if let Some(builder) = self.builder() {
let slot = builder_params.slot; let slot = builder_params.slot;
let pubkey = builder_params.pubkey; let pubkey = builder_params.pubkey;
match builder_params.chain_health { match builder_params.chain_health {
ChainHealth::Healthy => { ChainHealth::Healthy => {
info!( 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). // 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 { timed_future(metrics::GET_BLINDED_PAYLOAD_BUILDER, async {
builder builder
.get_builder_header::<T>(slot, parent_hash, &pubkey) .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!( info!(
self.log(), self.log(),
"Requested blinded execution payload"; "Requested blinded execution payload";
@ -939,7 +1006,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
"local_block_hash" => ?local.block_hash(), "local_block_hash" => ?local.block_hash(),
"parent_hash" => ?parent_hash, "parent_hash" => ?parent_hash,
); );
Ok(ProvenancedPayload::Local(local.try_into()?)) Ok(ProvenancedPayload::Local(BlockProposalContentsType::Full(
local.try_into()?,
)))
} }
(Ok(None), Ok(local)) => { (Ok(None), Ok(local)) => {
info!( info!(
@ -949,7 +1018,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
"local_block_hash" => ?local.block_hash(), "local_block_hash" => ?local.block_hash(),
"parent_hash" => ?parent_hash, "parent_hash" => ?parent_hash,
); );
Ok(ProvenancedPayload::Local(local.try_into()?)) Ok(ProvenancedPayload::Local(BlockProposalContentsType::Full(
local.try_into()?,
)))
} }
(Ok(Some(relay)), Ok(local)) => { (Ok(Some(relay)), Ok(local)) => {
let header = &relay.data.message.header(); let header = &relay.data.message.header();
@ -973,7 +1044,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
"local_block_value" => %local_value, "local_block_value" => %local_value,
"relay_value" => %relay_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) { } else if local.should_override_builder().unwrap_or(false) {
let percentage_difference = let percentage_difference =
percentage_difference_u256(local_value, *relay_value); percentage_difference_u256(local_value, *relay_value);
@ -989,7 +1062,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
"local_block_value" => %local_value, "local_block_value" => %local_value,
"relay_value" => %relay_value "relay_value" => %relay_value
); );
return Ok(ProvenancedPayload::Local(local.try_into()?)); return Ok(ProvenancedPayload::Local(
BlockProposalContentsType::Full(local.try_into()?),
));
} }
} else { } else {
info!( info!(
@ -1020,7 +1095,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
"relay_block_hash" => ?header.block_hash(), "relay_block_hash" => ?header.block_hash(),
"parent_hash" => ?parent_hash, "parent_hash" => ?parent_hash,
); );
Ok(ProvenancedPayload::Local(local.try_into()?)) Ok(ProvenancedPayload::Local(BlockProposalContentsType::Full(
local.try_into()?,
)))
} }
Err(reason) => { Err(reason) => {
metrics::inc_counter_vec( metrics::inc_counter_vec(
@ -1035,7 +1112,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
"relay_block_hash" => ?header.block_hash(), "relay_block_hash" => ?header.block_hash(),
"parent_hash" => ?parent_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, current_fork,
) )
.await .await
.and_then(GetPayloadResponse::try_into) .and_then(GetPayloadResponseType::try_into)
.map(ProvenancedPayload::Local) .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. /// Get a full payload and cache its result in the execution layer's payload cache.
async fn get_full_payload_caching( async fn get_full_payload_caching(
&self, &self,
@ -1161,7 +1222,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
payload_attributes: &PayloadAttributes, payload_attributes: &PayloadAttributes,
forkchoice_update_params: ForkchoiceUpdateParameters, forkchoice_update_params: ForkchoiceUpdateParameters,
current_fork: ForkName, current_fork: ForkName,
) -> Result<GetPayloadResponse<T>, Error> { ) -> Result<GetPayloadResponseType<T>, Error> {
self.get_full_payload_with( self.get_full_payload_with(
parent_hash, parent_hash,
payload_attributes, payload_attributes,
@ -1182,7 +1243,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
&ExecutionLayer<T>, &ExecutionLayer<T>,
PayloadContentsRefTuple<T>, PayloadContentsRefTuple<T>,
) -> Option<FullPayloadContents<T>>, ) -> Option<FullPayloadContents<T>>,
) -> Result<GetPayloadResponse<T>, Error> { ) -> Result<GetPayloadResponseType<T>, Error> {
self.engine() self.engine()
.request(move |engine| async move { .request(move |engine| async move {
let payload_id = if let Some(id) = engine let payload_id = if let Some(id) = engine
@ -1244,6 +1305,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
"timestamp" => payload_attributes.timestamp(), "timestamp" => payload_attributes.timestamp(),
"parent_hash" => ?parent_hash, "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 engine.api.get_payload::<T>(current_fork, payload_id).await
}.await?; }.await?;
@ -1268,7 +1333,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
); );
} }
Ok(payload_response) Ok(GetPayloadResponseType::Full(payload_response))
}) })
.await .await
.map_err(Box::new) .map_err(Box::new)

View File

@ -508,11 +508,7 @@ pub fn serve<E: EthSpec>(
finalized_hash: Some(finalized_execution_hash), finalized_hash: Some(finalized_execution_hash),
}; };
let (payload, _block_value, maybe_blobs_bundle): ( let payload_response_type = builder
ExecutionPayload<E>,
Uint256,
Option<BlobsBundle<E>>,
) = builder
.el .el
.get_full_payload_caching( .get_full_payload_caching(
head_execution_hash, head_execution_hash,
@ -521,10 +517,17 @@ pub fn serve<E: EthSpec>(
fork, fork,
) )
.await .await
.map_err(|_| reject("couldn't get payload"))? .map_err(|_| reject("couldn't get payload"))?;
.into();
let mut message = match fork { let mut message = match payload_response_type {
crate::GetPayloadResponseType::Full(payload_response) => {
let (payload, _block_value, maybe_blobs_bundle): (
ExecutionPayload<E>,
Uint256,
Option<BlobsBundle<E>>,
) = payload_response.into();
match fork {
ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb { ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb {
header: payload header: payload
.as_deneb() .as_deneb()
@ -552,7 +555,50 @@ pub fn serve<E: EthSpec>(
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
pubkey: builder.builder_sk.public_key().compress(), pubkey: builder.builder_sk.public_key().compress(),
}), }),
ForkName::Base | ForkName::Altair => return Err(reject("invalid fork")), ForkName::Base | ForkName::Altair => {
return Err(reject("invalid fork"))
}
}
}
crate::GetPayloadResponseType::Blinded(payload_response) => {
let (payload, _block_value, maybe_blobs_bundle): (
ExecutionPayload<E>,
Uint256,
Option<BlobsBundle<E>>,
) = payload_response.into();
match fork {
ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb {
header: payload
.as_deneb()
.map_err(|_| reject("incorrect payload variant"))?
.into(),
blinded_blobs_bundle: maybe_blobs_bundle
.map(Into::into)
.unwrap_or_default(),
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
pubkey: builder.builder_sk.public_key().compress(),
}),
ForkName::Capella => BuilderBid::Capella(BuilderBidCapella {
header: payload
.as_capella()
.map_err(|_| reject("incorrect payload variant"))?
.into(),
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
pubkey: builder.builder_sk.public_key().compress(),
}),
ForkName::Merge => BuilderBid::Merge(BuilderBidMerge {
header: payload
.as_merge()
.map_err(|_| reject("incorrect payload variant"))?
.into(),
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
pubkey: builder.builder_sk.public_key().compress(),
}),
ForkName::Base | ForkName::Altair => {
return Err(reject("invalid fork"))
}
}
}
}; };
message.set_gas_limit(cached_data.gas_limit); message.set_gas_limit(cached_data.gas_limit);

View File

@ -5,12 +5,12 @@ use crate::{
}, },
Config, *, Config, *,
}; };
use keccak_hash::H256;
use kzg::Kzg; use kzg::Kzg;
use sensitive_url::SensitiveUrl; use sensitive_url::SensitiveUrl;
use task_executor::TaskExecutor; use task_executor::TaskExecutor;
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
use tree_hash::TreeHash; use types::{Address, ChainSpec, Epoch, EthSpec, Hash256, MainnetEthSpec};
use types::{Address, ChainSpec, Epoch, EthSpec, FullPayload, Hash256, MainnetEthSpec};
pub struct MockExecutionLayer<T: EthSpec> { pub struct MockExecutionLayer<T: EthSpec> {
pub server: MockServer<T>, pub server: MockServer<T>,
@ -133,20 +133,25 @@ impl<T: EthSpec> MockExecutionLayer<T> {
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await; let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
let payload_attributes = let payload_attributes =
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None); PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
let payload: ExecutionPayload<T> = self
let block_proposal_content_type = self
.el .el
.get_payload::<FullPayload<T>>( .get_payload(
parent_hash, parent_hash,
&payload_attributes, &payload_attributes,
forkchoice_update_params, forkchoice_update_params,
builder_params, builder_params,
ForkName::Merge, ForkName::Merge,
&self.spec, &self.spec,
BlockProductionVersion::FullV2,
) )
.await .await
.unwrap() .unwrap();
.to_payload()
.into(); let payload: ExecutionPayload<T> = match block_proposal_content_type {
BlockProposalContentsType::Full(block) => block.to_payload().into(),
BlockProposalContentsType::Blinded(_) => panic!("Should always be a full payload"),
};
let block_hash = payload.block_hash(); let block_hash = payload.block_hash();
assert_eq!(payload.parent_hash(), parent_hash); assert_eq!(payload.parent_hash(), parent_hash);
@ -167,20 +172,64 @@ impl<T: EthSpec> MockExecutionLayer<T> {
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await; let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
let payload_attributes = let payload_attributes =
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None); PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
let payload_header = self
let block_proposal_content_type = self
.el .el
.get_payload::<BlindedPayload<T>>( .get_payload(
parent_hash, parent_hash,
&payload_attributes, &payload_attributes,
forkchoice_update_params, forkchoice_update_params,
builder_params, builder_params,
ForkName::Merge, ForkName::Merge,
&self.spec, &self.spec,
BlockProductionVersion::BlindedV2,
) )
.await .await
.unwrap() .unwrap();
.to_payload();
match block_proposal_content_type {
BlockProposalContentsType::Full(block) => {
let payload_header = block.to_payload();
self.assert_valid_execution_payload_on_head(
payload,
payload_header,
block_hash,
parent_hash,
block_number,
timestamp,
prev_randao,
)
.await;
}
BlockProposalContentsType::Blinded(block) => {
let payload_header = block.to_payload();
self.assert_valid_execution_payload_on_head(
payload,
payload_header,
block_hash,
parent_hash,
block_number,
timestamp,
prev_randao,
)
.await;
}
};
self
}
#[allow(clippy::too_many_arguments)]
pub async fn assert_valid_execution_payload_on_head<Payload: AbstractExecPayload<T>>(
&self,
payload: ExecutionPayload<T>,
payload_header: Payload,
block_hash: ExecutionBlockHash,
parent_hash: ExecutionBlockHash,
block_number: u64,
timestamp: u64,
prev_randao: H256,
) {
assert_eq!(payload_header.block_hash(), block_hash); assert_eq!(payload_header.block_hash(), block_hash);
assert_eq!(payload_header.parent_hash(), parent_hash); assert_eq!(payload_header.parent_hash(), parent_hash);
assert_eq!(payload_header.block_number(), block_number); assert_eq!(payload_header.block_number(), block_number);
@ -224,8 +273,6 @@ impl<T: EthSpec> MockExecutionLayer<T> {
assert_eq!(head_execution_block.block_number(), block_number); assert_eq!(head_execution_block.block_number(), block_number);
assert_eq!(head_execution_block.block_hash(), block_hash); assert_eq!(head_execution_block.block_hash(), block_hash);
assert_eq!(head_execution_block.parent_hash(), parent_hash); assert_eq!(head_execution_block.parent_hash(), parent_hash);
self
} }
pub fn move_to_block_prior_to_terminal_block(self) -> Self { pub fn move_to_block_prior_to_terminal_block(self) -> Self {

View File

@ -1,46 +1,15 @@
use beacon_chain::BlockProductionError; use beacon_chain::BlockProductionError;
use eth2::types::{BeaconBlockAndBlobSidecars, BlindedBeaconBlockAndBlobSidecars, BlockContents}; use eth2::types::{BeaconBlockAndBlobSidecars, BlindedBeaconBlockAndBlobSidecars, BlockContents};
use types::{ use types::{AbstractExecPayload, BeaconBlock, EthSpec, ForkName, SidecarList};
BeaconBlock, BlindedBlobSidecarList, BlindedPayload, BlobSidecarList, EthSpec, ForkName,
FullPayload,
};
type Error = warp::reject::Rejection; type Error = warp::reject::Rejection;
type FullBlockContents<E> = BlockContents<E, FullPayload<E>>;
type BlindedBlockContents<E> = BlockContents<E, BlindedPayload<E>>;
pub fn build_block_contents<E: EthSpec>( pub fn build_block_contents<E: EthSpec, Payload: AbstractExecPayload<E>>(
fork_name: ForkName, fork_name: ForkName,
block: BeaconBlock<E, FullPayload<E>>, block: BeaconBlock<E, Payload>,
maybe_blobs: Option<BlobSidecarList<E>>, maybe_blobs: Option<SidecarList<E, <Payload as AbstractExecPayload<E>>::Sidecar>>,
) -> Result<FullBlockContents<E>, Error> { ) -> Result<BlockContents<E, Payload>, Error> {
match fork_name { match Payload::block_type() {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { types::BlockType::Blinded => match fork_name {
Ok(BlockContents::Block(block))
}
ForkName::Deneb => {
if let Some(blob_sidecars) = maybe_blobs {
let block_and_blobs = BeaconBlockAndBlobSidecars {
block,
blob_sidecars,
};
Ok(BlockContents::BlockAndBlobSidecars(block_and_blobs))
} else {
Err(warp_utils::reject::block_production_error(
BlockProductionError::MissingBlobs,
))
}
}
}
}
pub fn build_blinded_block_contents<E: EthSpec>(
fork_name: ForkName,
block: BeaconBlock<E, BlindedPayload<E>>,
maybe_blobs: Option<BlindedBlobSidecarList<E>>,
) -> Result<BlindedBlockContents<E>, Error> {
match fork_name {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => { ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
Ok(BlockContents::Block(block)) Ok(BlockContents::Block(block))
} }
@ -58,5 +27,25 @@ pub fn build_blinded_block_contents<E: EthSpec>(
)) ))
} }
} }
},
types::BlockType::Full => match fork_name {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
Ok(BlockContents::Block(block))
}
ForkName::Deneb => {
if let Some(blob_sidecars) = maybe_blobs {
let block_and_blobs = BeaconBlockAndBlobSidecars {
block,
blob_sidecars,
};
Ok(BlockContents::BlockAndBlobSidecars(block_and_blobs))
} else {
Err(warp_utils::reject::block_production_error(
BlockProductionError::MissingBlobs,
))
}
}
},
} }
} }

View File

@ -14,6 +14,7 @@ mod build_block_contents;
mod builder_states; mod builder_states;
mod database; mod database;
mod metrics; mod metrics;
mod produce_block;
mod proposer_duties; mod proposer_duties;
mod publish_blocks; mod publish_blocks;
mod standard_block_rewards; mod standard_block_rewards;
@ -27,10 +28,11 @@ mod validator;
mod validator_inclusion; mod validator_inclusion;
mod version; mod version;
use crate::produce_block::{produce_blinded_block_v2, produce_block_v2, produce_block_v3};
use beacon_chain::{ use beacon_chain::{
attestation_verification::VerifiedAttestation, observed_operations::ObservationOutcome, attestation_verification::VerifiedAttestation, observed_operations::ObservationOutcome,
validator_monitor::timestamp_now, AttestationError as AttnError, BeaconChain, BeaconChainError, validator_monitor::timestamp_now, AttestationError as AttnError, BeaconChain, BeaconChainError,
BeaconChainTypes, ProduceBlockVerification, WhenSlotSkipped, BeaconChainTypes, WhenSlotSkipped,
}; };
use beacon_processor::BeaconProcessorSend; use beacon_processor::BeaconProcessorSend;
pub use block_id::BlockId; pub use block_id::BlockId;
@ -39,8 +41,7 @@ use bytes::Bytes;
use directory::DEFAULT_ROOT_DIR; use directory::DEFAULT_ROOT_DIR;
use eth2::types::{ use eth2::types::{
self as api_types, BroadcastValidation, EndpointVersion, ForkChoice, ForkChoiceNode, self as api_types, BroadcastValidation, EndpointVersion, ForkChoice, ForkChoiceNode,
SignedBlindedBlockContents, SignedBlockContents, SkipRandaoVerification, ValidatorId, SignedBlindedBlockContents, SignedBlockContents, ValidatorId, ValidatorStatus,
ValidatorStatus,
}; };
use lighthouse_network::{types::SyncState, EnrExt, NetworkGlobals, PeerId, PubsubMessage}; use lighthouse_network::{types::SyncState, EnrExt, NetworkGlobals, PeerId, PubsubMessage};
use lighthouse_version::version_with_platform; use lighthouse_version::version_with_platform;
@ -75,7 +76,7 @@ use tokio_stream::{
}; };
use types::{ use types::{
Attestation, AttestationData, AttestationShufflingId, AttesterSlashing, BeaconStateError, Attestation, AttestationData, AttestationShufflingId, AttesterSlashing, BeaconStateError,
BlindedPayload, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, FullPayload, BlindedPayload, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName,
ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof, ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof,
SignedBlsToExecutionChange, SignedContributionAndProof, SignedValidatorRegistrationData, SignedBlsToExecutionChange, SignedContributionAndProof, SignedValidatorRegistrationData,
SignedVoluntaryExit, Slot, SyncCommitteeMessage, SyncContributionData, SignedVoluntaryExit, Slot, SyncCommitteeMessage, SyncContributionData,
@ -83,7 +84,7 @@ use types::{
use validator::pubkey_to_validator_index; use validator::pubkey_to_validator_index;
use version::{ use version::{
add_consensus_version_header, execution_optimistic_finalized_fork_versioned_response, add_consensus_version_header, execution_optimistic_finalized_fork_versioned_response,
fork_versioned_response, inconsistent_fork_rejection, unsupported_version_rejection, V1, V2, inconsistent_fork_rejection, unsupported_version_rejection, V1, V2, V3,
}; };
use warp::http::StatusCode; use warp::http::StatusCode;
use warp::sse::Event; use warp::sse::Event;
@ -3052,17 +3053,17 @@ pub fn serve<T: BeaconChainTypes>(
)) ))
})) }))
.and(warp::path::end()) .and(warp::path::end())
.and(warp::header::optional::<api_types::Accept>("accept"))
.and(not_while_syncing_filter.clone()) .and(not_while_syncing_filter.clone())
.and(warp::query::<api_types::ValidatorBlocksQuery>()) .and(warp::query::<api_types::ValidatorBlocksQuery>())
.and(warp::header::optional::<api_types::Accept>("accept"))
.and(task_spawner_filter.clone()) .and(task_spawner_filter.clone())
.and(chain_filter.clone()) .and(chain_filter.clone())
.and(log_filter.clone()) .and(log_filter.clone())
.then( .then(
|endpoint_version: EndpointVersion, |endpoint_version: EndpointVersion,
slot: Slot, slot: Slot,
query: api_types::ValidatorBlocksQuery,
accept_header: Option<api_types::Accept>, accept_header: Option<api_types::Accept>,
query: api_types::ValidatorBlocksQuery,
task_spawner: TaskSpawner<T::EthSpec>, task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>, chain: Arc<BeaconChain<T>>,
log: Logger| { log: Logger| {
@ -3073,60 +3074,10 @@ pub fn serve<T: BeaconChainTypes>(
"slot" => slot "slot" => slot
); );
let randao_reveal = query.randao_reveal.decompress().map_err(|e| { if endpoint_version == V3 {
warp_utils::reject::custom_bad_request(format!( produce_block_v3(endpoint_version, accept_header, chain, slot, query).await
"randao reveal is not a valid BLS signature: {:?}",
e
))
})?;
let randao_verification =
if query.skip_randao_verification == SkipRandaoVerification::Yes {
if !randao_reveal.is_infinity() {
return Err(warp_utils::reject::custom_bad_request(
"randao_reveal must be point-at-infinity if verification is skipped"
.into(),
));
}
ProduceBlockVerification::NoVerification
} else { } else {
ProduceBlockVerification::VerifyRandao produce_block_v2(endpoint_version, accept_header, chain, slot, query).await
};
let (block, _, maybe_blobs) = chain
.produce_block_with_verification::<FullPayload<T::EthSpec>>(
randao_reveal,
slot,
query.graffiti.map(Into::into),
randao_verification,
)
.await
.map_err(warp_utils::reject::block_production_error)?;
let fork_name = block
.to_ref()
.fork_name(&chain.spec)
.map_err(inconsistent_fork_rejection)?;
let block_contents =
build_block_contents::build_block_contents(fork_name, block, maybe_blobs)?;
match accept_header {
Some(api_types::Accept::Ssz) => Response::builder()
.status(200)
.header("Content-Type", "application/octet-stream")
.body(block_contents.as_ssz_bytes().into())
.map(|res: Response<Bytes>| {
add_consensus_version_header(res, fork_name)
})
.map_err(|e| {
warp_utils::reject::custom_server_error(format!(
"failed to create response: {}",
e
))
}),
_ => fork_versioned_response(endpoint_version, fork_name, block_contents)
.map(|response| warp::reply::json(&response).into_response())
.map(|res| add_consensus_version_header(res, fork_name)),
} }
}) })
}, },
@ -3154,65 +3105,8 @@ pub fn serve<T: BeaconChainTypes>(
task_spawner: TaskSpawner<T::EthSpec>, task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>| { chain: Arc<BeaconChain<T>>| {
task_spawner.spawn_async_with_rejection(Priority::P0, async move { task_spawner.spawn_async_with_rejection(Priority::P0, async move {
let randao_reveal = query.randao_reveal.decompress().map_err(|e| { produce_blinded_block_v2(EndpointVersion(2), accept_header, chain, slot, query)
warp_utils::reject::custom_bad_request(format!(
"randao reveal is not a valid BLS signature: {:?}",
e
))
})?;
let randao_verification =
if query.skip_randao_verification == SkipRandaoVerification::Yes {
if !randao_reveal.is_infinity() {
return Err(warp_utils::reject::custom_bad_request(
"randao_reveal must be point-at-infinity if verification is skipped"
.into()
));
}
ProduceBlockVerification::NoVerification
} else {
ProduceBlockVerification::VerifyRandao
};
let (block, _, maybe_blobs) = chain
.produce_block_with_verification::<BlindedPayload<T::EthSpec>>(
randao_reveal,
slot,
query.graffiti.map(Into::into),
randao_verification,
)
.await .await
.map_err(warp_utils::reject::block_production_error)?;
let fork_name = block
.to_ref()
.fork_name(&chain.spec)
.map_err(inconsistent_fork_rejection)?;
let block_contents = build_block_contents::build_blinded_block_contents(
fork_name,
block,
maybe_blobs,
)?;
match accept_header {
Some(api_types::Accept::Ssz) => Response::builder()
.status(200)
.header("Content-Type", "application/octet-stream")
.body(block_contents.as_ssz_bytes().into())
.map(|res: Response<Bytes>| {
add_consensus_version_header(res, fork_name)
})
.map_err(|e| {
warp_utils::reject::custom_server_error(format!(
"failed to create response: {}",
e
))
}),
// Pose as a V2 endpoint so we return the fork `version`.
_ => fork_versioned_response(V2, fork_name, block_contents)
.map(|response| warp::reply::json(&response).into_response())
.map(|res| add_consensus_version_header(res, fork_name)),
}
}) })
}, },
); );
@ -3740,7 +3634,6 @@ pub fn serve<T: BeaconChainTypes>(
.as_ref() .as_ref()
.ok_or(BeaconChainError::BuilderMissing) .ok_or(BeaconChainError::BuilderMissing)
.map_err(warp_utils::reject::beacon_chain_error)?; .map_err(warp_utils::reject::beacon_chain_error)?;
builder builder
.post_builder_validators(&filtered_registration_data) .post_builder_validators(&filtered_registration_data)
.await .await

View File

@ -0,0 +1,231 @@
use bytes::Bytes;
use std::sync::Arc;
use types::{payload::BlockProductionVersion, *};
use beacon_chain::{
BeaconBlockResponse, BeaconBlockResponseType, BeaconChain, BeaconChainTypes,
ProduceBlockVerification,
};
use eth2::types::{self as api_types, EndpointVersion, SkipRandaoVerification};
use ssz::Encode;
use warp::{
hyper::{Body, Response},
Reply,
};
use crate::{
build_block_contents,
version::{
add_consensus_block_value_header, add_consensus_version_header,
add_execution_payload_blinded_header, add_execution_payload_value_header,
fork_versioned_response, inconsistent_fork_rejection,
},
};
pub fn get_randao_verification(
query: &api_types::ValidatorBlocksQuery,
randao_reveal_infinity: bool,
) -> Result<ProduceBlockVerification, warp::Rejection> {
let randao_verification = if query.skip_randao_verification == SkipRandaoVerification::Yes {
if !randao_reveal_infinity {
return Err(warp_utils::reject::custom_bad_request(
"randao_reveal must be point-at-infinity if verification is skipped".into(),
));
}
ProduceBlockVerification::NoVerification
} else {
ProduceBlockVerification::VerifyRandao
};
Ok(randao_verification)
}
pub async fn produce_block_v3<T: BeaconChainTypes>(
endpoint_version: EndpointVersion,
accept_header: Option<api_types::Accept>,
chain: Arc<BeaconChain<T>>,
slot: Slot,
query: api_types::ValidatorBlocksQuery,
) -> Result<Response<Body>, warp::Rejection> {
let randao_reveal = query.randao_reveal.decompress().map_err(|e| {
warp_utils::reject::custom_bad_request(format!(
"randao reveal is not a valid BLS signature: {:?}",
e
))
})?;
let randao_verification = get_randao_verification(&query, randao_reveal.is_infinity())?;
let block_response_type = chain
.produce_block_with_verification(
randao_reveal,
slot,
query.graffiti.map(Into::into),
randao_verification,
BlockProductionVersion::V3,
)
.await
.map_err(|e| {
warp_utils::reject::custom_bad_request(format!("failed to fetch a block: {:?}", e))
})?;
match block_response_type {
BeaconBlockResponseType::Full(block_response) => {
build_response_v3(chain, block_response, endpoint_version, accept_header)
}
BeaconBlockResponseType::Blinded(block_response) => {
build_response_v3(chain, block_response, endpoint_version, accept_header)
}
}
}
pub fn build_response_v3<T: BeaconChainTypes, E: EthSpec, Payload: AbstractExecPayload<E>>(
chain: Arc<BeaconChain<T>>,
block_response: BeaconBlockResponse<E, Payload>,
endpoint_version: EndpointVersion,
accept_header: Option<api_types::Accept>,
) -> Result<Response<Body>, warp::Rejection> {
let fork_name = block_response
.block
.to_ref()
.fork_name(&chain.spec)
.map_err(inconsistent_fork_rejection)?;
let block_contents = build_block_contents::build_block_contents(
fork_name,
block_response.block,
block_response.maybe_side_car,
)?;
let execution_payload_blinded = Payload::block_type() == BlockType::Blinded;
match accept_header {
Some(api_types::Accept::Ssz) => Response::builder()
.status(200)
.header("Content-Type", "application/ssz")
.body(block_contents.as_ssz_bytes().into())
.map(|res: Response<Body>| add_consensus_version_header(res, fork_name))
.map(|res| add_execution_payload_blinded_header(res, execution_payload_blinded))
.map(|res: Response<Body>| {
add_execution_payload_value_header(res, block_response.execution_payload_value)
})
.map(|res| add_consensus_block_value_header(res, block_response.consensus_block_value))
.map_err(|e| -> warp::Rejection {
warp_utils::reject::custom_server_error(format!("failed to create response: {}", e))
}),
_ => fork_versioned_response(endpoint_version, fork_name, block_contents)
.map(|response| warp::reply::json(&response).into_response())
.map(|res| add_consensus_version_header(res, fork_name))
.map(|res| add_execution_payload_blinded_header(res, execution_payload_blinded))
.map(|res| {
add_execution_payload_value_header(res, block_response.execution_payload_value)
})
.map(|res| add_consensus_block_value_header(res, block_response.consensus_block_value)),
}
}
pub async fn produce_blinded_block_v2<T: BeaconChainTypes>(
endpoint_version: EndpointVersion,
accept_header: Option<api_types::Accept>,
chain: Arc<BeaconChain<T>>,
slot: Slot,
query: api_types::ValidatorBlocksQuery,
) -> Result<Response<Body>, warp::Rejection> {
let randao_reveal = query.randao_reveal.decompress().map_err(|e| {
warp_utils::reject::custom_bad_request(format!(
"randao reveal is not a valid BLS signature: {:?}",
e
))
})?;
let randao_verification = get_randao_verification(&query, randao_reveal.is_infinity())?;
let block_response_type = chain
.produce_block_with_verification(
randao_reveal,
slot,
query.graffiti.map(Into::into),
randao_verification,
BlockProductionVersion::BlindedV2,
)
.await
.map_err(warp_utils::reject::block_production_error)?;
match block_response_type {
BeaconBlockResponseType::Full(block_response) => {
build_response_v2(chain, block_response, endpoint_version, accept_header)
}
BeaconBlockResponseType::Blinded(block_response) => {
build_response_v2(chain, block_response, endpoint_version, accept_header)
}
}
}
pub async fn produce_block_v2<T: BeaconChainTypes>(
endpoint_version: EndpointVersion,
accept_header: Option<api_types::Accept>,
chain: Arc<BeaconChain<T>>,
slot: Slot,
query: api_types::ValidatorBlocksQuery,
) -> Result<Response<Body>, warp::Rejection> {
let randao_reveal = query.randao_reveal.decompress().map_err(|e| {
warp_utils::reject::custom_bad_request(format!(
"randao reveal is not a valid BLS signature: {:?}",
e
))
})?;
let randao_verification = get_randao_verification(&query, randao_reveal.is_infinity())?;
let block_response_type = chain
.produce_block_with_verification(
randao_reveal,
slot,
query.graffiti.map(Into::into),
randao_verification,
BlockProductionVersion::FullV2,
)
.await
.map_err(warp_utils::reject::block_production_error)?;
match block_response_type {
BeaconBlockResponseType::Full(block_response) => {
build_response_v2(chain, block_response, endpoint_version, accept_header)
}
BeaconBlockResponseType::Blinded(block_response) => {
build_response_v2(chain, block_response, endpoint_version, accept_header)
}
}
}
pub fn build_response_v2<T: BeaconChainTypes, E: EthSpec, Payload: AbstractExecPayload<E>>(
chain: Arc<BeaconChain<T>>,
block_response: BeaconBlockResponse<E, Payload>,
endpoint_version: EndpointVersion,
accept_header: Option<api_types::Accept>,
) -> Result<Response<Body>, warp::Rejection> {
let fork_name = block_response
.block
.to_ref()
.fork_name(&chain.spec)
.map_err(inconsistent_fork_rejection)?;
let block_contents = build_block_contents::build_block_contents(
fork_name,
block_response.block,
block_response.maybe_side_car,
)?;
match accept_header {
Some(api_types::Accept::Ssz) => Response::builder()
.status(200)
.header("Content-Type", "application/octet-stream")
.body(block_contents.as_ssz_bytes().into())
.map(|res: Response<Bytes>| add_consensus_version_header(res, fork_name))
.map_err(|e| {
warp_utils::reject::custom_server_error(format!("failed to create response: {}", e))
}),
_ => fork_versioned_response(endpoint_version, fork_name, block_contents)
.map(|response| warp::reply::json(&response).into_response())
.map(|res| add_consensus_version_header(res, fork_name)),
}
}

View File

@ -1,5 +1,5 @@
use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes}; use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes};
use types::*; use types::{BeaconState, PublicKeyBytes};
/// Uses the `chain.validator_pubkey_cache` to resolve a pubkey to a validator /// Uses the `chain.validator_pubkey_cache` to resolve a pubkey to a validator
/// index and then ensures that the validator exists in the given `state`. /// index and then ensures that the validator exists in the given `state`.

View File

@ -1,12 +1,16 @@
use crate::api_types::fork_versioned_response::ExecutionOptimisticFinalizedForkVersionedResponse; use crate::api_types::fork_versioned_response::ExecutionOptimisticFinalizedForkVersionedResponse;
use crate::api_types::EndpointVersion; use crate::api_types::EndpointVersion;
use eth2::CONSENSUS_VERSION_HEADER; use eth2::{
CONSENSUS_BLOCK_VALUE_HEADER, CONSENSUS_VERSION_HEADER, EXECUTION_PAYLOAD_BLINDED_HEADER,
EXECUTION_PAYLOAD_VALUE_HEADER,
};
use serde::Serialize; use serde::Serialize;
use types::{ForkName, ForkVersionedResponse, InconsistentFork}; use types::{ForkName, ForkVersionedResponse, InconsistentFork, Uint256};
use warp::reply::{self, Reply, Response}; use warp::reply::{self, Reply, Response};
pub const V1: EndpointVersion = EndpointVersion(1); pub const V1: EndpointVersion = EndpointVersion(1);
pub const V2: EndpointVersion = EndpointVersion(2); pub const V2: EndpointVersion = EndpointVersion(2);
pub const V3: EndpointVersion = EndpointVersion(3);
pub fn fork_versioned_response<T: Serialize>( pub fn fork_versioned_response<T: Serialize>(
endpoint_version: EndpointVersion, endpoint_version: EndpointVersion,
@ -15,7 +19,7 @@ pub fn fork_versioned_response<T: Serialize>(
) -> Result<ForkVersionedResponse<T>, warp::reject::Rejection> { ) -> Result<ForkVersionedResponse<T>, warp::reject::Rejection> {
let fork_name = if endpoint_version == V1 { let fork_name = if endpoint_version == V1 {
None None
} else if endpoint_version == V2 { } else if endpoint_version == V2 || endpoint_version == V3 {
Some(fork_name) Some(fork_name)
} else { } else {
return Err(unsupported_version_rejection(endpoint_version)); return Err(unsupported_version_rejection(endpoint_version));
@ -53,6 +57,45 @@ pub fn add_consensus_version_header<T: Reply>(reply: T, fork_name: ForkName) ->
reply::with_header(reply, CONSENSUS_VERSION_HEADER, fork_name.to_string()).into_response() reply::with_header(reply, CONSENSUS_VERSION_HEADER, fork_name.to_string()).into_response()
} }
/// Add the `Eth-Execution-Payload-Blinded` header to a response.
pub fn add_execution_payload_blinded_header<T: Reply>(
reply: T,
execution_payload_blinded: bool,
) -> Response {
reply::with_header(
reply,
EXECUTION_PAYLOAD_BLINDED_HEADER,
execution_payload_blinded.to_string(),
)
.into_response()
}
/// Add the `Eth-Execution-Payload-Value` header to a response.
pub fn add_execution_payload_value_header<T: Reply>(
reply: T,
execution_payload_value: Option<Uint256>,
) -> Response {
reply::with_header(
reply,
EXECUTION_PAYLOAD_VALUE_HEADER,
execution_payload_value.unwrap_or_default().to_string(),
)
.into_response()
}
/// Add the `Eth-Consensus-Block-Value` header to a response.
pub fn add_consensus_block_value_header<T: Reply>(
reply: T,
consensus_payload_value: Option<u64>,
) -> Response {
reply::with_header(
reply,
CONSENSUS_BLOCK_VALUE_HEADER,
consensus_payload_value.unwrap_or_default().to_string(),
)
.into_response()
}
pub fn inconsistent_fork_rejection(error: InconsistentFork) -> warp::reject::Rejection { pub fn inconsistent_fork_rejection(error: InconsistentFork) -> warp::reject::Rejection {
warp_utils::reject::custom_server_error(format!("wrong fork: {:?}", error)) warp_utils::reject::custom_server_error(format!("wrong fork: {:?}", error))
} }

View File

@ -21,6 +21,8 @@ use types::{
MainnetEthSpec, MinimalEthSpec, ProposerPreparationData, Slot, MainnetEthSpec, MinimalEthSpec, ProposerPreparationData, Slot,
}; };
use eth2::types::ForkVersionedBeaconBlockType::{Blinded, Full};
type E = MainnetEthSpec; type E = MainnetEthSpec;
// Test that the deposit_contract endpoint returns the correct chain_id and address. // Test that the deposit_contract endpoint returns the correct chain_id and address.
@ -617,13 +619,18 @@ pub async fn proposer_boost_re_org_test(
let randao_reveal = harness let randao_reveal = harness
.sign_randao_reveal(&state_b, proposer_index, slot_c) .sign_randao_reveal(&state_b, proposer_index, slot_c)
.into(); .into();
let unsigned_block_contents_c = tester let unsigned_block_type = tester
.client .client
.get_validator_blocks(slot_c, &randao_reveal, None) .get_validator_blocks_v3::<E>(slot_c, &randao_reveal, None)
.await .await
.unwrap() .unwrap();
.data;
let (unsigned_block_c, block_c_blobs) = unsigned_block_contents_c.deconstruct(); let (unsigned_block_c, block_c_blobs) = match unsigned_block_type {
Full(unsigned_block_contents_c) => unsigned_block_contents_c.data.deconstruct(),
Blinded(_) => {
panic!("Should not be a blinded block");
}
};
let block_c = harness.sign_beacon_block(unsigned_block_c, &state_b); let block_c = harness.sign_beacon_block(unsigned_block_c, &state_b);
if should_re_org { if should_re_org {

View File

@ -3544,7 +3544,6 @@ impl ApiTester {
.cached_head() .cached_head()
.head_random() .head_random()
.unwrap(); .unwrap();
let (_, randao_reveal) = self.get_test_randao(slot, epoch).await; let (_, randao_reveal) = self.get_test_randao(slot, epoch).await;
let payload: BlindedPayload<E> = self let payload: BlindedPayload<E> = self
@ -4586,8 +4585,7 @@ impl ApiTester {
assert_eq!(withdrawal_response.finalized, Some(false)); assert_eq!(withdrawal_response.finalized, Some(false));
assert_eq!(withdrawal_response.data, expected_withdrawals.to_vec()); assert_eq!(withdrawal_response.data, expected_withdrawals.to_vec());
} }
Err(e) => { Err(_) => {
println!("{:?}", e);
panic!("query failed incorrectly"); panic!("query failed incorrectly");
} }
} }

View File

@ -38,8 +38,12 @@ use store::fork_versioned_response::ExecutionOptimisticFinalizedForkVersionedRes
pub const V1: EndpointVersion = EndpointVersion(1); pub const V1: EndpointVersion = EndpointVersion(1);
pub const V2: EndpointVersion = EndpointVersion(2); pub const V2: EndpointVersion = EndpointVersion(2);
pub const V3: EndpointVersion = EndpointVersion(3);
pub const CONSENSUS_VERSION_HEADER: &str = "Eth-Consensus-Version"; pub const CONSENSUS_VERSION_HEADER: &str = "Eth-Consensus-Version";
pub const EXECUTION_PAYLOAD_BLINDED_HEADER: &str = "Eth-Execution-Payload-Blinded";
pub const EXECUTION_PAYLOAD_VALUE_HEADER: &str = "Eth-Execution-Payload-Value";
pub const CONSENSUS_BLOCK_VALUE_HEADER: &str = "Eth-Consensus-Block-Value";
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -1628,6 +1632,26 @@ impl BeaconNodeHttpClient {
.await .await
} }
/// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blocks_modular<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
let path = self
.get_validator_blocks_path::<T, Payload>(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
)
.await?;
self.get(path).await
}
/// returns `GET v2/validator/blocks/{slot}` URL path /// returns `GET v2/validator/blocks/{slot}` URL path
pub async fn get_validator_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>( pub async fn get_validator_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self, &self,
@ -1660,24 +1684,70 @@ impl BeaconNodeHttpClient {
Ok(path) Ok(path)
} }
/// `GET v2/validator/blocks/{slot}` /// `GET v3/validator/blocks/{slot}`
pub async fn get_validator_blocks_modular<T: EthSpec, Payload: AbstractExecPayload<T>>( pub async fn get_validator_blocks_v3<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<ForkVersionedBeaconBlockType<T>, Error> {
self.get_validator_blocks_v3_modular(
slot,
randao_reveal,
graffiti,
SkipRandaoVerification::No,
)
.await
}
/// `GET v3/validator/blocks/{slot}`
pub async fn get_validator_blocks_v3_modular<T: EthSpec>(
&self, &self,
slot: Slot, slot: Slot,
randao_reveal: &SignatureBytes, randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>, graffiti: Option<&Graffiti>,
skip_randao_verification: SkipRandaoVerification, skip_randao_verification: SkipRandaoVerification,
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> { ) -> Result<ForkVersionedBeaconBlockType<T>, Error> {
let path = self let mut path = self.eth_path(V3)?;
.get_validator_blocks_path::<T, Payload>(
slot,
randao_reveal,
graffiti,
skip_randao_verification,
)
.await?;
self.get(path).await path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("blocks")
.push(&slot.to_string());
path.query_pairs_mut()
.append_pair("randao_reveal", &randao_reveal.to_string());
if let Some(graffiti) = graffiti {
path.query_pairs_mut()
.append_pair("graffiti", &graffiti.to_string());
}
if skip_randao_verification == SkipRandaoVerification::Yes {
path.query_pairs_mut()
.append_pair("skip_randao_verification", "");
}
let response = self.get_response(path, |b| b).await?;
let is_blinded_payload = response
.headers()
.get(EXECUTION_PAYLOAD_BLINDED_HEADER)
.map(|value| value.to_str().unwrap_or_default().to_lowercase() == "true")
.unwrap_or(false);
if is_blinded_payload {
let blinded_payload = response
.json::<ForkVersionedResponse<BlockContents<T, BlindedPayload<T>>>>()
.await?;
Ok(ForkVersionedBeaconBlockType::Blinded(blinded_payload))
} else {
let full_payload = response
.json::<ForkVersionedResponse<BlockContents<T, FullPayload<T>>>>()
.await?;
Ok(ForkVersionedBeaconBlockType::Full(full_payload))
}
} }
/// `GET v2/validator/blocks/{slot}` in ssz format /// `GET v2/validator/blocks/{slot}` in ssz format

View File

@ -1384,6 +1384,11 @@ pub mod serde_status_code {
} }
} }
pub enum ForkVersionedBeaconBlockType<T: EthSpec> {
Full(ForkVersionedResponse<BlockContents<T, FullPayload<T>>>),
Blinded(ForkVersionedResponse<BlockContents<T, BlindedPayload<T>>>),
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -979,3 +979,10 @@ impl<T: EthSpec> From<BlindedPayload<T>> for ExecutionPayloadHeader<T> {
} }
} }
} }
/// The block production flow version to be used.
pub enum BlockProductionVersion {
V3,
BlindedV2,
FullV2,
}

View File

@ -4,7 +4,8 @@ use crate::execution_engine::{
use crate::transactions::transactions; use crate::transactions::transactions;
use ethers_providers::Middleware; use ethers_providers::Middleware;
use execution_layer::{ use execution_layer::{
BuilderParams, ChainHealth, ExecutionLayer, PayloadAttributes, PayloadStatus, BlockProposalContentsType, BuilderParams, ChainHealth, ExecutionLayer, PayloadAttributes,
PayloadStatus,
}; };
use fork_choice::ForkchoiceUpdateParameters; use fork_choice::ForkchoiceUpdateParameters;
use reqwest::{header::CONTENT_TYPE, Client}; use reqwest::{header::CONTENT_TYPE, Client};
@ -14,9 +15,10 @@ use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use task_executor::TaskExecutor; use task_executor::TaskExecutor;
use tokio::time::sleep; use tokio::time::sleep;
use types::payload::BlockProductionVersion;
use types::{ use types::{
Address, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadHeader, Address, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadHeader,
ForkName, FullPayload, Hash256, MainnetEthSpec, PublicKeyBytes, Slot, Uint256, ForkName, Hash256, MainnetEthSpec, PublicKeyBytes, Slot, Uint256,
}; };
const EXECUTION_ENGINE_START_TIMEOUT: Duration = Duration::from_secs(60); const EXECUTION_ENGINE_START_TIMEOUT: Duration = Duration::from_secs(60);
@ -322,21 +324,26 @@ impl<E: GenericExecutionEngine> TestRig<E> {
Some(vec![]), Some(vec![]),
None, None,
); );
let valid_payload = self let block_proposal_content_type = self
.ee_a .ee_a
.execution_layer .execution_layer
.get_payload::<FullPayload<MainnetEthSpec>>( .get_payload(
parent_hash, parent_hash,
&payload_attributes, &payload_attributes,
forkchoice_update_params, forkchoice_update_params,
builder_params, builder_params,
TEST_FORK, TEST_FORK,
&self.spec, &self.spec,
BlockProductionVersion::FullV2,
) )
.await .await
.unwrap() .unwrap();
.to_payload()
.execution_payload(); let valid_payload = match block_proposal_content_type {
BlockProposalContentsType::Full(block) => block.to_payload().execution_payload(),
BlockProposalContentsType::Blinded(_) => panic!("Should always be a full payload"),
};
assert_eq!(valid_payload.transactions().len(), pending_txs.len()); assert_eq!(valid_payload.transactions().len(), pending_txs.len());
/* /*
@ -468,21 +475,25 @@ impl<E: GenericExecutionEngine> TestRig<E> {
Some(vec![]), Some(vec![]),
None, None,
); );
let second_payload = self let block_proposal_content_type = self
.ee_a .ee_a
.execution_layer .execution_layer
.get_payload::<FullPayload<MainnetEthSpec>>( .get_payload(
parent_hash, parent_hash,
&payload_attributes, &payload_attributes,
forkchoice_update_params, forkchoice_update_params,
builder_params, builder_params,
TEST_FORK, TEST_FORK,
&self.spec, &self.spec,
BlockProductionVersion::FullV2,
) )
.await .await
.unwrap() .unwrap();
.to_payload()
.execution_payload(); let second_payload = match block_proposal_content_type {
BlockProposalContentsType::Full(block) => block.to_payload().execution_payload(),
BlockProposalContentsType::Blinded(_) => panic!("Should always be a full payload"),
};
/* /*
* Execution Engine A: * Execution Engine A: