should_extend_payload and gossip bid verification changes

This commit is contained in:
Eitan Seri-Levi
2026-06-21 17:29:59 +03:00
parent 10568b139b
commit be705c8409
8 changed files with 42 additions and 5 deletions

View File

@@ -158,6 +158,14 @@ impl<T: BeaconChainTypes> GossipVerifiedPayloadBid<T> {
}); });
} }
// [REJECT] `bid.prev_randao` is the correct RANDAO mix -- i.e. validate that
// `bid.prev_randao == get_randao_mix(parent_state, get_current_epoch(parent_state))`
if signed_bid.message.prev_randao
!= *head_state.get_randao_mix(current_slot.epoch(T::EthSpec::slots_per_epoch()))?
{
return Err(PayloadBidError::InvalidPrevRandao { slot: bid_slot });
}
// TODO(gloas) reprocess bids whose parent_block_root becomes canonical after a reorg. // TODO(gloas) reprocess bids whose parent_block_root becomes canonical after a reorg.
let head_root = cached_head.head_block_root(); let head_root = cached_head.head_block_root();
if !fork_choice.is_descendant(bid_parent_block_root, head_root) { if !fork_choice.is_descendant(bid_parent_block_root, head_root) {

View File

@@ -59,6 +59,8 @@ pub enum PayloadBidError {
max_blobs_per_block: usize, max_blobs_per_block: usize,
blob_kzg_commitments_len: usize, blob_kzg_commitments_len: usize,
}, },
/// The bids prev randao value is invalid
InvalidPrevRandao { slot: Slot },
/// Some Beacon State error /// Some Beacon State error
BeaconStateError(BeaconStateError), BeaconStateError(BeaconStateError),
/// Internal error /// Internal error

View File

@@ -4025,6 +4025,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
| PayloadBidError::ExecutionPaymentNonZero { .. } | PayloadBidError::ExecutionPaymentNonZero { .. }
| PayloadBidError::InvalidBlobKzgCommitments { .. } | PayloadBidError::InvalidBlobKzgCommitments { .. }
| PayloadBidError::BidNotDescendantOfParent { .. }, | PayloadBidError::BidNotDescendantOfParent { .. },
| PayloadBidError::InvalidPrevRandao { .. },
) => { ) => {
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Reject); self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Reject);
self.gossip_penalize_peer( self.gossip_penalize_peer(

View File

@@ -1631,9 +1631,10 @@ where
/// Returns whether the proposer should extend the execution payload chain of the given block. /// Returns whether the proposer should extend the execution payload chain of the given block.
pub fn should_extend_payload(&self, block_root: &Hash256) -> Result<bool, Error<T::Error>> { pub fn should_extend_payload(&self, block_root: &Hash256) -> Result<bool, Error<T::Error>> {
let current_slot = self.fc_store.get_current_slot();
let proposer_boost_root = self.fc_store.proposer_boost_root(); let proposer_boost_root = self.fc_store.proposer_boost_root();
self.proto_array self.proto_array
.should_extend_payload::<E>(block_root, proposer_boost_root) .should_extend_payload::<E>(block_root, current_slot, proposer_boost_root)
.map_err(Error::ProtoArrayStringError) .map_err(Error::ProtoArrayStringError)
} }

View File

@@ -1,6 +1,6 @@
use crate::PayloadStatus; use crate::PayloadStatus;
use safe_arith::ArithError; use safe_arith::ArithError;
use types::{Epoch, ExecutionBlockHash, Hash256}; use types::{Epoch, ExecutionBlockHash, Hash256, Slot};
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum Error { pub enum Error {
@@ -63,6 +63,14 @@ pub enum Error {
block_root: Hash256, block_root: Hash256,
payload_status: PayloadStatus, payload_status: PayloadStatus,
}, },
/// `should_extend_payload` was called for a block whose slot is not the previous slot.
///
/// Spec equivalent: `assert store.blocks[root].slot + 1 == get_current_slot(store)`.
ShouldExtendPayloadInvalidSlot {
block_root: Hash256,
block_slot: Slot,
current_slot: Slot,
},
} }
impl From<ArithError> for Error { impl From<ArithError> for Error {

View File

@@ -1564,7 +1564,12 @@ impl ProtoArray {
Ok(fc_node.payload_status as u8) Ok(fc_node.payload_status as u8)
} else if fc_node.payload_status == PayloadStatus::Empty { } else if fc_node.payload_status == PayloadStatus::Empty {
Ok(1) Ok(1)
} else if self.should_extend_payload::<E>(fc_node, proto_node, proposer_boost_root)? { } else if self.should_extend_payload::<E>(
fc_node,
proto_node,
current_slot,
proposer_boost_root,
)? {
Ok(2) Ok(2)
} else { } else {
Ok(0) Ok(0)
@@ -1614,8 +1619,17 @@ impl ProtoArray {
&self, &self,
fc_node: &IndexedForkChoiceNode, fc_node: &IndexedForkChoiceNode,
proto_node: &ProtoNode, proto_node: &ProtoNode,
current_slot: Slot,
proposer_boost_root: Hash256, proposer_boost_root: Hash256,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
if proto_node.slot().saturating_add(1u64) != current_slot {
return Err(Error::ShouldExtendPayloadInvalidSlot {
block_root: fc_node.root,
block_slot: proto_node.slot(),
current_slot,
});
}
let Ok(node) = proto_node.as_v29() else { let Ok(node) = proto_node.as_v29() else {
return Err(Error::InvalidNodeVariant { return Err(Error::InvalidNodeVariant {
block_root: fc_node.root, block_root: fc_node.root,

View File

@@ -999,6 +999,7 @@ impl ProtoArrayForkChoice {
pub fn should_extend_payload<E: EthSpec>( pub fn should_extend_payload<E: EthSpec>(
&self, &self,
block_root: &Hash256, block_root: &Hash256,
current_slot: Slot,
proposer_boost_root: Hash256, proposer_boost_root: Hash256,
) -> Result<bool, String> { ) -> Result<bool, String> {
let block_index = self let block_index = self
@@ -1017,7 +1018,7 @@ impl ProtoArrayForkChoice {
payload_status: proto_node.get_parent_payload_status(), payload_status: proto_node.get_parent_payload_status(),
}; };
self.proto_array self.proto_array
.should_extend_payload::<E>(&fc_node, proto_node, proposer_boost_root) .should_extend_payload::<E>(&fc_node, proto_node, current_slot, proposer_boost_root)
.map_err(|e| format!("{e:?}")) .map_err(|e| format!("{e:?}"))
} }

View File

@@ -397,7 +397,9 @@ pub fn process_proposer_slashings<E: EthSpec>(
// [New in Gloas:EIP7732] // [New in Gloas:EIP7732]
// Remove the BuilderPendingPayment corresponding to this proposal // Remove the BuilderPendingPayment corresponding to this proposal
// if it is still in the 2-epoch window. // if it is still in the 2-epoch window. Only clear it when the slashed validator is
// the proposer associated with the payment; otherwise an unrelated same-slot
// equivocation could grief an honest proposer's payment.
if state.fork_name_unchecked().gloas_enabled() { if state.fork_name_unchecked().gloas_enabled() {
let slot = proposer_slashing.signed_header_1.message.slot; let slot = proposer_slashing.signed_header_1.message.slot;
let proposal_epoch = slot.epoch(E::slots_per_epoch()); let proposal_epoch = slot.epoch(E::slots_per_epoch());