Skip to content

Commit 647cc64

Browse files
committed
Merge updated base to resolve PR #9929 conflicts
2 parents 6b1bcef + 2a6fc09 commit 647cc64

111 files changed

Lines changed: 4474 additions & 827 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docker-reproducible.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ jobs:
4646
- arch: amd64
4747
rust_target: x86_64-unknown-linux-gnu
4848
rust_image: >-
49-
rust:1.88-bullseye@sha256:8e3c421122bf4cd3b2a866af41a4dd52d87ad9e315fd2cb5100e87a7187a9816
49+
rust:1.88-bookworm@sha256:4727898c104ecd2e22d780925832502faee9fe4e70581b8572af081370b315a0
5050
platform: linux/amd64
5151
runner: ubuntu-22.04
5252
- arch: arm64
5353
rust_target: aarch64-unknown-linux-gnu
5454
rust_image: >-
55-
rust:1.88-bullseye@sha256:8b22455a7ce2adb1355067638284ee99d21cc516fab63a96c4514beaf370aa94
55+
rust:1.88-bookworm@sha256:8aa70d1416cf5b1cff4b95ec6c57f1c5e4e649a3b53d616a26695cda6fbb46bc
5656
platform: linux/arm64
5757
runner: ubuntu-22.04-arm
5858
runs-on: ${{ matrix.runner }}

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ lockfile = { path = "common/lockfile" }
180180
log = "0.4"
181181
logging = { path = "common/logging" }
182182
logroller = "0.1.8"
183+
lru = "0.13"
183184
lru_cache = { path = "common/lru_cache" }
184185
malloc_utils = { path = "common/malloc_utils" }
185186
maplit = "1"

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM rust:1.88.0-bullseye AS builder
1+
FROM rust:1.88.0-bookworm AS builder
22
RUN apt-get update && apt-get -y upgrade && apt-get install -y cmake libclang-dev
33
ARG FEATURES
44
ARG PROFILE=release
@@ -15,7 +15,7 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
1515
--mount=type=cache,target=/lighthouse/target \
1616
make
1717

18-
FROM ubuntu:22.04
18+
FROM ubuntu:24.04
1919
RUN apt-get update && apt-get -y upgrade && apt-get install -y --no-install-recommends \
2020
libssl-dev \
2121
ca-certificates \

Dockerfile.reproducible

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# Define the Rust image as an argument with a default to x86_64 Rust 1.88 image based on Debian Bullseye
2-
ARG RUST_IMAGE="rust:1.88-bullseye@sha256:8e3c421122bf4cd3b2a866af41a4dd52d87ad9e315fd2cb5100e87a7187a9816"
1+
# Define the Rust image as an argument with a default to x86_64 Rust 1.88 image based on Debian Bookworm
2+
ARG RUST_IMAGE="rust:1.88-bookworm@sha256:4727898c104ecd2e22d780925832502faee9fe4e70581b8572af081370b315a0"
33
FROM ${RUST_IMAGE} AS builder
44

55
# Install specific version of the build dependencies
6-
RUN apt-get update && apt-get install -y libclang-dev=1:11.0-51+nmu5 cmake=3.18.4-2+deb11u1 libjemalloc-dev=5.2.1-3
6+
RUN apt-get update && apt-get install -y libclang-dev=1:14.0-55.7~deb12u1 cmake=3.25.1-1 libjemalloc-dev=5.3.0-1
77

88
ARG RUST_TARGET="x86_64-unknown-linux-gnu"
99

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ JEMALLOC_OVERRIDE = /usr/lib/$(JEMALLOC_LIB_ARCH)-linux-gnu/libjemalloc.a
117117
RUST_TARGET ?= x86_64-unknown-linux-gnu
118118

