Skip to content
1 change: 1 addition & 0 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6953,6 +6953,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.envelope_times_cache.write().prune(slot);
self.gossip_verified_payload_bid_cache.prune(slot);
self.gossip_verified_proposer_preferences_cache.prune(slot);
self.pending_payload_envelopes.write().prune(slot);

// Don't run heavy-weight tasks during sync.
if self.best_slot() + MAX_PER_SLOT_FORK_CHOICE_DISTANCE < slot {
Expand Down
46 changes: 30 additions & 16 deletions beacon_node/beacon_chain/src/block_production/gloas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ use tree_hash::TreeHash;
use types::consts::gloas::BUILDER_INDEX_SELF_BUILD;
use types::{
Address, Attestation, AttestationElectra, AttesterSlashing, AttesterSlashingElectra,
BeaconBlock, BeaconBlockBodyGloas, BeaconBlockGloas, BeaconState, BeaconStateError,
BeaconBlock, BeaconBlockBodyGloas, BeaconBlockGloas, BeaconState, BeaconStateError, BlobsList,
BuilderIndex, ChainSpec, Deposit, Eth1Data, EthSpec, ExecutionBlockHash, ExecutionPayloadBid,
ExecutionPayloadEnvelope, ExecutionPayloadGloas, ExecutionRequestsGloas, FullPayload, Graffiti,
Hash256, PayloadAttestation, ProposerSlashing, RelativeEpoch, SignedBeaconBlock,
Hash256, KzgProofs, PayloadAttestation, ProposerSlashing, RelativeEpoch, SignedBeaconBlock,
SignedBlsToExecutionChange, SignedExecutionPayloadBid, SignedExecutionPayloadEnvelope,
SignedVoluntaryExit, Slot, SyncAggregate, Withdrawal, Withdrawals,
};
Expand All @@ -48,7 +48,19 @@ pub const BID_VALUE_SELF_BUILD: u64 = 0;
pub const EXECUTION_PAYMENT_TRUSTLESS_BUILD: u64 = 0;

type ConsensusBlockValue = u64;
type BlockProductionResult<E> = (BeaconBlock<E>, BeaconState<E>, ConsensusBlockValue);

pub type PayloadEnvelopeContents<E> = (
Arc<ExecutionPayloadEnvelope<E>>,
KzgProofs<E>,
Arc<BlobsList<E>>,
);

type BlockProductionResult<E> = (
BeaconBlock<E>,
BeaconState<E>,
ConsensusBlockValue,
Option<PayloadEnvelopeContents<E>>,
);

pub type PreparePayloadResult<E> = Result<BlockProposalContentsGloas<E>, BlockProductionError>;
pub type PreparePayloadHandle<E> = JoinHandle<Option<PreparePayloadResult<E>>>;
Expand Down Expand Up @@ -653,7 +665,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// Construct and cache the ExecutionPayloadEnvelope if we have payload data.
// For local building, we always have payload data.
// For trustless building, the builder will provide the envelope separately.
if let Some(payload_data) = payload_data {
let payload_contents = if let Some(payload_data) = payload_data {
let beacon_block_root = block.tree_hash_root();
let parent_beacon_block_root = block.parent_root();
let execution_payload_envelope = ExecutionPayloadEnvelope {
Expand Down Expand Up @@ -681,23 +693,25 @@ impl<T: BeaconChainTypes> BeaconChain<T> {

// Cache the envelope for later retrieval by the validator for signing and publishing.
let envelope_slot = payload_data.slot;
// TODO(gloas) might be safer to cache by root instead of by slot.
// We should revisit this once this code path + beacon api spec matures
let (blobs, _) = payload_data.blobs_and_proofs;
self.pending_payload_envelopes.write().insert(
envelope_slot,
PendingEnvelopeData {
envelope: signed_envelope.message,
blobs: Some(blobs),
},
);
let (blobs, kzg_proofs) = payload_data.blobs_and_proofs;
let envelope = Arc::new(signed_envelope.message);
let blobs = Arc::new(blobs);
self.pending_payload_envelopes
.write()
.insert(PendingEnvelopeData {
envelope: envelope.clone(),
blobs: Some(blobs.clone()),
});

debug!(
%beacon_block_root,
slot = %envelope_slot,
"Cached pending execution payload envelope"
);
}
Some((envelope, kzg_proofs, blobs))
} else {
None
};

metrics::inc_counter(&metrics::BLOCK_PRODUCTION_SUCCESSES);

Expand All @@ -708,7 +722,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
"Produced beacon block"
);

Ok((block, state, consensus_block_value))
Ok((block, state, consensus_block_value, payload_contents))
}

/// Produce a self-build `ExecutionPayloadBid` for some `slot` upon the given `state`.
Expand Down
2 changes: 2 additions & 0 deletions beacon_node/beacon_chain/src/block_production/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use crate::{

mod gloas;

pub use gloas::PayloadEnvelopeContents;

/// State loaded from the database for block production.
pub(crate) struct BlockProductionState<E: EthSpec> {
pub state: BeaconState<E>,
Expand Down
97 changes: 65 additions & 32 deletions beacon_node/beacon_chain/src/kzg_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,38 +292,8 @@ pub fn blobs_to_data_column_sidecars<E: EthSpec>(
.map_err(|_err| DataColumnSidecarError::PreDeneb)?;
let signed_block_header = block.signed_block_header();

if cell_proofs.len() != blobs.len() * E::number_of_columns() {
return Err(DataColumnSidecarError::InvalidCellProofLength {
expected: blobs.len() * E::number_of_columns(),
actual: cell_proofs.len(),
});
}

let proof_chunks = cell_proofs
.chunks_exact(E::number_of_columns())
.collect::<Vec<_>>();

// NOTE: assumes blob sidecars are ordered by index
let zipped: Vec<_> = blobs.iter().zip(proof_chunks).collect();
let blob_cells_and_proofs_vec = zipped
.into_par_iter()
.map(|(blob, proofs)| {
let blob = blob.as_ref().try_into().map_err(|e| {
KzgError::InconsistentArrayLength(format!(
"blob should have a guaranteed size due to FixedVector: {e:?}"
))
})?;

kzg.compute_cells(blob).and_then(|cells| {
let proofs = proofs.try_into().map_err(|e| {
KzgError::InconsistentArrayLength(format!(
"proof chunks should have exactly `number_of_columns` proofs: {e:?}"
))
})?;
Ok((cells, proofs))
})
})
.collect::<Result<Vec<_>, KzgError>>()?;
let blob_cells_and_proofs_vec =
compute_cells_with_provided_proofs::<E>(blobs, cell_proofs, kzg)?;

if block.fork_name_unchecked().gloas_enabled() {
build_data_column_sidecars_gloas(
Expand Down Expand Up @@ -376,6 +346,69 @@ pub fn blobs_to_data_column_sidecars_gloas<E: EthSpec>(
.map_err(DataColumnSidecarError::BuildSidecarFailed)
}

/// Build Gloas data column sidecars from blobs and pre-computed cell proofs, computing only the
/// cells locally.
pub fn blobs_to_data_column_sidecars_gloas_with_proofs<E: EthSpec>(
blobs: &[&Blob<E>],
cell_proofs: Vec<KzgProof>,
beacon_block_root: Hash256,
slot: Slot,
kzg: &Kzg,
spec: &ChainSpec,
) -> Result<DataColumnSidecarList<E>, DataColumnSidecarError> {
if blobs.is_empty() {
return Ok(vec![]);
}

let blob_cells_and_proofs_vec =
compute_cells_with_provided_proofs::<E>(blobs, cell_proofs, kzg)?;

build_data_column_sidecars_gloas(beacon_block_root, slot, blob_cells_and_proofs_vec, spec)
.map_err(DataColumnSidecarError::BuildSidecarFailed)
}

/// Compute cells for each blob and pair them with the provided per-blob cell proofs.
fn compute_cells_with_provided_proofs<E: EthSpec>(
blobs: &[&Blob<E>],
cell_proofs: Vec<KzgProof>,
kzg: &Kzg,
) -> Result<Vec<CellsAndKzgProofs>, DataColumnSidecarError> {
if cell_proofs.len() != blobs.len() * E::number_of_columns() {
return Err(DataColumnSidecarError::InvalidCellProofLength {
expected: blobs.len() * E::number_of_columns(),
actual: cell_proofs.len(),
});
}

let proof_chunks = cell_proofs
.chunks_exact(E::number_of_columns())
.collect::<Vec<_>>();

// NOTE: assumes blobs and proofs are ordered by blob index
let zipped: Vec<_> = blobs.iter().zip(proof_chunks).collect();
let blob_cells_and_proofs_vec = zipped
.into_par_iter()
.map(|(blob, proofs)| {
let blob = blob.as_ref().try_into().map_err(|e| {
KzgError::InconsistentArrayLength(format!(
"blob should have a guaranteed size due to FixedVector: {e:?}"
))
})?;

kzg.compute_cells(blob).and_then(|cells| {
let proofs = proofs.try_into().map_err(|e| {
KzgError::InconsistentArrayLength(format!(
"proof chunks should have exactly `number_of_columns` proofs: {e:?}"
))
})?;
Ok((cells, proofs))
})
})
.collect::<Result<Vec<_>, KzgError>>()?;

Ok(blob_cells_and_proofs_vec)
}

/// Build data column sidecars from a signed beacon block and its blobs.
#[instrument(skip_all, level = "debug", fields(blob_count = blobs_and_proofs.len()))]
pub fn blobs_to_partial_data_columns<E: EthSpec>(
Expand Down
1 change: 1 addition & 0 deletions beacon_node/beacon_chain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub use self::beacon_chain::{
ProduceBlockVerification, StateSkipConfig, WhenSlotSkipped,
};
pub use self::beacon_snapshot::BeaconSnapshot;
pub use self::block_production::PayloadEnvelopeContents;
pub use self::chain_config::ChainConfig;
pub use self::errors::{BeaconChainError, BlockProductionError};
pub use self::historical_blocks::HistoricalBlockError;
Expand Down
Loading
Loading