Skip to content

Commit 4adaa36

Browse files
committed
Merge of #9962
2 parents fb35002 + e9fc2a2 commit 4adaa36

2 files changed

Lines changed: 69 additions & 5 deletions

File tree

consensus/proto_array/src/fork_choice_test_definition/gloas_payload.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,61 @@ pub fn get_gloas_should_build_on_full_test_definition() -> ForkChoiceTestDefinit
10031003
}
10041004
}
10051005

1006+
/// When the justified checkpoint has no viable descendants, `get_head` must still
1007+
/// resolve the PENDING seed to EMPTY or FULL.
1008+
pub fn get_pending_head_resolves_when_justified_subtree_non_viable_test_definition()
1009+
-> ForkChoiceTestDefinition {
1010+
let ops = vec![
1011+
// Justified block at slot 32.
1012+
Operation::ProcessBlock {
1013+
slot: Slot::new(32),
1014+
root: get_root(2),
1015+
parent_root: get_root(0),
1016+
justified_checkpoint: get_checkpoint(0),
1017+
finalized_checkpoint: get_checkpoint(0),
1018+
execution_payload_parent_hash: Some(get_hash(0)),
1019+
execution_payload_block_hash: Some(get_hash(2)),
1020+
},
1021+
// Child with stale voting source (below viability horizon).
1022+
Operation::ProcessBlock {
1023+
slot: Slot::new(64),
1024+
root: get_root(3),
1025+
parent_root: get_root(2),
1026+
justified_checkpoint: get_checkpoint(0),
1027+
finalized_checkpoint: get_checkpoint(0),
1028+
execution_payload_parent_hash: Some(get_hash(99)),
1029+
execution_payload_block_hash: Some(get_hash(3)),
1030+
},
1031+
Operation::FindHead {
1032+
justified_checkpoint: Checkpoint {
1033+
epoch: Epoch::new(1),
1034+
root: get_root(2),
1035+
},
1036+
finalized_checkpoint: get_checkpoint(0),
1037+
justified_state_balances: vec![1],
1038+
expected_head: get_root(2),
1039+
current_slot: Slot::new(128),
1040+
expected_payload_status: Some(PayloadStatus::Empty),
1041+
},
1042+
Operation::AssertShouldBuildOnFull {
1043+
block_root: get_root(2),
1044+
parent_payload_status: PayloadStatus::Empty,
1045+
proposal_slot: Slot::new(129),
1046+
expected: false,
1047+
},
1048+
];
1049+
1050+
ForkChoiceTestDefinition {
1051+
finalized_block_slot: Slot::new(0),
1052+
justified_checkpoint: get_checkpoint(0),
1053+
finalized_checkpoint: get_checkpoint(0),
1054+
operations: ops,
1055+
execution_payload_parent_hash: Some(get_hash(42)),
1056+
execution_payload_block_hash: Some(get_hash(0)),
1057+
spec: Some(gloas_spec()),
1058+
}
1059+
}
1060+
10061061
#[cfg(test)]
10071062
mod tests {
10081063
use super::*;
@@ -1198,6 +1253,11 @@ mod tests {
11981253
test.run();
11991254
}
12001255

1256+
#[test]
1257+
fn pending_head_resolves_when_justified_subtree_non_viable() {
1258+
get_pending_head_resolves_when_justified_subtree_non_viable_test_definition().run();
1259+
}
1260+
12011261
/// Test that execution payload invalidation propagates across the V17→V29 fork
12021262
/// boundary: after invalidating a V17 parent, head must not select any descendant.
12031263
///

consensus/proto_array/src/proto_array.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,11 +1262,15 @@ impl ProtoArray {
12621262
self.should_apply_proposer_boost::<E>(proposer_boost_root, justified_balances, spec)?;
12631263

12641264
loop {
1265-
let children: Vec<_> = self
1266-
.get_node_children(&head)?
1267-
.into_iter()
1268-
.filter(|(fc_node, _)| viable_nodes.contains(&fc_node.proto_node_index))
1269-
.collect();
1265+
let children: Vec<_> = if head.payload_status == PayloadStatus::Pending {
1266+
// Spec: `get_node_children` does not consult `get_filtered_block_tree` for PENDING.
1267+
self.get_node_children(&head)?
1268+
} else {
1269+
self.get_node_children(&head)?
1270+
.into_iter()
1271+
.filter(|(fc_node, _)| viable_nodes.contains(&fc_node.proto_node_index))
1272+
.collect()
1273+
};
12701274

12711275
if children.is_empty() {
12721276
return Ok(head);

0 commit comments

Comments
 (0)