mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-18 21:38:31 +00:00
Merge branch 'eip4844' into deneb-free-blobs
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use crate::{ForkChoiceStore, InvalidationOperation};
|
||||
use proto_array::{
|
||||
Block as ProtoBlock, ExecutionStatus, ProposerHeadError, ProposerHeadInfo,
|
||||
ProtoArrayForkChoice, ReOrgThreshold,
|
||||
Block as ProtoBlock, DisallowedReOrgOffsets, ExecutionStatus, ProposerHeadError,
|
||||
ProposerHeadInfo, ProtoArrayForkChoice, ReOrgThreshold,
|
||||
};
|
||||
use slog::{crit, debug, warn, Logger};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@@ -533,6 +533,7 @@ where
|
||||
current_slot: Slot,
|
||||
canonical_head: Hash256,
|
||||
re_org_threshold: ReOrgThreshold,
|
||||
disallowed_offsets: &DisallowedReOrgOffsets,
|
||||
max_epochs_since_finalization: Epoch,
|
||||
) -> Result<ProposerHeadInfo, ProposerHeadError<Error<proto_array::Error>>> {
|
||||
// Ensure that fork choice has already been updated for the current slot. This prevents
|
||||
@@ -564,6 +565,7 @@ where
|
||||
canonical_head,
|
||||
self.fc_store.justified_balances(),
|
||||
re_org_threshold,
|
||||
disallowed_offsets,
|
||||
max_epochs_since_finalization,
|
||||
)
|
||||
.map_err(ProposerHeadError::convert_inner_error)
|
||||
@@ -573,6 +575,7 @@ where
|
||||
&self,
|
||||
canonical_head: Hash256,
|
||||
re_org_threshold: ReOrgThreshold,
|
||||
disallowed_offsets: &DisallowedReOrgOffsets,
|
||||
max_epochs_since_finalization: Epoch,
|
||||
) -> Result<ProposerHeadInfo, ProposerHeadError<Error<proto_array::Error>>> {
|
||||
let current_slot = self.fc_store.get_current_slot();
|
||||
@@ -582,6 +585,7 @@ where
|
||||
canonical_head,
|
||||
self.fc_store.justified_balances(),
|
||||
re_org_threshold,
|
||||
disallowed_offsets,
|
||||
max_epochs_since_finalization,
|
||||
)
|
||||
.map_err(ProposerHeadError::convert_inner_error)
|
||||
|
||||
@@ -50,6 +50,7 @@ pub enum Error {
|
||||
block_root: Hash256,
|
||||
parent_root: Hash256,
|
||||
},
|
||||
InvalidEpochOffset(u64),
|
||||
Arith(ArithError),
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ mod ssz_container;
|
||||
pub use crate::justified_balances::JustifiedBalances;
|
||||
pub use crate::proto_array::{calculate_committee_fraction, InvalidationOperation};
|
||||
pub use crate::proto_array_fork_choice::{
|
||||
Block, DoNotReOrg, ExecutionStatus, ProposerHeadError, ProposerHeadInfo, ProtoArrayForkChoice,
|
||||
ReOrgThreshold,
|
||||
Block, DisallowedReOrgOffsets, DoNotReOrg, ExecutionStatus, ProposerHeadError,
|
||||
ProposerHeadInfo, ProtoArrayForkChoice, ReOrgThreshold,
|
||||
};
|
||||
pub use error::Error;
|
||||
|
||||
|
||||
@@ -250,6 +250,9 @@ pub enum DoNotReOrg {
|
||||
ParentDistance,
|
||||
HeadDistance,
|
||||
ShufflingUnstable,
|
||||
DisallowedOffset {
|
||||
offset: u64,
|
||||
},
|
||||
JustificationAndFinalizationNotCompetitive,
|
||||
ChainNotFinalizing {
|
||||
epochs_since_finalization: u64,
|
||||
@@ -271,6 +274,9 @@ impl std::fmt::Display for DoNotReOrg {
|
||||
Self::ParentDistance => write!(f, "parent too far from head"),
|
||||
Self::HeadDistance => write!(f, "head too far from current slot"),
|
||||
Self::ShufflingUnstable => write!(f, "shuffling unstable at epoch boundary"),
|
||||
Self::DisallowedOffset { offset } => {
|
||||
write!(f, "re-orgs disabled at offset {offset}")
|
||||
}
|
||||
Self::JustificationAndFinalizationNotCompetitive => {
|
||||
write!(f, "justification or finalization not competitive")
|
||||
}
|
||||
@@ -304,6 +310,31 @@ impl std::fmt::Display for DoNotReOrg {
|
||||
#[serde(transparent)]
|
||||
pub struct ReOrgThreshold(pub u64);
|
||||
|
||||
/// New-type for disallowed re-org slots.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct DisallowedReOrgOffsets {
|
||||
// Vecs are faster than hashmaps for small numbers of items.
|
||||
offsets: Vec<u64>,
|
||||
}
|
||||
|
||||
impl Default for DisallowedReOrgOffsets {
|
||||
fn default() -> Self {
|
||||
DisallowedReOrgOffsets { offsets: vec![0] }
|
||||
}
|
||||
}
|
||||
|
||||
impl DisallowedReOrgOffsets {
|
||||
pub fn new<E: EthSpec>(offsets: Vec<u64>) -> Result<Self, Error> {
|
||||
for &offset in &offsets {
|
||||
if offset >= E::slots_per_epoch() {
|
||||
return Err(Error::InvalidEpochOffset(offset));
|
||||
}
|
||||
}
|
||||
Ok(Self { offsets })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct ProtoArrayForkChoice {
|
||||
pub(crate) proto_array: ProtoArray,
|
||||
@@ -460,6 +491,7 @@ impl ProtoArrayForkChoice {
|
||||
canonical_head: Hash256,
|
||||
justified_balances: &JustifiedBalances,
|
||||
re_org_threshold: ReOrgThreshold,
|
||||
disallowed_offsets: &DisallowedReOrgOffsets,
|
||||
max_epochs_since_finalization: Epoch,
|
||||
) -> Result<ProposerHeadInfo, ProposerHeadError<Error>> {
|
||||
let info = self.get_proposer_head_info::<E>(
|
||||
@@ -467,6 +499,7 @@ impl ProtoArrayForkChoice {
|
||||
canonical_head,
|
||||
justified_balances,
|
||||
re_org_threshold,
|
||||
disallowed_offsets,
|
||||
max_epochs_since_finalization,
|
||||
)?;
|
||||
|
||||
@@ -501,6 +534,7 @@ impl ProtoArrayForkChoice {
|
||||
canonical_head: Hash256,
|
||||
justified_balances: &JustifiedBalances,
|
||||
re_org_threshold: ReOrgThreshold,
|
||||
disallowed_offsets: &DisallowedReOrgOffsets,
|
||||
max_epochs_since_finalization: Epoch,
|
||||
) -> Result<ProposerHeadInfo, ProposerHeadError<Error>> {
|
||||
let mut nodes = self
|
||||
@@ -545,6 +579,12 @@ impl ProtoArrayForkChoice {
|
||||
return Err(DoNotReOrg::ShufflingUnstable.into());
|
||||
}
|
||||
|
||||
// Check allowed slot offsets.
|
||||
let offset = (re_org_block_slot % E::slots_per_epoch()).as_u64();
|
||||
if disallowed_offsets.offsets.contains(&offset) {
|
||||
return Err(DoNotReOrg::DisallowedOffset { offset }.into());
|
||||
}
|
||||
|
||||
// Check FFG.
|
||||
let ffg_competitive = parent_node.unrealized_justified_checkpoint
|
||||
== head_node.unrealized_justified_checkpoint
|
||||
|
||||
@@ -41,4 +41,4 @@ pub use per_epoch_processing::{
|
||||
errors::EpochProcessingError, process_epoch as per_epoch_processing,
|
||||
};
|
||||
pub use per_slot_processing::{per_slot_processing, Error as SlotProcessingError};
|
||||
pub use verify_operation::{SigVerifiedOp, VerifyOperation};
|
||||
pub use verify_operation::{SigVerifiedOp, VerifyOperation, VerifyOperationAt};
|
||||
|
||||
@@ -283,7 +283,8 @@ pub fn process_exits<T: EthSpec>(
|
||||
// Verify and apply each exit in series. We iterate in series because higher-index exits may
|
||||
// become invalid due to the application of lower-index ones.
|
||||
for (i, exit) in voluntary_exits.iter().enumerate() {
|
||||
verify_exit(state, exit, verify_signatures, spec).map_err(|e| e.into_with_index(i))?;
|
||||
verify_exit(state, None, exit, verify_signatures, spec)
|
||||
.map_err(|e| e.into_with_index(i))?;
|
||||
|
||||
initiate_validator_exit(state, exit.message.validator_index as usize, spec)?;
|
||||
}
|
||||
|
||||
@@ -978,8 +978,14 @@ async fn fork_spanning_exit() {
|
||||
let head = harness.chain.canonical_head.cached_head();
|
||||
let head_state = &head.snapshot.beacon_state;
|
||||
assert!(head_state.current_epoch() < spec.altair_fork_epoch.unwrap());
|
||||
verify_exit(head_state, &signed_exit, VerifySignatures::True, &spec)
|
||||
.expect("phase0 exit verifies against phase0 state");
|
||||
verify_exit(
|
||||
head_state,
|
||||
None,
|
||||
&signed_exit,
|
||||
VerifySignatures::True,
|
||||
&spec,
|
||||
)
|
||||
.expect("phase0 exit verifies against phase0 state");
|
||||
|
||||
/*
|
||||
* Ensure the exit verifies after Altair.
|
||||
@@ -992,8 +998,14 @@ async fn fork_spanning_exit() {
|
||||
let head_state = &head.snapshot.beacon_state;
|
||||
assert!(head_state.current_epoch() >= spec.altair_fork_epoch.unwrap());
|
||||
assert!(head_state.current_epoch() < spec.bellatrix_fork_epoch.unwrap());
|
||||
verify_exit(head_state, &signed_exit, VerifySignatures::True, &spec)
|
||||
.expect("phase0 exit verifies against altair state");
|
||||
verify_exit(
|
||||
head_state,
|
||||
None,
|
||||
&signed_exit,
|
||||
VerifySignatures::True,
|
||||
&spec,
|
||||
)
|
||||
.expect("phase0 exit verifies against altair state");
|
||||
|
||||
/*
|
||||
* Ensure the exit no longer verifies after Bellatrix.
|
||||
@@ -1009,6 +1021,12 @@ async fn fork_spanning_exit() {
|
||||
let head = harness.chain.canonical_head.cached_head();
|
||||
let head_state = &head.snapshot.beacon_state;
|
||||
assert!(head_state.current_epoch() >= spec.bellatrix_fork_epoch.unwrap());
|
||||
verify_exit(head_state, &signed_exit, VerifySignatures::True, &spec)
|
||||
.expect_err("phase0 exit does not verify against bellatrix state");
|
||||
verify_exit(
|
||||
head_state,
|
||||
None,
|
||||
&signed_exit,
|
||||
VerifySignatures::True,
|
||||
&spec,
|
||||
)
|
||||
.expect_err("phase0 exit does not verify against bellatrix state");
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@ fn error(reason: ExitInvalid) -> BlockOperationError<ExitInvalid> {
|
||||
/// Spec v0.12.1
|
||||
pub fn verify_exit<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
current_epoch: Option<Epoch>,
|
||||
signed_exit: &SignedVoluntaryExit,
|
||||
verify_signatures: VerifySignatures,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<()> {
|
||||
let current_epoch = current_epoch.unwrap_or(state.current_epoch());
|
||||
let exit = &signed_exit.message;
|
||||
|
||||
let validator = state
|
||||
@@ -33,7 +35,7 @@ pub fn verify_exit<T: EthSpec>(
|
||||
|
||||
// Verify the validator is active.
|
||||
verify!(
|
||||
validator.is_active_at(state.current_epoch()),
|
||||
validator.is_active_at(current_epoch),
|
||||
ExitInvalid::NotActive(exit.validator_index)
|
||||
);
|
||||
|
||||
@@ -45,9 +47,9 @@ pub fn verify_exit<T: EthSpec>(
|
||||
|
||||
// Exits must specify an epoch when they become valid; they are not valid before then.
|
||||
verify!(
|
||||
state.current_epoch() >= exit.epoch,
|
||||
current_epoch >= exit.epoch,
|
||||
ExitInvalid::FutureEpoch {
|
||||
state: state.current_epoch(),
|
||||
state: current_epoch,
|
||||
exit: exit.epoch
|
||||
}
|
||||
);
|
||||
@@ -57,9 +59,9 @@ pub fn verify_exit<T: EthSpec>(
|
||||
.activation_epoch
|
||||
.safe_add(spec.shard_committee_period)?;
|
||||
verify!(
|
||||
state.current_epoch() >= earliest_exit_epoch,
|
||||
current_epoch >= earliest_exit_epoch,
|
||||
ExitInvalid::TooYoungToExit {
|
||||
current_epoch: state.current_epoch(),
|
||||
current_epoch,
|
||||
earliest_exit_epoch,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -134,7 +134,7 @@ impl<E: EthSpec> VerifyOperation<E> for SignedVoluntaryExit {
|
||||
state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<SigVerifiedOp<Self, E>, Self::Error> {
|
||||
verify_exit(state, &self, VerifySignatures::True, spec)?;
|
||||
verify_exit(state, None, &self, VerifySignatures::True, spec)?;
|
||||
Ok(SigVerifiedOp::new(self, state))
|
||||
}
|
||||
|
||||
@@ -205,3 +205,35 @@ impl<E: EthSpec> VerifyOperation<E> for SignedBlsToExecutionChange {
|
||||
smallvec![]
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for operations that can be verified and transformed into a
|
||||
/// `SigVerifiedOp`.
|
||||
///
|
||||
/// The `At` suffix indicates that we can specify a particular epoch at which to
|
||||
/// verify the operation.
|
||||
pub trait VerifyOperationAt<E: EthSpec>: VerifyOperation<E> + Sized {
|
||||
fn validate_at(
|
||||
self,
|
||||
state: &BeaconState<E>,
|
||||
validate_at_epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<SigVerifiedOp<Self, E>, Self::Error>;
|
||||
}
|
||||
|
||||
impl<E: EthSpec> VerifyOperationAt<E> for SignedVoluntaryExit {
|
||||
fn validate_at(
|
||||
self,
|
||||
state: &BeaconState<E>,
|
||||
validate_at_epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<SigVerifiedOp<Self, E>, Self::Error> {
|
||||
verify_exit(
|
||||
state,
|
||||
Some(validate_at_epoch),
|
||||
&self,
|
||||
VerifySignatures::True,
|
||||
spec,
|
||||
)?;
|
||||
Ok(SigVerifiedOp::new(self, state))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ impl ValidatorsListTreeHashCache {
|
||||
validators.len(),
|
||||
),
|
||||
list_arena,
|
||||
values: ParallelValidatorTreeHash::new::<E>(validators),
|
||||
values: ParallelValidatorTreeHash::new(validators),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,7 +468,7 @@ impl ParallelValidatorTreeHash {
|
||||
///
|
||||
/// Allocates the necessary memory to store all of the cached Merkle trees but does perform any
|
||||
/// hashing.
|
||||
fn new<E: EthSpec>(validators: &[Validator]) -> Self {
|
||||
fn new(validators: &[Validator]) -> Self {
|
||||
let num_arenas = std::cmp::max(
|
||||
1,
|
||||
(validators.len() + VALIDATORS_PER_ARENA - 1) / VALIDATORS_PER_ARENA,
|
||||
|
||||
@@ -45,43 +45,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||
pub struct ExecutionOptimisticForkVersionedResponse<T> {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub version: Option<ForkName>,
|
||||
pub execution_optimistic: Option<bool>,
|
||||
pub data: T,
|
||||
}
|
||||
|
||||
impl<'de, F> serde::Deserialize<'de> for ExecutionOptimisticForkVersionedResponse<F>
|
||||
where
|
||||
F: ForkVersionDeserialize,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Helper {
|
||||
version: Option<ForkName>,
|
||||
execution_optimistic: Option<bool>,
|
||||
data: serde_json::Value,
|
||||
}
|
||||
|
||||
let helper = Helper::deserialize(deserializer)?;
|
||||
let data = match helper.version {
|
||||
Some(fork_name) => F::deserialize_by_fork::<'de, D>(helper.data, fork_name)?,
|
||||
None => serde_json::from_value(helper.data).map_err(serde::de::Error::custom)?,
|
||||
};
|
||||
|
||||
Ok(ExecutionOptimisticForkVersionedResponse {
|
||||
version: helper.version,
|
||||
execution_optimistic: helper.execution_optimistic,
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ForkVersionDeserialize: Sized + DeserializeOwned {
|
||||
fn deserialize_by_fork<'de, D: Deserializer<'de>>(
|
||||
value: Value,
|
||||
|
||||
@@ -148,9 +148,7 @@ pub use crate::fork::Fork;
|
||||
pub use crate::fork_context::ForkContext;
|
||||
pub use crate::fork_data::ForkData;
|
||||
pub use crate::fork_name::{ForkName, InconsistentFork};
|
||||
pub use crate::fork_versioned_response::{
|
||||
ExecutionOptimisticForkVersionedResponse, ForkVersionDeserialize, ForkVersionedResponse,
|
||||
};
|
||||
pub use crate::fork_versioned_response::{ForkVersionDeserialize, ForkVersionedResponse};
|
||||
pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN};
|
||||
pub use crate::historical_batch::HistoricalBatch;
|
||||
pub use crate::indexed_attestation::IndexedAttestation;
|
||||
|
||||
Reference in New Issue
Block a user