Move block production to gloas file (no logic change).

This commit is contained in:
Jimmy Chen
2026-02-05 10:53:13 +11:00
parent 75bb4288ff
commit f9bfaf9da6
6 changed files with 183 additions and 217 deletions

View File

@@ -3,6 +3,7 @@ use std::marker::PhantomData;
use std::sync::Arc;
use bls::Signature;
use execution_layer::{BlockProposalContentsType, BuilderParams};
use operation_pool::CompactAttestationRef;
use ssz::Encode;
use state_processing::common::get_attesting_indices_from_state;
@@ -12,19 +13,21 @@ use state_processing::{
BlockSignatureStrategy, ConsensusContext, VerifyBlockRoot, VerifySignatures,
};
use state_processing::{VerifyOperation, state_advance::complete_state_advance};
use tracing::{Span, debug, debug_span, error, trace, warn};
use tracing::{Span, debug, debug_span, error, instrument, trace, warn};
use tree_hash::TreeHash;
use types::{
Attestation, AttestationElectra, AttesterSlashing, AttesterSlashingElectra, BeaconBlock,
BeaconBlockBodyGloas, BeaconBlockGloas, BeaconState, Deposit, Eth1Data, EthSpec,
ExecutionPayloadEnvelope, FullPayload, Graffiti, Hash256, PayloadAttestation, ProposerSlashing,
RelativeEpoch, SignedBeaconBlock, SignedBlsToExecutionChange, SignedExecutionPayloadBid,
SignedVoluntaryExit, Slot, SyncAggregate,
Address, Attestation, AttestationElectra, AttesterSlashing, AttesterSlashingElectra,
BeaconBlock, BeaconBlockBodyGloas, BeaconBlockGloas, BeaconState, BlockProductionVersion,
BuilderIndex, Deposit, Eth1Data, EthSpec, ExecutionPayloadBid, ExecutionPayloadEnvelope,
ExecutionPayloadGloas, ExecutionRequests, FullPayload, Graffiti, Hash256, PayloadAttestation,
ProposerSlashing, RelativeEpoch, SignedBeaconBlock, SignedBlsToExecutionChange,
SignedExecutionPayloadBid, SignedVoluntaryExit, Slot, SyncAggregate,
};
use crate::execution_payload::get_execution_payload;
use crate::{
BeaconChain, BeaconChainTypes, BlockProductionError, ProduceBlockVerification,
execution_payload_bid::ExecutionPayloadData, graffiti_calculator::GraffitiSettings, metrics,
BeaconChain, BeaconChainError, BeaconChainTypes, BlockProductionError,
ProduceBlockVerification, graffiti_calculator::GraffitiSettings, metrics,
};
pub struct PartialBeaconBlock<E: EthSpec> {
@@ -44,7 +47,16 @@ pub struct PartialBeaconBlock<E: EthSpec> {
bls_to_execution_changes: Vec<SignedBlsToExecutionChange>,
}
// We'll need to add that once we include trusted/trustless bids
/// Data needed to construct an ExecutionPayloadEnvelope.
/// The envelope requires the beacon_block_root which can only be computed after the block exists.
pub struct ExecutionPayloadData<E: types::EthSpec> {
pub payload: ExecutionPayloadGloas<E>,
pub execution_requests: ExecutionRequests<E>,
pub builder_index: BuilderIndex,
pub slot: Slot,
pub state_root: Hash256,
}
impl<T: BeaconChainTypes> BeaconChain<T> {
pub async fn produce_block_with_verification_gloas(
self: &Arc<Self>,
@@ -53,14 +65,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
graffiti_settings: GraffitiSettings,
verification: ProduceBlockVerification,
_builder_boost_factor: Option<u64>,
) -> Result<
(
BeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>,
BeaconState<T::EthSpec>,
u64,
),
BlockProductionError,
> {
) -> Result<(BeaconBlock<T::EthSpec, FullPayload<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)
@@ -105,14 +110,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
randao_reveal: Signature,
graffiti_settings: GraffitiSettings,
verification: ProduceBlockVerification,
) -> Result<
(
BeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>,
BeaconState<T::EthSpec>,
u64,
),
BlockProductionError,
> {
) -> Result<(BeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>, u64), BlockProductionError> {
// Part 1/3 (blocking)
//
// Perform the state advance and block-packing functions.
@@ -419,14 +417,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
payload_data: Option<ExecutionPayloadData<T::EthSpec>>,
mut state: BeaconState<T::EthSpec>,
verification: ProduceBlockVerification,
) -> Result<
(
BeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>,
BeaconState<T::EthSpec>,
u64,
),
BlockProductionError,
> {
) -> Result<(BeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>, u64), BlockProductionError> {
let PartialBeaconBlock {
slot,
proposer_index,
@@ -602,6 +593,157 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
"Produced beacon block"
);
Ok((block, state, consensus_block_value))
Ok((block, consensus_block_value))
}
// 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.
///
/// Returns the signed bid, the state, and optionally the payload data needed to construct
/// the `ExecutionPayloadEnvelope` after the beacon block is created.
///
/// For local building, payload data is always returned (`Some`).
/// For trustless building, the builder provides the envelope separately, so `None` is returned.
#[allow(clippy::type_complexity)]
#[instrument(level = "debug", skip_all)]
pub async fn produce_execution_payload_bid(
self: Arc<Self>,
state: BeaconState<T::EthSpec>,
state_root_opt: Option<Hash256>,
produce_at_slot: Slot,
bid_value: u64,
builder_index: BuilderIndex,
) -> Result<
(
SignedExecutionPayloadBid<T::EthSpec>,
BeaconState<T::EthSpec>,
Option<ExecutionPayloadData<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
let parent_root = if state.slot() > 0 {
*state
.get_block_root(state.slot() - 1)
.map_err(|_| BlockProductionError::UnableToGetBlockRootFromState)?
} else {
state.latest_block_header().canonical_root()
};
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)))?,
};
// TODO(gloas) this should be BlockProductionVersion::V4
// V3 is okay for now as long as we're not connected to a builder
// TODO(gloas) add builder boost factor
let prepare_payload_handle = get_execution_payload(
self.clone(),
&state,
parent_root,
proposer_index,
builder_params,
None,
BlockProductionVersion::V3,
)?;
let block_contents_type = prepare_payload_handle
.await
.map_err(BlockProductionError::TokioJoin)?
.ok_or(BlockProductionError::ShuttingDown)??;
let (execution_payload, blob_kzg_commitments, execution_requests) =
match block_contents_type {
BlockProposalContentsType::Full(block_proposal_contents) => {
let (payload, blob_kzg_commitments, _, execution_requests, _) =
block_proposal_contents.deconstruct();
if let Some(blob_kzg_commitments) = blob_kzg_commitments
&& let Some(execution_requests) = execution_requests
{
(
payload.execution_payload(),
blob_kzg_commitments,
execution_requests,
)
} else {
return Err(BlockProductionError::MissingKzgCommitment(
"No KZG commitments from the payload".to_owned(),
));
}
}
// TODO(gloas) we should never receive a blinded response.
// Should return some type of `Unexpected` error variant as this should never happen
// in the V4 block production flow
BlockProposalContentsType::Blinded(_) => {
return Err(BlockProductionError::GloasNotImplemented);
}
};
let state_root = state_root_opt.ok_or_else(|| BlockProductionError::MissingStateRoot)?;
// TODO(gloas) this is just a dummy error variant for now
let execution_payload_gloas = execution_payload
.as_gloas()
.map_err(|_| BlockProductionError::GloasNotImplemented)?
.to_owned();
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(),
prev_randao: execution_payload.prev_randao(),
fee_recipient: Address::ZERO,
gas_limit: execution_payload.gas_limit(),
builder_index,
slot: produce_at_slot,
value: bid_value,
execution_payment: 0,
blob_kzg_commitments,
};
// Store payload data for envelope construction after block is created
let payload_data = ExecutionPayloadData {
payload: execution_payload_gloas,
execution_requests,
builder_index,
slot: produce_at_slot,
state_root,
};
// 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,
// Local building always returns payload data.
// Trustless building would return None here.
Some(payload_data),
))
}
}

