mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-04 05:14:33 +00:00
Add new block production endpoint
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use std::u64;
|
||||
|
||||
use bls::Signature;
|
||||
use execution_layer::BuilderParams;
|
||||
use operation_pool::CompactAttestationRef;
|
||||
use ssz::Encode;
|
||||
use state_processing::common::get_attesting_indices_from_state;
|
||||
@@ -22,14 +22,12 @@ use types::{
|
||||
SyncAggregate,
|
||||
};
|
||||
|
||||
use crate::BeaconBlockResponse;
|
||||
use crate::{
|
||||
BeaconChain, BeaconChainError, BeaconChainTypes, BlockProductionError,
|
||||
ProduceBlockVerification, graffiti_calculator::GraffitiSettings,
|
||||
BeaconChain, BeaconChainTypes, BlockProductionError, ProduceBlockVerification,
|
||||
graffiti_calculator::GraffitiSettings, metrics,
|
||||
};
|
||||
|
||||
pub struct PartialBeaconBlock<E: EthSpec> {
|
||||
state: BeaconState<E>,
|
||||
slot: Slot,
|
||||
proposer_index: u64,
|
||||
parent_root: Hash256,
|
||||
@@ -46,66 +44,136 @@ pub struct PartialBeaconBlock<E: EthSpec> {
|
||||
bls_to_execution_changes: Vec<SignedBlsToExecutionChange>,
|
||||
}
|
||||
|
||||
// We'll need to add that once we include trusted/trustless bids
|
||||
impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
pub async fn produce_block_on_bid(
|
||||
pub async fn produce_block_with_verification_gloas(
|
||||
self: &Arc<Self>,
|
||||
randao_reveal: Signature,
|
||||
slot: Slot,
|
||||
graffiti_settings: GraffitiSettings,
|
||||
verification: ProduceBlockVerification,
|
||||
_builder_boost_factor: Option<u64>,
|
||||
) -> Result<
|
||||
(
|
||||
BeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>,
|
||||
BeaconState<T::EthSpec>,
|
||||
u64,
|
||||
),
|
||||
BlockProductionError,
|
||||
> {
|
||||
metrics::inc_counter(&metrics::BLOCK_PRODUCTION_REQUESTS);
|
||||
let _complete_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_TIMES);
|
||||
// Part 1/2 (blocking)
|
||||
//
|
||||
// Load the parent state from disk.
|
||||
let chain = self.clone();
|
||||
let span = Span::current();
|
||||
let (state, state_root_opt) = self
|
||||
.task_executor
|
||||
.spawn_blocking_handle(
|
||||
move || {
|
||||
let _guard =
|
||||
debug_span!(parent: span, "load_state_for_block_production").entered();
|
||||
chain.load_state_for_block_production(slot)
|
||||
},
|
||||
"load_state_for_block_production",
|
||||
)
|
||||
.ok_or(BlockProductionError::ShuttingDown)?
|
||||
.await
|
||||
.map_err(BlockProductionError::TokioJoin)??;
|
||||
|
||||
// Part 2/2 (async, with some blocking components)
|
||||
//
|
||||
// Produce the block upon the state
|
||||
self.produce_block_on_state_gloas(
|
||||
state,
|
||||
state_root_opt,
|
||||
slot,
|
||||
randao_reveal,
|
||||
graffiti_settings,
|
||||
verification,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// TODO(gloas) need to implement builder boost factor logic
|
||||
pub async fn produce_block_on_state_gloas(
|
||||
self: &Arc<Self>,
|
||||
state: BeaconState<T::EthSpec>,
|
||||
execution_payload_bid: SignedExecutionPayloadBid<T::EthSpec>,
|
||||
state_root_opt: Option<Hash256>,
|
||||
produce_at_slot: Slot,
|
||||
randao_reveal: Signature,
|
||||
graffiti_settings: GraffitiSettings,
|
||||
verification: ProduceBlockVerification,
|
||||
builder_boost_factor: Option<u64>,
|
||||
) -> Result<BeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>, BlockProductionError> {
|
||||
) -> Result<
|
||||
(
|
||||
BeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>,
|
||||
BeaconState<T::EthSpec>,
|
||||
u64,
|
||||
),
|
||||
BlockProductionError,
|
||||
> {
|
||||
// Part 1/3 (blocking)
|
||||
//
|
||||
// Perform the state advance and block-packing functions.
|
||||
let chain = self.clone();
|
||||
let graffiti = self
|
||||
.graffiti_calculator
|
||||
.get_graffiti(graffiti_settings)
|
||||
.await;
|
||||
let span = Span::current();
|
||||
let mut partial_beacon_block = self
|
||||
let (partial_beacon_block, state) = self
|
||||
.task_executor
|
||||
.spawn_blocking_handle(
|
||||
move || {
|
||||
let _guard =
|
||||
debug_span!(parent: span, "produce_partial_beacon_block").entered();
|
||||
debug_span!(parent: span, "produce_partial_beacon_block_gloas").entered();
|
||||
chain.produce_partial_beacon_block_gloas(
|
||||
state,
|
||||
state_root_opt,
|
||||
produce_at_slot,
|
||||
randao_reveal,
|
||||
graffiti,
|
||||
builder_boost_factor,
|
||||
)
|
||||
},
|
||||
"produce_partial_beacon_block",
|
||||
"produce_partial_beacon_block_gloas",
|
||||
)
|
||||
.ok_or(BlockProductionError::ShuttingDown)?
|
||||
.await
|
||||
.map_err(BlockProductionError::TokioJoin)??;
|
||||
|
||||
// Part 2/3 (async)
|
||||
//
|
||||
// Produce the execution payload bid.
|
||||
// TODO(gloas) this is strictly for building local bids
|
||||
// We'll need to build out trustless/trusted bid paths.
|
||||
let (execution_payload_bid, state) = self
|
||||
.clone()
|
||||
.produce_execution_payload_bid(state, state_root_opt, produce_at_slot, 0, u64::MAX)
|
||||
.await?;
|
||||
|
||||
// Part 3/3 (blocking)
|
||||
//
|
||||
// Complete the block with the execution payload bid.
|
||||
let chain = self.clone();
|
||||
let span = Span::current();
|
||||
let beacon_block_response = self
|
||||
.task_executor
|
||||
self.task_executor
|
||||
.spawn_blocking_handle(
|
||||
move || {
|
||||
let _guard =
|
||||
debug_span!(parent: span, "complete_partial_beacon_block").entered();
|
||||
debug_span!(parent: span, "complete_partial_beacon_block_gloas").entered();
|
||||
chain.complete_partial_beacon_block_gloas(
|
||||
partial_beacon_block,
|
||||
execution_payload_bid,
|
||||
state,
|
||||
verification,
|
||||
)
|
||||
},
|
||||
"complete_partial_beacon_block",
|
||||
"complete_partial_beacon_block_gloas",
|
||||
)
|
||||
.ok_or(BlockProductionError::ShuttingDown)?
|
||||
.await
|
||||
.map_err(BlockProductionError::TokioJoin)??;
|
||||
|
||||
todo!()
|
||||
.map_err(BlockProductionError::TokioJoin)?
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -116,8 +184,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
produce_at_slot: Slot,
|
||||
randao_reveal: Signature,
|
||||
graffiti: Graffiti,
|
||||
builder_boost_factor: Option<u64>,
|
||||
) -> Result<PartialBeaconBlock<T::EthSpec>, BlockProductionError> {
|
||||
) -> Result<(PartialBeaconBlock<T::EthSpec>, BeaconState<T::EthSpec>), BlockProductionError>
|
||||
{
|
||||
// It is invalid to try to produce a block using a state from a future slot.
|
||||
if state.slot() > produce_at_slot {
|
||||
return Err(BlockProductionError::StateSlotTooHigh {
|
||||
@@ -148,22 +216,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot(), &self.spec)? as u64;
|
||||
|
||||
let pubkey = state
|
||||
.validators()
|
||||
.get(proposer_index as usize)
|
||||
.map(|v| v.pubkey)
|
||||
.ok_or(BlockProductionError::BeaconChain(Box::new(
|
||||
BeaconChainError::ValidatorIndexUnknown(proposer_index as usize),
|
||||
)))?;
|
||||
|
||||
let builder_params = BuilderParams {
|
||||
pubkey,
|
||||
slot: state.slot(),
|
||||
chain_health: self
|
||||
.is_healthy(&parent_root)
|
||||
.map_err(|e| BlockProductionError::BeaconChain(Box::new(e)))?,
|
||||
};
|
||||
|
||||
let slashings_and_exits_span = debug_span!("get_slashings_and_exits").entered();
|
||||
let (mut proposer_slashings, mut attester_slashings, mut voluntary_exits) =
|
||||
self.op_pool.get_slashings_and_exits(&state, &self.spec);
|
||||
@@ -339,8 +391,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
Some(sync_aggregate)
|
||||
};
|
||||
|
||||
Ok(PartialBeaconBlock {
|
||||
state,
|
||||
Ok((
|
||||
PartialBeaconBlock {
|
||||
slot,
|
||||
proposer_index,
|
||||
parent_root,
|
||||
@@ -356,13 +408,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// TODO(gloas) need to implement payload attestations
|
||||
payload_attestations: vec![],
|
||||
bls_to_execution_changes,
|
||||
})
|
||||
},
|
||||
state,
|
||||
))
|
||||
}
|
||||
|
||||
fn complete_partial_beacon_block_gloas(
|
||||
&self,
|
||||
partial_beacon_block: PartialBeaconBlock<T::EthSpec>,
|
||||
signed_execution_payload_bid: SignedExecutionPayloadBid<T::EthSpec>,
|
||||
mut state: BeaconState<T::EthSpec>,
|
||||
verification: ProduceBlockVerification,
|
||||
) -> Result<
|
||||
(
|
||||
@@ -373,7 +428,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
BlockProductionError,
|
||||
> {
|
||||
let PartialBeaconBlock {
|
||||
mut state,
|
||||
slot,
|
||||
proposer_index,
|
||||
parent_root,
|
||||
@@ -392,36 +446,39 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
let beacon_block = match &state {
|
||||
BeaconState::Base(_) => {
|
||||
(
|
||||
// TODO(gloas) this should be an error
|
||||
todo!()
|
||||
)
|
||||
return Err(BlockProductionError::InvalidBlockVariant(
|
||||
"Cannot construct a block pre-Gloas".to_owned(),
|
||||
));
|
||||
}
|
||||
BeaconState::Altair(_) => {
|
||||
(
|
||||
// TODO(gloas) this should be an error
|
||||
todo!()
|
||||
)
|
||||
return Err(BlockProductionError::InvalidBlockVariant(
|
||||
"Cannot construct a block pre-Gloas".to_owned(),
|
||||
));
|
||||
}
|
||||
BeaconState::Bellatrix(_) => {
|
||||
// TODO(gloas) this should be an error
|
||||
todo!()
|
||||
return Err(BlockProductionError::InvalidBlockVariant(
|
||||
"Cannot construct a block pre-Gloas".to_owned(),
|
||||
));
|
||||
}
|
||||
BeaconState::Capella(_) => {
|
||||
// TODO(gloas) this should be an error
|
||||
todo!()
|
||||
return Err(BlockProductionError::InvalidBlockVariant(
|
||||
"Cannot construct a block pre-Gloas".to_owned(),
|
||||
));
|
||||
}
|
||||
BeaconState::Deneb(_) => {
|
||||
// TODO(gloas) this should be an error
|
||||
todo!()
|
||||
return Err(BlockProductionError::InvalidBlockVariant(
|
||||
"Cannot construct a block pre-Gloas".to_owned(),
|
||||
));
|
||||
}
|
||||
BeaconState::Electra(_) => {
|
||||
// TODO(gloas) this should be an error
|
||||
todo!()
|
||||
return Err(BlockProductionError::InvalidBlockVariant(
|
||||
"Cannot construct a block pre-Gloas".to_owned(),
|
||||
));
|
||||
}
|
||||
BeaconState::Fulu(_) => {
|
||||
// TODO(gloas) this should be an error
|
||||
todo!()
|
||||
return Err(BlockProductionError::InvalidBlockVariant(
|
||||
"Cannot construct a block pre-Gloas".to_owned(),
|
||||
));
|
||||
}
|
||||
BeaconState::Gloas(_) => BeaconBlock::Gloas(BeaconBlockGloas {
|
||||
slot,
|
||||
|
||||
@@ -4601,7 +4601,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
/// Load a beacon state from the database for block production. This is a long-running process
|
||||
/// that should not be performed in an `async` context.
|
||||
fn load_state_for_block_production(
|
||||
pub fn load_state_for_block_production(
|
||||
self: &Arc<Self>,
|
||||
slot: Slot,
|
||||
) -> Result<(BeaconState<T::EthSpec>, Option<Hash256>), BlockProductionError> {
|
||||
|
||||
@@ -310,6 +310,7 @@ pub enum BlockProductionError {
|
||||
MissingSyncAggregate,
|
||||
MissingExecutionPayload,
|
||||
MissingKzgCommitment(String),
|
||||
MissingStateRoot,
|
||||
TokioJoin(JoinError),
|
||||
BeaconChain(Box<BeaconChainError>),
|
||||
InvalidPayloadFork,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::sync::Arc;
|
||||
use std::{sync::Arc, u64};
|
||||
|
||||
use bls::Signature;
|
||||
use execution_layer::{BlockProposalContentsType, BuilderParams};
|
||||
use ssz_types::VariableList;
|
||||
use state_processing::state_advance::complete_state_advance;
|
||||
use tracing::instrument;
|
||||
use types::{
|
||||
Address, BeaconState, BlockProductionVersion, BuilderIndex, ExecutionPayload,
|
||||
ExecutionPayloadBid, Hash256, ProposerPreferences, Slot,
|
||||
Address, BeaconState, BlockProductionVersion, BuilderIndex, ExecutionPayloadBid, Hash256,
|
||||
SignedExecutionPayloadBid, Slot,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -15,34 +14,30 @@ use crate::{
|
||||
};
|
||||
|
||||
impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// TODO(gloas) introduce `ProposerPreferences` so we can build out trustless
|
||||
// bid building. Right now this only works for local building.
|
||||
/// Produce an `ExecutionPayloadBid` for some `slot` upon the given `state`.
|
||||
/// This function assumes we've already done the state advance.
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn produce_execution_payload_bid(
|
||||
self: &Arc<Self>,
|
||||
mut state: BeaconState<T::EthSpec>,
|
||||
state_root: Hash256,
|
||||
execution_payload: ExecutionPayload<T::EthSpec>,
|
||||
self: Arc<Self>,
|
||||
state: BeaconState<T::EthSpec>,
|
||||
state_root_opt: Option<Hash256>,
|
||||
produce_at_slot: Slot,
|
||||
proposer_preferences: Option<ProposerPreferences>,
|
||||
bid_value: u64,
|
||||
builder_index: BuilderIndex,
|
||||
value: u64,
|
||||
) -> Result<ExecutionPayloadBid<T::EthSpec>, BlockProductionError> {
|
||||
// It is invalid to try to produce a block using a state from a future slot.
|
||||
if state.slot() > produce_at_slot {
|
||||
return Err(BlockProductionError::StateSlotTooHigh {
|
||||
produce_at_slot,
|
||||
state_slot: state.slot(),
|
||||
});
|
||||
}
|
||||
|
||||
// TODO(gloas) add sanity check on value
|
||||
) -> Result<
|
||||
(
|
||||
SignedExecutionPayloadBid<T::EthSpec>,
|
||||
BeaconState<T::EthSpec>,
|
||||
),
|
||||
BlockProductionError,
|
||||
> {
|
||||
// TODO(gloas) For non local building, add sanity check on value
|
||||
// The builder MUST have enough excess balance to fulfill this bid (i.e. `value`) and all pending payments.
|
||||
|
||||
// TODO(gloas) add metrics for execution payload bid production
|
||||
|
||||
// Ensure the state has performed a complete transition into the required slot.
|
||||
complete_state_advance(&mut state, Some(state_root), produce_at_slot, &self.spec)?;
|
||||
|
||||
let parent_root = if state.slot() > 0 {
|
||||
*state
|
||||
.get_block_root(state.slot() - 1)
|
||||
@@ -82,21 +77,21 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
BlockProductionVersion::V3,
|
||||
)?;
|
||||
|
||||
let block_contents_type_option = Some(
|
||||
prepare_payload_handle
|
||||
let block_contents_type = prepare_payload_handle
|
||||
.await
|
||||
.map_err(BlockProductionError::TokioJoin)?
|
||||
.ok_or(BlockProductionError::ShuttingDown)??,
|
||||
);
|
||||
.ok_or(BlockProductionError::ShuttingDown)??;
|
||||
|
||||
let blob_kzg_commitments = if let Some(block_contents_type) = block_contents_type_option {
|
||||
match block_contents_type {
|
||||
let (execution_payload, blob_kzg_commitments) = match block_contents_type {
|
||||
BlockProposalContentsType::Full(block_proposal_contents) => {
|
||||
let blob_kzg_commitments =
|
||||
block_proposal_contents.blob_kzg_commitments().cloned();
|
||||
|
||||
if let Some(blob_kzg_commitments) = blob_kzg_commitments {
|
||||
blob_kzg_commitments
|
||||
(
|
||||
block_proposal_contents.to_payload().execution_payload(),
|
||||
blob_kzg_commitments,
|
||||
)
|
||||
} else {
|
||||
return Err(BlockProductionError::MissingKzgCommitment(
|
||||
"No KZG commitments from the payload".to_owned(),
|
||||
@@ -109,32 +104,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
BlockProposalContentsType::Blinded(_) => {
|
||||
return Err(BlockProductionError::GloasNotImplemented);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
todo!()
|
||||
};
|
||||
|
||||
let bid = if let Some(proposer_preferences) = proposer_preferences
|
||||
&& proposer_preferences.proposal_slot == produce_at_slot
|
||||
{
|
||||
// Trustless bid
|
||||
ExecutionPayloadBid::<T::EthSpec> {
|
||||
parent_block_hash: state.latest_block_hash()?.to_owned(),
|
||||
parent_block_root: state.get_latest_block_root(state_root),
|
||||
block_hash: execution_payload.block_hash(),
|
||||
prev_randao: execution_payload.prev_randao(),
|
||||
fee_recipient: proposer_preferences.fee_recipient,
|
||||
// TODO(gloas) payload construction should factor in the proposers gas limit preferences
|
||||
gas_limit: execution_payload.gas_limit(),
|
||||
builder_index,
|
||||
slot: produce_at_slot,
|
||||
value,
|
||||
execution_payment: 0,
|
||||
blob_kzg_commitments,
|
||||
}
|
||||
} else if builder_index == u64::MAX {
|
||||
// Local bid
|
||||
ExecutionPayloadBid::<T::EthSpec> {
|
||||
let state_root = state_root_opt.ok_or_else(|| {
|
||||
BlockProductionError::MissingStateRoot
|
||||
})?;
|
||||
|
||||
let bid = ExecutionPayloadBid::<T::EthSpec> {
|
||||
parent_block_hash: state.latest_block_hash()?.to_owned(),
|
||||
parent_block_root: state.get_latest_block_root(state_root),
|
||||
block_hash: execution_payload.block_hash(),
|
||||
@@ -143,18 +119,21 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
gas_limit: execution_payload.gas_limit(),
|
||||
builder_index,
|
||||
slot: produce_at_slot,
|
||||
value,
|
||||
value: bid_value,
|
||||
execution_payment: 0,
|
||||
blob_kzg_commitments,
|
||||
}
|
||||
} else {
|
||||
// No proposer preferences and this isn't local building
|
||||
// TODO(gloas) this should return a specific error type
|
||||
// i.e if proposer prefs are missing and its a trustless bid
|
||||
// return an error that communicates that.
|
||||
return Err(BlockProductionError::GloasNotImplemented);
|
||||
};
|
||||
|
||||
Ok(bid)
|
||||
// TODO(gloas) this is only local building
|
||||
// we'll need to implement builder signature for the trustless path
|
||||
Ok((
|
||||
SignedExecutionPayloadBid {
|
||||
message: bid,
|
||||
// TODO(gloas) return better error variant here
|
||||
signature: Signature::infinity()
|
||||
.map_err(|_| BlockProductionError::GloasNotImplemented)?,
|
||||
},
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,49 @@ pub fn get_randao_verification(
|
||||
Ok(randao_verification)
|
||||
}
|
||||
|
||||
#[instrument(
|
||||
name = "lh_produce_block_v4",
|
||||
skip_all,
|
||||
fields(%slot)
|
||||
)]
|
||||
pub async fn produce_block_v4<T: BeaconChainTypes>(
|
||||
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 builder_boost_factor = if query.builder_boost_factor == Some(DEFAULT_BOOST_FACTOR) {
|
||||
None
|
||||
} else {
|
||||
query.builder_boost_factor
|
||||
};
|
||||
|
||||
let graffiti_settings = GraffitiSettings::new(query.graffiti, query.graffiti_policy);
|
||||
|
||||
let (block, _state, consensus_block_value) = chain
|
||||
.produce_block_with_verification_gloas(
|
||||
randao_reveal,
|
||||
slot,
|
||||
graffiti_settings,
|
||||
randao_verification,
|
||||
builder_boost_factor,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warp_utils::reject::custom_bad_request(format!("failed to fetch a block: {:?}", e))
|
||||
})?;
|
||||
|
||||
build_response_v4(chain, block, consensus_block_value, accept_header)
|
||||
}
|
||||
|
||||
#[instrument(
|
||||
name = "lh_produce_block_v3",
|
||||
skip_all,
|
||||
@@ -87,6 +130,38 @@ pub async fn produce_block_v3<T: BeaconChainTypes>(
|
||||
build_response_v3(chain, block_response_type, accept_header)
|
||||
}
|
||||
|
||||
pub fn build_response_v4<T: BeaconChainTypes>(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
block: BeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>,
|
||||
consensus_block_value: u64,
|
||||
accept_header: Option<api_types::Accept>,
|
||||
) -> Result<Response<Body>, warp::Rejection> {
|
||||
let fork_name = block
|
||||
.to_ref()
|
||||
.fork_name(&chain.spec)
|
||||
.map_err(inconsistent_fork_rejection)?;
|
||||
let consensus_block_value_wei = Uint256::from(consensus_block_value) * Uint256::from(1_000_000_000u64);
|
||||
|
||||
match accept_header {
|
||||
Some(api_types::Accept::Ssz) => Response::builder()
|
||||
.status(200)
|
||||
.body(block.as_ssz_bytes().into())
|
||||
.map(|res: Response<Body>| add_ssz_content_type_header(res))
|
||||
.map(|res: Response<Body>| add_consensus_version_header(res, fork_name))
|
||||
.map(|res| add_consensus_block_value_header(res, consensus_block_value_wei))
|
||||
.map_err(|e| -> warp::Rejection {
|
||||
warp_utils::reject::custom_server_error(format!("failed to create response: {}", e))
|
||||
}),
|
||||
_ => Ok(warp::reply::json(&beacon_response(
|
||||
ResponseIncludesVersion::Yes(fork_name),
|
||||
block,
|
||||
))
|
||||
.into_response())
|
||||
.map(|res| add_consensus_version_header(res, fork_name))
|
||||
.map(|res| add_consensus_block_value_header(res, consensus_block_value_wei)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_response_v3<T: BeaconChainTypes>(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
block_response: BeaconBlockResponseWrapper<T::EthSpec>,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::produce_block::{produce_blinded_block_v2, produce_block_v2, produce_block_v3};
|
||||
use crate::produce_block::{produce_blinded_block_v2, produce_block_v2, produce_block_v3, produce_block_v4};
|
||||
use crate::task_spawner::{Priority, TaskSpawner};
|
||||
use crate::utils::{
|
||||
AnyVersionFilter, ChainFilter, EthV1Filter, NetworkTxFilter, NotWhileSyncingFilter,
|
||||
ResponseFilter, TaskSpawnerFilter, ValidatorSubscriptionTxFilter, publish_network_message,
|
||||
};
|
||||
use crate::version::V3;
|
||||
use crate::version::{V3, V4};
|
||||
use crate::{StateId, attester_duties, proposer_duties, sync_committees};
|
||||
use beacon_chain::attestation_verification::VerifiedAttestation;
|
||||
use beacon_chain::validator_monitor::timestamp_now;
|
||||
@@ -316,7 +316,11 @@ pub fn get_validator_blocks<T: BeaconChainTypes>(
|
||||
|
||||
not_synced_filter?;
|
||||
|
||||
if endpoint_version == V3 {
|
||||
// Use V4 block production for Gloas fork
|
||||
let fork_name = chain.spec.fork_name_at_slot::<T::EthSpec>(slot);
|
||||
if fork_name.gloas_enabled() {
|
||||
produce_block_v4(accept_header, chain, slot, query).await
|
||||
} else if endpoint_version == V3 {
|
||||
produce_block_v3(accept_header, chain, slot, query).await
|
||||
} else {
|
||||
produce_block_v2(accept_header, chain, slot, query).await
|
||||
@@ -327,6 +331,47 @@ pub fn get_validator_blocks<T: BeaconChainTypes>(
|
||||
.boxed()
|
||||
}
|
||||
|
||||
// GET validator/execution_payload_bid/
|
||||
pub fn get_validator_execution_payload_bid<T: BeaconChainTypes>(
|
||||
eth_v1: EthV1Filter,
|
||||
chain_filter: ChainFilter<T>,
|
||||
not_while_syncing_filter: NotWhileSyncingFilter,
|
||||
task_spawner_filter: TaskSpawnerFilter<T>,
|
||||
) -> ResponseFilter {
|
||||
eth_v1
|
||||
.and(warp::path("validator"))
|
||||
.and(warp::path("execution_payload_bid"))
|
||||
.and(warp::path::param::<Slot>().or_else(|_| async {
|
||||
Err(warp_utils::reject::custom_bad_request(
|
||||
"Invalid slot".to_string(),
|
||||
))
|
||||
}))
|
||||
.and(warp::path::end())
|
||||
.and(warp::header::optional::<Accept>("accept"))
|
||||
.and(not_while_syncing_filter)
|
||||
.and(task_spawner_filter)
|
||||
.and(chain_filter)
|
||||
.then(
|
||||
|slot: Slot,
|
||||
accept_header: Option<Accept>,
|
||||
not_synced_filter: Result<(), Rejection>,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>| {
|
||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||
debug!(
|
||||
?slot,
|
||||
"Execution paylaod bid production request from HTTP API"
|
||||
);
|
||||
|
||||
not_synced_filter?;
|
||||
|
||||
todo!()
|
||||
})
|
||||
},
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
// POST validator/liveness/{epoch}
|
||||
pub fn post_validator_liveness_epoch<T: BeaconChainTypes>(
|
||||
eth_v1: EthV1Filter,
|
||||
|
||||
@@ -14,6 +14,7 @@ use warp::reply::{self, Reply, Response};
|
||||
pub const V1: EndpointVersion = EndpointVersion(1);
|
||||
pub const V2: EndpointVersion = EndpointVersion(2);
|
||||
pub const V3: EndpointVersion = EndpointVersion(3);
|
||||
pub const V4: EndpointVersion = EndpointVersion(4);
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||
pub enum ResponseIncludesVersion {
|
||||
|
||||
@@ -3736,6 +3736,167 @@ impl ApiTester {
|
||||
self
|
||||
}
|
||||
|
||||
/// Test V4 block production (JSON). Only runs if Gloas is scheduled.
|
||||
pub async fn test_block_production_v4(self) -> Self {
|
||||
if !self.chain.spec.is_gloas_scheduled() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let fork = self.chain.canonical_head.cached_head().head_fork();
|
||||
let genesis_validators_root = self.chain.genesis_validators_root;
|
||||
|
||||
for _ in 0..E::slots_per_epoch() * 3 {
|
||||
let slot = self.chain.slot().unwrap();
|
||||
let epoch = self.chain.epoch().unwrap();
|
||||
|
||||
// Skip if not in Gloas fork yet
|
||||
let fork_name = self.chain.spec.fork_name_at_slot::<E>(slot);
|
||||
if !fork_name.gloas_enabled() {
|
||||
self.chain.slot_clock.set_slot(slot.as_u64() + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
let proposer_pubkey_bytes = self
|
||||
.client
|
||||
.get_validator_duties_proposer(epoch)
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.into_iter()
|
||||
.find(|duty| duty.slot == slot)
|
||||
.map(|duty| duty.pubkey)
|
||||
.unwrap();
|
||||
let proposer_pubkey = (&proposer_pubkey_bytes).try_into().unwrap();
|
||||
|
||||
let sk = self
|
||||
.validator_keypairs()
|
||||
.iter()
|
||||
.find(|kp| kp.pk == proposer_pubkey)
|
||||
.map(|kp| kp.sk.clone())
|
||||
.unwrap();
|
||||
|
||||
let randao_reveal = {
|
||||
let domain = self.chain.spec.get_domain(
|
||||
epoch,
|
||||
Domain::Randao,
|
||||
&fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = epoch.signing_root(domain);
|
||||
sk.sign(message).into()
|
||||
};
|
||||
|
||||
let (response, metadata) = self
|
||||
.client
|
||||
.get_validator_blocks_v4::<E>(slot, &randao_reveal, None, None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let block = response.data;
|
||||
assert_eq!(
|
||||
metadata.consensus_version,
|
||||
block.to_ref().fork_name(&self.chain.spec).unwrap()
|
||||
);
|
||||
assert!(!metadata.consensus_block_value.is_zero());
|
||||
|
||||
// Sign and publish the block
|
||||
let signed_block = block.sign(&sk, &fork, genesis_validators_root, &self.chain.spec);
|
||||
let signed_block_request =
|
||||
PublishBlockRequest::try_from(Arc::new(signed_block.clone())).unwrap();
|
||||
|
||||
self.client
|
||||
.post_beacon_blocks_v2(&signed_block_request, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(self.chain.head_beacon_block(), Arc::new(signed_block));
|
||||
|
||||
self.chain.slot_clock.set_slot(slot.as_u64() + 1);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Test V4 block production (SSZ). Only runs if Gloas is scheduled.
|
||||
pub async fn test_block_production_v4_ssz(self) -> Self {
|
||||
if !self.chain.spec.is_gloas_scheduled() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let fork = self.chain.canonical_head.cached_head().head_fork();
|
||||
let genesis_validators_root = self.chain.genesis_validators_root;
|
||||
|
||||
for _ in 0..E::slots_per_epoch() * 3 {
|
||||
let slot = self.chain.slot().unwrap();
|
||||
let epoch = self.chain.epoch().unwrap();
|
||||
|
||||
// Skip if not in Gloas fork yet
|
||||
let fork_name = self.chain.spec.fork_name_at_slot::<E>(slot);
|
||||
if !fork_name.gloas_enabled() {
|
||||
self.chain.slot_clock.set_slot(slot.as_u64() + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
let proposer_pubkey_bytes = self
|
||||
.client
|
||||
.get_validator_duties_proposer(epoch)
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.into_iter()
|
||||
.find(|duty| duty.slot == slot)
|
||||
.map(|duty| duty.pubkey)
|
||||
.unwrap();
|
||||
let proposer_pubkey = (&proposer_pubkey_bytes).try_into().unwrap();
|
||||
|
||||
let sk = self
|
||||
.validator_keypairs()
|
||||
.iter()
|
||||
.find(|kp| kp.pk == proposer_pubkey)
|
||||
.map(|kp| kp.sk.clone())
|
||||
.unwrap();
|
||||
|
||||
let randao_reveal = {
|
||||
let domain = self.chain.spec.get_domain(
|
||||
epoch,
|
||||
Domain::Randao,
|
||||
&fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = epoch.signing_root(domain);
|
||||
sk.sign(message).into()
|
||||
};
|
||||
|
||||
let (block, metadata) = self
|
||||
.client
|
||||
.get_validator_blocks_v4_ssz::<E>(slot, &randao_reveal, None, None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
metadata.consensus_version,
|
||||
block.to_ref().fork_name(&self.chain.spec).unwrap()
|
||||
);
|
||||
assert!(!metadata.consensus_block_value.is_zero());
|
||||
|
||||
// Sign and publish the block
|
||||
let signed_block = block.sign(&sk, &fork, genesis_validators_root, &self.chain.spec);
|
||||
let signed_block_request =
|
||||
PublishBlockRequest::try_from(Arc::new(signed_block.clone())).unwrap();
|
||||
|
||||
self.client
|
||||
.post_beacon_blocks_v2_ssz(&signed_block_request, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(self.chain.head_beacon_block(), Arc::new(signed_block));
|
||||
|
||||
self.chain.slot_clock.set_slot(slot.as_u64() + 1);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn test_block_production_no_verify_randao(self) -> Self {
|
||||
for _ in 0..E::slots_per_epoch() {
|
||||
let slot = self.chain.slot().unwrap();
|
||||
@@ -7469,6 +7630,22 @@ async fn block_production_v3_ssz_with_skip_slots() {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn block_production_v4() {
|
||||
ApiTester::new_with_hard_forks()
|
||||
.await
|
||||
.test_block_production_v4()
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn block_production_v4_ssz() {
|
||||
ApiTester::new_with_hard_forks()
|
||||
.await
|
||||
.test_block_production_v4_ssz()
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn blinded_block_production_full_payload_premerge() {
|
||||
ApiTester::new().await.test_blinded_block_production().await;
|
||||
|
||||
@@ -50,6 +50,7 @@ use std::time::Duration;
|
||||
pub const V1: EndpointVersion = EndpointVersion(1);
|
||||
pub const V2: EndpointVersion = EndpointVersion(2);
|
||||
pub const V3: EndpointVersion = EndpointVersion(3);
|
||||
pub const V4: EndpointVersion = EndpointVersion(4);
|
||||
|
||||
pub const CONSENSUS_VERSION_HEADER: &str = "Eth-Consensus-Version";
|
||||
pub const EXECUTION_PAYLOAD_BLINDED_HEADER: &str = "Eth-Execution-Payload-Blinded";
|
||||
@@ -2399,6 +2400,175 @@ impl BeaconNodeHttpClient {
|
||||
opt_response.ok_or(Error::StatusCode(StatusCode::NOT_FOUND))
|
||||
}
|
||||
|
||||
/// returns `GET v4/validator/blocks/{slot}` URL path
|
||||
pub async fn get_validator_blocks_v4_path(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
skip_randao_verification: SkipRandaoVerification,
|
||||
builder_booster_factor: Option<u64>,
|
||||
graffiti_policy: Option<GraffitiPolicy>,
|
||||
) -> Result<Url, Error> {
|
||||
let mut path = self.eth_path(V4)?;
|
||||
|
||||
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", "");
|
||||
}
|
||||
|
||||
if let Some(builder_booster_factor) = builder_booster_factor {
|
||||
path.query_pairs_mut()
|
||||
.append_pair("builder_boost_factor", &builder_booster_factor.to_string());
|
||||
}
|
||||
|
||||
if let Some(GraffitiPolicy::AppendClientVersions) = graffiti_policy {
|
||||
path.query_pairs_mut()
|
||||
.append_pair("graffiti_policy", "AppendClientVersions");
|
||||
}
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// `GET v4/validator/blocks/{slot}`
|
||||
pub async fn get_validator_blocks_v4<E: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
builder_booster_factor: Option<u64>,
|
||||
graffiti_policy: Option<GraffitiPolicy>,
|
||||
) -> Result<(ForkVersionedResponse<BeaconBlock<E>, ProduceBlockV4Metadata>, ProduceBlockV4Metadata), Error> {
|
||||
self.get_validator_blocks_v4_modular(
|
||||
slot,
|
||||
randao_reveal,
|
||||
graffiti,
|
||||
SkipRandaoVerification::No,
|
||||
builder_booster_factor,
|
||||
graffiti_policy,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `GET v4/validator/blocks/{slot}`
|
||||
pub async fn get_validator_blocks_v4_modular<E: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
skip_randao_verification: SkipRandaoVerification,
|
||||
builder_booster_factor: Option<u64>,
|
||||
graffiti_policy: Option<GraffitiPolicy>,
|
||||
) -> Result<(ForkVersionedResponse<BeaconBlock<E>, ProduceBlockV4Metadata>, ProduceBlockV4Metadata), Error> {
|
||||
let path = self
|
||||
.get_validator_blocks_v4_path(
|
||||
slot,
|
||||
randao_reveal,
|
||||
graffiti,
|
||||
skip_randao_verification,
|
||||
builder_booster_factor,
|
||||
graffiti_policy,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let opt_result = self
|
||||
.get_response_with_response_headers(
|
||||
path,
|
||||
Accept::Json,
|
||||
self.timeouts.get_validator_block,
|
||||
|response, headers| async move {
|
||||
let header_metadata = ProduceBlockV4Metadata::try_from(&headers)
|
||||
.map_err(Error::InvalidHeaders)?;
|
||||
let block_response = response
|
||||
.json::<ForkVersionedResponse<BeaconBlock<E>, ProduceBlockV4Metadata>>()
|
||||
.await?;
|
||||
Ok((block_response, header_metadata))
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
opt_result.ok_or(Error::StatusCode(StatusCode::NOT_FOUND))
|
||||
}
|
||||
|
||||
/// `GET v4/validator/blocks/{slot}` in ssz format
|
||||
pub async fn get_validator_blocks_v4_ssz<E: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
builder_booster_factor: Option<u64>,
|
||||
graffiti_policy: Option<GraffitiPolicy>,
|
||||
) -> Result<(BeaconBlock<E>, ProduceBlockV4Metadata), Error> {
|
||||
self.get_validator_blocks_v4_modular_ssz::<E>(
|
||||
slot,
|
||||
randao_reveal,
|
||||
graffiti,
|
||||
SkipRandaoVerification::No,
|
||||
builder_booster_factor,
|
||||
graffiti_policy,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `GET v4/validator/blocks/{slot}` in ssz format
|
||||
pub async fn get_validator_blocks_v4_modular_ssz<E: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
skip_randao_verification: SkipRandaoVerification,
|
||||
builder_booster_factor: Option<u64>,
|
||||
graffiti_policy: Option<GraffitiPolicy>,
|
||||
) -> Result<(BeaconBlock<E>, ProduceBlockV4Metadata), Error> {
|
||||
let path = self
|
||||
.get_validator_blocks_v4_path(
|
||||
slot,
|
||||
randao_reveal,
|
||||
graffiti,
|
||||
skip_randao_verification,
|
||||
builder_booster_factor,
|
||||
graffiti_policy,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let opt_response = self
|
||||
.get_response_with_response_headers(
|
||||
path,
|
||||
Accept::Ssz,
|
||||
self.timeouts.get_validator_block,
|
||||
|response, headers| async move {
|
||||
let metadata = ProduceBlockV4Metadata::try_from(&headers)
|
||||
.map_err(Error::InvalidHeaders)?;
|
||||
let response_bytes = response.bytes().await?;
|
||||
|
||||
let block = BeaconBlock::from_ssz_bytes_for_fork(
|
||||
&response_bytes,
|
||||
metadata.consensus_version,
|
||||
)
|
||||
.map_err(Error::InvalidSsz)?;
|
||||
|
||||
Ok((block, metadata))
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
opt_response.ok_or(Error::StatusCode(StatusCode::NOT_FOUND))
|
||||
}
|
||||
|
||||
/// `GET v2/validator/blocks/{slot}` in ssz format
|
||||
pub async fn get_validator_blocks_ssz<E: EthSpec>(
|
||||
&self,
|
||||
|
||||
@@ -1742,6 +1742,21 @@ pub struct ProduceBlockV3Metadata {
|
||||
pub consensus_block_value: Uint256,
|
||||
}
|
||||
|
||||
|
||||
/// Metadata about a `ProduceBlockV3Response` which is returned in the body & headers.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ProduceBlockV4Metadata {
|
||||
// The consensus version is serialized & deserialized by `ForkVersionedResponse`.
|
||||
#[serde(
|
||||
skip_serializing,
|
||||
skip_deserializing,
|
||||
default = "dummy_consensus_version"
|
||||
)]
|
||||
pub consensus_version: ForkName,
|
||||
#[serde(with = "serde_utils::u256_dec")]
|
||||
pub consensus_block_value: Uint256,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> FullBlockContents<E> {
|
||||
pub fn new(block: BeaconBlock<E>, blob_data: Option<(KzgProofs<E>, BlobsList<E>)>) -> Self {
|
||||
match blob_data {
|
||||
@@ -1898,6 +1913,27 @@ impl TryFrom<&HeaderMap> for ProduceBlockV3Metadata {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&HeaderMap> for ProduceBlockV4Metadata {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(headers: &HeaderMap) -> Result<Self, Self::Error> {
|
||||
let consensus_version = parse_required_header(headers, CONSENSUS_VERSION_HEADER, |s| {
|
||||
s.parse::<ForkName>()
|
||||
.map_err(|e| format!("invalid {CONSENSUS_VERSION_HEADER}: {e:?}"))
|
||||
})?;
|
||||
let consensus_block_value =
|
||||
parse_required_header(headers, CONSENSUS_BLOCK_VALUE_HEADER, |s| {
|
||||
Uint256::from_str_radix(s, 10)
|
||||
.map_err(|e| format!("invalid {CONSENSUS_BLOCK_VALUE_HEADER}: {e:?}"))
|
||||
})?;
|
||||
|
||||
Ok(ProduceBlockV4Metadata {
|
||||
consensus_version,
|
||||
consensus_block_value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper over a [`SignedBeaconBlock`] or a [`SignedBlockContents`].
|
||||
#[derive(Clone, Debug, PartialEq, Encode, Serialize)]
|
||||
#[serde(untagged)]
|
||||
|
||||
@@ -459,6 +459,77 @@ impl<S: ValidatorStore + 'static, T: SlotClock + 'static> BlockService<S, T> {
|
||||
|
||||
info!(slot = slot.as_u64(), "Requesting unsigned block");
|
||||
|
||||
// Check if Gloas fork is active at this slot
|
||||
let fork_name = self_ref.chain_spec.fork_name_at_slot::<S::E>(slot);
|
||||
|
||||
let (block_proposer, unsigned_block) = if fork_name.gloas_enabled() {
|
||||
// Use V4 block production for Gloas
|
||||
// Request an SSZ block from all beacon nodes in order, returning on the first successful response.
|
||||
// If all nodes fail, run a second pass falling back to JSON.
|
||||
let ssz_block_response = proposer_fallback
|
||||
.request_proposers_last(|beacon_node| async move {
|
||||
let _get_timer = validator_metrics::start_timer_vec(
|
||||
&validator_metrics::BLOCK_SERVICE_TIMES,
|
||||
&[validator_metrics::BEACON_BLOCK_HTTP_GET],
|
||||
);
|
||||
beacon_node
|
||||
.get_validator_blocks_v4_ssz::<S::E>(
|
||||
slot,
|
||||
randao_reveal_ref,
|
||||
graffiti.as_ref(),
|
||||
builder_boost_factor,
|
||||
self_ref.graffiti_policy,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await;
|
||||
|
||||
let block_response = match ssz_block_response {
|
||||
Ok((ssz_block_response, _metadata)) => ssz_block_response,
|
||||
Err(e) => {
|
||||
warn!(
|
||||
slot = slot.as_u64(),
|
||||
error = %e,
|
||||
"SSZ V4 block production failed, falling back to JSON"
|
||||
);
|
||||
|
||||
proposer_fallback
|
||||
.request_proposers_last(|beacon_node| async move {
|
||||
let _get_timer = validator_metrics::start_timer_vec(
|
||||
&validator_metrics::BLOCK_SERVICE_TIMES,
|
||||
&[validator_metrics::BEACON_BLOCK_HTTP_GET],
|
||||
);
|
||||
let (json_block_response, _metadata) = beacon_node
|
||||
.get_validator_blocks_v4::<S::E>(
|
||||
slot,
|
||||
randao_reveal_ref,
|
||||
graffiti.as_ref(),
|
||||
builder_boost_factor,
|
||||
self_ref.graffiti_policy,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
BlockError::Recoverable(format!(
|
||||
"Error from beacon node when producing block: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(json_block_response.data)
|
||||
})
|
||||
.await
|
||||
.map_err(BlockError::from)?
|
||||
}
|
||||
};
|
||||
|
||||
// Gloas blocks don't have blobs (they're in the execution layer)
|
||||
let block_contents = eth2::types::FullBlockContents::Block(block_response);
|
||||
(
|
||||
block_contents.block().proposer_index(),
|
||||
UnsignedBlock::Full(block_contents),
|
||||
)
|
||||
} else {
|
||||
// Use V3 block production for pre-Gloas forks
|
||||
// Request an SSZ block from all beacon nodes in order, returning on the first successful response.
|
||||
// If all nodes fail, run a second pass falling back to JSON.
|
||||
//
|
||||
@@ -520,13 +591,14 @@ impl<S: ValidatorStore + 'static, T: SlotClock + 'static> BlockService<S, T> {
|
||||
}
|
||||
};
|
||||
|
||||
let (block_proposer, unsigned_block) = match block_response {
|
||||
match block_response {
|
||||
eth2::types::ProduceBlockV3Response::Full(block) => {
|
||||
(block.block().proposer_index(), UnsignedBlock::Full(block))
|
||||
}
|
||||
eth2::types::ProduceBlockV3Response::Blinded(block) => {
|
||||
(block.proposer_index(), UnsignedBlock::Blinded(block))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
info!(slot = slot.as_u64(), "Received unsigned block");
|
||||
|
||||
Reference in New Issue
Block a user