119119
# Default images for different architectures
120-
RUST_IMAGE_AMD64 ?= rust:1.88-bullseye@sha256:8e3c421122bf4cd3b2a866af41a4dd52d87ad9e315fd2cb5100e87a7187a9816
121-
RUST_IMAGE_ARM64 ?= rust:1.88-bullseye@sha256:8b22455a7ce2adb1355067638284ee99d21cc516fab63a96c4514beaf370aa94
120+
RUST_IMAGE_AMD64 ?= rust:1.88-bookworm@sha256:4727898c104ecd2e22d780925832502faee9fe4e70581b8572af081370b315a0
121+
RUST_IMAGE_ARM64 ?= rust:1.88-bookworm@sha256:8aa70d1416cf5b1cff4b95ec6c57f1c5e4e649a3b53d616a26695cda6fbb46bc
122122

123123
.PHONY: build-reproducible
124124
build-reproducible: ## Build the lighthouse binary into `target` directory with reproducible builds

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 101 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use crate::envelope_times_cache::EnvelopeTimesCache;
3434
use crate::errors::{BeaconChainError as Error, BlockProductionError};
3535
use crate::events::ServerSentEventHandler;
3636
use crate::execution_payload::{NotifyExecutionLayer, PreparePayloadHandle, get_execution_payload};
37-
use crate::execution_proof_verification::ObservedExecutionProofs;
37+
use crate::execution_proof_verification::{GossipVerifiedExecutionProof, ObservedExecutionProofs};
3838
use crate::fork_choice_signal::{ForkChoiceSignalRx, ForkChoiceSignalTx};
3939
use crate::graffiti_calculator::{GraffitiCalculator, GraffitiSettings};
4040
use crate::light_client_finality_update_verification::{
@@ -58,6 +58,7 @@ use crate::observed_attesters::{
5858
};
5959
use crate::observed_block_producers::ObservedBlockProducers;
6060
use crate::observed_data_sidecars::ObservedDataSidecars;
61+
use crate::observed_execution_payloads::ObservedExecutionPayloads;
6162
use crate::observed_operations::{ObservationOutcome, ObservedOperations};
6263
use crate::observed_slashable::ObservedSlashable;
6364
use crate::partial_data_column_assembler::PartialMergeResult;
@@ -123,7 +124,9 @@ use slasher::Slasher;
123124
use slot_clock::SlotClock;
124125
use ssz::Encode;
125126
use state_processing::{
126-
BlockSignatureStrategy, ConsensusContext, SigVerifiedOp, VerifyBlockRoot, VerifyOperation,
127+
BlockSignatureStrategy, ConsensusContext, GloasVerificationContext, SigVerifiedOp,
128+
VerifyBlockRoot, VerifyOperation,
129+
builder_deposits_cache::OnboardBuildersCache,
127130
common::get_attesting_indices_from_state,
128131
epoch_cache::initialize_epoch_cache,
129132
per_block_processing,
@@ -441,6 +444,8 @@ pub struct BeaconChain<T: BeaconChainTypes> {
441444
pub observed_slashable: RwLock<ObservedSlashable<T::EthSpec>>,
442445
/// Maintains a record of execution proofs seen over the gossip network.
443446
pub observed_execution_proofs: RwLock<ObservedExecutionProofs>,
447+
/// Maintains the gas limit of execution payloads seen through gossip or trusted imports.
448+
pub observed_execution_payloads: ObservedExecutionPayloads,
444449
/// Cache of pending execution payload envelopes for local block building.
445450
/// Envelopes are stored here during block production and eventually published.
446451
pub pending_payload_envelopes: RwLock<PendingPayloadEnvelopes<T::EthSpec>>,
@@ -522,6 +527,10 @@ pub struct BeaconChain<T: BeaconChainTypes> {
522527
pub pending_payload_cache: Arc<PendingPayloadCache<T>>,
523528
/// The KZG trusted setup used by this chain.
524529
pub kzg: Arc<Kzg>,
530+
/// Pre-verifies pending deposit signatures ahead of the Gloas fork transition.
531+
/// Only present when gloas is scheduled and the chain had not yet transitioned to gloas
532+
/// at startup, so nodes started post-fork skip the cache's allocation entirely.
533+
pub builder_onboarding_cache: Option<Arc<OnboardBuildersCache>>,
525534
/// RNG instance used by the chain. Currently used for shuffling column sidecars in block publishing.
526535
pub rng: Arc<Mutex<Box<dyn RngCore + Send>>>,
527536
}
@@ -1527,7 +1536,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
15271536
while state.slot() < slot {
15281537
// Note: supplying some `state_root` when it is known would be a cheap and easy
15291538
// optimization.
1530-
match per_slot_processing(&mut state, skip_state_root, &self.spec) {
1539+
match per_slot_processing(
1540+
&mut state,
1541+
skip_state_root,
1542+
GloasVerificationContext::from_cache(
1543+
self.builder_onboarding_cache.as_deref(),
1544+
),
1545+
&self.spec,
1546+
) {
15311547
Ok(_) => (),
15321548
Err(e) => {
15331549
warn!(
@@ -2133,6 +2149,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
21332149
&mut state,
21342150
Some(advanced_state_root),
21352151
request_epoch.start_slot(T::EthSpec::slots_per_epoch()),
2152+
self.builder_onboarding_cache.as_deref(),
21362153
&self.spec,
21372154
)
21382155
.map_err(Error::StateAdvanceError)?;
@@ -4203,6 +4220,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
42034220
}
42044221
}
42054222

4223+
/// Caches an execution proof, importing the payload envelope if that was the last piece.
4224+
pub async fn check_execution_proof_availability_and_import(
4225+
self: &Arc<Self>,
4226+
verified_proof: GossipVerifiedExecutionProof,
4227+
) -> Result<AvailabilityProcessingStatus, BlockError> {
4228+
let GossipVerifiedExecutionProof { proof, block_slot } = verified_proof;
4229+
let availability = self
4230+
.pending_payload_cache
4231+
.put_execution_proof(proof)
4232+
.map_err(BlockError::from)?;
4233+
self.process_payload_envelope_availability(block_slot, availability, || Ok(()))
4234+
.await
4235+
}
4236+
42064237
fn check_data_column_sidecar_header_signature_and_slashability<'a>(
42074238
self: &Arc<Self>,
42084239
block_root: Hash256,
@@ -4626,14 +4657,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
46264657

46274658
if let Err(e) = self.store.do_atomically_with_block_and_blobs_cache(ops) {
46284659
error!(
4629-
msg = "Restoring fork choice from disk",
46304660
error = ?e,
46314661
"Database write failed!"
46324662
);
4633-
return Err(self
4634-
.handle_import_block_db_write_error(fork_choice)
4635-
.err()
4636-
.unwrap_or(e.into()));
4663+
self.handle_import_block_db_write_error(fork_choice, block_root);
4664+
return Err(e.into());
46374665
}
46384666

46394667
drop(db_span);
@@ -4642,6 +4670,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
46424670
// This prevents inconsistency between the two at the expense of concurrency.
46434671
drop(fork_choice);
46444672

4673+
// Keep pre-Gloas payloads available across a live transition to Gloas.
4674+
if self.spec.is_gloas_scheduled()
4675+
&& !block.fork_name_unchecked().gloas_enabled()
4676+
&& let Ok(payload) = block.body().execution_payload()
4677+
&& payload.block_hash() != ExecutionBlockHash::zero()
4678+
{
4679+
self.observed_execution_payloads
4680+
.insert(payload.block_hash(), payload.gas_limit());
4681+
}
4682+
46454683
// We're declaring the block "imported" at this point, since fork choice and the DB know
46464684
// about it.
46474685
let block_time_imported = self.slot_clock.now_duration().unwrap_or(Duration::MAX);
@@ -4676,39 +4714,57 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
46764714
current_slot,
46774715
);
46784716

4717+
// Pre-verify the signatures of any deposits this block added to the `pending_deposits`
4718+
// queue, so that builder onboarding at the gloas fork transition is a cache lookup.
4719+
// Post-gloas the fork transition has already happened and the cache is no longer needed.
4720+
if !state.fork_name_unchecked().gloas_enabled()
4721+
&& let Some(builder_onboarding_cache) = &self.builder_onboarding_cache
4722+
{
4723+
let cache = builder_onboarding_cache.clone();
4724+
let spec = self.spec.clone();
4725+
// Using the rayon pool here since `add_new_pending_deposits` uses rayon threads to
4726+
// perform the signature verification in batches. We have until the fork transition
4727+
// for the cache to be populated, so use the low priority pool.
4728+
self.task_executor.clone().spawn_blocking_with_rayon(
4729+
move || cache.add_new_pending_deposits::<T::EthSpec>(&state, &spec),
4730+
RayonPoolType::LowPriority,
4731+
"pre_verify_pending_deposits",
4732+
);
4733+
}
4734+
46794735
Ok(block_root)
46804736
}
46814737

4738+
/// Handle a database write failure during block import, which causes fork choice
4739+
/// to contain a block that the store does not.
4740+
///
4741+
/// Poison fork choice so the diverged version is never persisted, and shut down the
4742+
/// node. On restart, the normal startup procedure loads the last consistent fork
4743+
/// choice from disk.
46824744
fn handle_import_block_db_write_error(
46834745
&self,
46844746
// We don't actually need this value, however it's always present when we call this function
46854747
// and it needs to be dropped to prevent a dead-lock. Requiring it to be passed here is
46864748
// defensive programming.
46874749
fork_choice_write_lock: ForkChoiceWriteGuard<T>,
4688-
) -> Result<(), BlockError> {
4750+
block_root: Hash256,
4751+
) {
4752+
drop(fork_choice_write_lock);
4753+
46894754
// Clear the early attester cache to prevent attestations which we would later be unable
46904755
// to verify due to the failure.
46914756
self.early_attester_cache.clear();
46924757

4693-
// Since the write failed, try to revert the canonical head back to what was stored
4694-
// in the database. This attempts to prevent inconsistency between the database and
4695-
// fork choice.
4696-
if let Err(e) = self.canonical_head.restore_from_store(
4697-
fork_choice_write_lock,
4698-
ResetPayloadStatuses::always_reset_conditionally(
4699-
self.config.always_reset_payload_statuses,
4700-
),
4701-
&self.store,
4702-
&self.spec,
4703-
) {
4704-
crit!(
4705-
error = ?e,
4706-
warning = "The database is likely corrupt now, consider --purge-db",
4707-
"No stored fork choice found to restore from"
4708-
);
4709-
Err(BlockError::BeaconChainError(Box::new(e)))
4710-
} else {
4711-
Ok(())
4758+
self.canonical_head.poison_fork_choice();
4759+
crit!(
4760+
?block_root,
4761+
advice = "restart the node to recover the last consistent fork choice from disk",
4762+
"Shutting down due to database write failure"
4763+
);
4764+
if let Err(e) = self.shutdown_sender().try_send(ShutdownReason::Failure(
4765+
"Database write failure during block import",
4766+
)) {
4767+
crit!(error = ?e, "Failed to send shutdown signal");
47124768
}
47134769
}
47144770

@@ -5286,6 +5342,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
52865342
&mut advanced_state,
52875343
Some(unadvanced_state_root),
52885344
proposal_slot,
5345+
self.builder_onboarding_cache.as_deref(),
52895346
&self.spec,
52905347
)?;
52915348

@@ -5703,7 +5760,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
57035760
let slot_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_SLOT_PROCESS_TIMES);
57045761

57055762
// Ensure the state has performed a complete transition into the required slot.
5706-
complete_state_advance(&mut state, state_root_opt, produce_at_slot, &self.spec)?;
5763+
complete_state_advance(
5764+
&mut state,
5765+
state_root_opt,
5766+
produce_at_slot,
5767+
self.builder_onboarding_cache.as_deref(),
5768+
&self.spec,
5769+
)?;
57075770

57085771
drop(slot_timer);
57095772

@@ -7195,6 +7258,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
71957258
proposal_epoch,
71967259
accessor,
71977260
state_provider,
7261+
self.builder_onboarding_cache.as_deref(),
71987262
&self.spec,
71997263
)
72007264
}
@@ -7240,6 +7304,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
72407304
&self.canonical_head,
72417305
&self.shuffling_cache,
72427306
&self.store,
7307+
self.builder_onboarding_cache.as_deref(),
72437308
&self.spec,
72447309
head_block_root,
72457310
shuffling_epoch,
@@ -7780,10 +7845,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
77807845

