mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-11 18:04:18 +00:00
Merge branch 'eip4844' into deneb-free-blobs
This commit is contained in:
@@ -118,7 +118,6 @@ use types::beacon_block_body::KzgCommitments;
|
||||
use types::beacon_state::CloneConfig;
|
||||
use types::blob_sidecar::{BlobIdentifier, BlobSidecarList, Blobs};
|
||||
use types::consts::deneb::MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS;
|
||||
use types::consts::merge::INTERVALS_PER_SLOT;
|
||||
use types::*;
|
||||
|
||||
pub type ForkChoiceError = fork_choice::Error<crate::ForkChoiceStoreError>;
|
||||
@@ -140,12 +139,6 @@ pub const VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT: Duration = Duration::from_secs(1)
|
||||
/// The timeout for the eth1 finalization cache
|
||||
pub const ETH1_FINALIZATION_CACHE_LOCK_TIMEOUT: Duration = Duration::from_millis(200);
|
||||
|
||||
/// The latest delay from the start of the slot at which to attempt a 1-slot re-org.
|
||||
fn max_re_org_slot_delay(seconds_per_slot: u64) -> Duration {
|
||||
// Allow at least half of the attestation deadline for the block to propagate.
|
||||
Duration::from_secs(seconds_per_slot) / INTERVALS_PER_SLOT as u32 / 2
|
||||
}
|
||||
|
||||
// These keys are all zero because they get stored in different columns, see `DBColumn` type.
|
||||
pub const BEACON_CHAIN_DB_KEY: Hash256 = Hash256::zero();
|
||||
pub const OP_POOL_DB_KEY: Hash256 = Hash256::zero();
|
||||
@@ -401,7 +394,7 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
||||
/// in recent epochs.
|
||||
pub(crate) observed_sync_aggregators: RwLock<ObservedSyncAggregators<T::EthSpec>>,
|
||||
/// Maintains a record of which validators have proposed blocks for each slot.
|
||||
pub(crate) observed_block_producers: RwLock<ObservedBlockProducers<T::EthSpec>>,
|
||||
pub observed_block_producers: RwLock<ObservedBlockProducers<T::EthSpec>>,
|
||||
/// Maintains a record of blob sidecars seen over the gossip network.
|
||||
pub(crate) observed_blob_sidecars: RwLock<ObservedBlobSidecars<T::EthSpec>>,
|
||||
/// Maintains a record of which validators have submitted voluntary exits.
|
||||
@@ -1106,7 +1099,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.execution_layer
|
||||
.as_ref()
|
||||
.ok_or(Error::ExecutionLayerMissing)?
|
||||
.get_payload_by_block_hash(exec_block_hash, fork)
|
||||
.get_payload_for_header(&execution_payload_header, fork)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::ExecutionLayerErrorPayloadReconstruction(exec_block_hash, Box::new(e))
|
||||
@@ -2298,12 +2291,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
&self,
|
||||
exit: SignedVoluntaryExit,
|
||||
) -> Result<ObservationOutcome<SignedVoluntaryExit, T::EthSpec>, Error> {
|
||||
// NOTE: this could be more efficient if it avoided cloning the head state
|
||||
let wall_clock_state = self.wall_clock_state()?;
|
||||
let head_snapshot = self.head().snapshot;
|
||||
let head_state = &head_snapshot.beacon_state;
|
||||
let wall_clock_epoch = self.epoch()?;
|
||||
|
||||
Ok(self
|
||||
.observed_voluntary_exits
|
||||
.lock()
|
||||
.verify_and_observe(exit, &wall_clock_state, &self.spec)
|
||||
.verify_and_observe_at(exit, wall_clock_epoch, head_state, &self.spec)
|
||||
.map(|exit| {
|
||||
// this method is called for both API and gossip exits, so this covers all exit events
|
||||
if let Some(event_handler) = self.event_handler.as_ref() {
|
||||
@@ -3802,7 +3797,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let (state, state_root_opt) = self
|
||||
.task_executor
|
||||
.spawn_blocking_handle(
|
||||
move || chain.load_state_for_block_production::<Payload>(slot),
|
||||
move || chain.load_state_for_block_production(slot),
|
||||
"produce_partial_beacon_block",
|
||||
)
|
||||
.ok_or(BlockProductionError::ShuttingDown)?
|
||||
@@ -3825,7 +3820,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
/// Load a beacon state from the database for block production. This is a long-running process
|
||||
/// that should not be performed in an `async` context.
|
||||
fn load_state_for_block_production<Payload: ExecPayload<T::EthSpec>>(
|
||||
fn load_state_for_block_production(
|
||||
self: &Arc<Self>,
|
||||
slot: Slot,
|
||||
) -> Result<(BeaconState<T::EthSpec>, Option<Hash256>), BlockProductionError> {
|
||||
@@ -3939,7 +3934,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// 1. It seems we have time to propagate and still receive the proposer boost.
|
||||
// 2. The current head block was seen late.
|
||||
// 3. The `get_proposer_head` conditions from fork choice pass.
|
||||
let proposing_on_time = slot_delay < max_re_org_slot_delay(self.spec.seconds_per_slot);
|
||||
let proposing_on_time = slot_delay < self.config.re_org_cutoff(self.spec.seconds_per_slot);
|
||||
if !proposing_on_time {
|
||||
debug!(
|
||||
self.log,
|
||||
@@ -3969,6 +3964,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
slot,
|
||||
canonical_head,
|
||||
re_org_threshold,
|
||||
&self.config.re_org_disallowed_offsets,
|
||||
self.config.re_org_max_epochs_since_finalization,
|
||||
)
|
||||
.map_err(|e| match e {
|
||||
@@ -4247,6 +4243,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.get_preliminary_proposer_head(
|
||||
head_block_root,
|
||||
re_org_threshold,
|
||||
&self.config.re_org_disallowed_offsets,
|
||||
self.config.re_org_max_epochs_since_finalization,
|
||||
)
|
||||
.map_err(|e| e.map_inner_error(Error::ProposerHeadForkChoiceError))?;
|
||||
@@ -4257,7 +4254,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let re_org_block_slot = head_slot + 1;
|
||||
let fork_choice_slot = info.current_slot;
|
||||
|
||||
// If a re-orging proposal isn't made by the `max_re_org_slot_delay` then we give up
|
||||
// If a re-orging proposal isn't made by the `re_org_cutoff` then we give up
|
||||
// and allow the fork choice update for the canonical head through so that we may attest
|
||||
// correctly.
|
||||
let current_slot_ok = if head_slot == fork_choice_slot {
|
||||
@@ -4268,7 +4265,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.and_then(|slot_start| {
|
||||
let now = self.slot_clock.now_duration()?;
|
||||
let slot_delay = now.saturating_sub(slot_start);
|
||||
Some(slot_delay <= max_re_org_slot_delay(self.spec.seconds_per_slot))
|
||||
Some(slot_delay <= self.config.re_org_cutoff(self.spec.seconds_per_slot))
|
||||
})
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
|
||||
@@ -25,7 +25,7 @@ use futures::channel::mpsc::Sender;
|
||||
use kzg::{Kzg, TrustedSetup};
|
||||
use operation_pool::{OperationPool, PersistedOperationPool};
|
||||
use parking_lot::RwLock;
|
||||
use proto_array::ReOrgThreshold;
|
||||
use proto_array::{DisallowedReOrgOffsets, ReOrgThreshold};
|
||||
use slasher::Slasher;
|
||||
use slog::{crit, error, info, Logger};
|
||||
use slot_clock::{SlotClock, TestingSlotClock};
|
||||
@@ -180,6 +180,15 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the proposer re-org disallowed offsets list.
|
||||
pub fn proposer_re_org_disallowed_offsets(
|
||||
mut self,
|
||||
disallowed_offsets: DisallowedReOrgOffsets,
|
||||
) -> Self {
|
||||
self.chain_config.re_org_disallowed_offsets = disallowed_offsets;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the store (database).
|
||||
///
|
||||
/// Should generally be called early in the build chain.
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
pub use proto_array::ReOrgThreshold;
|
||||
pub use proto_array::{DisallowedReOrgOffsets, ReOrgThreshold};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use types::{Checkpoint, Epoch};
|
||||
|
||||
pub const DEFAULT_RE_ORG_THRESHOLD: ReOrgThreshold = ReOrgThreshold(20);
|
||||
pub const DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION: Epoch = Epoch::new(2);
|
||||
/// Default to 1/12th of the slot, which is 1 second on mainnet.
|
||||
pub const DEFAULT_RE_ORG_CUTOFF_DENOMINATOR: u32 = 12;
|
||||
pub const DEFAULT_FORK_CHOICE_BEFORE_PROPOSAL_TIMEOUT: u64 = 250;
|
||||
|
||||
/// Default fraction of a slot lookahead for payload preparation (12/3 = 4 seconds on mainnet).
|
||||
@@ -34,6 +36,13 @@ pub struct ChainConfig {
|
||||
pub re_org_threshold: Option<ReOrgThreshold>,
|
||||
/// Maximum number of epochs since finalization for attempting a proposer re-org.
|
||||
pub re_org_max_epochs_since_finalization: Epoch,
|
||||
/// Maximum delay after the start of the slot at which to propose a reorging block.
|
||||
pub re_org_cutoff_millis: Option<u64>,
|
||||
/// Additional epoch offsets at which re-orging block proposals are not permitted.
|
||||
///
|
||||
/// By default this list is empty, but it can be useful for reacting to network conditions, e.g.
|
||||
/// slow gossip of re-org blocks at slot 1 in the epoch.
|
||||
pub re_org_disallowed_offsets: DisallowedReOrgOffsets,
|
||||
/// Number of milliseconds to wait for fork choice before proposing a block.
|
||||
///
|
||||
/// If set to 0 then block proposal will not wait for fork choice at all.
|
||||
@@ -82,6 +91,8 @@ impl Default for ChainConfig {
|
||||
max_network_size: 10 * 1_048_576, // 10M
|
||||
re_org_threshold: Some(DEFAULT_RE_ORG_THRESHOLD),
|
||||
re_org_max_epochs_since_finalization: DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION,
|
||||
re_org_cutoff_millis: None,
|
||||
re_org_disallowed_offsets: DisallowedReOrgOffsets::default(),
|
||||
fork_choice_before_proposal_timeout_ms: DEFAULT_FORK_CHOICE_BEFORE_PROPOSAL_TIMEOUT,
|
||||
// Builder fallback configs that are set in `clap` will override these.
|
||||
builder_fallback_skips: 3,
|
||||
@@ -100,3 +111,14 @@ impl Default for ChainConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChainConfig {
|
||||
/// The latest delay from the start of the slot at which to attempt a 1-slot re-org.
|
||||
pub fn re_org_cutoff(&self, seconds_per_slot: u64) -> Duration {
|
||||
self.re_org_cutoff_millis
|
||||
.map(Duration::from_millis)
|
||||
.unwrap_or_else(|| {
|
||||
Duration::from_secs(seconds_per_slot) / DEFAULT_RE_ORG_CUTOFF_DENOMINATOR
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ fn get_sync_status<T: EthSpec>(
|
||||
let period = T::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let voting_period_start_slot = (current_slot / period) * period;
|
||||
|
||||
let period_start = slot_start_seconds::<T>(
|
||||
let period_start = slot_start_seconds(
|
||||
genesis_time,
|
||||
spec.seconds_per_slot,
|
||||
voting_period_start_slot,
|
||||
@@ -470,7 +470,7 @@ impl<T: EthSpec> Eth1ChainBackend<T> for CachingEth1Backend<T> {
|
||||
fn eth1_data(&self, state: &BeaconState<T>, spec: &ChainSpec) -> Result<Eth1Data, Error> {
|
||||
let period = T::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let voting_period_start_slot = (state.slot() / period) * period;
|
||||
let voting_period_start_seconds = slot_start_seconds::<T>(
|
||||
let voting_period_start_seconds = slot_start_seconds(
|
||||
state.genesis_time(),
|
||||
spec.seconds_per_slot,
|
||||
voting_period_start_slot,
|
||||
@@ -658,11 +658,7 @@ fn find_winning_vote(valid_votes: Eth1DataVoteCount) -> Option<Eth1Data> {
|
||||
}
|
||||
|
||||
/// Returns the unix-epoch seconds at the start of the given `slot`.
|
||||
fn slot_start_seconds<T: EthSpec>(
|
||||
genesis_unix_seconds: u64,
|
||||
seconds_per_slot: u64,
|
||||
slot: Slot,
|
||||
) -> u64 {
|
||||
fn slot_start_seconds(genesis_unix_seconds: u64, seconds_per_slot: u64, slot: Slot) -> u64 {
|
||||
genesis_unix_seconds + slot.as_u64() * seconds_per_slot
|
||||
}
|
||||
|
||||
@@ -698,7 +694,7 @@ mod test {
|
||||
fn get_voting_period_start_seconds(state: &BeaconState<E>, spec: &ChainSpec) -> u64 {
|
||||
let period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let voting_period_start_slot = (state.slot() / period) * period;
|
||||
slot_start_seconds::<E>(
|
||||
slot_start_seconds(
|
||||
state.genesis_time(),
|
||||
spec.seconds_per_slot,
|
||||
voting_period_start_slot,
|
||||
@@ -708,23 +704,23 @@ mod test {
|
||||
#[test]
|
||||
fn slot_start_time() {
|
||||
let zero_sec = 0;
|
||||
assert_eq!(slot_start_seconds::<E>(100, zero_sec, Slot::new(2)), 100);
|
||||
assert_eq!(slot_start_seconds(100, zero_sec, Slot::new(2)), 100);
|
||||
|
||||
let one_sec = 1;
|
||||
assert_eq!(slot_start_seconds::<E>(100, one_sec, Slot::new(0)), 100);
|
||||
assert_eq!(slot_start_seconds::<E>(100, one_sec, Slot::new(1)), 101);
|
||||
assert_eq!(slot_start_seconds::<E>(100, one_sec, Slot::new(2)), 102);
|
||||
assert_eq!(slot_start_seconds(100, one_sec, Slot::new(0)), 100);
|
||||
assert_eq!(slot_start_seconds(100, one_sec, Slot::new(1)), 101);
|
||||
assert_eq!(slot_start_seconds(100, one_sec, Slot::new(2)), 102);
|
||||
|
||||
let three_sec = 3;
|
||||
assert_eq!(slot_start_seconds::<E>(100, three_sec, Slot::new(0)), 100);
|
||||
assert_eq!(slot_start_seconds::<E>(100, three_sec, Slot::new(1)), 103);
|
||||
assert_eq!(slot_start_seconds::<E>(100, three_sec, Slot::new(2)), 106);
|
||||
assert_eq!(slot_start_seconds(100, three_sec, Slot::new(0)), 100);
|
||||
assert_eq!(slot_start_seconds(100, three_sec, Slot::new(1)), 103);
|
||||
assert_eq!(slot_start_seconds(100, three_sec, Slot::new(2)), 106);
|
||||
|
||||
let five_sec = 5;
|
||||
assert_eq!(slot_start_seconds::<E>(100, five_sec, Slot::new(0)), 100);
|
||||
assert_eq!(slot_start_seconds::<E>(100, five_sec, Slot::new(1)), 105);
|
||||
assert_eq!(slot_start_seconds::<E>(100, five_sec, Slot::new(2)), 110);
|
||||
assert_eq!(slot_start_seconds::<E>(100, five_sec, Slot::new(3)), 115);
|
||||
assert_eq!(slot_start_seconds(100, five_sec, Slot::new(0)), 100);
|
||||
assert_eq!(slot_start_seconds(100, five_sec, Slot::new(1)), 105);
|
||||
assert_eq!(slot_start_seconds(100, five_sec, Slot::new(2)), 110);
|
||||
assert_eq!(slot_start_seconds(100, five_sec, Slot::new(3)), 115);
|
||||
}
|
||||
|
||||
fn get_eth1_block(timestamp: u64, number: u64) -> Eth1Block {
|
||||
|
||||
@@ -37,7 +37,7 @@ mod naive_aggregation_pool;
|
||||
mod observed_aggregates;
|
||||
mod observed_attesters;
|
||||
mod observed_blob_sidecars;
|
||||
mod observed_block_producers;
|
||||
pub mod observed_block_producers;
|
||||
pub mod observed_operations;
|
||||
pub mod otb_verification_service;
|
||||
mod persisted_beacon_chain;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use derivative::Derivative;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use ssz::{Decode, Encode};
|
||||
use state_processing::{SigVerifiedOp, VerifyOperation};
|
||||
use state_processing::{SigVerifiedOp, VerifyOperation, VerifyOperationAt};
|
||||
use std::collections::HashSet;
|
||||
use std::marker::PhantomData;
|
||||
use types::{
|
||||
AttesterSlashing, BeaconState, ChainSpec, EthSpec, ForkName, ProposerSlashing,
|
||||
AttesterSlashing, BeaconState, ChainSpec, Epoch, EthSpec, ForkName, ProposerSlashing,
|
||||
SignedBlsToExecutionChange, SignedVoluntaryExit, Slot,
|
||||
};
|
||||
|
||||
@@ -87,12 +87,16 @@ impl<E: EthSpec> ObservableOperation<E> for SignedBlsToExecutionChange {
|
||||
}
|
||||
|
||||
impl<T: ObservableOperation<E>, E: EthSpec> ObservedOperations<T, E> {
|
||||
pub fn verify_and_observe(
|
||||
pub fn verify_and_observe_parametric<F>(
|
||||
&mut self,
|
||||
op: T,
|
||||
validate: F,
|
||||
head_state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<ObservationOutcome<T, E>, T::Error> {
|
||||
) -> Result<ObservationOutcome<T, E>, T::Error>
|
||||
where
|
||||
F: Fn(T) -> Result<SigVerifiedOp<T, E>, T::Error>,
|
||||
{
|
||||
self.reset_at_fork_boundary(head_state.slot(), spec);
|
||||
|
||||
let observed_validator_indices = &mut self.observed_validator_indices;
|
||||
@@ -112,7 +116,7 @@ impl<T: ObservableOperation<E>, E: EthSpec> ObservedOperations<T, E> {
|
||||
}
|
||||
|
||||
// Validate the op using operation-specific logic (`verify_attester_slashing`, etc).
|
||||
let verified_op = op.validate(head_state, spec)?;
|
||||
let verified_op = validate(op)?;
|
||||
|
||||
// Add the relevant indices to the set of known indices to prevent processing of duplicates
|
||||
// in the future.
|
||||
@@ -121,6 +125,16 @@ impl<T: ObservableOperation<E>, E: EthSpec> ObservedOperations<T, E> {
|
||||
Ok(ObservationOutcome::New(verified_op))
|
||||
}
|
||||
|
||||
pub fn verify_and_observe(
|
||||
&mut self,
|
||||
op: T,
|
||||
head_state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<ObservationOutcome<T, E>, T::Error> {
|
||||
let validate = |op: T| op.validate(head_state, spec);
|
||||
self.verify_and_observe_parametric(op, validate, head_state, spec)
|
||||
}
|
||||
|
||||
/// Reset the cache when crossing a fork boundary.
|
||||
///
|
||||
/// This prevents an attacker from crafting a self-slashing which is only valid before the fork
|
||||
@@ -140,3 +154,16 @@ impl<T: ObservableOperation<E>, E: EthSpec> ObservedOperations<T, E> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObservableOperation<E> + VerifyOperationAt<E>, E: EthSpec> ObservedOperations<T, E> {
|
||||
pub fn verify_and_observe_at(
|
||||
&mut self,
|
||||
op: T,
|
||||
verify_at_epoch: Epoch,
|
||||
head_state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<ObservationOutcome<T, E>, T::Error> {
|
||||
let validate = |op: T| op.validate_at(head_state, verify_at_epoch, spec);
|
||||
self.verify_and_observe_parametric(op, validate, head_state, spec)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user