mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-30 19:34:37 +00:00
Gloas alpha spec 9 (#9393)
Changes implemented Ensure bids are for a higher slot than their parent (https://github.com/ethereum/consensus-specs/pull/5302) Ignore PTC attestations for empty assigned slots (https://github.com/ethereum/consensus-specs/pull/5281) Limit should_build_on_full checks to the previous slot (https://github.com/ethereum/consensus-specs/pull/5309) Apply proposer boost if dependent roots match (https://github.com/ethereum/consensus-specs/pull/5306) Exclude slashed validators from proposing (EIP-8045) (https://github.com/ethereum/consensus-specs/pull/5115) Force the proposer to reorg late payloads (https://github.com/ethereum/consensus-specs/pull/5210) Remove support for old deposit mechanism in Fulu (https://github.com/ethereum/consensus-specs/pull/4704) Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu> Co-Authored-By: dapplion <35266934+dapplion@users.noreply.github.com> Co-Authored-By: Eitan Seri-Levi <eserilev@gmail.com> Co-Authored-By: Michael Sproul <michael@sigmaprime.io> Co-Authored-By: Michael Sproul <michaelsproul@users.noreply.github.com>
This commit is contained in:
@@ -124,6 +124,15 @@ pub enum Operation {
|
||||
#[serde(default)]
|
||||
proposer_boost_root: Option<Hash256>,
|
||||
},
|
||||
/// Assert the result of `should_build_on_full` for the parent `block_root`, where
|
||||
/// `parent_payload_status` is the status the proposer would build on and `proposal_slot`
|
||||
/// is the slot being proposed.
|
||||
AssertShouldBuildOnFull {
|
||||
block_root: Hash256,
|
||||
parent_payload_status: PayloadStatus,
|
||||
proposal_slot: Slot,
|
||||
expected: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -606,6 +615,30 @@ impl ForkChoiceTestDefinition {
|
||||
op_index
|
||||
);
|
||||
}
|
||||
Operation::AssertShouldBuildOnFull {
|
||||
block_root,
|
||||
parent_payload_status,
|
||||
proposal_slot,
|
||||
expected,
|
||||
} => {
|
||||
let actual = fork_choice
|
||||
.should_build_on_full::<MainnetEthSpec>(
|
||||
&block_root,
|
||||
parent_payload_status,
|
||||
proposal_slot,
|
||||
)
|
||||
.unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"should_build_on_full op at index {} returned error: {}",
|
||||
op_index, e
|
||||
)
|
||||
});
|
||||
assert_eq!(
|
||||
actual, expected,
|
||||
"should_build_on_full mismatch at op index {}",
|
||||
op_index
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -971,6 +971,90 @@ pub fn get_gloas_proposer_boost_flips_ancestor_test_definition() -> ForkChoiceTe
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests the slot check in `should_build_on_full`. When the parent is from an earlier slot the
|
||||
/// function returns `true` and ignores PTC data-availability votes. It only checks those votes
|
||||
/// when the parent is from the immediately preceding slot.
|
||||
pub fn get_gloas_should_build_on_full_test_definition() -> ForkChoiceTestDefinition {
|
||||
let mut ops = vec![];
|
||||
|
||||
// Block 1 at slot 1, child of 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)),
|
||||
});
|
||||
|
||||
// PTC has voted the payload data unavailable. `is_timely` sets `payload_received` so the votes
|
||||
// are consulted, and clearing the data-availability bits gives the "false" votes a majority.
|
||||
ops.push(Operation::SetPayloadTiebreak {
|
||||
block_root: get_root(1),
|
||||
is_timely: true,
|
||||
is_data_available: false,
|
||||
});
|
||||
|
||||
// When the parent is `Empty` `should_build_on_full` returns `false`. This check runs before
|
||||
// the slot check, so the result is `false` for both the previous-slot case (block slot 1, proposal slot 2)
|
||||
// and an earlier-slot case (proposal slot 3).
|
||||
ops.push(Operation::AssertShouldBuildOnFull {
|
||||
block_root: get_root(1),
|
||||
parent_payload_status: PayloadStatus::Empty,
|
||||
proposal_slot: Slot::new(2),
|
||||
expected: false,
|
||||
});
|
||||
ops.push(Operation::AssertShouldBuildOnFull {
|
||||
block_root: get_root(1),
|
||||
parent_payload_status: PayloadStatus::Empty,
|
||||
proposal_slot: Slot::new(3),
|
||||
expected: false,
|
||||
});
|
||||
|
||||
// `Full` parent from the immediately preceding slot (block slot 1, proposal slot 2). The PTC
|
||||
// votes are consulted, and since data is unavailable the proposer does not build on full.
|
||||
ops.push(Operation::AssertShouldBuildOnFull {
|
||||
block_root: get_root(1),
|
||||
parent_payload_status: PayloadStatus::Full,
|
||||
proposal_slot: Slot::new(2),
|
||||
expected: false,
|
||||
});
|
||||
|
||||
// `Full` parent from an *earlier* slot (block slot 1, proposal slot 3). The slot check
|
||||
// short-circuits to `true` without consulting the (unavailable) PTC votes.
|
||||
ops.push(Operation::AssertShouldBuildOnFull {
|
||||
block_root: get_root(1),
|
||||
parent_payload_status: PayloadStatus::Full,
|
||||
proposal_slot: Slot::new(3),
|
||||
expected: true,
|
||||
});
|
||||
|
||||
// Flip the PTC view to *available* and re-check the previous-slot case. The votes now permit
|
||||
// building on full.
|
||||
ops.push(Operation::SetPayloadTiebreak {
|
||||
block_root: get_root(1),
|
||||
is_timely: true,
|
||||
is_data_available: true,
|
||||
});
|
||||
ops.push(Operation::AssertShouldBuildOnFull {
|
||||
block_root: get_root(1),
|
||||
parent_payload_status: PayloadStatus::Full,
|
||||
proposal_slot: Slot::new(2),
|
||||
expected: true,
|
||||
});
|
||||
|
||||
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()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -1160,6 +1244,12 @@ mod tests {
|
||||
test.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_build_on_full_slot_check() {
|
||||
let test = get_gloas_should_build_on_full_test_definition();
|
||||
test.run();
|
||||
}
|
||||
|
||||
/// Test that execution payload invalidation propagates across the V17→V29 fork
|
||||
/// boundary: after invalidating a V17 parent, head must not select any descendant.
|
||||
///
|
||||
|
||||
@@ -1569,10 +1569,13 @@ impl ProtoArray {
|
||||
|
||||
/// Called by the proposer to decide whether to build on the full or empty
|
||||
/// parent pending node. Returns false if the PTC has voted the data as unavailable.
|
||||
/// For a parent from an earlier slot the `Empty` or `Full` node has already been resolved
|
||||
/// by attestation weight in `get_head`.
|
||||
pub fn should_build_on_full<E: EthSpec>(
|
||||
&self,
|
||||
fc_node: &IndexedForkChoiceNode,
|
||||
proto_node: &ProtoNode,
|
||||
current_slot: Slot,
|
||||
) -> Result<bool, Error> {
|
||||
if fc_node.payload_status == PayloadStatus::Pending {
|
||||
return Err(Error::InvalidPayloadStatus {
|
||||
@@ -1584,10 +1587,23 @@ impl ProtoArray {
|
||||
if fc_node.payload_status == PayloadStatus::Empty {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if proto_node.slot().saturating_add(1u64) != current_slot {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Check that false votes have not achieved an absolute majority. This allows the payload to be
|
||||
// considered available when either a majority have voted true or not enough votes have
|
||||
// been cast either way.
|
||||
Ok(!proto_node.payload_data_availability::<E>(false)?)
|
||||
if proto_node.payload_data_availability::<E>(false)? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if proto_node.payload_timeliness::<E>(false)? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn should_extend_payload<E: EthSpec>(
|
||||
|
||||
@@ -968,6 +968,7 @@ impl ProtoArrayForkChoice {
|
||||
&self,
|
||||
block_root: &Hash256,
|
||||
parent_payload_status: PayloadStatus,
|
||||
current_slot: Slot,
|
||||
) -> Result<bool, String> {
|
||||
let block_index = self
|
||||
.proto_array
|
||||
@@ -985,7 +986,7 @@ impl ProtoArrayForkChoice {
|
||||
payload_status: parent_payload_status,
|
||||
};
|
||||
self.proto_array
|
||||
.should_build_on_full::<E>(&fc_node, proto_node)
|
||||
.should_build_on_full::<E>(&fc_node, proto_node, current_slot)
|
||||
.map_err(|e| format!("{e:?}"))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user