Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions beacon_node/beacon_chain/src/beacon_block_reward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
BeaconChainError::BlockRewardAttestationError
})?
} else {
self.compute_beacon_block_attestation_reward_altair_deneb(block, state)
self.compute_beacon_block_attestation_reward_altair_and_later(block, state)
.map_err(|e| {
error!(
error = ?e,
Expand Down Expand Up @@ -249,7 +249,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
Ok(block_reward)
}

fn compute_beacon_block_attestation_reward_altair_deneb<
fn compute_beacon_block_attestation_reward_altair_and_later<
Payload: AbstractExecPayload<T::EthSpec>,
>(
&self,
Expand All @@ -267,13 +267,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let mut previous_epoch_participation =
state.previous_epoch_participation()?.to_owned_list();

let parent_slot = state
.latest_execution_payload_bid()
.ok()
.map(|bid| bid.slot);

for attestation in block.body().attestations() {
let data = attestation.data();
let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64();

// [Modified in Deneb:EIP7045]
let participation_flag_indices = get_attestation_participation_flag_indices(
state,
data,
parent_slot,
inclusion_delay,
&self.spec,
)?;
Expand Down
4 changes: 2 additions & 2 deletions beacon_node/beacon_chain/src/block_production/gloas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use types::{
Withdrawals,
};

use crate::payload_bid_verification::payload_bid_cache::BidParent;
use crate::pending_payload_envelopes::PendingEnvelopeData;
use crate::{
BeaconChain, BeaconChainError, BeaconChainTypes, BlockProductionError,
Expand Down Expand Up @@ -924,8 +925,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
) -> WinningBid<T::EthSpec> {
let cached_bid = self.gossip_verified_payload_bid_cache.get_highest_bid(
local_signed_bid.message.slot,
local_signed_bid.message.parent_block_hash,
local_signed_bid.message.parent_block_root,
BidParent::from_bid(&local_signed_bid.message),
);
select_payload_bid_pure(
local_signed_bid,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use std::sync::Arc;

use crate::{
BeaconChain, BeaconChainTypes, CanonicalHead,
payload_bid_verification::{PayloadBidError, payload_bid_cache::GossipVerifiedPayloadBidCache},
BeaconChain, BeaconChainTypes, BeaconStore, CachedHead, CanonicalHead,
canonical_head::ForkChoiceReadGuard,
payload_bid_verification::{
PayloadBidError,
payload_bid_cache::{BidParent, GossipVerifiedPayloadBidCache},
},
proposer_preferences_verification::proposer_preference_cache::GossipVerifiedProposerPreferenceCache,
};
use educe::Educe;
Expand Down Expand Up @@ -82,12 +86,84 @@ pub(crate) fn verify_bid_consistency<E: EthSpec>(
Ok(())
}

/// Checks if `bid` is compatible with the head branch
pub(crate) fn is_bid_compatible_with_head<T: BeaconChainTypes>(
cached_head: &CachedHead<T::EthSpec>,
fork_choice_read: &ForkChoiceReadGuard<'_, T>,
bid: &ExecutionPayloadBid<T::EthSpec>,
spec: &ChainSpec,
) -> Result<bool, PayloadBidError> {
let head_block_root = cached_head.head_block_root();

let head_block = fork_choice_read
.get_block(&head_block_root)
.ok_or_else(|| {
PayloadBidError::InternalError(format!(
"head block {head_block_root:?} not found in fork choice"
))
})?;

// TODO(post-gloas) this can be removed after the gloas fork
let head_is_pre_gloas = !spec
.fork_name_at_slot::<T::EthSpec>(head_block.slot)
.gloas_enabled();

let (head_bid_parent_block_hash, head_bid_block_hash) = if head_is_pre_gloas {
let parent_payload_hash = head_block
.parent_root
.and_then(|parent_root| fork_choice_read.get_block(&parent_root))
.and_then(|parent| parent.execution_status.block_hash());
(
parent_payload_hash,
head_block.execution_status.block_hash(),
)
} else {
(
head_block.execution_payload_parent_hash,
head_block.execution_payload_block_hash,
)
};

let builds_on_parent_block = Some(bid.parent_block_root) == head_block.parent_root;
let builds_on_parent_payload = Some(bid.parent_block_hash) == head_bid_parent_block_hash;

if builds_on_parent_block && builds_on_parent_payload {
return Ok(true);
}

if bid.parent_block_root != head_block.root {
return Ok(false);
}

let builds_on_head_payload = Some(bid.parent_block_hash) == head_bid_block_hash;

if head_is_pre_gloas {
return Ok(builds_on_head_payload);
}

if fork_choice_read
.should_build_on_full(
&head_block_root,
cached_head.head_payload_status(),
bid.slot,
)
.map_err(|e| {
PayloadBidError::InternalError(format!("should_build_on_full failed: {e:?}"))
})?
{
return Ok(builds_on_head_payload);
}

Ok(builds_on_parent_payload)
}

pub struct GossipVerificationContext<'a, T: BeaconChainTypes> {
pub canonical_head: &'a CanonicalHead<T>,
pub gossip_verified_payload_bid_cache: &'a GossipVerifiedPayloadBidCache<T::EthSpec>,
pub gossip_verified_proposer_preferences_cache: &'a GossipVerifiedProposerPreferenceCache,
pub slot_clock: &'a T::SlotClock,
pub spec: &'a ChainSpec,
pub store: &'a BeaconStore<T>,
}

/// A wrapper around a `SignedExecutionPayloadBid` that indicates it has been approved for re-gossiping on
Expand All @@ -107,13 +183,13 @@ impl<E: EthSpec> GossipVerifiedPayloadBid<E> {
T: BeaconChainTypes<EthSpec = E>,
{
let bid_slot = signed_bid.message.slot;
let bid_parent_block_hash = signed_bid.message.parent_block_hash;
let bid_parent = BidParent::from_bid(&signed_bid.message);
let bid_parent_block_root = signed_bid.message.parent_block_root;
let bid_value = signed_bid.message.value;

if ctx
.gossip_verified_payload_bid_cache
.seen_builder_index(&bid_slot, signed_bid.message.builder_index)
.seen_builder_bid_for_parent(&bid_slot, bid_parent, signed_bid.message.builder_index)
{
return Err(PayloadBidError::BuilderAlreadySeen {
builder_index: signed_bid.message.builder_index,
Expand All @@ -123,11 +199,10 @@ impl<E: EthSpec> GossipVerifiedPayloadBid<E> {

// TODO(gloas): Extract into `bid_value_over_threshold` on the bid cache and potentially
// make this more sophisticate than just a <= check.
if let Some(cached_bid) = ctx.gossip_verified_payload_bid_cache.get_highest_bid(
bid_slot,
bid_parent_block_hash,
bid_parent_block_root,
) && bid_value <= cached_bid.message.value
if let Some(cached_bid) = ctx
.gossip_verified_payload_bid_cache
.get_highest_bid(bid_slot, bid_parent)
&& bid_value <= cached_bid.message.value
{
return Err(PayloadBidError::BidValueBelowCached {
cached_value: cached_bid.message.value,
Expand All @@ -140,7 +215,43 @@ impl<E: EthSpec> GossipVerifiedPayloadBid<E> {
.slot_clock
.now()
.ok_or(PayloadBidError::UnableToReadSlot)?;
let head_state = &cached_head.snapshot.beacon_state;
let snapshot_state = &cached_head.snapshot.beacon_state;

// At the Gloas fork boundary the head snapshot is still a pre-Gloas state, so we must
// use the advanced state instead.
// TODO(post-gloas) this can be removed after the gloas fork
let advanced_state;
let head_state = if ctx
.spec
.fork_name_at_slot::<T::EthSpec>(bid_slot)
.gloas_enabled()
&& !snapshot_state.fork_name_unchecked().gloas_enabled()
{
let (_, state) = ctx
.store
.get_advanced_hot_state(
cached_head.head_block_root(),
bid_slot,
cached_head.head_state_root(),
)
.map_err(|e| {
PayloadBidError::InternalError(format!(
"failed to load advanced head state: {e:?}"
))
})?
.ok_or_else(|| {
PayloadBidError::InternalError("advanced head state unavailable".to_string())
})?;
if !state.fork_name_unchecked().gloas_enabled() {
return Err(PayloadBidError::InternalError(
"head state not yet advanced to Gloas".to_string(),
));
}
advanced_state = state;
&advanced_state
} else {
snapshot_state
};

// Look up the preferences keyed by the dependent root that is canonical from our head's
// perspective, so we don't pick up preferences cached for a competing branch's proposer.
Expand Down Expand Up @@ -183,10 +294,10 @@ impl<E: EthSpec> GossipVerifiedPayloadBid<E> {
return Err(PayloadBidError::InvalidPrevRandao { slot: bid_slot });
}

// TODO(gloas) reprocess bids whose parent_block_root becomes canonical after a reorg.
let head_root = cached_head.head_block_root();
if !fork_choice.is_descendant(bid_parent_block_root, head_root) {
return Err(PayloadBidError::ParentBlockRootNotCanonical {
// TODO(gloas) should we reprocess a dropped bid when the head changes to its parent?
if !is_bid_compatible_with_head(&cached_head, &fork_choice, &signed_bid.message, ctx.spec)?
{
return Err(PayloadBidError::BidNotCompatibleWithHead {
parent_block_root: bid_parent_block_root,
});
}
Expand Down Expand Up @@ -235,7 +346,7 @@ impl<E: EthSpec> GossipVerifiedPayloadBid<E> {
let gossip_verified_bid = GossipVerifiedPayloadBid { signed_bid };

ctx.gossip_verified_payload_bid_cache
.insert_seen_builder(&gossip_verified_bid);
.insert_seen_builder_bid(&gossip_verified_bid);

ctx.gossip_verified_payload_bid_cache
.insert_highest_bid(gossip_verified_bid.clone());
Expand All @@ -254,6 +365,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.gossip_verified_proposer_preferences_cache,
slot_clock: &self.slot_clock,
spec: &self.spec,
store: &self.store,
}
}

Expand Down
4 changes: 2 additions & 2 deletions beacon_node/beacon_chain/src/payload_bid_verification/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ mod tests;
pub enum PayloadBidError {
/// The bid's parent block root is unknown.
ParentBlockRootUnknown { parent_block_root: Hash256 },
/// The bid's parent block root is known but not on the canonical chain.
ParentBlockRootNotCanonical { parent_block_root: Hash256 },
/// The bid does not build on the head block or on the head block's parent.
BidNotCompatibleWithHead { parent_block_root: Hash256 },
/// The signature is invalid.
BadSignature,
/// A bid for this builder at this slot has already been seen.
Expand Down
Loading
Loading