Merge branch 'unstable' into eip4844

This commit is contained in:
Diva M
2023-03-10 11:19:56 -05:00
29 changed files with 1012 additions and 148 deletions

View File

@@ -60,7 +60,7 @@ use crate::validator_monitor::{
};
use crate::validator_pubkey_cache::ValidatorPubkeyCache;
use crate::{metrics, BeaconChainError, BeaconForkChoiceStore, BeaconSnapshot, CachedHead};
use eth2::types::{EventKind, SseBlock, SyncDuty};
use eth2::types::{EventKind, SseBlock, SseExtendedPayloadAttributes, SyncDuty};
use execution_layer::{
BlockProposalContents, BuilderParams, ChainHealth, ExecutionLayer, FailedCondition,
PayloadAttributes, PayloadStatus,
@@ -92,6 +92,7 @@ use state_processing::{
state_advance::{complete_state_advance, partial_state_advance},
BlockSignatureStrategy, ConsensusContext, SigVerifiedOp, VerifyBlockRoot, VerifyOperation,
};
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::HashSet;
@@ -3987,6 +3988,75 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}))
}
pub fn get_expected_withdrawals(
&self,
forkchoice_update_params: &ForkchoiceUpdateParameters,
proposal_slot: Slot,
) -> Result<Withdrawals<T::EthSpec>, Error> {
let cached_head = self.canonical_head.cached_head();
let head_state = &cached_head.snapshot.beacon_state;
let parent_block_root = forkchoice_update_params.head_root;
let (unadvanced_state, unadvanced_state_root) =
if cached_head.head_block_root() == parent_block_root {
(Cow::Borrowed(head_state), cached_head.head_state_root())
} else if let Some(snapshot) = self
.snapshot_cache
.try_read_for(BLOCK_PROCESSING_CACHE_LOCK_TIMEOUT)
.ok_or(Error::SnapshotCacheLockTimeout)?
.get_cloned(parent_block_root, CloneConfig::none())
{
debug!(
self.log,
"Hit snapshot cache during withdrawals calculation";
"slot" => proposal_slot,
"parent_block_root" => ?parent_block_root,
);
let state_root = snapshot.beacon_state_root();
(Cow::Owned(snapshot.beacon_state), state_root)
} else {
info!(
self.log,
"Missed snapshot cache during withdrawals calculation";
"slot" => proposal_slot,
"parent_block_root" => ?parent_block_root
);
let block = self
.get_blinded_block(&parent_block_root)?
.ok_or(Error::MissingBeaconBlock(parent_block_root))?;
let state = self
.get_state(&block.state_root(), Some(block.slot()))?
.ok_or(Error::MissingBeaconState(block.state_root()))?;
(Cow::Owned(state), block.state_root())
};
// Parent state epoch is the same as the proposal, we don't need to advance because the
// list of expected withdrawals can only change after an epoch advance or a
// block application.
let proposal_epoch = proposal_slot.epoch(T::EthSpec::slots_per_epoch());
if head_state.current_epoch() == proposal_epoch {
return get_expected_withdrawals(&unadvanced_state, &self.spec)
.map_err(Error::PrepareProposerFailed);
}
// Advance the state using the partial method.
debug!(
self.log,
"Advancing state for withdrawals calculation";
"proposal_slot" => proposal_slot,
"parent_block_root" => ?parent_block_root,
);
let mut advanced_state = unadvanced_state.into_owned();
partial_state_advance(
&mut advanced_state,
Some(unadvanced_state_root),
proposal_epoch.start_slot(T::EthSpec::slots_per_epoch()),
&self.spec,
)?;
get_expected_withdrawals(&advanced_state, &self.spec).map_err(Error::PrepareProposerFailed)
}
/// Determine whether a fork choice update to the execution layer should be overridden.
///
/// This is *only* necessary when proposer re-orgs are enabled, because we have to prevent the
@@ -4858,7 +4928,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// Nothing to do if there are no proposers registered with the EL, exit early to avoid
// wasting cycles.
if !execution_layer.has_any_proposer_preparation_data().await {
if !self.config.always_prepare_payload
&& !execution_layer.has_any_proposer_preparation_data().await
{
return Ok(());
}
@@ -4915,64 +4987,60 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// If the execution layer doesn't have any proposer data for this validator then we assume
// it's not connected to this BN and no action is required.
let proposer = pre_payload_attributes.proposer_index;
if !execution_layer
.has_proposer_preparation_data(proposer)
.await
if !self.config.always_prepare_payload
&& !execution_layer
.has_proposer_preparation_data(proposer)
.await
{
return Ok(());
}
let withdrawals = match self.spec.fork_name_at_slot::<T::EthSpec>(prepare_slot) {
ForkName::Base | ForkName::Altair | ForkName::Merge => None,
ForkName::Capella | ForkName::Eip4844 => {
// We must use the advanced state because balances can change at epoch boundaries
// and balances affect withdrawals.
// FIXME(mark)
// Might implement caching here in the future..
let prepare_state = self
.state_at_slot(prepare_slot, StateSkipConfig::WithoutStateRoots)
.map_err(|e| {
error!(self.log, "State advance for withdrawals failed"; "error" => ?e);
e
})?;
Some(get_expected_withdrawals(&prepare_state, &self.spec))
}
}
.transpose()
.map_err(|e| {
error!(self.log, "Error preparing beacon proposer"; "error" => ?e);
e
})
.map(|withdrawals_opt| withdrawals_opt.map(|w| w.into()))
.map_err(Error::PrepareProposerFailed)?;
// Fetch payoad attributes from the execution layer's cache, or compute them from scratch
// if no matching entry is found. This saves recomputing the withdrawals which can take
// considerable time to compute if a state load is required.
let head_root = forkchoice_update_params.head_root;
let payload_attributes = PayloadAttributes::new(
self.slot_clock
.start_of(prepare_slot)
.ok_or(Error::InvalidSlot(prepare_slot))?
.as_secs(),
pre_payload_attributes.prev_randao,
execution_layer.get_suggested_fee_recipient(proposer).await,
withdrawals,
);
let payload_attributes = if let Some(payload_attributes) = execution_layer
.payload_attributes(prepare_slot, head_root)
.await
{
payload_attributes
} else {
let withdrawals = match self.spec.fork_name_at_slot::<T::EthSpec>(prepare_slot) {
ForkName::Base | ForkName::Altair | ForkName::Merge => None,
ForkName::Capella | ForkName::Eip4844 => {
let chain = self.clone();
self.spawn_blocking_handle(
move || {
chain.get_expected_withdrawals(&forkchoice_update_params, prepare_slot)
},
"prepare_beacon_proposer_withdrawals",
)
.await?
.map(Some)?
}
};
debug!(
self.log,
"Preparing beacon proposer";
"payload_attributes" => ?payload_attributes,
"prepare_slot" => prepare_slot,
"validator" => proposer,
"parent_root" => ?head_root,
);
let payload_attributes = PayloadAttributes::new(
self.slot_clock
.start_of(prepare_slot)
.ok_or(Error::InvalidSlot(prepare_slot))?
.as_secs(),
pre_payload_attributes.prev_randao,
execution_layer.get_suggested_fee_recipient(proposer).await,
withdrawals.map(Into::into),
);
let already_known = execution_layer
.insert_proposer(prepare_slot, head_root, proposer, payload_attributes)
.await;
execution_layer
.insert_proposer(
prepare_slot,
head_root,
proposer,
payload_attributes.clone(),
)
.await;
// Only push a log to the user if this is the first time we've seen this proposer for this
// slot.
if !already_known {
// Only push a log to the user if this is the first time we've seen this proposer for
// this slot.
info!(
self.log,
"Prepared beacon proposer";
@@ -4980,6 +5048,23 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
"validator" => proposer,
"parent_root" => ?head_root,
);
payload_attributes
};
// Push a server-sent event (probably to a block builder or relay).
if let Some(event_handler) = &self.event_handler {
if event_handler.has_payload_attributes_subscribers() {
event_handler.register(EventKind::PayloadAttributes(ForkVersionedResponse {
data: SseExtendedPayloadAttributes {
proposal_slot: prepare_slot,
proposer_index: proposer,
parent_block_root: head_root,
parent_block_hash: forkchoice_update_params.head_hash.unwrap_or_default(),
payload_attributes: payload_attributes.into(),
},
version: Some(self.spec.fork_name_at_slot::<T::EthSpec>(prepare_slot)),
}));
}
}
let till_prepare_slot =
@@ -5002,7 +5087,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// If we are close enough to the proposal slot, send an fcU, which will have payload
// attributes filled in by the execution layer cache we just primed.
if till_prepare_slot <= self.config.prepare_payload_lookahead {
if self.config.always_prepare_payload
|| till_prepare_slot <= self.config.prepare_payload_lookahead
{
debug!(
self.log,
"Sending forkchoiceUpdate for proposer prep";