Merge branch 'eip4844' into deneb-free-blobs

This commit is contained in:
Pawan Dhananjay
2023-04-21 14:34:50 -07:00
62 changed files with 778 additions and 279 deletions

View File

@@ -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 {

View File

@@ -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.

View File

@@ -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
})
}
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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)
}
}