Ensure payload envelope streamer always serves canonical envelopes after the split slot (#9085)

Co-Authored-By: Eitan Seri- Levi <eserilev@gmail.com>

Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu>
This commit is contained in:
Eitan Seri-Levi
2026-04-23 20:32:26 +09:00
committed by GitHub
parent cfc748309f
commit 82dc8b4edc
9 changed files with 533 additions and 36 deletions

View File

@@ -383,11 +383,24 @@ impl<T: BeaconChainTypes> CanonicalHead<T> {
Ok((head, execution_status))
}
// TODO(gloas) just a stub for now, implement this once we have fork choice.
/// Returns true if the payload for this block is canonical according to fork choice
/// Returns an error if the block root doesn't exist in fork choice.
pub fn block_has_canonical_payload(&self, _root: &Hash256) -> Result<bool, Error> {
Ok(true)
/// Returns `true` if the payload for this block is canonical (Full) according to fork choice.
pub fn block_has_canonical_payload(
&self,
root: &Hash256,
spec: &ChainSpec,
) -> Result<bool, Error> {
let cached_head = self.cached_head();
let head_root = cached_head.head_block_root();
let head_payload_status = cached_head.head_payload_status();
if *root == head_root {
return Ok(head_payload_status == PayloadStatus::Full);
}
self.fork_choice_read_lock()
.get_canonical_payload_status(root, spec)
.map(|status| status == PayloadStatus::Full)
.map_err(Error::ForkChoiceError)
}
/// Returns a clone of `self.cached_head`.

View File

@@ -37,6 +37,8 @@ impl<T: BeaconChainTypes> EnvelopeStreamerBeaconAdapter<T> {
&self,
root: &Hash256,
) -> Result<bool, BeaconChainError> {
self.chain.canonical_head.block_has_canonical_payload(root)
self.chain
.canonical_head
.block_has_canonical_payload(root, &self.chain.spec)
}
}

View File

@@ -132,13 +132,8 @@ impl<T: BeaconChainTypes> PayloadEnvelopeStreamer<T> {
results.push((*root, Ok(None)));
}
}
Err(_) => {
results.push((
*root,
Err(BeaconChainError::EnvelopeStreamerError(
Error::BlockMissingFromForkChoice,
)),
));
Err(e) => {
results.push((*root, Err(e)));
}
}
} else {

View File

@@ -1,4 +1,5 @@
use super::*;
use crate::beacon_chain::ForkChoiceError;
use crate::payload_envelope_streamer::beacon_chain_adapter::MockEnvelopeStreamerBeaconAdapter;
use crate::test_utils::EphemeralHarnessType;
use bls::{FixedBytesExtended, Signature};
@@ -279,15 +280,18 @@ async fn stream_envelopes_by_root() {
}
/// When `block_has_canonical_payload` returns an error, the streamer should
/// yield `Err(EnvelopeStreamerError(BlockMissingFromForkChoice))` for those roots.
/// propagate that error for those roots.
#[tokio::test]
async fn stream_envelopes_error() {
let chain = build_chain(4, &[], &[], &[]);
let (mut mock, _runtime) = mock_adapter();
mock.expect_get_split_slot().return_const(Slot::new(0));
mock_envelopes(&mut mock, &chain);
mock.expect_block_has_canonical_payload()
.returning(|_| Err(BeaconChainError::CanonicalHeadLockTimeout));
mock.expect_block_has_canonical_payload().returning(|_| {
Err(BeaconChainError::ForkChoiceError(
ForkChoiceError::DoesNotDescendFromFinalizedCheckpoint,
))
});
let streamer = PayloadEnvelopeStreamer::new(mock, EnvelopeRequestSource::ByRange);
let mut stream = streamer.launch_stream(roots(&chain));
@@ -299,13 +303,8 @@ async fn stream_envelopes_error() {
.unwrap_or_else(|| panic!("stream ended early at index {i}"));
assert_eq!(root, entry.block_root, "root mismatch at index {i}");
assert!(
matches!(
result.as_ref(),
Err(BeaconChainError::EnvelopeStreamerError(
Error::BlockMissingFromForkChoice
))
),
"expected BlockMissingFromForkChoice error at index {i}, got {:?}",
result.as_ref().is_err(),
"expected error at index {i}, got {:?}",
result
);
}