mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-31 21:27:12 +00:00
Resolve merge conflicts
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
use crate::error::InvalidBestNodeInfo;
|
||||
use crate::{error::Error, Block, ExecutionStatus, JustifiedBalances};
|
||||
use crate::{Block, ExecutionStatus, JustifiedBalances, error::Error};
|
||||
use fixed_bytes::FixedBytesExtended;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ssz::four_byte_option_impl;
|
||||
use ssz::Encode;
|
||||
use ssz::four_byte_option_impl;
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use superstruct::superstruct;
|
||||
use tracing::info;
|
||||
use types::{
|
||||
AttestationShufflingId, ChainSpec, Checkpoint, Epoch, EthSpec, ExecutionBlockHash,
|
||||
FixedBytesExtended, Hash256, Slot,
|
||||
AttestationShufflingId, ChainSpec, Checkpoint, Epoch, EthSpec, ExecutionBlockHash, Hash256,
|
||||
Slot,
|
||||
};
|
||||
|
||||
// Define a "legacy" implementation of `Option<usize>` which uses four bytes for encoding the union
|
||||
@@ -131,8 +132,6 @@ pub struct ProtoArray {
|
||||
/// Do not attempt to prune the tree unless it has at least this many nodes. Small prunes
|
||||
/// simply waste time.
|
||||
pub prune_threshold: usize,
|
||||
pub justified_checkpoint: Checkpoint,
|
||||
pub finalized_checkpoint: Checkpoint,
|
||||
pub nodes: Vec<ProtoNode>,
|
||||
pub indices: HashMap<Hash256, usize>,
|
||||
pub unsatisfied_inclusion_list_blocks: HashMap<Slot, Hash256>,
|
||||
@@ -157,8 +156,8 @@ impl ProtoArray {
|
||||
pub fn apply_score_changes<E: EthSpec>(
|
||||
&mut self,
|
||||
mut deltas: Vec<i64>,
|
||||
justified_checkpoint: Checkpoint,
|
||||
finalized_checkpoint: Checkpoint,
|
||||
best_justified_checkpoint: Checkpoint,
|
||||
best_finalized_checkpoint: Checkpoint,
|
||||
new_justified_balances: &JustifiedBalances,
|
||||
proposer_boost_root: Hash256,
|
||||
current_slot: Slot,
|
||||
@@ -171,13 +170,6 @@ impl ProtoArray {
|
||||
});
|
||||
}
|
||||
|
||||
if justified_checkpoint != self.justified_checkpoint
|
||||
|| finalized_checkpoint != self.finalized_checkpoint
|
||||
{
|
||||
self.justified_checkpoint = justified_checkpoint;
|
||||
self.finalized_checkpoint = finalized_checkpoint;
|
||||
}
|
||||
|
||||
// Default the proposer boost score to zero.
|
||||
let mut proposer_score = 0;
|
||||
|
||||
@@ -242,21 +234,18 @@ impl ProtoArray {
|
||||
// the delta by the new score amount (unless the block has an invalid execution status).
|
||||
//
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/fork-choice.md#get_latest_attesting_balance
|
||||
if let Some(proposer_score_boost) = spec.proposer_score_boost {
|
||||
if proposer_boost_root != Hash256::zero()
|
||||
if let Some(proposer_score_boost) = spec.proposer_score_boost
|
||||
&& proposer_boost_root != Hash256::zero()
|
||||
&& proposer_boost_root == node.root
|
||||
// Invalid nodes (or their ancestors) should not receive a proposer boost.
|
||||
&& !execution_status_is_invalid
|
||||
{
|
||||
proposer_score = calculate_committee_fraction::<E>(
|
||||
new_justified_balances,
|
||||
proposer_score_boost,
|
||||
)
|
||||
.ok_or(Error::ProposerBoostOverflow(node_index))?;
|
||||
node_delta = node_delta
|
||||
.checked_add(proposer_score as i64)
|
||||
.ok_or(Error::DeltaOverflow(node_index))?;
|
||||
}
|
||||
{
|
||||
proposer_score =
|
||||
calculate_committee_fraction::<E>(new_justified_balances, proposer_score_boost)
|
||||
.ok_or(Error::ProposerBoostOverflow(node_index))?;
|
||||
node_delta = node_delta
|
||||
.checked_add(proposer_score as i64)
|
||||
.ok_or(Error::DeltaOverflow(node_index))?;
|
||||
}
|
||||
|
||||
// Apply the delta to the node.
|
||||
@@ -318,6 +307,8 @@ impl ProtoArray {
|
||||
parent_index,
|
||||
node_index,
|
||||
current_slot,
|
||||
best_justified_checkpoint,
|
||||
best_finalized_checkpoint,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -328,7 +319,13 @@ impl ProtoArray {
|
||||
/// Register a block with the fork choice.
|
||||
///
|
||||
/// It is only sane to supply a `None` parent for the genesis block.
|
||||
pub fn on_block<E: EthSpec>(&mut self, block: Block, current_slot: Slot) -> Result<(), Error> {
|
||||
pub fn on_block<E: EthSpec>(
|
||||
&mut self,
|
||||
block: Block,
|
||||
current_slot: Slot,
|
||||
best_justified_checkpoint: Checkpoint,
|
||||
best_finalized_checkpoint: Checkpoint,
|
||||
) -> Result<(), Error> {
|
||||
// If the block is already known, simply ignore it.
|
||||
if self.indices.contains_key(&block.root) {
|
||||
return Ok(());
|
||||
@@ -379,6 +376,8 @@ impl ProtoArray {
|
||||
parent_index,
|
||||
node_index,
|
||||
current_slot,
|
||||
best_justified_checkpoint,
|
||||
best_finalized_checkpoint,
|
||||
)?;
|
||||
|
||||
if matches!(block.execution_status, ExecutionStatus::Valid(_)) {
|
||||
@@ -447,7 +446,7 @@ impl ProtoArray {
|
||||
return Err(Error::InvalidAncestorOfValidPayload {
|
||||
ancestor_block_root: node.root,
|
||||
ancestor_payload_block_hash,
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -461,6 +460,7 @@ impl ProtoArray {
|
||||
pub fn propagate_execution_payload_invalidation<E: EthSpec>(
|
||||
&mut self,
|
||||
op: &InvalidationOperation,
|
||||
best_finalized_checkpoint: Checkpoint,
|
||||
) -> Result<(), Error> {
|
||||
let mut invalidated_indices: HashSet<usize> = <_>::default();
|
||||
let head_block_root = op.block_root();
|
||||
@@ -489,7 +489,10 @@ impl ProtoArray {
|
||||
let latest_valid_ancestor_is_descendant =
|
||||
latest_valid_ancestor_root.is_some_and(|ancestor_root| {
|
||||
self.is_descendant(ancestor_root, head_block_root)
|
||||
&& self.is_finalized_checkpoint_or_descendant::<E>(ancestor_root)
|
||||
&& self.is_finalized_checkpoint_or_descendant::<E>(
|
||||
ancestor_root,
|
||||
best_finalized_checkpoint,
|
||||
)
|
||||
});
|
||||
|
||||
// Collect all *ancestors* which were declared invalid since they reside between the
|
||||
@@ -556,7 +559,7 @@ impl ProtoArray {
|
||||
return Err(Error::ValidExecutionStatusBecameInvalid {
|
||||
block_root: node.root,
|
||||
payload_block_hash: *hash,
|
||||
})
|
||||
});
|
||||
}
|
||||
ExecutionStatus::Optimistic(hash) => {
|
||||
invalidated_indices.insert(index);
|
||||
@@ -613,27 +616,27 @@ impl ProtoArray {
|
||||
.get_mut(index)
|
||||
.ok_or(Error::InvalidNodeIndex(index))?;
|
||||
|
||||
if let Some(parent_index) = node.parent {
|
||||
if invalidated_indices.contains(&parent_index) {
|
||||
match &node.execution_status {
|
||||
ExecutionStatus::Valid(hash) => {
|
||||
return Err(Error::ValidExecutionStatusBecameInvalid {
|
||||
block_root: node.root,
|
||||
payload_block_hash: *hash,
|
||||
})
|
||||
}
|
||||
ExecutionStatus::Optimistic(hash) | ExecutionStatus::Invalid(hash) => {
|
||||
node.execution_status = ExecutionStatus::Invalid(*hash)
|
||||
}
|
||||
ExecutionStatus::Irrelevant(_) => {
|
||||
return Err(Error::IrrelevantDescendant {
|
||||
block_root: node.root,
|
||||
})
|
||||
}
|
||||
if let Some(parent_index) = node.parent
|
||||
&& invalidated_indices.contains(&parent_index)
|
||||
{
|
||||
match &node.execution_status {
|
||||
ExecutionStatus::Valid(hash) => {
|
||||
return Err(Error::ValidExecutionStatusBecameInvalid {
|
||||
block_root: node.root,
|
||||
payload_block_hash: *hash,
|
||||
});
|
||||
}
|
||||
ExecutionStatus::Optimistic(hash) | ExecutionStatus::Invalid(hash) => {
|
||||
node.execution_status = ExecutionStatus::Invalid(*hash)
|
||||
}
|
||||
ExecutionStatus::Irrelevant(_) => {
|
||||
return Err(Error::IrrelevantDescendant {
|
||||
block_root: node.root,
|
||||
});
|
||||
}
|
||||
|
||||
invalidated_indices.insert(index);
|
||||
}
|
||||
|
||||
invalidated_indices.insert(index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,6 +655,8 @@ impl ProtoArray {
|
||||
&self,
|
||||
justified_root: &Hash256,
|
||||
current_slot: Slot,
|
||||
best_justified_checkpoint: Checkpoint,
|
||||
best_finalized_checkpoint: Checkpoint,
|
||||
) -> Result<Hash256, Error> {
|
||||
let justified_index = self
|
||||
.indices
|
||||
@@ -685,12 +690,17 @@ impl ProtoArray {
|
||||
.ok_or(Error::InvalidBestDescendant(best_descendant_index))?;
|
||||
|
||||
// Perform a sanity check that the node is indeed valid to be the head.
|
||||
if !self.node_is_viable_for_head::<E>(best_node, current_slot) {
|
||||
if !self.node_is_viable_for_head::<E>(
|
||||
best_node,
|
||||
current_slot,
|
||||
best_justified_checkpoint,
|
||||
best_finalized_checkpoint,
|
||||
) {
|
||||
return Err(Error::InvalidBestNode(Box::new(InvalidBestNodeInfo {
|
||||
current_slot,
|
||||
start_root: *justified_root,
|
||||
justified_checkpoint: self.justified_checkpoint,
|
||||
finalized_checkpoint: self.finalized_checkpoint,
|
||||
justified_checkpoint: best_justified_checkpoint,
|
||||
finalized_checkpoint: best_finalized_checkpoint,
|
||||
head_root: best_node.root,
|
||||
head_justified_checkpoint: best_node.justified_checkpoint,
|
||||
head_finalized_checkpoint: best_node.finalized_checkpoint,
|
||||
@@ -787,6 +797,8 @@ impl ProtoArray {
|
||||
parent_index: usize,
|
||||
child_index: usize,
|
||||
current_slot: Slot,
|
||||
best_justified_checkpoint: Checkpoint,
|
||||
best_finalized_checkpoint: Checkpoint,
|
||||
) -> Result<(), Error> {
|
||||
let child = self
|
||||
.nodes
|
||||
@@ -798,8 +810,12 @@ impl ProtoArray {
|
||||
.get(parent_index)
|
||||
.ok_or(Error::InvalidNodeIndex(parent_index))?;
|
||||
|
||||
let child_leads_to_viable_head =
|
||||
self.node_leads_to_viable_head::<E>(child, current_slot)?;
|
||||
let child_leads_to_viable_head = self.node_leads_to_viable_head::<E>(
|
||||
child,
|
||||
current_slot,
|
||||
best_justified_checkpoint,
|
||||
best_finalized_checkpoint,
|
||||
)?;
|
||||
|
||||
// These three variables are aliases to the three options that we may set the
|
||||
// `parent.best_child` and `parent.best_descendant` to.
|
||||
@@ -828,8 +844,12 @@ impl ProtoArray {
|
||||
.get(best_child_index)
|
||||
.ok_or(Error::InvalidBestDescendant(best_child_index))?;
|
||||
|
||||
let best_child_leads_to_viable_head =
|
||||
self.node_leads_to_viable_head::<E>(best_child, current_slot)?;
|
||||
let best_child_leads_to_viable_head = self.node_leads_to_viable_head::<E>(
|
||||
best_child,
|
||||
current_slot,
|
||||
best_justified_checkpoint,
|
||||
best_finalized_checkpoint,
|
||||
)?;
|
||||
|
||||
if child_leads_to_viable_head && !best_child_leads_to_viable_head {
|
||||
// The child leads to a viable head, but the current best-child doesn't.
|
||||
@@ -878,6 +898,8 @@ impl ProtoArray {
|
||||
&self,
|
||||
node: &ProtoNode,
|
||||
current_slot: Slot,
|
||||
best_justified_checkpoint: Checkpoint,
|
||||
best_finalized_checkpoint: Checkpoint,
|
||||
) -> Result<bool, Error> {
|
||||
let best_descendant_is_viable_for_head =
|
||||
if let Some(best_descendant_index) = node.best_descendant {
|
||||
@@ -886,13 +908,23 @@ impl ProtoArray {
|
||||
.get(best_descendant_index)
|
||||
.ok_or(Error::InvalidBestDescendant(best_descendant_index))?;
|
||||
|
||||
self.node_is_viable_for_head::<E>(best_descendant, current_slot)
|
||||
self.node_is_viable_for_head::<E>(
|
||||
best_descendant,
|
||||
current_slot,
|
||||
best_justified_checkpoint,
|
||||
best_finalized_checkpoint,
|
||||
)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
Ok(best_descendant_is_viable_for_head
|
||||
|| self.node_is_viable_for_head::<E>(node, current_slot))
|
||||
|| self.node_is_viable_for_head::<E>(
|
||||
node,
|
||||
current_slot,
|
||||
best_justified_checkpoint,
|
||||
best_finalized_checkpoint,
|
||||
))
|
||||
}
|
||||
|
||||
/// This is the equivalent to the `filter_block_tree` function in the eth2 spec:
|
||||
@@ -901,7 +933,13 @@ impl ProtoArray {
|
||||
///
|
||||
/// Any node that has a different finalized or justified epoch should not be viable for the
|
||||
/// head.
|
||||
fn node_is_viable_for_head<E: EthSpec>(&self, node: &ProtoNode, current_slot: Slot) -> bool {
|
||||
fn node_is_viable_for_head<E: EthSpec>(
|
||||
&self,
|
||||
node: &ProtoNode,
|
||||
current_slot: Slot,
|
||||
best_justified_checkpoint: Checkpoint,
|
||||
best_finalized_checkpoint: Checkpoint,
|
||||
) -> bool {
|
||||
if node.execution_status.is_invalid() {
|
||||
return false;
|
||||
}
|
||||
@@ -938,12 +976,13 @@ impl ProtoArray {
|
||||
node_justified_checkpoint
|
||||
};
|
||||
|
||||
let correct_justified = self.justified_checkpoint.epoch == genesis_epoch
|
||||
|| voting_source.epoch == self.justified_checkpoint.epoch
|
||||
let correct_justified = best_justified_checkpoint.epoch == genesis_epoch
|
||||
|| voting_source.epoch == best_justified_checkpoint.epoch
|
||||
|| voting_source.epoch + 2 >= current_epoch;
|
||||
|
||||
let correct_finalized = self.finalized_checkpoint.epoch == genesis_epoch
|
||||
|| self.is_finalized_checkpoint_or_descendant::<E>(node.root);
|
||||
let correct_finalized = best_finalized_checkpoint.epoch == genesis_epoch
|
||||
|| self
|
||||
.is_finalized_checkpoint_or_descendant::<E>(node.root, best_finalized_checkpoint);
|
||||
|
||||
correct_justified && correct_finalized
|
||||
}
|
||||
@@ -998,10 +1037,13 @@ impl ProtoArray {
|
||||
///
|
||||
/// Notably, this function is checking ancestory of the finalized
|
||||
/// *checkpoint* not the finalized *block*.
|
||||
pub fn is_finalized_checkpoint_or_descendant<E: EthSpec>(&self, root: Hash256) -> bool {
|
||||
let finalized_root = self.finalized_checkpoint.root;
|
||||
let finalized_slot = self
|
||||
.finalized_checkpoint
|
||||
pub fn is_finalized_checkpoint_or_descendant<E: EthSpec>(
|
||||
&self,
|
||||
root: Hash256,
|
||||
best_finalized_checkpoint: Checkpoint,
|
||||
) -> bool {
|
||||
let finalized_root = best_finalized_checkpoint.root;
|
||||
let finalized_slot = best_finalized_checkpoint
|
||||
.epoch
|
||||
.start_slot(E::slots_per_epoch());
|
||||
|
||||
@@ -1024,7 +1066,7 @@ impl ProtoArray {
|
||||
// If the conditions don't match for this node then they're unlikely to
|
||||
// start matching for its ancestors.
|
||||
for checkpoint in &[node.finalized_checkpoint, node.justified_checkpoint] {
|
||||
if checkpoint == &self.finalized_checkpoint {
|
||||
if checkpoint == &best_finalized_checkpoint {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1033,7 +1075,7 @@ impl ProtoArray {
|
||||
node.unrealized_finalized_checkpoint,
|
||||
node.unrealized_justified_checkpoint,
|
||||
] {
|
||||
if checkpoint.is_some_and(|cp| cp == self.finalized_checkpoint) {
|
||||
if checkpoint.is_some_and(|cp| cp == best_finalized_checkpoint) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1081,12 +1123,18 @@ impl ProtoArray {
|
||||
/// For informational purposes like the beacon HTTP API, we use this as the list of known heads,
|
||||
/// even though some of them might not be viable. We do this to maintain consistency between the
|
||||
/// definition of "head" used by pruning (which does not consider viability) and fork choice.
|
||||
pub fn heads_descended_from_finalization<E: EthSpec>(&self) -> Vec<&ProtoNode> {
|
||||
pub fn heads_descended_from_finalization<E: EthSpec>(
|
||||
&self,
|
||||
best_finalized_checkpoint: Checkpoint,
|
||||
) -> Vec<&ProtoNode> {
|
||||
self.nodes
|
||||
.iter()
|
||||
.filter(|node| {
|
||||
node.best_child.is_none()
|
||||
&& self.is_finalized_checkpoint_or_descendant::<E>(node.root)
|
||||
&& self.is_finalized_checkpoint_or_descendant::<E>(
|
||||
node.root,
|
||||
best_finalized_checkpoint,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user