adding tests and payload changes

This commit is contained in:
hopinheimer
2026-02-26 03:14:57 -05:00
parent d5c5077a31
commit e04a8c31ea
13 changed files with 627 additions and 73 deletions

View File

@@ -18,6 +18,14 @@ fn main() {
"execution_status_03.yaml",
get_execution_status_test_definition_03(),
);
write_test_def_to_yaml(
"gloas_chain_following.yaml",
get_gloas_chain_following_test_definition(),
);
write_test_def_to_yaml(
"gloas_payload_probe.yaml",
get_gloas_payload_probe_test_definition(),
);
}
fn write_test_def_to_yaml(filename: &str, def: ForkChoiceTestDefinition) {

View File

@@ -56,8 +56,14 @@ pub enum Operation {
validator_index: usize,
block_root: Hash256,
attestation_slot: Slot,
#[serde(default)]
},
ProcessPayloadAttestation {
validator_index: usize,
block_root: Hash256,
attestation_slot: Slot,
payload_present: bool,
#[serde(default)]
blob_data_available: bool,
},
Prune {
finalized_root: Hash256,
@@ -277,18 +283,35 @@ impl ForkChoiceTestDefinition {
validator_index,
block_root,
attestation_slot,
payload_present,
} => {
fork_choice
.process_attestation(
.process_attestation(validator_index, block_root, attestation_slot)
.unwrap_or_else(|_| {
panic!(
"process_attestation op at index {} returned error",
op_index
)
});
check_bytes_round_trip(&fork_choice);
}
Operation::ProcessPayloadAttestation {
validator_index,
block_root,
attestation_slot,
payload_present,
blob_data_available,
} => {
fork_choice
.process_payload_attestation(
validator_index,
block_root,
attestation_slot,
payload_present,
blob_data_available,
)
.unwrap_or_else(|_| {
panic!(
"process_attestation op at index {} returned error",
"process_payload_attestation op at index {} returned error",
op_index
)
});

View File

@@ -106,7 +106,6 @@ pub fn get_execution_status_test_definition_01() -> ForkChoiceTestDefinition {
validator_index: 0,
block_root: get_root(1),
attestation_slot: Slot::new(2),
payload_present: false,
});
// Ensure that the head is now 1, because 1 has a vote.
@@ -149,7 +148,6 @@ pub fn get_execution_status_test_definition_01() -> ForkChoiceTestDefinition {
validator_index: 1,
block_root: get_root(2),
attestation_slot: Slot::new(2),
payload_present: false,
});
// Ensure that the head is 2 since 1 and 2 both have a vote
@@ -254,7 +252,6 @@ pub fn get_execution_status_test_definition_01() -> ForkChoiceTestDefinition {
validator_index: 0,
block_root: get_root(3),
attestation_slot: Slot::new(3),
payload_present: false,
});
// Ensure that the head is still 2
@@ -357,7 +354,6 @@ pub fn get_execution_status_test_definition_01() -> ForkChoiceTestDefinition {
validator_index: 1,
block_root: get_root(1),
attestation_slot: Slot::new(3),
payload_present: false,
});
// Ensure that the head has switched back to 1
@@ -521,7 +517,6 @@ pub fn get_execution_status_test_definition_02() -> ForkChoiceTestDefinition {
validator_index: 0,
block_root: get_root(1),
attestation_slot: Slot::new(2),
payload_present: false,
});
// Ensure that the head is now 1, because 1 has a vote.
@@ -564,7 +559,6 @@ pub fn get_execution_status_test_definition_02() -> ForkChoiceTestDefinition {
validator_index: 1,
block_root: get_root(2),
attestation_slot: Slot::new(2),
payload_present: false,
});
// Ensure that the head is 2 since 1 and 2 both have a vote
@@ -669,7 +663,6 @@ pub fn get_execution_status_test_definition_02() -> ForkChoiceTestDefinition {
validator_index: 0,
block_root: get_root(3),
attestation_slot: Slot::new(3),
payload_present: false,
});
// Move validator #1 vote from 2 to 3
@@ -683,7 +676,6 @@ pub fn get_execution_status_test_definition_02() -> ForkChoiceTestDefinition {
validator_index: 1,
block_root: get_root(3),
attestation_slot: Slot::new(3),
payload_present: false,
});
// Ensure that the head is now 3.
@@ -898,7 +890,6 @@ pub fn get_execution_status_test_definition_03() -> ForkChoiceTestDefinition {
validator_index: 0,
block_root: get_root(1),
attestation_slot: Slot::new(2),
payload_present: false,
});
// Ensure that the head is now 1, because 1 has a vote.
@@ -941,7 +932,6 @@ pub fn get_execution_status_test_definition_03() -> ForkChoiceTestDefinition {
validator_index: 1,
block_root: get_root(1),
attestation_slot: Slot::new(2),
payload_present: false,
});
// Ensure that the head is 1.

