mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-15 10:52:43 +00:00
Retrospective invalidation of exec. payloads for opt. sync (#2837)
## Issue Addressed
NA
## Proposed Changes
Adds the functionality to allow blocks to be validated/invalidated after their import as per the [optimistic sync spec](https://github.com/ethereum/consensus-specs/blob/dev/sync/optimistic.md#how-to-optimistically-import-blocks). This means:
- Updating `ProtoArray` to allow flipping the `execution_status` of ancestors/descendants based on payload validity updates.
- Creating separation between `execution_layer` and the `beacon_chain` by creating a `PayloadStatus` struct.
- Refactoring how the `execution_layer` selects a `PayloadStatus` from the multiple statuses returned from multiple EEs.
- Adding testing framework for optimistic imports.
- Add `ExecutionBlockHash(Hash256)` new-type struct to avoid confusion between *beacon block roots* and *execution payload hashes*.
- Add `merge` to [`FORKS`](c3a793fd73/Makefile (L17)) in the `Makefile` to ensure we test the beacon chain with merge settings.
- Fix some tests here that were failing due to a missing execution layer.
## TODO
- [ ] Balance tests
Co-authored-by: Mark Mackey <mark@sigmaprime.io>
This commit is contained in:
@@ -5,7 +5,10 @@ use serde_derive::{Deserialize, Serialize};
|
||||
use ssz::{Decode, Encode};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::collections::HashMap;
|
||||
use types::{AttestationShufflingId, ChainSpec, Checkpoint, Epoch, EthSpec, Hash256, Slot};
|
||||
use types::{
|
||||
AttestationShufflingId, ChainSpec, Checkpoint, Epoch, EthSpec, ExecutionBlockHash, Hash256,
|
||||
Slot,
|
||||
};
|
||||
|
||||
pub const DEFAULT_PRUNE_THRESHOLD: usize = 256;
|
||||
|
||||
@@ -21,11 +24,11 @@ pub struct VoteTracker {
|
||||
#[ssz(enum_behaviour = "union")]
|
||||
pub enum ExecutionStatus {
|
||||
/// An EL has determined that the payload is valid.
|
||||
Valid(Hash256),
|
||||
Valid(ExecutionBlockHash),
|
||||
/// An EL has determined that the payload is invalid.
|
||||
Invalid(Hash256),
|
||||
Invalid(ExecutionBlockHash),
|
||||
/// An EL has not yet verified the execution payload.
|
||||
Unknown(Hash256),
|
||||
Unknown(ExecutionBlockHash),
|
||||
/// The block is either prior to the merge fork, or after the merge fork but before the terminal
|
||||
/// PoW block has been found.
|
||||
///
|
||||
@@ -41,7 +44,7 @@ impl ExecutionStatus {
|
||||
ExecutionStatus::Irrelevant(false)
|
||||
}
|
||||
|
||||
pub fn block_hash(&self) -> Option<Hash256> {
|
||||
pub fn block_hash(&self) -> Option<ExecutionBlockHash> {
|
||||
match self {
|
||||
ExecutionStatus::Valid(hash)
|
||||
| ExecutionStatus::Invalid(hash)
|
||||
@@ -49,6 +52,37 @@ impl ExecutionStatus {
|
||||
ExecutionStatus::Irrelevant(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the block:
|
||||
///
|
||||
/// - Has execution enabled
|
||||
/// - Has a valid payload
|
||||
pub fn is_valid(&self) -> bool {
|
||||
matches!(self, ExecutionStatus::Valid(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if the block:
|
||||
///
|
||||
/// - Has execution enabled
|
||||
/// - Has a payload that has not yet been verified by an EL.
|
||||
pub fn is_not_verified(&self) -> bool {
|
||||
matches!(self, ExecutionStatus::Unknown(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if the block:
|
||||
///
|
||||
/// - Has execution enabled
|
||||
/// - Has an invalid payload.
|
||||
pub fn is_invalid(&self) -> bool {
|
||||
matches!(self, ExecutionStatus::Invalid(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if the block:
|
||||
///
|
||||
/// - Does not have execution enabled (before or after Bellatrix fork)
|
||||
pub fn is_irrelevant(&self) -> bool {
|
||||
matches!(self, ExecutionStatus::Irrelevant(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// A block that is to be applied to the fork choice.
|
||||
@@ -150,6 +184,17 @@ impl ProtoArrayForkChoice {
|
||||
})
|
||||
}
|
||||
|
||||
/// See `ProtoArray::propagate_execution_payload_invalidation` for documentation.
|
||||
pub fn process_execution_payload_invalidation(
|
||||
&mut self,
|
||||
head_block_root: Hash256,
|
||||
latest_valid_ancestor_root: Option<ExecutionBlockHash>,
|
||||
) -> Result<(), String> {
|
||||
self.proto_array
|
||||
.propagate_execution_payload_invalidation(head_block_root, latest_valid_ancestor_root)
|
||||
.map_err(|e| format!("Failed to process invalid payload: {:?}", e))
|
||||
}
|
||||
|
||||
pub fn process_attestation(
|
||||
&mut self,
|
||||
validator_index: usize,
|
||||
@@ -267,25 +312,19 @@ impl ProtoArrayForkChoice {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the `descendant_root` has an ancestor with `ancestor_root`. Always
|
||||
/// returns `false` if either input roots are unknown.
|
||||
///
|
||||
/// ## Notes
|
||||
///
|
||||
/// Still returns `true` if `ancestor_root` is known and `ancestor_root == descendant_root`.
|
||||
/// Returns the weight of a given block.
|
||||
pub fn get_weight(&self, block_root: &Hash256) -> Option<u64> {
|
||||
let block_index = self.proto_array.indices.get(block_root)?;
|
||||
self.proto_array
|
||||
.nodes
|
||||
.get(*block_index)
|
||||
.map(|node| node.weight)
|
||||
}
|
||||
|
||||
/// See `ProtoArray` documentation.
|
||||
pub fn is_descendant(&self, ancestor_root: Hash256, descendant_root: Hash256) -> bool {
|
||||
self.proto_array
|
||||
.indices
|
||||
.get(&ancestor_root)
|
||||
.and_then(|ancestor_index| self.proto_array.nodes.get(*ancestor_index))
|
||||
.and_then(|ancestor| {
|
||||
self.proto_array
|
||||
.iter_block_roots(&descendant_root)
|
||||
.take_while(|(_root, slot)| *slot >= ancestor.slot)
|
||||
.find(|(_root, slot)| *slot == ancestor.slot)
|
||||
.map(|(root, _slot)| root == ancestor_root)
|
||||
})
|
||||
.unwrap_or(false)
|
||||
.is_descendant(ancestor_root, descendant_root)
|
||||
}
|
||||
|
||||
pub fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Epoch)> {
|
||||
|
||||
Reference in New Issue
Block a user