77817846
impl<T: BeaconChainTypes> Drop for BeaconChain<T> {
77827847
fn drop(&mut self) {
7848+
if self.canonical_head.fork_choice_poisoned() {
7849+
warn!("Skipping persistence on drop: fork choice is poisoned");
7850+
return;
7851+
}
7852+
77837853
let drop = || -> Result<(), Error> {
7784-
self.persist_fork_choice()?;
77857854
self.persist_op_pool()?;
7786-
self.persist_custody_context()
7855+
self.persist_custody_context()?;
7856+
self.persist_fork_choice()
77877857
};
77887858

77897859
if let Err(e) = drop() {

beacon_node/beacon_chain/src/beacon_proposer_cache.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use once_cell::sync::OnceCell;
1515
use parking_lot::Mutex;
1616
use safe_arith::SafeArith;
1717
use smallvec::SmallVec;
18+
use state_processing::builder_deposits_cache::OnboardBuildersCache;
1819
use state_processing::state_advance::partial_state_advance;
1920
use std::sync::Arc;
2021
use tracing::{debug, instrument};
@@ -177,6 +178,7 @@ pub fn with_proposer_cache<Spec, V, Err>(
177178
proposal_epoch: Epoch,
178179
accessor: impl Fn(&EpochBlockProposers) -> Result<V, BeaconChainError>,
179180
state_provider: impl FnOnce() -> Result<(Hash256, BeaconState<Spec>), Err>,
181+
builder_onboarding_cache: Option<&OnboardBuildersCache>,
180182
spec: &ChainSpec,
181183
) -> Result<V, Err>
182184
where
@@ -203,6 +205,7 @@ where
203205
&mut state,
204206
state_root,
205207
proposal_epoch,
208+
builder_onboarding_cache,
206209
spec,
207210
)?;
208211

@@ -274,6 +277,7 @@ pub fn compute_proposer_duties_from_head<T: BeaconChainTypes>(
274277
&mut state,
275278
head_state_root,
276279
request_epoch,
280+
chain.builder_onboarding_cache.as_deref(),
277281
&chain.spec,
278282
)?;
279283

@@ -317,6 +321,7 @@ pub fn ensure_state_can_determine_proposers_for_epoch<E: EthSpec>(
317321
state: &mut BeaconState<E>,
318322
state_root: Hash256,
319323
target_epoch: Epoch,
324+
builder_onboarding_cache: Option<&OnboardBuildersCache>,
320325
spec: &ChainSpec,
321326
) -> Result<(), BeaconChainError> {
322327
// The decision slot is the end of an epoch, so we add 1 to reach the first slot of the epoch
@@ -337,7 +342,13 @@ pub fn ensure_state_can_determine_proposers_for_epoch<E: EthSpec>(
337342
} else {
338343
// State's current epoch is less than the minimum epoch.
339344
// Advance the state up to the minimum epoch.
340-
partial_state_advance(state, Some(state_root), minimum_slot, spec)
341-
.map_err(BeaconChainError::from)
345+
partial_state_advance(
346+
state,
347+
Some(state_root),
348+
minimum_slot,
349+
builder_onboarding_cache,
350+
spec,
351+
)
352+
.map_err(BeaconChainError::from)
342353
}
343354
}

0 commit comments

Comments
 (0)