Compare commits

...

1 Commits

Author SHA1 Message Date
Michael Sproul
6a8888991b Start prototyping ePBS fork choice 2025-12-11 12:33:39 +11:00
6 changed files with 59 additions and 16 deletions

View File

@@ -2,7 +2,7 @@ use crate::metrics::{self, scrape_for_metrics};
use crate::{ForkChoiceStore, InvalidationOperation}; use crate::{ForkChoiceStore, InvalidationOperation};
use logging::crit; use logging::crit;
use proto_array::{ use proto_array::{
Block as ProtoBlock, DisallowedReOrgOffsets, ExecutionStatus, JustifiedBalances, Block as ProtoBlock, DisallowedReOrgOffsets, ExecutionStatus, JustifiedBalances, LatestMessage,
ProposerHeadError, ProposerHeadInfo, ProtoArrayForkChoice, ReOrgThreshold, ProposerHeadError, ProposerHeadInfo, ProtoArrayForkChoice, ReOrgThreshold,
}; };
use ssz::{Decode, Encode}; use ssz::{Decode, Encode};
@@ -240,6 +240,7 @@ pub struct QueuedAttestation {
attesting_indices: Vec<u64>, attesting_indices: Vec<u64>,
block_root: Hash256, block_root: Hash256,
target_epoch: Epoch, target_epoch: Epoch,
payload_present: bool,
} }
impl<'a, E: EthSpec> From<IndexedAttestationRef<'a, E>> for QueuedAttestation { impl<'a, E: EthSpec> From<IndexedAttestationRef<'a, E>> for QueuedAttestation {
@@ -249,6 +250,8 @@ impl<'a, E: EthSpec> From<IndexedAttestationRef<'a, E>> for QueuedAttestation {
attesting_indices: a.attesting_indices_to_vec(), attesting_indices: a.attesting_indices_to_vec(),
block_root: a.data().beacon_block_root, block_root: a.data().beacon_block_root,
target_epoch: a.data().target.epoch, target_epoch: a.data().target.epoch,
// FIXME(sproul): replace by func?
payload_present: a.data().index == 1,
} }
} }
} }
@@ -1101,10 +1104,13 @@ where
if attestation.data().slot < self.fc_store.get_current_slot() { if attestation.data().slot < self.fc_store.get_current_slot() {
for validator_index in attestation.attesting_indices_iter() { for validator_index in attestation.attesting_indices_iter() {
// FIXME(sproul): backwards compat/fork abstraction
let payload_present = attestation.data().index == 1;
self.proto_array.process_attestation( self.proto_array.process_attestation(
*validator_index as usize, *validator_index as usize,
attestation.data().beacon_block_root, attestation.data().beacon_block_root,
attestation.data().target.epoch, attestation.data().slot,
payload_present,
)?; )?;
} }
} else { } else {
@@ -1224,10 +1230,12 @@ where
&mut self.queued_attestations, &mut self.queued_attestations,
) { ) {
for validator_index in attestation.attesting_indices.iter() { for validator_index in attestation.attesting_indices.iter() {
// FIXME(sproul): backwards compat/fork abstraction
self.proto_array.process_attestation( self.proto_array.process_attestation(
*validator_index as usize, *validator_index as usize,
attestation.block_root, attestation.block_root,
attestation.target_epoch, attestation.slot,
attestation.payload_present,
)?; )?;
} }
} }
@@ -1357,13 +1365,15 @@ where
/// Returns the latest message for a given validator, if any. /// Returns the latest message for a given validator, if any.
/// ///
/// Returns `(block_root, block_slot)`. /// Returns `block_root, block_slot, payload_present`.
/// ///
/// ## Notes /// ## Notes
/// ///
/// It may be prudent to call `Self::update_time` before calling this function, /// It may be prudent to call `Self::update_time` before calling this function,
/// since some attestations might be queued and awaiting processing. /// since some attestations might be queued and awaiting processing.
pub fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Epoch)> { ///
/// This function is only used in tests.
pub fn latest_message(&self, validator_index: usize) -> Option<LatestMessage> {
self.proto_array.latest_message(validator_index) self.proto_array.latest_message(validator_index)
} }