View File

@@ -0,0 +1 @@
mod gloas;

View File

@@ -1,177 +0,0 @@
use std::sync::Arc;
use bls::Signature;
use execution_layer::{BlockProposalContentsType, BuilderParams};
use tracing::instrument;
use types::{
Address, BeaconState, BlockProductionVersion, BuilderIndex, ExecutionPayloadBid,
ExecutionPayloadGloas, ExecutionRequests, Hash256, SignedExecutionPayloadBid, Slot,
};
use crate::{
BeaconChain, BeaconChainError, BeaconChainTypes, BlockProductionError,
execution_payload::get_execution_payload,
};
/// Data needed to construct an ExecutionPayloadEnvelope.
/// The envelope requires the beacon_block_root which can only be computed after the block exists.
pub struct ExecutionPayloadData<E: types::EthSpec> {
pub payload: ExecutionPayloadGloas<E>,
pub execution_requests: ExecutionRequests<E>,
pub builder_index: BuilderIndex,
pub slot: Slot,
pub state_root: Hash256,
}
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.
///
/// Returns the signed bid, the state, and optionally the payload data needed to construct
/// the `ExecutionPayloadEnvelope` after the beacon block is created.
///
/// For local building, payload data is always returned (`Some`).
/// For trustless building, the builder provides the envelope separately, so `None` is returned.
#[allow(clippy::type_complexity)]
#[instrument(level = "debug", skip_all)]
pub async fn produce_execution_payload_bid(
self: Arc<Self>,
state: BeaconState<T::EthSpec>,
state_root_opt: Option<Hash256>,
produce_at_slot: Slot,
bid_value: u64,
builder_index: BuilderIndex,
) -> Result<
(
SignedExecutionPayloadBid<T::EthSpec>,
BeaconState<T::EthSpec>,
Option<ExecutionPayloadData<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
let parent_root = if state.slot() > 0 {
*state
.get_block_root(state.slot() - 1)
.map_err(|_| BlockProductionError::UnableToGetBlockRootFromState)?
} else {
state.latest_block_header().canonical_root()
};
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)))?,
};
// TODO(gloas) this should be BlockProductionVersion::V4
// V3 is okay for now as long as we're not connected to a builder
// TODO(gloas) add builder boost factor
let prepare_payload_handle = get_execution_payload(
self.clone(),
&state,
parent_root,
proposer_index,
builder_params,
None,
BlockProductionVersion::V3,
)?;
let block_contents_type = prepare_payload_handle
.await
.map_err(BlockProductionError::TokioJoin)?
.ok_or(BlockProductionError::ShuttingDown)??;
let (execution_payload, blob_kzg_commitments, execution_requests) =
match block_contents_type {
BlockProposalContentsType::Full(block_proposal_contents) => {
let (payload, blob_kzg_commitments, _, execution_requests, _) =
block_proposal_contents.deconstruct();
if let Some(blob_kzg_commitments) = blob_kzg_commitments
&& let Some(execution_requests) = execution_requests
{
(
payload.execution_payload(),
blob_kzg_commitments,
execution_requests,
)
} else {
return Err(BlockProductionError::MissingKzgCommitment(
"No KZG commitments from the payload".to_owned(),
));
}
}
// TODO(gloas) we should never receive a blinded response.
// Should return some type of `Unexpected` error variant as this should never happen
// in the V4 block production flow
BlockProposalContentsType::Blinded(_) => {
return Err(BlockProductionError::GloasNotImplemented);
}
};
let state_root = state_root_opt.ok_or_else(|| BlockProductionError::MissingStateRoot)?;
// TODO(gloas) this is just a dummy error variant for now
let execution_payload_gloas = execution_payload
.as_gloas()
.map_err(|_| BlockProductionError::GloasNotImplemented)?
.to_owned();
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(),
prev_randao: execution_payload.prev_randao(),
fee_recipient: Address::ZERO,
gas_limit: execution_payload.gas_limit(),
builder_index,
slot: produce_at_slot,
value: bid_value,
execution_payment: 0,
blob_kzg_commitments,
};
// Store payload data for envelope construction after block is created
let payload_data = ExecutionPayloadData {
payload: execution_payload_gloas,
execution_requests,
builder_index,
slot: produce_at_slot,
state_root,
};
// 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,
// Local building always returns payload data.
// Trustless building would return None here.
Some(payload_data),
))
}
}

