mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-08 01:05:47 +00:00
Resolve merge confclits from unstable
This commit is contained in:
@@ -26,6 +26,7 @@ use types::{
|
||||
#[derive(Debug)]
|
||||
pub enum Error<T> {
|
||||
InvalidAttestation(InvalidAttestation),
|
||||
InvalidPayloadAttestation(InvalidPayloadAttestation),
|
||||
InvalidAttesterSlashing(AttesterSlashingValidationError),
|
||||
InvalidBlock(InvalidBlock),
|
||||
ProtoArrayStringError(String),
|
||||
@@ -85,6 +86,12 @@ impl<T> From<InvalidAttestation> for Error<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<InvalidPayloadAttestation> for Error<T> {
|
||||
fn from(e: InvalidPayloadAttestation) -> Self {
|
||||
Error::InvalidPayloadAttestation(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<AttesterSlashingValidationError> for Error<T> {
|
||||
fn from(e: AttesterSlashingValidationError) -> Self {
|
||||
Error::InvalidAttesterSlashing(e)
|
||||
@@ -177,14 +184,26 @@ pub enum InvalidAttestation {
|
||||
/// Post-Gloas: attestation with index == 1 (payload_present) requires the block's
|
||||
/// payload to have been received (`root in store.payload_states`).
|
||||
PayloadNotReceived { beacon_block_root: Hash256 },
|
||||
/// A payload attestation votes payload_present for a block in the current slot, which is
|
||||
/// invalid because the payload cannot be known yet.
|
||||
PayloadPresentDuringSameSlot { slot: Slot },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum InvalidPayloadAttestation {
|
||||
/// The payload attestation's attesting indices were empty.
|
||||
EmptyAggregationBitfield,
|
||||
/// The `payload_attestation.data.beacon_block_root` block is unknown.
|
||||
UnknownHeadBlock { beacon_block_root: Hash256 },
|
||||
/// The payload attestation is attesting to a block that is later than itself.
|
||||
AttestsToFutureBlock { block: Slot, attestation: Slot },
|
||||
/// A gossip payload attestation must be for the current slot.
|
||||
PayloadAttestationNotCurrentSlot {
|
||||
attestation_slot: Slot,
|
||||
current_slot: Slot,
|
||||
},
|
||||
/// One or more payload attesters are not part of the PTC.
|
||||
PayloadAttestationAttestersNotInPtc {
|
||||
attesting_indices_len: usize,
|
||||
attesting_indices_in_ptc: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T> From<String> for Error<T> {
|
||||
@@ -654,6 +673,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark a Gloas payload envelope as valid and received.
|
||||
///
|
||||
/// This must only be called for valid Gloas payloads.
|
||||
pub fn on_valid_payload_envelope_received(
|
||||
&mut self,
|
||||
block_root: Hash256,
|
||||
) -> Result<(), Error<T::Error>> {
|
||||
self.proto_array
|
||||
.on_valid_payload_envelope_received(block_root)
|
||||
.map_err(Error::FailedToProcessValidExecutionPayload)
|
||||
}
|
||||
|
||||
/// Pre-Gloas only.
|
||||
///
|
||||
/// See `ProtoArrayForkChoice::process_execution_payload_validation` for documentation.
|
||||
pub fn on_valid_execution_payload(
|
||||
&mut self,
|
||||
@@ -664,6 +697,8 @@ where
|
||||
.map_err(Error::FailedToProcessValidExecutionPayload)
|
||||
}
|
||||
|
||||
/// Pre-Gloas only.
|
||||
///
|
||||
/// See `ProtoArrayForkChoice::process_execution_payload_invalidation` for documentation.
|
||||
pub fn on_invalid_execution_payload(
|
||||
&mut self,
|
||||
@@ -977,12 +1012,6 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn on_execution_payload(&mut self, block_root: Hash256) -> Result<(), Error<T::Error>> {
|
||||
self.proto_array
|
||||
.on_execution_payload(block_root)
|
||||
.map_err(Error::FailedToProcessValidExecutionPayload)
|
||||
}
|
||||
|
||||
/// Update checkpoints in store if necessary
|
||||
fn update_checkpoints(
|
||||
&mut self,
|
||||
@@ -1158,50 +1187,48 @@ where
|
||||
&self,
|
||||
indexed_payload_attestation: &IndexedPayloadAttestation<E>,
|
||||
is_from_block: AttestationFromBlock,
|
||||
) -> Result<(), InvalidAttestation> {
|
||||
) -> Result<(), InvalidPayloadAttestation> {
|
||||
// This check is from `is_valid_indexed_payload_attestation`, but we do it immediately to
|
||||
// avoid wasting time on junk attestations.
|
||||
if indexed_payload_attestation.attesting_indices.is_empty() {
|
||||
return Err(InvalidAttestation::EmptyAggregationBitfield);
|
||||
return Err(InvalidPayloadAttestation::EmptyAggregationBitfield);
|
||||
}
|
||||
|
||||
// PTC attestation must be for a known block. If block is unknown, delay consideration until
|
||||
// the block is found (responsibility of caller).
|
||||
let block = self
|
||||
.proto_array
|
||||
.get_block(&indexed_payload_attestation.data.beacon_block_root)
|
||||
.ok_or(InvalidAttestation::UnknownHeadBlock {
|
||||
.ok_or(InvalidPayloadAttestation::UnknownHeadBlock {
|
||||
beacon_block_root: indexed_payload_attestation.data.beacon_block_root,
|
||||
})?;
|
||||
|
||||
// Not strictly part of the spec, but payload attestations to future slots are MORE INVALID
|
||||
// than payload attestations to blocks at previous slots.
|
||||
if block.slot > indexed_payload_attestation.data.slot {
|
||||
return Err(InvalidAttestation::AttestsToFutureBlock {
|
||||
return Err(InvalidPayloadAttestation::AttestsToFutureBlock {
|
||||
block: block.slot,
|
||||
attestation: indexed_payload_attestation.data.slot,
|
||||
});
|
||||
}
|
||||
|
||||
// Spec: `if data.slot != state.slot: return` — PTC votes can only
|
||||
// change the vote for their assigned beacon block.
|
||||
// PTC votes can only change the vote for their assigned beacon block, return early otherwise
|
||||
if block.slot != indexed_payload_attestation.data.slot {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Gossip payload attestations must be for the current slot.
|
||||
// NOTE: signature is assumed to have been verified by caller.
|
||||
// https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/fork-choice.md
|
||||
if matches!(is_from_block, AttestationFromBlock::False)
|
||||
&& indexed_payload_attestation.data.slot != self.fc_store.get_current_slot()
|
||||
{
|
||||
return Err(InvalidAttestation::PayloadAttestationNotCurrentSlot {
|
||||
attestation_slot: indexed_payload_attestation.data.slot,
|
||||
current_slot: self.fc_store.get_current_slot(),
|
||||
});
|
||||
}
|
||||
|
||||
// A payload attestation voting payload_present for a block in the current slot is
|
||||
// invalid: the payload cannot be known yet. This only applies to gossip attestations;
|
||||
// payload attestations from blocks have already been validated by the block producer.
|
||||
if matches!(is_from_block, AttestationFromBlock::False)
|
||||
&& self.fc_store.get_current_slot() == block.slot
|
||||
&& indexed_payload_attestation.data.payload_present
|
||||
{
|
||||
return Err(InvalidAttestation::PayloadPresentDuringSameSlot { slot: block.slot });
|
||||
return Err(
|
||||
InvalidPayloadAttestation::PayloadAttestationNotCurrentSlot {
|
||||
attestation_slot: indexed_payload_attestation.data.slot,
|
||||
current_slot: self.fc_store.get_current_slot(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -1308,10 +1335,22 @@ where
|
||||
|
||||
// Resolve validator indices to PTC committee positions.
|
||||
let ptc_indices: Vec<usize> = attestation
|
||||
.attesting_indices_iter()
|
||||
.attesting_indices
|
||||
.iter()
|
||||
.filter_map(|vi| ptc.iter().position(|&p| p == *vi as usize))
|
||||
.collect();
|
||||
|
||||
// Check that all the attesters are in the PTC
|
||||
if ptc_indices.len() != attestation.attesting_indices.len() {
|
||||
return Err(
|
||||
InvalidPayloadAttestation::PayloadAttestationAttestersNotInPtc {
|
||||
attesting_indices_len: attestation.attesting_indices.len(),
|
||||
attesting_indices_in_ptc: ptc_indices.len(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
for &ptc_index in &ptc_indices {
|
||||
self.proto_array.process_payload_attestation(
|
||||
attestation.data.beacon_block_root,
|
||||
@@ -1614,7 +1653,6 @@ where
|
||||
persisted_proto_array: proto_array::core::SszContainer,
|
||||
justified_balances: JustifiedBalances,
|
||||
reset_payload_statuses: ResetPayloadStatuses,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<ProtoArrayForkChoice, Error<T::Error>> {
|
||||
let mut proto_array = ProtoArrayForkChoice::from_container(
|
||||
persisted_proto_array.clone(),
|
||||
@@ -1639,7 +1677,7 @@ where
|
||||
|
||||
// Reset all blocks back to being "optimistic". This helps recover from an EL consensus
|
||||
// fault where an invalid payload becomes valid.
|
||||
if let Err(e) = proto_array.set_all_blocks_to_optimistic::<E>(spec) {
|
||||
if let Err(e) = proto_array.set_all_blocks_to_optimistic::<E>() {
|
||||
// If there is an error resetting the optimistic status then log loudly and revert
|
||||
// back to a proto-array which does not have the reset applied. This indicates a
|
||||
// significant error in Lighthouse and warrants detailed investigation.
|
||||
@@ -1669,7 +1707,6 @@ where
|
||||
persisted.proto_array,
|
||||
justified_balances,
|
||||
reset_payload_statuses,
|
||||
spec,
|
||||
)?;
|
||||
|
||||
let current_slot = fc_store.get_current_slot();
|
||||
@@ -1703,7 +1740,7 @@ where
|
||||
// get a different result.
|
||||
fork_choice
|
||||
.proto_array
|
||||
.set_all_blocks_to_optimistic::<E>(spec)?;
|
||||
.set_all_blocks_to_optimistic::<E>()?;
|
||||
// If the second attempt at finding a head fails, return an error since we do not
|
||||
// expect this scenario.
|
||||
let _ = fork_choice.get_head(current_slot, spec)?;
|
||||
|
||||
@@ -4,8 +4,9 @@ mod metrics;
|
||||
|
||||
pub use crate::fork_choice::{
|
||||
AttestationFromBlock, Error, ForkChoice, ForkChoiceView, ForkchoiceUpdateParameters,
|
||||
InvalidAttestation, InvalidBlock, PayloadVerificationStatus, PersistedForkChoice,
|
||||
PersistedForkChoiceV28, PersistedForkChoiceV29, QueuedAttestation, ResetPayloadStatuses,
|
||||
InvalidAttestation, InvalidBlock, InvalidPayloadAttestation, PayloadVerificationStatus,
|
||||
PersistedForkChoice, PersistedForkChoiceV28, PersistedForkChoiceV29, QueuedAttestation,
|
||||
ResetPayloadStatuses,
|
||||
};
|
||||
pub use fork_choice_store::ForkChoiceStore;
|
||||
pub use proto_array::{
|
||||
|
||||
@@ -11,7 +11,7 @@ use bls::AggregateSignature;
|
||||
use fixed_bytes::FixedBytesExtended;
|
||||
use fork_choice::{
|
||||
AttestationFromBlock, ForkChoiceStore, InvalidAttestation, InvalidBlock,
|
||||
PayloadVerificationStatus, QueuedAttestation,
|
||||
InvalidPayloadAttestation, PayloadVerificationStatus, QueuedAttestation,
|
||||
};
|
||||
use state_processing::state_advance::complete_state_advance;
|
||||
use std::fmt;
|
||||
@@ -969,8 +969,8 @@ async fn non_block_payload_attestation_for_previous_slot_is_rejected() {
|
||||
assert!(
|
||||
matches!(
|
||||
result,
|
||||
Err(ForkChoiceError::InvalidAttestation(
|
||||
InvalidAttestation::PayloadAttestationNotCurrentSlot { .. }
|
||||
Err(ForkChoiceError::InvalidPayloadAttestation(
|
||||
InvalidPayloadAttestation::PayloadAttestationNotCurrentSlot { .. }
|
||||
))
|
||||
),
|
||||
"gossip payload attestation for previous slot should be rejected, got: {:?}",
|
||||
|
||||
@@ -98,7 +98,7 @@ pub enum Operation {
|
||||
},
|
||||
/// Simulate receiving and validating an execution payload for `block_root`.
|
||||
/// Sets `payload_received = true` on the V29 node via the live validation path.
|
||||
ProcessExecutionPayload {
|
||||
ProcessExecutionPayloadEnvelope {
|
||||
block_root: Hash256,
|
||||
},
|
||||
AssertPayloadReceived {
|
||||
@@ -500,9 +500,9 @@ impl ForkChoiceTestDefinition {
|
||||
// the payload to be in payload_states (payload_received).
|
||||
node_v29.payload_received = is_timely || is_data_available;
|
||||
}
|
||||
Operation::ProcessExecutionPayload { block_root } => {
|
||||
Operation::ProcessExecutionPayloadEnvelope { block_root } => {
|
||||
fork_choice
|
||||
.on_execution_payload(block_root)
|
||||
.on_valid_payload_envelope_received(block_root)
|
||||
.unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"on_execution_payload op at index {} returned error: {}",
|
||||
|
||||
@@ -53,7 +53,7 @@ pub fn get_gloas_chain_following_test_definition() -> ForkChoiceTestDefinition {
|
||||
|
||||
// Mark root_1 as having received its execution payload so that
|
||||
// its FULL virtual node exists in the Gloas fork choice tree.
|
||||
ops.push(Operation::ProcessExecutionPayload {
|
||||
ops.push(Operation::ProcessExecutionPayloadEnvelope {
|
||||
block_root: get_root(1),
|
||||
});
|
||||
|
||||
@@ -263,7 +263,7 @@ pub fn get_gloas_find_head_vote_transition_test_definition() -> ForkChoiceTestDe
|
||||
|
||||
// Mark root_1 as having received its execution payload so that
|
||||
// its FULL virtual node exists in the Gloas fork choice tree.
|
||||
ops.push(Operation::ProcessExecutionPayload {
|
||||
ops.push(Operation::ProcessExecutionPayloadEnvelope {
|
||||
block_root: get_root(1),
|
||||
});
|
||||
|
||||
@@ -368,7 +368,7 @@ pub fn get_gloas_weight_priority_over_payload_preference_test_definition()
|
||||
|
||||
// Mark root_1 as having received its execution payload so that
|
||||
// its FULL virtual node exists in the Gloas fork choice tree.
|
||||
ops.push(Operation::ProcessExecutionPayload {
|
||||
ops.push(Operation::ProcessExecutionPayloadEnvelope {
|
||||
block_root: get_root(1),
|
||||
});
|
||||
|
||||
@@ -538,7 +538,7 @@ pub fn get_gloas_interleaved_attestations_test_definition() -> ForkChoiceTestDef
|
||||
|
||||
// Mark root_1 as having received its execution payload so that
|
||||
// its FULL virtual node exists in the Gloas fork choice tree.
|
||||
ops.push(Operation::ProcessExecutionPayload {
|
||||
ops.push(Operation::ProcessExecutionPayloadEnvelope {
|
||||
block_root: get_root(1),
|
||||
});
|
||||
|
||||
@@ -674,8 +674,8 @@ pub fn get_gloas_payload_received_interleaving_test_definition() -> ForkChoiceTe
|
||||
expected_payload_status: None,
|
||||
});
|
||||
|
||||
// ProcessExecutionPayload on genesis is a no-op (already received at init).
|
||||
ops.push(Operation::ProcessExecutionPayload {
|
||||
// ProcessExecutionPayloadEnvelope on genesis is a no-op (already received at init).
|
||||
ops.push(Operation::ProcessExecutionPayloadEnvelope {
|
||||
block_root: get_root(0),
|
||||
});
|
||||
|
||||
@@ -778,7 +778,7 @@ mod tests {
|
||||
|
||||
// Mark root 2's execution payload as received so the Full virtual child exists.
|
||||
if first_gloas_block_full {
|
||||
ops.push(Operation::ProcessExecutionPayload {
|
||||
ops.push(Operation::ProcessExecutionPayloadEnvelope {
|
||||
block_root: get_root(2),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -165,8 +165,6 @@ pub struct ProtoNode {
|
||||
pub payload_data_availability_votes: BitVector<U512>,
|
||||
/// Whether the execution payload for this block has been received and validated locally.
|
||||
/// Maps to `root in store.payload_states` in the spec.
|
||||
/// When true, `is_payload_timely` and `is_payload_data_available` return true
|
||||
/// regardless of PTC vote counts.
|
||||
#[superstruct(only(V29), partial_getter(copy))]
|
||||
pub payload_received: bool,
|
||||
/// The proposer index for this block, used by `should_apply_proposer_boost`
|
||||
@@ -213,7 +211,7 @@ impl ProtoNode {
|
||||
return false;
|
||||
}
|
||||
|
||||
node.payload_timeliness_votes.num_set_bits() > E::ptc_size() / 2
|
||||
node.payload_timeliness_votes.num_set_bits() > E::payload_timely_threshold()
|
||||
}
|
||||
|
||||
pub fn is_payload_data_available<E: EthSpec>(&self) -> bool {
|
||||
@@ -226,8 +224,8 @@ impl ProtoNode {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(gloas): add function on EthSpec for DATA_AVAILABILITY_TIMELY_THRESHOLD
|
||||
node.payload_data_availability_votes.num_set_bits() > E::ptc_size() / 2
|
||||
node.payload_data_availability_votes.num_set_bits()
|
||||
> E::data_availability_timely_threshold()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,7 +367,6 @@ pub struct ProtoArray {
|
||||
pub prune_threshold: usize,
|
||||
pub nodes: Vec<ProtoNode>,
|
||||
pub indices: HashMap<Hash256, usize>,
|
||||
pub previous_proposer_boost: ProposerBoost,
|
||||
}
|
||||
|
||||
impl ProtoArray {
|
||||
@@ -492,20 +489,14 @@ impl ProtoArray {
|
||||
.ok_or(Error::DeltaOverflow(parent_index))?;
|
||||
}
|
||||
} else {
|
||||
// V17 child of a V29 parent (fork transition): treat as FULL
|
||||
// since V17 nodes always have execution payloads inline.
|
||||
parent_delta.full_delta = parent_delta
|
||||
.full_delta
|
||||
.checked_add(delta)
|
||||
.ok_or(Error::DeltaOverflow(parent_index))?;
|
||||
// This is a v17 node with a v17 parent.
|
||||
// There is no empty or full weight for v17 nodes, so nothing to propagate.
|
||||
// In the tree walk, the v17 nodes have an empty child with 0 weight, which
|
||||
// wins by default (it is the only child).
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Proposer boost is now applied on-the-fly in `get_weight` during the
|
||||
// walk, so clear any stale boost from a prior call.
|
||||
self.previous_proposer_boost = ProposerBoost::default();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -641,11 +632,9 @@ impl ProtoArray {
|
||||
// Anchor gets [True, True]. Others computed from time_into_slot.
|
||||
block_timeliness_attestation_threshold: is_genesis
|
||||
|| (is_current_slot
|
||||
&& time_into_slot < spec.get_unaggregated_attestation_due()),
|
||||
// TODO(gloas): use Gloas-specific PTC due threshold once
|
||||
// `get_payload_attestation_due_ms` is on ChainSpec.
|
||||
&& time_into_slot < spec.get_attestation_due::<E>(current_slot)),
|
||||
block_timeliness_ptc_threshold: is_genesis
|
||||
|| (is_current_slot && time_into_slot < spec.get_slot_duration() / 2),
|
||||
|| (is_current_slot && time_into_slot < spec.get_payload_attestation_due()),
|
||||
equivocating_attestation_score: 0,
|
||||
})
|
||||
};
|
||||
@@ -682,11 +671,17 @@ impl ProtoArray {
|
||||
}
|
||||
|
||||
/// Spec: `is_head_weak`.
|
||||
///
|
||||
/// The spec adds weight from equivocating validators in the head slot's
|
||||
/// committees. We approximate this with `equivocating_attestation_score`
|
||||
/// which tracks equivocating validators that voted for this block (close
|
||||
/// but not identical to committee membership).
|
||||
// TODO(gloas): the spec adds weight from equivocating validators in the
|
||||
// head slot's *committees*, regardless of who they voted for. We approximate
|
||||
// with `equivocating_attestation_score` which only tracks equivocating
|
||||
// validators whose vote pointed at this block. This under-counts when an
|
||||
// equivocating validator is in the committee but voted for a different fork,
|
||||
// which could allow a re-org the spec wouldn't. In practice the deviation
|
||||
// is small — it requires equivocating validators voting for competing forks
|
||||
// AND the head weight to be exactly at the reorg threshold boundary.
|
||||
// Fixing this properly requires committee computation from BeaconState,
|
||||
// which is not available in proto_array. The fix would be to pass
|
||||
// pre-computed equivocating committee weight from the beacon_chain caller.
|
||||
fn is_head_weak<E: EthSpec>(
|
||||
&self,
|
||||
head_node: &ProtoNode,
|
||||
@@ -729,7 +724,6 @@ impl ProtoArray {
|
||||
.nodes
|
||||
.get(block_index)
|
||||
.ok_or(Error::InvalidNodeIndex(block_index))?;
|
||||
// TODO(gloas): handle parent unknown case?
|
||||
let parent_index = block
|
||||
.parent()
|
||||
.ok_or(Error::NodeUnknown(proposer_boost_root))?;
|
||||
@@ -753,7 +747,6 @@ impl ProtoArray {
|
||||
// the parent's slot from the same proposer.
|
||||
let parent_slot = parent.slot();
|
||||
let parent_root = parent.root();
|
||||
// TODO(gloas): handle proposer index for pre-Gloas blocks?
|
||||
let parent_proposer = parent.proposer_index();
|
||||
|
||||
let has_equivocation = self.nodes.iter().any(|node| {
|
||||
@@ -773,12 +766,10 @@ impl ProtoArray {
|
||||
Ok(!has_equivocation)
|
||||
}
|
||||
|
||||
/// Process an execution payload for a Gloas block.
|
||||
/// Process a valid execution payload envelope for a Gloas block.
|
||||
///
|
||||
/// Sets `payload_received` to true, which makes `is_payload_timely` and
|
||||
/// `is_payload_data_available` return true regardless of PTC votes.
|
||||
/// This maps to `store.payload_states[root] = state` in the spec.
|
||||
pub fn on_valid_execution_payload(&mut self, block_root: Hash256) -> Result<(), Error> {
|
||||
/// Sets `payload_received` to true.
|
||||
pub fn on_valid_payload_envelope_received(&mut self, block_root: Hash256) -> Result<(), Error> {
|
||||
let index = *self
|
||||
.indices
|
||||
.get(&block_root)
|
||||
@@ -814,6 +805,8 @@ impl ProtoArray {
|
||||
|
||||
/// Updates the `verified_node_index` and all ancestors to have validated execution payloads.
|
||||
///
|
||||
/// This function is a no-op if called for a Gloas block.
|
||||
///
|
||||
/// Returns an error if:
|
||||
///
|
||||
/// - The `verified_node_index` is unknown.
|
||||
@@ -857,18 +850,10 @@ impl ProtoArray {
|
||||
});
|
||||
}
|
||||
},
|
||||
// Gloas nodes don't carry `ExecutionStatus`. Mark the validated
|
||||
// block as payload-received so that `is_payload_timely` /
|
||||
// `is_payload_data_available` and `index == 1` attestations work.
|
||||
ProtoNode::V29(node) => {
|
||||
if index == verified_node_index {
|
||||
node.payload_received = true;
|
||||
}
|
||||
if let Some(parent_index) = node.parent {
|
||||
parent_index
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
// Gloas nodes should not be marked valid by this function, which exists only
|
||||
// for pre-Gloas fork choice.
|
||||
ProtoNode::V29(_) => {
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -879,6 +864,7 @@ impl ProtoArray {
|
||||
/// Invalidate zero or more blocks, as specified by the `InvalidationOperation`.
|
||||
///
|
||||
/// See the documentation of `InvalidationOperation` for usage.
|
||||
// TODO(gloas): this needs some tests for the mixed Gloas/pre-Gloas case.
|
||||
pub fn propagate_execution_payload_invalidation<E: EthSpec>(
|
||||
&mut self,
|
||||
op: &InvalidationOperation,
|
||||
@@ -978,7 +964,7 @@ impl ProtoArray {
|
||||
// This block is pre-merge, therefore it has no execution status. Nor do its
|
||||
// ancestors.
|
||||
Ok(ExecutionStatus::Irrelevant(_)) => break,
|
||||
Err(_) => (),
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1087,9 +1073,6 @@ impl ProtoArray {
|
||||
});
|
||||
}
|
||||
|
||||
// In the post-Gloas world, always use a virtual tree walk.
|
||||
//
|
||||
// Best child/best descendant is dead.
|
||||
let best_fc_node = self.find_head_walk::<E>(
|
||||
justified_index,
|
||||
current_slot,
|
||||
@@ -1125,26 +1108,6 @@ impl ProtoArray {
|
||||
Ok((best_fc_node.root, best_fc_node.payload_status))
|
||||
}
|
||||
|
||||
/// Build a parent->children index. Invalid nodes are excluded
|
||||
/// (they aren't in store.blocks in the spec).
|
||||
fn build_children_index(&self) -> Vec<Vec<usize>> {
|
||||
let mut children = vec![vec![]; self.nodes.len()];
|
||||
for (i, node) in self.nodes.iter().enumerate() {
|
||||
if node
|
||||
.execution_status()
|
||||
.is_ok_and(|status| status.is_invalid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if let Some(parent) = node.parent()
|
||||
&& parent < children.len()
|
||||
{
|
||||
children[parent].push(i);
|
||||
}
|
||||
}
|
||||
children
|
||||
}
|
||||
|
||||
/// Spec: `get_filtered_block_tree`.
|
||||
///
|
||||
/// Returns the set of node indices on viable branches — those with at least
|
||||
@@ -1155,7 +1118,6 @@ impl ProtoArray {
|
||||
current_slot: Slot,
|
||||
best_justified_checkpoint: Checkpoint,
|
||||
best_finalized_checkpoint: Checkpoint,
|
||||
children_index: &[Vec<usize>],
|
||||
) -> HashSet<usize> {
|
||||
let mut viable = HashSet::new();
|
||||
self.filter_block_tree::<E>(
|
||||
@@ -1163,7 +1125,6 @@ impl ProtoArray {
|
||||
current_slot,
|
||||
best_justified_checkpoint,
|
||||
best_finalized_checkpoint,
|
||||
children_index,
|
||||
&mut viable,
|
||||
);
|
||||
viable
|
||||
@@ -1176,17 +1137,25 @@ impl ProtoArray {
|
||||
current_slot: Slot,
|
||||
best_justified_checkpoint: Checkpoint,
|
||||
best_finalized_checkpoint: Checkpoint,
|
||||
children_index: &[Vec<usize>],
|
||||
viable: &mut HashSet<usize>,
|
||||
) -> bool {
|
||||
let Some(node) = self.nodes.get(node_index) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let children = children_index
|
||||
.get(node_index)
|
||||
.map(|c| c.as_slice())
|
||||
.unwrap_or(&[]);
|
||||
// Skip invalid children — they aren't in store.blocks in the spec.
|
||||
let children: Vec<usize> = self
|
||||
.nodes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, child)| {
|
||||
child.parent() == Some(node_index)
|
||||
&& !child
|
||||
.execution_status()
|
||||
.is_ok_and(|status| status.is_invalid())
|
||||
})
|
||||
.map(|(i, _)| i)
|
||||
.collect();
|
||||
|
||||
if !children.is_empty() {
|
||||
// Evaluate ALL children (no short-circuit) to mark all viable branches.
|
||||
@@ -1198,7 +1167,6 @@ impl ProtoArray {
|
||||
current_slot,
|
||||
best_justified_checkpoint,
|
||||
best_finalized_checkpoint,
|
||||
children_index,
|
||||
viable,
|
||||
)
|
||||
})
|
||||
@@ -1243,16 +1211,12 @@ impl ProtoArray {
|
||||
payload_status: PayloadStatus::Pending,
|
||||
};
|
||||
|
||||
// Build parent->children index once for O(1) lookups.
|
||||
let children_index = self.build_children_index();
|
||||
|
||||
// Spec: `get_filtered_block_tree`.
|
||||
let viable_nodes = self.get_filtered_block_tree::<E>(
|
||||
start_index,
|
||||
current_slot,
|
||||
best_justified_checkpoint,
|
||||
best_finalized_checkpoint,
|
||||
&children_index,
|
||||
);
|
||||
|
||||
// Compute once rather than per-child per-level.
|
||||
@@ -1261,7 +1225,7 @@ impl ProtoArray {
|
||||
|
||||
loop {
|
||||
let children: Vec<_> = self
|
||||
.get_node_children(&head, &children_index)?
|
||||
.get_node_children(&head)?
|
||||
.into_iter()
|
||||
.filter(|(fc_node, _)| viable_nodes.contains(&fc_node.proto_node_index))
|
||||
.collect();
|
||||
@@ -1272,11 +1236,7 @@ impl ProtoArray {
|
||||
|
||||
head = children
|
||||
.into_iter()
|
||||
.map(|(child, _)| -> Result<_, Error> {
|
||||
let proto_node = self
|
||||
.nodes
|
||||
.get(child.proto_node_index)
|
||||
.ok_or(Error::InvalidNodeIndex(child.proto_node_index))?;
|
||||
.map(|(child, ref proto_node)| -> Result<_, Error> {
|
||||
let weight = self.get_weight::<E>(
|
||||
&child,
|
||||
proto_node,
|
||||
@@ -1424,7 +1384,6 @@ impl ProtoArray {
|
||||
fn get_node_children(
|
||||
&self,
|
||||
node: &IndexedForkChoiceNode,
|
||||
children_index: &[Vec<usize>],
|
||||
) -> Result<Vec<(IndexedForkChoiceNode, ProtoNode)>, Error> {
|
||||
if node.payload_status == PayloadStatus::Pending {
|
||||
let proto_node = self
|
||||
@@ -1438,25 +1397,23 @@ impl ProtoArray {
|
||||
}
|
||||
Ok(children)
|
||||
} else {
|
||||
let child_indices = children_index
|
||||
.get(node.proto_node_index)
|
||||
.map(|c| c.as_slice())
|
||||
.unwrap_or(&[]);
|
||||
Ok(child_indices
|
||||
Ok(self
|
||||
.nodes
|
||||
.iter()
|
||||
.filter_map(|&child_index| {
|
||||
let child_node = self.nodes.get(child_index)?;
|
||||
if child_node.get_parent_payload_status() != node.payload_status {
|
||||
return None;
|
||||
}
|
||||
Some((
|
||||
.enumerate()
|
||||
.filter(|(_, child_node)| {
|
||||
child_node.parent() == Some(node.proto_node_index)
|
||||
&& child_node.get_parent_payload_status() == node.payload_status
|
||||
})
|
||||
.map(|(child_index, child_node)| {
|
||||
(
|
||||
IndexedForkChoiceNode {
|
||||
root: child_node.root(),
|
||||
proto_node_index: child_index,
|
||||
payload_status: PayloadStatus::Pending,
|
||||
},
|
||||
child_node.clone(),
|
||||
))
|
||||
)
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ use crate::{
|
||||
JustifiedBalances,
|
||||
error::Error,
|
||||
proto_array::{
|
||||
InvalidationOperation, Iter, NodeDelta, ProposerBoost, ProtoArray, ProtoNode,
|
||||
calculate_committee_fraction,
|
||||
InvalidationOperation, Iter, NodeDelta, ProtoArray, ProtoNode, calculate_committee_fraction,
|
||||
},
|
||||
ssz_container::SszContainer,
|
||||
};
|
||||
@@ -74,6 +73,7 @@ impl From<VoteTracker> for VoteTrackerV28 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Spec's `LatestMessage` type. Only used in tests.
|
||||
pub struct LatestMessage {
|
||||
pub slot: Slot,
|
||||
pub root: Hash256,
|
||||
@@ -527,7 +527,6 @@ impl ProtoArrayForkChoice {
|
||||
prune_threshold: DEFAULT_PRUNE_THRESHOLD,
|
||||
nodes: Vec::with_capacity(1),
|
||||
indices: HashMap::with_capacity(1),
|
||||
previous_proposer_boost: ProposerBoost::default(),
|
||||
};
|
||||
|
||||
let block = Block {
|
||||
@@ -569,11 +568,18 @@ impl ProtoArrayForkChoice {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn on_execution_payload(&mut self, block_root: Hash256) -> Result<(), String> {
|
||||
/// Mark a Gloas payload envelope as valid and received.
|
||||
///
|
||||
/// This must only be called for valid Gloas payloads.
|
||||
pub fn on_valid_payload_envelope_received(
|
||||
&mut self,
|
||||
block_root: Hash256,
|
||||
) -> Result<(), String> {
|
||||
self.proto_array
|
||||
.on_valid_execution_payload(block_root)
|
||||
.on_valid_payload_envelope_received(block_root)
|
||||
.map_err(|e| format!("Failed to process execution payload: {:?}", e))
|
||||
}
|
||||
|
||||
/// See `ProtoArray::propagate_execution_payload_validation` for documentation.
|
||||
pub fn process_execution_payload_validation(
|
||||
&mut self,
|
||||
@@ -880,10 +886,7 @@ impl ProtoArrayForkChoice {
|
||||
/// status to be optimistic.
|
||||
///
|
||||
/// In practice this means forgetting any `VALID` or `INVALID` statuses.
|
||||
pub fn set_all_blocks_to_optimistic<E: EthSpec>(
|
||||
&mut self,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), String> {
|
||||
pub fn set_all_blocks_to_optimistic<E: EthSpec>(&mut self) -> Result<(), String> {
|
||||
// Iterate backwards through all nodes in the `proto_array`. Whilst it's not strictly
|
||||
// required to do this process in reverse, it seems natural when we consider how LMD votes
|
||||
// are counted.
|
||||
@@ -906,7 +909,7 @@ impl ProtoArrayForkChoice {
|
||||
|
||||
// Restore the weight of the node, it would have been set to `0` in
|
||||
// `apply_score_changes` when it was invalidated.
|
||||
let mut restored_weight: u64 = self
|
||||
let restored_weight: u64 = self
|
||||
.votes
|
||||
.0
|
||||
.iter()
|
||||
@@ -922,26 +925,6 @@ impl ProtoArrayForkChoice {
|
||||
})
|
||||
.sum();
|
||||
|
||||
// If the invalid root was boosted, apply the weight to it and
|
||||
// ancestors.
|
||||
if let Some(proposer_score_boost) = spec.proposer_score_boost
|
||||
&& self.proto_array.previous_proposer_boost.root == node.root()
|
||||
{
|
||||
// Compute the score based upon the current balances. We can't rely on
|
||||
// the `previous_proposr_boost.score` since it is set to zero with an
|
||||
// invalid node.
|
||||
let proposer_score =
|
||||
calculate_committee_fraction::<E>(&self.balances, proposer_score_boost)
|
||||
.ok_or("Failed to compute proposer boost")?;
|
||||
// Store the score we've applied here so it can be removed in
|
||||
// a later call to `apply_score_changes`.
|
||||
self.proto_array.previous_proposer_boost.score = proposer_score;
|
||||
// Apply this boost to this node.
|
||||
restored_weight = restored_weight
|
||||
.checked_add(proposer_score)
|
||||
.ok_or("Overflow when adding boost to weight")?;
|
||||
}
|
||||
|
||||
// Add the restored weight to the node and all ancestors.
|
||||
if restored_weight > 0 {
|
||||
let mut node_or_ancestor = node;
|
||||
@@ -1082,10 +1065,9 @@ impl ProtoArrayForkChoice {
|
||||
.is_finalized_checkpoint_or_descendant::<E>(descendant_root, best_finalized_checkpoint)
|
||||
}
|
||||
|
||||
/// NOTE: only used in tests.
|
||||
pub fn latest_message(&self, validator_index: usize) -> Option<LatestMessage> {
|
||||
if validator_index < self.votes.0.len() {
|
||||
let vote = &self.votes.0[validator_index];
|
||||
|
||||
if let Some(vote) = self.votes.0.get(validator_index) {
|
||||
if *vote == VoteTracker::default() {
|
||||
None
|
||||
} else {
|
||||
|
||||
@@ -38,6 +38,7 @@ pub struct SszContainer {
|
||||
#[superstruct(only(V29))]
|
||||
pub nodes: Vec<ProtoNode>,
|
||||
pub indices: Vec<(Hash256, usize)>,
|
||||
#[superstruct(only(V28))]
|
||||
pub previous_proposer_boost: ProposerBoost,
|
||||
}
|
||||
|
||||
@@ -50,7 +51,6 @@ impl SszContainerV29 {
|
||||
prune_threshold: proto_array.prune_threshold,
|
||||
nodes: proto_array.nodes.clone(),
|
||||
indices: proto_array.indices.iter().map(|(k, v)| (*k, *v)).collect(),
|
||||
previous_proposer_boost: proto_array.previous_proposer_boost,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,6 @@ impl TryFrom<(SszContainerV29, JustifiedBalances)> for ProtoArrayForkChoice {
|
||||
prune_threshold: from.prune_threshold,
|
||||
nodes: from.nodes,
|
||||
indices: from.indices.into_iter().collect::<HashMap<_, _>>(),
|
||||
previous_proposer_boost: from.previous_proposer_boost,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
@@ -92,7 +91,6 @@ impl From<SszContainerV28> for SszContainerV29 {
|
||||
})
|
||||
.collect(),
|
||||
indices: v28.indices,
|
||||
previous_proposer_boost: v28.previous_proposer_boost,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,7 +114,8 @@ impl From<SszContainerV29> for SszContainerV28 {
|
||||
})
|
||||
.collect(),
|
||||
indices: v29.indices,
|
||||
previous_proposer_boost: v29.previous_proposer_boost,
|
||||
// Proposer boost is not tracked in V29 (computed on-the-fly), so reset it.
|
||||
previous_proposer_boost: ProposerBoost::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::test_utils::TestRandom;
|
||||
use crate::{EthSpec, ForkName, PayloadAttestationData};
|
||||
use bls::AggregateSignature;
|
||||
use context_deserialize::context_deserialize;
|
||||
use core::slice::Iter;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use ssz_types::VariableList;
|
||||
@@ -21,12 +20,6 @@ pub struct IndexedPayloadAttestation<E: EthSpec> {
|
||||
pub signature: AggregateSignature,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> IndexedPayloadAttestation<E> {
|
||||
pub fn attesting_indices_iter(&self) -> Iter<'_, u64> {
|
||||
self.attesting_indices.iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -107,6 +107,8 @@ pub struct ChainSpec {
|
||||
pub shard_committee_period: u64,
|
||||
pub proposer_reorg_cutoff_bps: u64,
|
||||
pub attestation_due_bps: u64,
|
||||
pub attestation_due_bps_gloas: u64,
|
||||
pub payload_attestation_due_bps: u64,
|
||||
pub aggregate_due_bps: u64,
|
||||
pub sync_message_due_bps: u64,
|
||||
pub contribution_due_bps: u64,
|
||||
@@ -115,6 +117,8 @@ pub struct ChainSpec {
|
||||
* Derived time values (computed at startup via `compute_derived_values()`)
|
||||
*/
|
||||
pub unaggregated_attestation_due: Duration,
|
||||
pub unaggregated_attestation_due_gloas: Duration,
|
||||
pub payload_attestation_due: Duration,
|
||||
pub aggregate_attestation_due: Duration,
|
||||
pub sync_message_due: Duration,
|
||||
pub contribution_and_proof_due: Duration,
|
||||
@@ -877,6 +881,20 @@ impl ChainSpec {
|
||||
self.unaggregated_attestation_due
|
||||
}
|
||||
|
||||
/// Spec: `get_attestation_due_ms`. Returns the epoch-appropriate threshold.
|
||||
pub fn get_attestation_due<E: EthSpec>(&self, slot: Slot) -> Duration {
|
||||
if self.fork_name_at_slot::<E>(slot).gloas_enabled() {
|
||||
self.unaggregated_attestation_due_gloas
|
||||
} else {
|
||||
self.unaggregated_attestation_due
|
||||
}
|
||||
}
|
||||
|
||||
/// Spec: `get_payload_attestation_due_ms`.
|
||||
pub fn get_payload_attestation_due(&self) -> Duration {
|
||||
self.payload_attestation_due
|
||||
}
|
||||
|
||||
/// Get the duration into a slot in which an aggregated attestation is due.
|
||||
/// Returns the pre-computed value from `compute_derived_values()`.
|
||||
pub fn get_aggregate_attestation_due(&self) -> Duration {
|
||||
@@ -949,6 +967,12 @@ impl ChainSpec {
|
||||
self.unaggregated_attestation_due = self
|
||||
.compute_slot_component_duration(self.attestation_due_bps)
|
||||
.expect("invalid chain spec: cannot compute unaggregated_attestation_due");
|
||||
self.unaggregated_attestation_due_gloas = self
|
||||
.compute_slot_component_duration(self.attestation_due_bps_gloas)
|
||||
.expect("invalid chain spec: cannot compute unaggregated_attestation_due_gloas");
|
||||
self.payload_attestation_due = self
|
||||
.compute_slot_component_duration(self.payload_attestation_due_bps)
|
||||
.expect("invalid chain spec: cannot compute payload_attestation_due");
|
||||
self.aggregate_attestation_due = self
|
||||
.compute_slot_component_duration(self.aggregate_due_bps)
|
||||
.expect("invalid chain spec: cannot compute aggregate_attestation_due");
|
||||
@@ -1079,6 +1103,8 @@ impl ChainSpec {
|
||||
shard_committee_period: 256,
|
||||
proposer_reorg_cutoff_bps: 1667,
|
||||
attestation_due_bps: 3333,
|
||||
attestation_due_bps_gloas: 2500,
|
||||
payload_attestation_due_bps: 7500,
|
||||
aggregate_due_bps: 6667,
|
||||
sync_message_due_bps: 3333,
|
||||
contribution_due_bps: 6667,
|
||||
@@ -1087,6 +1113,8 @@ impl ChainSpec {
|
||||
* Derived time values (set by `compute_derived_values()`)
|
||||
*/
|
||||
unaggregated_attestation_due: Duration::from_millis(3999),
|
||||
unaggregated_attestation_due_gloas: Duration::from_millis(3000),
|
||||
payload_attestation_due: Duration::from_millis(9000),
|
||||
aggregate_attestation_due: Duration::from_millis(8000),
|
||||
sync_message_due: Duration::from_millis(3999),
|
||||
contribution_and_proof_due: Duration::from_millis(8000),
|
||||
@@ -1390,6 +1418,8 @@ impl ChainSpec {
|
||||
* Precomputed for 6000ms slot: 3333 bps = 1999ms, 6667 bps = 4000ms
|
||||
*/
|
||||
unaggregated_attestation_due: Duration::from_millis(1999),
|
||||
unaggregated_attestation_due_gloas: Duration::from_millis(1500),
|
||||
payload_attestation_due: Duration::from_millis(4500),
|
||||
aggregate_attestation_due: Duration::from_millis(4000),
|
||||
sync_message_due: Duration::from_millis(1999),
|
||||
contribution_and_proof_due: Duration::from_millis(4000),
|
||||
@@ -1479,6 +1509,8 @@ impl ChainSpec {
|
||||
shard_committee_period: 256,
|
||||
proposer_reorg_cutoff_bps: 1667,
|
||||
attestation_due_bps: 3333,
|
||||
attestation_due_bps_gloas: 2500,
|
||||
payload_attestation_due_bps: 7500,
|
||||
aggregate_due_bps: 6667,
|
||||
|
||||
/*
|
||||
@@ -1486,6 +1518,8 @@ impl ChainSpec {
|
||||
* Precomputed for 5000ms slot: 3333 bps = 1666ms, 6667 bps = 3333ms
|
||||
*/
|
||||
unaggregated_attestation_due: Duration::from_millis(1666),
|
||||
unaggregated_attestation_due_gloas: Duration::from_millis(1250),
|
||||
payload_attestation_due: Duration::from_millis(3750),
|
||||
aggregate_attestation_due: Duration::from_millis(3333),
|
||||
sync_message_due: Duration::from_millis(1666),
|
||||
contribution_and_proof_due: Duration::from_millis(3333),
|
||||
@@ -2062,6 +2096,12 @@ pub struct Config {
|
||||
#[serde(default = "default_attestation_due_bps")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
attestation_due_bps: u64,
|
||||
#[serde(default = "default_attestation_due_bps_gloas")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
attestation_due_bps_gloas: u64,
|
||||
#[serde(default = "default_payload_attestation_due_bps")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
payload_attestation_due_bps: u64,
|
||||
#[serde(default = "default_aggregate_due_bps")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
aggregate_due_bps: u64,
|
||||
@@ -2288,6 +2328,14 @@ const fn default_attestation_due_bps() -> u64 {
|
||||
3333
|
||||
}
|
||||
|
||||
const fn default_attestation_due_bps_gloas() -> u64 {
|
||||
2500
|
||||
}
|
||||
|
||||
const fn default_payload_attestation_due_bps() -> u64 {
|
||||
7500
|
||||
}
|
||||
|
||||
const fn default_aggregate_due_bps() -> u64 {
|
||||
6667
|
||||
}
|
||||
@@ -2539,6 +2587,8 @@ impl Config {
|
||||
|
||||
proposer_reorg_cutoff_bps: spec.proposer_reorg_cutoff_bps,
|
||||
attestation_due_bps: spec.attestation_due_bps,
|
||||
attestation_due_bps_gloas: spec.attestation_due_bps_gloas,
|
||||
payload_attestation_due_bps: spec.payload_attestation_due_bps,
|
||||
aggregate_due_bps: spec.aggregate_due_bps,
|
||||
sync_message_due_bps: spec.sync_message_due_bps,
|
||||
contribution_due_bps: spec.contribution_due_bps,
|
||||
@@ -2632,6 +2682,8 @@ impl Config {
|
||||
min_epochs_for_data_column_sidecars_requests,
|
||||
proposer_reorg_cutoff_bps,
|
||||
attestation_due_bps,
|
||||
attestation_due_bps_gloas,
|
||||
payload_attestation_due_bps,
|
||||
aggregate_due_bps,
|
||||
sync_message_due_bps,
|
||||
contribution_due_bps,
|
||||
@@ -2731,6 +2783,8 @@ impl Config {
|
||||
|
||||
proposer_reorg_cutoff_bps,
|
||||
attestation_due_bps,
|
||||
attestation_due_bps_gloas,
|
||||
payload_attestation_due_bps,
|
||||
aggregate_due_bps,
|
||||
sync_message_due_bps,
|
||||
contribution_due_bps,
|
||||
@@ -3634,11 +3688,9 @@ mod yaml_tests {
|
||||
"EIP7928_FORK_VERSION",
|
||||
"EIP7928_FORK_EPOCH",
|
||||
// Gloas params not yet in Config
|
||||
"ATTESTATION_DUE_BPS_GLOAS",
|
||||
"AGGREGATE_DUE_BPS_GLOAS",
|
||||
"SYNC_MESSAGE_DUE_BPS_GLOAS",
|
||||
"CONTRIBUTION_DUE_BPS_GLOAS",
|
||||
"PAYLOAD_ATTESTATION_DUE_BPS",
|
||||
"MAX_REQUEST_PAYLOADS",
|
||||
// Gloas fork choice params not yet in Config
|
||||
"REORG_HEAD_WEIGHT_THRESHOLD",
|
||||
|
||||
@@ -448,6 +448,11 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq +
|
||||
fn payload_timely_threshold() -> usize {
|
||||
Self::PTCSize::to_usize() / 2
|
||||
}
|
||||
|
||||
/// Returns the `DATA_AVAILABILITY_TIMELY_THRESHOLD` constant (PTC_SIZE / 2).
|
||||
fn data_availability_timely_threshold() -> usize {
|
||||
Self::PTCSize::to_usize() / 2
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro to inherit some type values from another EthSpec.
|
||||
|
||||
Reference in New Issue
Block a user