View File

@@ -1,3 +1,4 @@
/* FIXME(sproul)
use proto_array::fork_choice_test_definition::*; use proto_array::fork_choice_test_definition::*;
use std::fs::File; use std::fs::File;
@@ -24,3 +25,5 @@ fn write_test_def_to_yaml(filename: &str, def: ForkChoiceTestDefinition) {
let file = File::create(filename).expect("Should be able to open file"); let file = File::create(filename).expect("Should be able to open file");
serde_yaml::to_writer(file, &def).expect("Should be able to write YAML to file"); serde_yaml::to_writer(file, &def).expect("Should be able to write YAML to file");
} }
*/
fn main() {}

View File

@@ -1,3 +1,4 @@
/* FIXME(sproul) fix these tests later
mod execution_status; mod execution_status;
mod ffg_updates; mod ffg_updates;
mod no_votes; mod no_votes;
@@ -226,13 +227,14 @@ impl ForkChoiceTestDefinition {
}); });
check_bytes_round_trip(&fork_choice); check_bytes_round_trip(&fork_choice);
} }
// FIXME(sproul): update with payload_present
Operation::ProcessAttestation { Operation::ProcessAttestation {
validator_index, validator_index,
block_root, block_root,
target_epoch, target_epoch,
} => { } => {
fork_choice fork_choice
.process_attestation(validator_index, block_root, target_epoch) .process_attestation(validator_index, block_root, target_epoch, false)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
panic!( panic!(
"process_attestation op at index {} returned error", "process_attestation op at index {} returned error",
@@ -322,3 +324,4 @@ fn check_bytes_round_trip(original: &ProtoArrayForkChoice) {
"fork choice should encode and decode without change" "fork choice should encode and decode without change"
); );
} }
*/

View File

@@ -8,8 +8,8 @@ mod ssz_container;
pub use crate::justified_balances::JustifiedBalances; pub use crate::justified_balances::JustifiedBalances;
pub use crate::proto_array::{InvalidationOperation, calculate_committee_fraction}; pub use crate::proto_array::{InvalidationOperation, calculate_committee_fraction};
pub use crate::proto_array_fork_choice::{ pub use crate::proto_array_fork_choice::{
Block, DisallowedReOrgOffsets, DoNotReOrg, ExecutionStatus, ProposerHeadError, Block, DisallowedReOrgOffsets, DoNotReOrg, ExecutionStatus, LatestMessage, PayloadStatus,
ProposerHeadInfo, ProtoArrayForkChoice, ReOrgThreshold, ProposerHeadError, ProposerHeadInfo, ProtoArrayForkChoice, ReOrgThreshold,
}; };
pub use error::Error; pub use error::Error;

View File

