Fix more issues (mainly payload attestation application)

This commit is contained in:
Michael Sproul
2026-05-11 22:04:24 +10:00
parent ed4de4ee52
commit df9399e957
4 changed files with 97 additions and 15 deletions

View File

@@ -685,7 +685,7 @@ impl ProtoArray {
/// Returns `true` if the proposer boost should be kept. Returns `false` if the /// Returns `true` if the proposer boost should be kept. Returns `false` if the
/// boost should be subtracted (invalidated) because the parent is weak and there /// boost should be subtracted (invalidated) because the parent is weak and there
/// are no equivocating blocks at the parent's slot. /// are no equivocating blocks at the parent's slot.
fn should_apply_proposer_boost<E: EthSpec>( pub(crate) fn should_apply_proposer_boost<E: EthSpec>(
&self, &self,
proposer_boost_root: Hash256, proposer_boost_root: Hash256,
justified_balances: &JustifiedBalances, justified_balances: &JustifiedBalances,
@@ -1328,7 +1328,7 @@ impl ProtoArray {
/// Spec: `get_weight`. /// Spec: `get_weight`.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn get_weight<E: EthSpec>( pub(crate) fn get_weight<E: EthSpec>(
&self, &self,
fc_node: &IndexedForkChoiceNode, fc_node: &IndexedForkChoiceNode,
proto_node: &ProtoNode, proto_node: &ProtoNode,

View File

@@ -1089,7 +1089,10 @@ impl ProtoArrayForkChoice {
current_slot: Slot, current_slot: Slot,
justified_checkpoint: Checkpoint, justified_checkpoint: Checkpoint,
finalized_checkpoint: Checkpoint, finalized_checkpoint: Checkpoint,
) -> Result<Vec<(Hash256, u64)>, String> { proposer_boost_root: Hash256,
justified_balances: &JustifiedBalances,
spec: &ChainSpec,
) -> Result<Vec<(Hash256, PayloadStatus, u64)>, String> {
let start_index = self let start_index = self
.proto_array .proto_array
.indices .indices
@@ -1107,6 +1110,12 @@ impl ProtoArrayForkChoice {
justified_checkpoint, justified_checkpoint,
finalized_checkpoint, finalized_checkpoint,
); );
let apply_proposer_boost = self
.proto_array
.should_apply_proposer_boost::<E>(proposer_boost_root, justified_balances, spec)
.map_err(|e| format!("should_apply_proposer_boost failed: {e:?}"))?;
let mut leaves = Vec::with_capacity(viable.len()); let mut leaves = Vec::with_capacity(viable.len());
for &i in &viable { for &i in &viable {
let has_viable_child = viable let has_viable_child = viable
@@ -1120,7 +1129,70 @@ impl ProtoArrayForkChoice {
.nodes .nodes
.get(i) .get(i)
.ok_or_else(|| format!("invalid viable node index {i}"))?; .ok_or_else(|| format!("invalid viable node index {i}"))?;
leaves.push((node.root(), node.weight()));
let is_gloas = node.payload_received().is_ok();
if is_gloas {
// Gloas: expand into Empty/Full virtual children.
let empty_fc = IndexedForkChoiceNode {
root: node.root(),
proto_node_index: i,
payload_status: PayloadStatus::Empty,
};
let empty_weight = self
.proto_array
.get_weight::<E>(
&empty_fc,
node,
apply_proposer_boost,
proposer_boost_root,
current_slot,
justified_balances,
spec,
)
.map_err(|e| format!("get_weight failed: {e:?}"))?;
leaves.push((node.root(), PayloadStatus::Empty, empty_weight));
if node.payload_received().is_ok_and(|r| r) {
let full_fc = IndexedForkChoiceNode {
root: node.root(),
proto_node_index: i,
payload_status: PayloadStatus::Full,
};
let full_weight = self
.proto_array
.get_weight::<E>(
&full_fc,
node,
apply_proposer_boost,
proposer_boost_root,
current_slot,
justified_balances,
spec,
)
.map_err(|e| format!("get_weight failed: {e:?}"))?;
leaves.push((node.root(), PayloadStatus::Full, full_weight));
}
} else {
// Pre-Gloas: use Pending status (no payload split).
let fc_node = IndexedForkChoiceNode {
root: node.root(),
proto_node_index: i,
payload_status: PayloadStatus::Pending,
};
let weight = self
.proto_array
.get_weight::<E>(
&fc_node,
node,
apply_proposer_boost,
proposer_boost_root,
current_slot,
justified_balances,
spec,
)
.map_err(|e| format!("get_weight failed: {e:?}"))?;
leaves.push((node.root(), PayloadStatus::Pending, weight));
}
} }
Ok(leaves) Ok(leaves)
} }

View File

@@ -1,7 +1,8 @@
use super::*; use super::*;
use crate::decode::{ssz_decode_file, ssz_decode_file_with, ssz_decode_state, yaml_decode_file}; use crate::decode::{ssz_decode_file, ssz_decode_file_with, ssz_decode_state, yaml_decode_file};
use ::fork_choice::{ use ::fork_choice::{
AttestationFromBlock, ForkChoiceStore, PayloadVerificationStatus, ProposerHeadError, AttestationFromBlock, ForkChoiceStore, PayloadStatus as FcPayloadStatus,
PayloadVerificationStatus, ProposerHeadError,
}; };
use beacon_chain::beacon_proposer_cache::compute_proposer_duties_from_head; use beacon_chain::beacon_proposer_cache::compute_proposer_duties_from_head;
use beacon_chain::blob_verification::GossipBlobError; use beacon_chain::blob_verification::GossipBlobError;
@@ -85,10 +86,10 @@ pub struct Checks {
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RootAndWeight { pub struct RootAndWeight {
pub root: Hash256, pub root: Hash256,
pub weight: u64, pub weight: u64,
pub payload_status: Option<u8>,
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
@@ -624,10 +625,7 @@ impl<E: EthSpec> Tester<E> {
.slot_clock .slot_clock
.set_current_time(Duration::from_secs(tick)); .set_current_time(Duration::from_secs(tick));
// Compute the slot time manually to ensure the slot clock is correct.
let slot = self.tick_to_slot(tick).unwrap(); let slot = self.tick_to_slot(tick).unwrap();
assert_eq!(slot, self.harness.chain.slot().unwrap());
self.harness self.harness
.chain .chain
.canonical_head .canonical_head
@@ -920,11 +918,15 @@ impl<E: EthSpec> Tester<E> {
signature: AggregateSignature::from(&message.signature), signature: AggregateSignature::from(&message.signature),
}; };
let current_slot = self.harness.chain.slot().map_err(|e| {
Error::InternalError(format!("reading current slot failed with {:?}", e))
})?;
self.harness self.harness
.chain .chain
.canonical_head .canonical_head
.fork_choice_write_lock() .fork_choice_write_lock()
.on_payload_attestation(slot, &indexed, AttestationFromBlock::False, &ptc.0) .on_payload_attestation(current_slot, &indexed, AttestationFromBlock::False, &ptc.0)
.map_err(|e| { .map_err(|e| {
Error::InternalError(format!("payload attestation import failed with {:?}", e)) Error::InternalError(format!("payload attestation import failed with {:?}", e))
}) })
@@ -1220,6 +1222,8 @@ impl<E: EthSpec> Tester<E> {
let justified = fork_choice.justified_checkpoint(); let justified = fork_choice.justified_checkpoint();
let finalized = fork_choice.finalized_checkpoint(); let finalized = fork_choice.finalized_checkpoint();
let current_slot = fork_choice.fc_store().get_current_slot(); let current_slot = fork_choice.fc_store().get_current_slot();
let proposer_boost_root = fork_choice.proposer_boost_root();
let justified_balances = fork_choice.fc_store().justified_balances().clone();
let actual = fork_choice let actual = fork_choice
.proto_array() .proto_array()
.filtered_block_tree_leaves_and_weights::<E>( .filtered_block_tree_leaves_and_weights::<E>(
@@ -1227,6 +1231,9 @@ impl<E: EthSpec> Tester<E> {
current_slot, current_slot,
justified, justified,
finalized, finalized,
proposer_boost_root,
&justified_balances,
&self.spec,
) )
.map_err(|e| { .map_err(|e| {
Error::InternalError(format!( Error::InternalError(format!(
@@ -1235,10 +1242,15 @@ impl<E: EthSpec> Tester<E> {
})?; })?;
drop(fork_choice); drop(fork_choice);
let mut actual_sorted = actual; let mut actual_sorted: Vec<(Hash256, u8, u64)> = actual
.into_iter()
.map(|(root, status, weight)| (root, status as u8, weight))
.collect();
actual_sorted.sort(); actual_sorted.sort();
let mut expected_sorted: Vec<(Hash256, u64)> = let mut expected_sorted: Vec<(Hash256, u8, u64)> = expected
expected.iter().map(|x| (x.root, x.weight)).collect(); .iter()
.map(|x| (x.root, x.payload_status.unwrap_or(FcPayloadStatus::Pending as u8), x.weight))
.collect();
expected_sorted.sort(); expected_sorted.sort();
check_equal( check_equal(

View File

@@ -748,7 +748,6 @@ impl<E: EthSpec + TypeName> Handler for ForkChoiceHandler<E> {
pub struct ForkChoiceComplianceHandler<E> { pub struct ForkChoiceComplianceHandler<E> {
handler_name: String, handler_name: String,
only_fork: Option<ForkName>,
_phantom: PhantomData<E>, _phantom: PhantomData<E>,
} }
@@ -756,7 +755,6 @@ impl<E: EthSpec> ForkChoiceComplianceHandler<E> {
pub fn new(handler_name: &str) -> Self { pub fn new(handler_name: &str) -> Self {
Self { Self {
handler_name: handler_name.into(), handler_name: handler_name.into(),
only_fork: None,
_phantom: PhantomData, _phantom: PhantomData,
} }
} }