View File

@@ -1,7 +1,6 @@
pub mod attestation_rewards;
pub mod attestation_simulator;
pub mod attestation_verification;
mod beacon_block;
pub mod beacon_block_reward;
mod beacon_block_streamer;
mod beacon_chain;
@@ -10,6 +9,7 @@ pub mod beacon_proposer_cache;
mod beacon_snapshot;
pub mod bellatrix_readiness;
pub mod blob_verification;
mod block_production;
pub mod block_reward;
mod block_times_cache;
mod block_verification;
@@ -24,7 +24,6 @@ mod early_attester_cache;
mod errors;
pub mod events;
pub mod execution_payload;
pub mod execution_payload_bid;
pub mod fetch_blobs;
pub mod fork_choice_signal;
pub mod fork_revert;

View File

@@ -39,6 +39,7 @@ impl<E: EthSpec> PendingPayloadEnvelopes<E> {
/// Insert a pending envelope into the cache.
pub fn insert(&mut self, slot: Slot, envelope: ExecutionPayloadEnvelope<E>) {
// TODO(gloas): we may want to check for duplicates here, which shouldn't be allowed
self.envelopes.insert(slot, envelope);
}

View File

@@ -70,7 +70,7 @@ pub async fn produce_block_v4<T: BeaconChainTypes>(
let graffiti_settings = GraffitiSettings::new(query.graffiti, query.graffiti_policy);
let (block, _state, consensus_block_value) = chain
let (block, consensus_block_value) = chain
.produce_block_with_verification_gloas(
randao_reveal,
slot,
@@ -83,7 +83,7 @@ pub async fn produce_block_v4<T: BeaconChainTypes>(
warp_utils::reject::custom_bad_request(format!("failed to fetch a block: {:?}", e))
})?;
build_response_v4(chain, block, consensus_block_value, accept_header)
build_response_v4::<T>(block, consensus_block_value, accept_header, &chain.spec)
}
#[instrument(
@@ -131,14 +131,14 @@ pub async fn produce_block_v3<T: BeaconChainTypes>(
}
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>,
spec: &ChainSpec,
) -> Result<Response<Body>, warp::Rejection> {
let fork_name = block
.to_ref()
.fork_name(&chain.spec)
.fork_name(&spec)
.map_err(inconsistent_fork_rejection)?;
let consensus_block_value_wei =
Uint256::from(consensus_block_value) * Uint256::from(1_000_000_000u64);