@@ -102,7 +102,7 @@ pub struct ProtoNode {
#[ssz(with = "four_byte_option_usize")] #[ssz(with = "four_byte_option_usize")]
pub best_descendant: Option<usize>, pub best_descendant: Option<usize>,
/// Indicates if an execution node has marked this block as valid. Also contains the execution /// Indicates if an execution node has marked this block as valid. Also contains the execution
/// block hash. /// block hash. This is only used pre-Gloas.
pub execution_status: ExecutionStatus, pub execution_status: ExecutionStatus,
#[ssz(with = "four_byte_option_checkpoint")] #[ssz(with = "four_byte_option_checkpoint")]
pub unrealized_justified_checkpoint: Option<Checkpoint>, pub unrealized_justified_checkpoint: Option<Checkpoint>,

View File

@@ -22,13 +22,23 @@ use types::{
pub const DEFAULT_PRUNE_THRESHOLD: usize = 256; pub const DEFAULT_PRUNE_THRESHOLD: usize = 256;
#[derive(Default, PartialEq, Clone, Encode, Decode)] #[derive(Default, PartialEq, Clone, Encode, Decode)]
// FIXME(sproul): the "next" naming here is a bit odd
// FIXME(sproul): version this type?
pub struct VoteTracker { pub struct VoteTracker {
current_root: Hash256, current_root: Hash256,
next_root: Hash256, next_root: Hash256,
next_epoch: Epoch, next_slot: Slot,
next_payload_present: bool,
} }
/// Represents the verification status of an execution payload. // FIXME(sproul): version this type
pub struct LatestMessage {
slot: Slot,
root: Hash256,
payload_present: bool,
}
/// Represents the verification status of an execution payload pre-Gloas.
#[derive(Clone, Copy, Debug, PartialEq, Encode, Decode, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Encode, Decode, Serialize, Deserialize)]
#[ssz(enum_behaviour = "union")] #[ssz(enum_behaviour = "union")]
pub enum ExecutionStatus { pub enum ExecutionStatus {
@@ -48,6 +58,16 @@ pub enum ExecutionStatus {
Irrelevant(bool), Irrelevant(bool),
} }
/// Represents the status of an execution payload post-Gloas.
#[derive(Clone, Copy, Debug, PartialEq, Encode, Decode, Serialize, Deserialize)]
#[ssz(enum_behaviour = "tag")]
#[repr(u8)]
pub enum PayloadStatus {
Pending = 0,
Empty = 1,
Full = 2,
}
impl ExecutionStatus { impl ExecutionStatus {
pub fn is_execution_enabled(&self) -> bool { pub fn is_execution_enabled(&self) -> bool {
!matches!(self, ExecutionStatus::Irrelevant(_)) !matches!(self, ExecutionStatus::Irrelevant(_))
@@ -487,13 +507,15 @@ impl ProtoArrayForkChoice {
&mut self, &mut self,
validator_index: usize, validator_index: usize,
block_root: Hash256, block_root: Hash256,
target_epoch: Epoch, attestation_slot: Slot,
payload_present: bool,
) -> Result<(), String> { ) -> Result<(), String> {
let vote = self.votes.get_mut(validator_index); let vote = self.votes.get_mut(validator_index);
if target_epoch > vote.next_epoch || *vote == VoteTracker::default() { if attestation_slot > vote.next_slot || *vote == VoteTracker::default() {
vote.next_root = block_root; vote.next_root = block_root;
vote.next_epoch = target_epoch; vote.next_slot = attestation_slot;
vote.next_payload_present = payload_present;
} }
Ok(()) Ok(())
@@ -906,14 +928,18 @@ impl ProtoArrayForkChoice {
.is_finalized_checkpoint_or_descendant::<E>(descendant_root, best_finalized_checkpoint) .is_finalized_checkpoint_or_descendant::<E>(descendant_root, best_finalized_checkpoint)
} }
pub fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Epoch)> { pub fn latest_message(&self, validator_index: usize) -> Option<LatestMessage> {
if validator_index < self.votes.0.len() { if validator_index < self.votes.0.len() {
let vote = &self.votes.0[validator_index]; let vote = &self.votes.0[validator_index];
if *vote == VoteTracker::default() { if *vote == VoteTracker::default() {
None None
} else { } else {
Some((vote.next_root, vote.next_epoch)) Some(LatestMessage {
root: vote.next_root,
slot: vote.next_slot,
payload_present: vote.next_payload_present,
})
} }
} else { } else {
None None
@@ -999,6 +1025,7 @@ impl ProtoArrayForkChoice {
/// - If a value in `indices` is greater to or equal to `indices.len()`. /// - If a value in `indices` is greater to or equal to `indices.len()`.
/// - If some `Hash256` in `votes` is not a key in `indices` (except for `Hash256::zero()`, this is /// - If some `Hash256` in `votes` is not a key in `indices` (except for `Hash256::zero()`, this is
/// always valid). /// always valid).
// FIXME(sproul): implement get-weight changes here
fn compute_deltas( fn compute_deltas(
indices: &HashMap<Hash256, usize>, indices: &HashMap<Hash256, usize>,
votes: &mut ElasticList<VoteTracker>, votes: &mut ElasticList<VoteTracker>,