View File

@@ -312,7 +312,6 @@ pub fn get_ffg_case_02_test_definition() -> ForkChoiceTestDefinition {
validator_index: 0,
block_root: get_root(1),
attestation_slot: Slot::new(0),
payload_present: false,
});
// Ensure that if we start at 0 we find 9 (just: 0, fin: 0).
@@ -376,7 +375,6 @@ pub fn get_ffg_case_02_test_definition() -> ForkChoiceTestDefinition {
validator_index: 1,
block_root: get_root(2),
attestation_slot: Slot::new(0),
payload_present: false,
});
// Ensure that if we start at 0 we find 10 (just: 0, fin: 0).

View File

@@ -109,18 +109,21 @@ pub fn get_gloas_payload_probe_test_definition() -> ForkChoiceTestDefinition {
execution_payload_block_hash: Some(get_hash(1)),
});
// One Full and one Empty vote for the same head block: tie should probe as Full.
ops.push(Operation::ProcessAttestation {
// One Full and one Empty vote for the same head block: tie probes via runtime tiebreak,
// which defaults to Empty unless timely+data-available evidence is set.
ops.push(Operation::ProcessPayloadAttestation {
validator_index: 0,
block_root: get_root(1),
attestation_slot: Slot::new(2),
payload_present: true,
blob_data_available: false,
});
ops.push(Operation::ProcessAttestation {
ops.push(Operation::ProcessPayloadAttestation {
validator_index: 1,
block_root: get_root(1),
attestation_slot: Slot::new(2),
payload_present: false,
blob_data_available: false,
});
ops.push(Operation::FindHead {
justified_checkpoint: get_checkpoint(0),
@@ -135,15 +138,16 @@ pub fn get_gloas_payload_probe_test_definition() -> ForkChoiceTestDefinition {
});
ops.push(Operation::AssertHeadPayloadStatus {
head_root: get_root(1),
expected_status: PayloadStatus::Full,
expected_status: PayloadStatus::Empty,
});
// Flip validator 0 to Empty; probe should now report Empty.
ops.push(Operation::ProcessAttestation {
ops.push(Operation::ProcessPayloadAttestation {
validator_index: 0,
block_root: get_root(1),
attestation_slot: Slot::new(3),
payload_present: false,
blob_data_available: false,
});
ops.push(Operation::FindHead {
justified_checkpoint: get_checkpoint(0),
@@ -171,11 +175,12 @@ pub fn get_gloas_payload_probe_test_definition() -> ForkChoiceTestDefinition {
execution_payload_parent_hash: Some(get_hash(1)),
execution_payload_block_hash: Some(get_hash(5)),
});
ops.push(Operation::ProcessAttestation {
ops.push(Operation::ProcessPayloadAttestation {
validator_index: 2,
block_root: get_root(5),
attestation_slot: Slot::new(3),
payload_present: true,
blob_data_available: false,
});
ops.push(Operation::FindHead {
justified_checkpoint: get_checkpoint(0),
@@ -190,7 +195,250 @@ pub fn get_gloas_payload_probe_test_definition() -> ForkChoiceTestDefinition {
});
ops.push(Operation::AssertHeadPayloadStatus {
head_root: get_root(5),
expected_status: PayloadStatus::Full,
expected_status: PayloadStatus::Empty,
});
ForkChoiceTestDefinition {
finalized_block_slot: Slot::new(0),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
operations: ops,
execution_payload_parent_hash: Some(get_hash(42)),
execution_payload_block_hash: Some(get_hash(0)),
spec: Some(gloas_spec()),
}
}
pub fn get_gloas_find_head_vote_transition_test_definition() -> ForkChoiceTestDefinition {
let mut ops = vec![];
// Competing branches with distinct payload ancestry (Full vs Empty from genesis).
ops.push(Operation::ProcessBlock {
slot: Slot::new(1),
root: get_root(1),
parent_root: get_root(0),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
execution_payload_parent_hash: Some(get_hash(0)),
execution_payload_block_hash: Some(get_hash(1)),
});
ops.push(Operation::ProcessBlock {
slot: Slot::new(1),
root: get_root(2),
parent_root: get_root(0),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
execution_payload_parent_hash: Some(get_hash(99)),
execution_payload_block_hash: Some(get_hash(2)),
});
ops.push(Operation::ProcessBlock {
slot: Slot::new(2),
root: get_root(3),
parent_root: get_root(1),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
execution_payload_parent_hash: Some(get_hash(1)),
execution_payload_block_hash: Some(get_hash(3)),
});
ops.push(Operation::ProcessBlock {
slot: Slot::new(2),
root: get_root(4),
parent_root: get_root(2),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
execution_payload_parent_hash: Some(get_hash(100)),
execution_payload_block_hash: Some(get_hash(4)),
});
// Equal branch weights: tiebreak FULL picks branch rooted at 3.
ops.push(Operation::SetPayloadTiebreak {
block_root: get_root(0),
is_timely: true,
is_data_available: true,
});
ops.push(Operation::FindHead {
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
justified_state_balances: vec![1],
expected_head: get_root(3),
});
// Validator 0 votes Empty branch -> head flips to 4.
ops.push(Operation::ProcessPayloadAttestation {
validator_index: 0,
block_root: get_root(4),
attestation_slot: Slot::new(3),
payload_present: false,
blob_data_available: false,
});
ops.push(Operation::FindHead {
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
justified_state_balances: vec![1],
expected_head: get_root(4),
});
// Latest-message update back to Full branch -> head returns to 3.
ops.push(Operation::ProcessPayloadAttestation {
validator_index: 0,
block_root: get_root(3),
attestation_slot: Slot::new(4),
payload_present: true,
blob_data_available: false,
});
ops.push(Operation::FindHead {
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
justified_state_balances: vec![1],
expected_head: get_root(3),
});
ops.push(Operation::AssertPayloadWeights {
block_root: get_root(3),
expected_full_weight: 1,
expected_empty_weight: 0,
});
ForkChoiceTestDefinition {
finalized_block_slot: Slot::new(0),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
operations: ops,
execution_payload_parent_hash: Some(get_hash(42)),
execution_payload_block_hash: Some(get_hash(0)),
spec: Some(gloas_spec()),
}
}
pub fn get_gloas_weight_priority_over_payload_preference_test_definition()
-> ForkChoiceTestDefinition {
let mut ops = vec![];
// Build two branches where one child extends payload (Full) and the other doesn't (Empty).
ops.push(Operation::ProcessBlock {
slot: Slot::new(1),
root: get_root(1),
parent_root: get_root(0),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
execution_payload_parent_hash: Some(get_hash(0)),
execution_payload_block_hash: Some(get_hash(1)),
});
ops.push(Operation::ProcessBlock {
slot: Slot::new(1),
root: get_root(2),
parent_root: get_root(0),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
execution_payload_parent_hash: Some(get_hash(99)),
execution_payload_block_hash: Some(get_hash(2)),
});
ops.push(Operation::ProcessBlock {
slot: Slot::new(2),
root: get_root(3),
parent_root: get_root(1),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
execution_payload_parent_hash: Some(get_hash(1)),
execution_payload_block_hash: Some(get_hash(3)),
});
ops.push(Operation::ProcessBlock {
slot: Slot::new(2),
root: get_root(4),
parent_root: get_root(2),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
execution_payload_parent_hash: Some(get_hash(100)),
execution_payload_block_hash: Some(get_hash(4)),
});
// Parent prefers Full on equal branch weights.
ops.push(Operation::SetPayloadTiebreak {
block_root: get_root(0),
is_timely: true,
is_data_available: true,
});
ops.push(Operation::FindHead {
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
justified_state_balances: vec![1],
expected_head: get_root(3),
});
// Add two Empty votes to make the Empty branch strictly heavier.
ops.push(Operation::ProcessPayloadAttestation {
validator_index: 0,
block_root: get_root(4),
attestation_slot: Slot::new(3),
payload_present: false,
blob_data_available: false,
});
ops.push(Operation::ProcessPayloadAttestation {
validator_index: 1,
block_root: get_root(4),
attestation_slot: Slot::new(3),
payload_present: false,
blob_data_available: false,
});
ops.push(Operation::FindHead {
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
justified_state_balances: vec![1, 1],
expected_head: get_root(4),
});
ForkChoiceTestDefinition {
finalized_block_slot: Slot::new(0),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
operations: ops,
execution_payload_parent_hash: Some(get_hash(42)),
execution_payload_block_hash: Some(get_hash(0)),
spec: Some(gloas_spec()),
}
}
pub fn get_gloas_parent_empty_when_child_points_to_grandparent_test_definition()
-> ForkChoiceTestDefinition {
let mut ops = vec![];
// Build a three-block chain A -> B -> C (CL parent links).
// A: EL parent = genesis hash(0), EL hash = hash(1).
ops.push(Operation::ProcessBlock {
slot: Slot::new(1),
root: get_root(1),
parent_root: get_root(0),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
execution_payload_parent_hash: Some(get_hash(0)),
execution_payload_block_hash: Some(get_hash(1)),
});
// B: EL parent = hash(1), EL hash = hash(2).
ops.push(Operation::ProcessBlock {
slot: Slot::new(2),
root: get_root(2),
parent_root: get_root(1),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
execution_payload_parent_hash: Some(get_hash(1)),
execution_payload_block_hash: Some(get_hash(2)),
});
// C: CL parent is B, but EL parent points to A (hash 1), not B (hash 2).
// This models B's payload not arriving in time, so C records parent status as Empty.
ops.push(Operation::ProcessBlock {
slot: Slot::new(3),
root: get_root(3),
parent_root: get_root(2),
justified_checkpoint: get_checkpoint(0),
finalized_checkpoint: get_checkpoint(0),
execution_payload_parent_hash: Some(get_hash(1)),
execution_payload_block_hash: Some(get_hash(3)),
});
ops.push(Operation::AssertParentPayloadStatus {
block_root: get_root(3),
expected_status: PayloadStatus::Empty,
});
ForkChoiceTestDefinition {
@@ -219,4 +467,22 @@ mod tests {
let test = get_gloas_payload_probe_test_definition();
test.run();
}
#[test]
fn find_head_vote_transition() {
let test = get_gloas_find_head_vote_transition_test_definition();
test.run();
}
#[test]
fn weight_priority_over_payload_preference() {
let test = get_gloas_weight_priority_over_payload_preference_test_definition();
test.run();
}
#[test]
fn parent_empty_when_child_points_to_grandparent() {
let test = get_gloas_parent_empty_when_child_points_to_grandparent_test_definition();
test.run();
}
}

View File

@@ -106,7 +106,6 @@ pub fn get_votes_test_definition() -> ForkChoiceTestDefinition {
validator_index: 0,
block_root: get_root(1),
attestation_slot: Slot::new(2),
payload_present: false,
});
// Ensure that the head is now 1, because 1 has a vote.
@@ -136,7 +135,6 @@ pub fn get_votes_test_definition() -> ForkChoiceTestDefinition {
validator_index: 1,
block_root: get_root(2),
attestation_slot: Slot::new(2),
payload_present: false,
});
// Ensure that the head is 2 since 1 and 2 both have a vote
@@ -211,7 +209,6 @@ pub fn get_votes_test_definition() -> ForkChoiceTestDefinition {
validator_index: 0,
block_root: get_root(3),
attestation_slot: Slot::new(3),
payload_present: false,
});
// Ensure that the head is still 2
@@ -246,7 +243,6 @@ pub fn get_votes_test_definition() -> ForkChoiceTestDefinition {
validator_index: 1,
block_root: get_root(1),
attestation_slot: Slot::new(3),
payload_present: false,
});
// Ensure that the head is now 3
@@ -409,13 +405,11 @@ pub fn get_votes_test_definition() -> ForkChoiceTestDefinition {
validator_index: 0,
block_root: get_root(5),
attestation_slot: Slot::new(4),
payload_present: false,
});
ops.push(Operation::ProcessAttestation {
validator_index: 1,
block_root: get_root(5),
attestation_slot: Slot::new(4),
payload_present: false,
});
// Add blocks 7, 8 and 9. Adding these blocks helps test the `best_descendant`
@@ -570,13 +564,11 @@ pub fn get_votes_test_definition() -> ForkChoiceTestDefinition {
validator_index: 0,
block_root: get_root(9),
attestation_slot: Slot::new(5),
payload_present: false,
});
ops.push(Operation::ProcessAttestation {
validator_index: 1,
block_root: get_root(9),
attestation_slot: Slot::new(5),
payload_present: false,
});
// Add block 10
@@ -650,13 +642,11 @@ pub fn get_votes_test_definition() -> ForkChoiceTestDefinition {
validator_index: 2,
block_root: get_root(10),
attestation_slot: Slot::new(5),
payload_present: false,
});
ops.push(Operation::ProcessAttestation {
validator_index: 3,
block_root: get_root(10),
attestation_slot: Slot::new(5),
payload_present: false,
});
// Check the head is now 10.

View File

@@ -357,6 +357,9 @@ impl ProtoArray {
apply_delta(node.empty_payload_weight, node_empty_delta, node_index)?;
node.full_payload_weight =
apply_delta(node.full_payload_weight, node_full_delta, node_index)?;
if let Some(payload_tiebreaker) = node_deltas.payload_tiebreaker {
node.payload_tiebreak = payload_tiebreaker;
}
}
// Update the parent delta (if any).
@@ -1052,10 +1055,14 @@ impl ProtoArray {
} else if !child_leads_to_viable_head && best_child_leads_to_viable_head {
// The best child leads to a viable head, but the child doesn't.
no_change
} else if child.weight() > best_child.weight() {
// Weight is the primary ordering criterion.
change_to_child
} else if child.weight() < best_child.weight() {
no_change
} else {
// Both viable or both non-viable. For V29 parents, prefer the child
// whose parent_payload_status matches the parent's payload preference
// (Full if full_payload_weight >= empty_payload_weight, else Empty).
// Equal weights: for V29 parents, prefer the child whose
// parent_payload_status matches the parent's payload preference.
let child_matches = child_matches_parent_payload_preference(parent, child);
let best_child_matches =
child_matches_parent_payload_preference(parent, best_child);
@@ -1064,20 +1071,11 @@ impl ProtoArray {
change_to_child
} else if !child_matches && best_child_matches {
no_change
} else if child.weight() == best_child.weight() {
// Tie-breaker of equal weights by root.
if *child.root() >= *best_child.root() {
change_to_child
} else {
no_change
}
} else if *child.root() >= *best_child.root() {
// Final tie-breaker of equal weights by root.
change_to_child
} else {
// Choose the winner by weight.
if child.weight() > best_child.weight() {
change_to_child
} else {
no_change
}
no_change
}
}
}

View File

@@ -32,6 +32,8 @@ pub struct VoteTracker {
next_slot: Slot,
current_payload_present: bool,
next_payload_present: bool,
current_blob_data_available: bool,
next_blob_data_available: bool,
}
// FIXME(sproul): version this type
@@ -39,6 +41,7 @@ pub struct LatestMessage {
pub slot: Slot,
pub root: Hash256,
pub payload_present: bool,
pub blob_data_available: bool,
}
/// Represents the verification status of an execution payload pre-Gloas.
@@ -521,7 +524,24 @@ impl ProtoArrayForkChoice {
validator_index: usize,
block_root: Hash256,
attestation_slot: Slot,
) -> Result<(), String> {
let vote = self.votes.get_mut(validator_index);
if attestation_slot > vote.next_slot || *vote == VoteTracker::default() {
vote.next_root = block_root;
vote.next_slot = attestation_slot;
}
Ok(())
}
pub fn process_payload_attestation(
&mut self,
validator_index: usize,
block_root: Hash256,
attestation_slot: Slot,
payload_present: bool,
blob_data_available: bool,
) -> Result<(), String> {
let vote = self.votes.get_mut(validator_index);
@@ -529,6 +549,7 @@ impl ProtoArrayForkChoice {
vote.next_root = block_root;
vote.next_slot = attestation_slot;
vote.next_payload_present = payload_present;
vote.next_blob_data_available = blob_data_available;
}
Ok(())
@@ -945,13 +966,19 @@ impl ProtoArrayForkChoice {
/// Returns the payload status of the head node based on accumulated weights.
///
/// Returns `Full` if `full_payload_weight >= empty_payload_weight` (Full wins ties per spec's
/// `get_payload_status_tiebreaker` natural ordering FULL=2 > EMPTY=1).
/// Returns `Full` if `full_payload_weight > empty_payload_weight`.
/// Returns `Empty` if `empty_payload_weight > full_payload_weight`.
/// On ties, consult the node's runtime `payload_tiebreak`: prefer `Full` only when timely and
/// data is available, otherwise `Empty`.
/// Returns `Empty` otherwise. Returns `None` for V17 nodes.
pub fn head_payload_status(&self, head_root: &Hash256) -> Option<PayloadStatus> {
let node = self.get_proto_node(head_root)?;
let v29 = node.as_v29().ok()?;
if v29.full_payload_weight >= v29.empty_payload_weight {
if v29.full_payload_weight > v29.empty_payload_weight {
Some(PayloadStatus::Full)
} else if v29.empty_payload_weight > v29.full_payload_weight {
Some(PayloadStatus::Empty)
} else if v29.payload_tiebreak.is_timely && v29.payload_tiebreak.is_data_available {
Some(PayloadStatus::Full)
} else {
Some(PayloadStatus::Empty)
@@ -985,6 +1012,7 @@ impl ProtoArrayForkChoice {
root: vote.next_root,
slot: vote.next_slot,
payload_present: vote.next_payload_present,
blob_data_available: vote.next_blob_data_available,
})
}
} else {
@@ -1079,6 +1107,17 @@ fn compute_deltas(
new_balances: &[u64],
equivocating_indices: &BTreeSet<u64>,
) -> Result<Vec<NodeDelta>, Error> {
let merge_payload_tiebreaker =
|delta: &mut NodeDelta, incoming: crate::proto_array::PayloadTiebreak| {
delta.payload_tiebreaker = Some(match delta.payload_tiebreaker {
Some(existing) => crate::proto_array::PayloadTiebreak {
is_timely: existing.is_timely || incoming.is_timely,
is_data_available: existing.is_data_available || incoming.is_data_available,
},
None => incoming,
});
};
let block_slot = |index: usize| -> Result<Slot, Error> {
node_slots
.get(index)
@@ -1138,6 +1177,7 @@ fn compute_deltas(
vote.current_root = Hash256::zero();
vote.current_slot = Slot::new(0);
vote.current_payload_present = false;
vote.current_blob_data_available = false;
}
// We've handled this slashed validator, continue without applying an ordinary delta.
continue;
@@ -1195,11 +1235,21 @@ fn compute_deltas(
block_slot(next_delta_index)?,
);
node_delta.add_payload_delta(status, new_balance, next_delta_index)?;
if status != PayloadStatus::Pending {
merge_payload_tiebreaker(
node_delta,
crate::proto_array::PayloadTiebreak {
is_timely: vote.next_payload_present,
is_data_available: vote.next_blob_data_available,
},
);
}
}
vote.current_root = vote.next_root;
vote.current_slot = vote.next_slot;
vote.current_payload_present = vote.next_payload_present;
vote.current_blob_data_available = vote.next_blob_data_available;
}
}
@@ -1552,6 +1602,8 @@ mod test_compute_deltas {
next_slot: Slot::new(0),
current_payload_present: false,
next_payload_present: false,
current_blob_data_available: false,
next_blob_data_available: false,
});
old_balances.push(0);
new_balances.push(0);
@@ -1607,6 +1659,8 @@ mod test_compute_deltas {
next_slot: Slot::new(0),
current_payload_present: false,
next_payload_present: false,
current_blob_data_available: false,
next_blob_data_available: false,
});
old_balances.push(BALANCE);
new_balances.push(BALANCE);
@@ -1669,6 +1723,8 @@ mod test_compute_deltas {
next_slot: Slot::new(0),
current_payload_present: false,
next_payload_present: false,
current_blob_data_available: false,
next_blob_data_available: false,
});
old_balances.push(BALANCE);
new_balances.push(BALANCE);
@@ -1726,6 +1782,8 @@ mod test_compute_deltas {
next_slot: Slot::new(0),
current_payload_present: false,
next_payload_present: false,
current_blob_data_available: false,
next_blob_data_available: false,
});
old_balances.push(BALANCE);
new_balances.push(BALANCE);
@@ -1794,6 +1852,8 @@ mod test_compute_deltas {
next_slot: Slot::new(0),
current_payload_present: false,
next_payload_present: false,
current_blob_data_available: false,
next_blob_data_available: false,
});
// One validator moves their vote from the block to something outside the tree.
@@ -1804,6 +1864,8 @@ mod test_compute_deltas {
next_slot: Slot::new(0),
current_payload_present: false,
next_payload_present: false,
current_blob_data_available: false,
next_blob_data_available: false,
});
let deltas = compute_deltas(
@@ -1854,6 +1916,8 @@ mod test_compute_deltas {
next_slot: Slot::new(0),
current_payload_present: false,
next_payload_present: false,
current_blob_data_available: false,
next_blob_data_available: false,
});
old_balances.push(OLD_BALANCE);
new_balances.push(NEW_BALANCE);
@@ -1927,6 +1991,8 @@ mod test_compute_deltas {
next_slot: Slot::new(0),
current_payload_present: false,
next_payload_present: false,
current_blob_data_available: false,
next_blob_data_available: false,
});
}
@@ -1987,6 +2053,8 @@ mod test_compute_deltas {
next_slot: Slot::new(0),
current_payload_present: false,
next_payload_present: false,
current_blob_data_available: false,
next_blob_data_available: false,
});
}
@@ -2045,6 +2113,8 @@ mod test_compute_deltas {
next_slot: Slot::new(0),
current_payload_present: false,
next_payload_present: false,
current_blob_data_available: false,
next_blob_data_available: false,
});
}
@@ -2108,6 +2178,8 @@ mod test_compute_deltas {
next_slot: Slot::new(1),
current_payload_present: false,
next_payload_present: true,
current_blob_data_available: false,
next_blob_data_available: false,
}]);
let deltas = compute_deltas(
@@ -2140,6 +2212,8 @@ mod test_compute_deltas {
next_slot: Slot::new(0),
current_payload_present: false,
next_payload_present: true,
current_blob_data_available: false,
next_blob_data_available: false,
}]);
let deltas = compute_deltas(