Skip to content

Commit 683da40

Browse files
committed
Merge branch 'unstable' of https://github.com/sigp/lighthouse into move-to-workspace
2 parents 499d600 + f92b856 commit 683da40

File tree

32 files changed

+644
-377
lines changed

32 files changed

+644
-377
lines changed

.github/workflows/docker-antithesis.yml

-35
This file was deleted.

Cargo.lock

-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

beacon_node/beacon_chain/src/beacon_chain.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -4656,6 +4656,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
46564656
self.log,
46574657
"Produced block on state";
46584658
"block_size" => block_size,
4659+
"slot" => block.slot(),
46594660
);
46604661

46614662
metrics::observe(&metrics::BLOCK_SIZE, block_size as f64);
@@ -5571,14 +5572,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
55715572
let (mut state, state_root) = if let Some((state, state_root)) = head_state_opt {
55725573
(state, state_root)
55735574
} else {
5574-
let state_root = head_block.state_root;
5575-
let state = self
5575+
let block_state_root = head_block.state_root;
5576+
let max_slot = shuffling_epoch.start_slot(T::EthSpec::slots_per_epoch());
5577+
let (state_root, state) = self
55765578
.store
55775579
.get_inconsistent_state_for_attestation_verification_only(
5578-
&state_root,
5579-
Some(head_block.slot),
5580+
&head_block_root,
5581+
max_slot,
5582+
block_state_root,
55805583
)?
5581-
.ok_or(Error::MissingBeaconState(head_block.state_root))?;
5584+
.ok_or(Error::MissingBeaconState(block_state_root))?;
55825585
(state, state_root)
55835586
};
55845587

beacon_node/beacon_chain/src/beacon_fork_choice_store.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,17 @@ where
321321
.deconstruct()
322322
.0;
323323

324-
let state = self
324+
let max_slot = self
325+
.justified_checkpoint
326+
.epoch
327+
.start_slot(E::slots_per_epoch());
328+
let (_, state) = self
325329
.store
326-
.get_state(&justified_block.state_root(), Some(justified_block.slot()))
330+
.get_advanced_hot_state(
331+
self.justified_checkpoint.root,
332+
max_slot,
333+
justified_block.state_root(),
334+
)
327335
.map_err(Error::FailedToReadState)?
328336
.ok_or_else(|| Error::MissingState(justified_block.state_root()))?;
329337

beacon_node/beacon_chain/src/block_verification.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -1261,7 +1261,7 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
12611261

12621262
// Perform a sanity check on the pre-state.
12631263
let parent_slot = parent.beacon_block.slot();
1264-
if state.slot() < parent_slot || state.slot() > parent_slot + 1 {
1264+
if state.slot() < parent_slot || state.slot() > block.slot() {
12651265
return Err(BeaconChainError::BadPreState {
12661266
parent_root: parent.beacon_block_root,
12671267
parent_slot,
@@ -1760,13 +1760,18 @@ fn load_parent<T: BeaconChainTypes>(
17601760
BlockError::from(BeaconChainError::MissingBeaconBlock(block.parent_root()))
17611761
})?;
17621762

1763-
// Load the parent blocks state from the database, returning an error if it is not found.
1763+
// Load the parent block's state from the database, returning an error if it is not found.
17641764
// It is an error because if we know the parent block we should also know the parent state.
1765-
let parent_state_root = parent_block.state_root();
1766-
let parent_state = chain
1767-
.get_state(&parent_state_root, Some(parent_block.slot()))?
1765+
// Retrieve any state that is advanced through to at most `block.slot()`: this is
1766+
// particularly important if `block` descends from the finalized/split block, but at a slot
1767+
// prior to the finalized slot (which is invalid and inaccessible in our DB schema).
1768+
let (parent_state_root, parent_state) = chain
1769+
.store
1770+
.get_advanced_hot_state(root, block.slot(), parent_block.state_root())?
17681771
.ok_or_else(|| {
1769-
BeaconChainError::DBInconsistent(format!("Missing state {:?}", parent_state_root))
1772+
BeaconChainError::DBInconsistent(
1773+
format!("Missing state for parent block {root:?}",),
1774+
)
17701775
})?;
17711776

17721777
metrics::inc_counter(&metrics::BLOCK_PROCESSING_SNAPSHOT_CACHE_MISSES);

beacon_node/beacon_chain/src/builder.rs

+49-37
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ use operation_pool::{OperationPool, PersistedOperationPool};
2424
use parking_lot::RwLock;
2525
use proto_array::{DisallowedReOrgOffsets, ReOrgThreshold};
2626
use slasher::Slasher;
27-
use slog::{crit, error, info, Logger};
27+
use slog::{crit, debug, error, info, Logger};
2828
use slot_clock::{SlotClock, TestingSlotClock};
29+
use state_processing::per_slot_processing;
2930
use std::marker::PhantomData;
3031
use std::sync::Arc;
3132
use std::time::Duration;
@@ -287,7 +288,7 @@ where
287288
let genesis_state = store
288289
.get_state(&genesis_block.state_root(), Some(genesis_block.slot()))
289290
.map_err(|e| descriptive_db_error("genesis state", &e))?
290-
.ok_or("Genesis block not found in store")?;
291+
.ok_or("Genesis state not found in store")?;
291292

292293
self.genesis_time = Some(genesis_state.genesis_time());
293294

@@ -382,6 +383,16 @@ where
382383
let (genesis, updated_builder) = self.set_genesis_state(beacon_state)?;
383384
self = updated_builder;
384385

386+
// Stage the database's metadata fields for atomic storage when `build` is called.
387+
// Since v4.4.0 we will set the anchor with a dummy state upper limit in order to prevent
388+
// historic states from being retained (unless `--reconstruct-historic-states` is set).
389+
let retain_historic_states = self.chain_config.reconstruct_historic_states;
390+
self.pending_io_batch.push(
391+
store
392+
.init_anchor_info(genesis.beacon_block.message(), retain_historic_states)
393+
.map_err(|e| format!("Failed to initialize genesis anchor: {:?}", e))?,
394+
);
395+
385396
let fc_store = BeaconForkChoiceStore::get_forkchoice_store(store, &genesis)
386397
.map_err(|e| format!("Unable to initialize fork choice store: {e:?}"))?;
387398
let current_slot = None;
@@ -408,46 +419,48 @@ where
408419
weak_subj_block: SignedBeaconBlock<TEthSpec>,
409420
genesis_state: BeaconState<TEthSpec>,
410421
) -> Result<Self, String> {
411-
let store = self.store.clone().ok_or("genesis_state requires a store")?;
412-
413-
let weak_subj_slot = weak_subj_state.slot();
414-
let weak_subj_block_root = weak_subj_block.canonical_root();
415-
let weak_subj_state_root = weak_subj_block.state_root();
416-
417-
// Check that the given block lies on an epoch boundary. Due to the database only storing
418-
// full states on epoch boundaries and at restore points it would be difficult to support
419-
// starting from a mid-epoch state.
420-
if weak_subj_slot % TEthSpec::slots_per_epoch() != 0 {
421-
return Err(format!(
422-
"Checkpoint block at slot {} is not aligned to epoch start. \
423-
Please supply an aligned checkpoint with block.slot % 32 == 0",
424-
weak_subj_block.slot(),
425-
));
426-
}
422+
let store = self
423+
.store
424+
.clone()
425+
.ok_or("weak_subjectivity_state requires a store")?;
426+
let log = self
427+
.log
428+
.as_ref()
429+
.ok_or("weak_subjectivity_state requires a log")?;
427430

428-
// Check that the block and state have consistent slots and state roots.
429-
if weak_subj_state.slot() != weak_subj_block.slot() {
430-
return Err(format!(
431-
"Slot of snapshot block ({}) does not match snapshot state ({})",
432-
weak_subj_block.slot(),
433-
weak_subj_state.slot(),
434-
));
431+
// Ensure the state is advanced to an epoch boundary.
432+
let slots_per_epoch = TEthSpec::slots_per_epoch();
433+
if weak_subj_state.slot() % slots_per_epoch != 0 {
434+
debug!(
435+
log,
436+
"Advancing checkpoint state to boundary";
437+
"state_slot" => weak_subj_state.slot(),
438+
"block_slot" => weak_subj_block.slot(),
439+
);
440+
while weak_subj_state.slot() % slots_per_epoch != 0 {
441+
per_slot_processing(&mut weak_subj_state, None, &self.spec)
442+
.map_err(|e| format!("Error advancing state: {e:?}"))?;
443+
}
435444
}
436445

437446
// Prime all caches before storing the state in the database and computing the tree hash
438447
// root.
439448
weak_subj_state
440449
.build_caches(&self.spec)
441450
.map_err(|e| format!("Error building caches on checkpoint state: {e:?}"))?;
442-
443-
let computed_state_root = weak_subj_state
451+
let weak_subj_state_root = weak_subj_state
444452
.update_tree_hash_cache()
445453
.map_err(|e| format!("Error computing checkpoint state root: {:?}", e))?;
446454

447-
if weak_subj_state_root != computed_state_root {
455+
let weak_subj_slot = weak_subj_state.slot();
456+
let weak_subj_block_root = weak_subj_block.canonical_root();
457+
458+
// Validate the state's `latest_block_header` against the checkpoint block.
459+
let state_latest_block_root = weak_subj_state.get_latest_block_root(weak_subj_state_root);
460+
if weak_subj_block_root != state_latest_block_root {
448461
return Err(format!(
449-
"Snapshot state root does not match block, expected: {:?}, got: {:?}",
450-
weak_subj_state_root, computed_state_root
462+
"Snapshot state's most recent block root does not match block, expected: {:?}, got: {:?}",
463+
weak_subj_block_root, state_latest_block_root
451464
));
452465
}
453466

@@ -464,7 +477,7 @@ where
464477

465478
// Set the store's split point *before* storing genesis so that genesis is stored
466479
// immediately in the freezer DB.
467-
store.set_split(weak_subj_slot, weak_subj_state_root);
480+
store.set_split(weak_subj_slot, weak_subj_state_root, weak_subj_block_root);
468481
let (_, updated_builder) = self.set_genesis_state(genesis_state)?;
469482
self = updated_builder;
470483

@@ -480,10 +493,11 @@ where
480493
// Stage the database's metadata fields for atomic storage when `build` is called.
481494
// This prevents the database from restarting in an inconsistent state if the anchor
482495
// info or split point is written before the `PersistedBeaconChain`.
496+
let retain_historic_states = self.chain_config.reconstruct_historic_states;
483497
self.pending_io_batch.push(store.store_split_in_batch());
484498
self.pending_io_batch.push(
485499
store
486-
.init_anchor_info(weak_subj_block.message())
500+
.init_anchor_info(weak_subj_block.message(), retain_historic_states)
487501
.map_err(|e| format!("Failed to initialize anchor info: {:?}", e))?,
488502
);
489503

@@ -503,13 +517,12 @@ where
503517
let fc_store = BeaconForkChoiceStore::get_forkchoice_store(store, &snapshot)
504518
.map_err(|e| format!("Unable to initialize fork choice store: {e:?}"))?;
505519

506-
let current_slot = Some(snapshot.beacon_block.slot());
507520
let fork_choice = ForkChoice::from_anchor(
508521
fc_store,
509522
snapshot.beacon_block_root,
510523
&snapshot.beacon_block,
511524
&snapshot.beacon_state,
512-
current_slot,
525+
Some(weak_subj_slot),
513526
&self.spec,
514527
)
515528
.map_err(|e| format!("Unable to initialize ForkChoice: {:?}", e))?;
@@ -672,9 +685,8 @@ where
672685
Err(e) => return Err(descriptive_db_error("head block", &e)),
673686
};
674687

675-
let head_state_root = head_block.state_root();
676-
let head_state = store
677-
.get_state(&head_state_root, Some(head_block.slot()))
688+
let (_head_state_root, head_state) = store
689+
.get_advanced_hot_state(head_block_root, current_slot, head_block.state_root())
678690
.map_err(|e| descriptive_db_error("head state", &e))?
679691
.ok_or("Head state not found in store")?;
680692

beacon_node/beacon_chain/src/canonical_head.rs

+14-9
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ use crate::{
4747
};
4848
use eth2::types::{EventKind, SseChainReorg, SseFinalizedCheckpoint, SseHead, SseLateHead};
4949
use fork_choice::{
50-
ExecutionStatus, ForkChoiceView, ForkchoiceUpdateParameters, ProtoBlock, ResetPayloadStatuses,
50+
ExecutionStatus, ForkChoiceStore, ForkChoiceView, ForkchoiceUpdateParameters, ProtoBlock,
51+
ResetPayloadStatuses,
5152
};
5253
use itertools::process_results;
5354
use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
@@ -298,10 +299,10 @@ impl<T: BeaconChainTypes> CanonicalHead<T> {
298299
let beacon_block = store
299300
.get_full_block(&beacon_block_root)?
300301
.ok_or(Error::MissingBeaconBlock(beacon_block_root))?;
301-
let beacon_state_root = beacon_block.state_root();
302-
let beacon_state = store
303-
.get_state(&beacon_state_root, Some(beacon_block.slot()))?
304-
.ok_or(Error::MissingBeaconState(beacon_state_root))?;
302+
let current_slot = fork_choice.fc_store().get_current_slot();
303+
let (_, beacon_state) = store
304+
.get_advanced_hot_state(beacon_block_root, current_slot, beacon_block.state_root())?
305+
.ok_or(Error::MissingBeaconState(beacon_block.state_root()))?;
305306

306307
let snapshot = BeaconSnapshot {
307308
beacon_block_root,
@@ -669,10 +670,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
669670
.get_full_block(&new_view.head_block_root)?
670671
.ok_or(Error::MissingBeaconBlock(new_view.head_block_root))?;
671672

672-
let beacon_state_root = beacon_block.state_root();
673-
let beacon_state: BeaconState<T::EthSpec> = self
674-
.get_state(&beacon_state_root, Some(beacon_block.slot()))?
675-
.ok_or(Error::MissingBeaconState(beacon_state_root))?;
673+
let (_, beacon_state) = self
674+
.store
675+
.get_advanced_hot_state(
676+
new_view.head_block_root,
677+
current_slot,
678+
beacon_block.state_root(),
679+
)?
680+
.ok_or(Error::MissingBeaconState(beacon_block.state_root()))?;
676681

677682
Ok(BeaconSnapshot {
678683
beacon_block: Arc::new(beacon_block),

beacon_node/beacon_chain/src/migrate.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
266266
debug!(log, "Database consolidation started");
267267

268268
let finalized_state_root = notif.finalized_state_root;
269+
let finalized_block_root = notif.finalized_checkpoint.root;
269270

270271
let finalized_state = match db.get_state(&finalized_state_root.into(), None) {
271272
Ok(Some(state)) => state,
@@ -319,7 +320,12 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
319320
}
320321
};
321322

322-
match migrate_database(db.clone(), finalized_state_root.into(), &finalized_state) {
323+
match migrate_database(
324+
db.clone(),
325+
finalized_state_root.into(),
326+
finalized_block_root,
327+
&finalized_state,
328+
) {
323329
Ok(()) => {}
324330
Err(Error::HotColdDBError(HotColdDBError::FreezeSlotUnaligned(slot))) => {
325331
debug!(

beacon_node/beacon_chain/tests/attestation_verification.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use beacon_chain::{
99
test_utils::{
1010
test_spec, AttestationStrategy, BeaconChainHarness, BlockStrategy, EphemeralHarnessType,
1111
},
12-
BeaconChain, BeaconChainError, BeaconChainTypes, WhenSlotSkipped,
12+
BeaconChain, BeaconChainError, BeaconChainTypes, ChainConfig, WhenSlotSkipped,
1313
};
1414
use genesis::{interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH};
1515
use int_to_bytes::int_to_bytes32;
@@ -47,6 +47,10 @@ fn get_harness(validator_count: usize) -> BeaconChainHarness<EphemeralHarnessTyp
4747

4848
let harness = BeaconChainHarness::builder(MainnetEthSpec)
4949
.spec(spec)
50+
.chain_config(ChainConfig {
51+
reconstruct_historic_states: true,
52+
..ChainConfig::default()
53+
})
5054
.keypairs(KEYPAIRS[0..validator_count].to_vec())
5155
.fresh_ephemeral_store()
5256
.mock_execution_layer()
@@ -79,6 +83,10 @@ fn get_harness_capella_spec(
7983

8084
let harness = BeaconChainHarness::builder(MainnetEthSpec)
8185
.spec(spec.clone())
86+
.chain_config(ChainConfig {
87+
reconstruct_historic_states: true,
88+
..ChainConfig::default()
89+
})
8290
.keypairs(validator_keypairs)
8391
.withdrawal_keypairs(
8492
KEYPAIRS[0..validator_count]

0 commit comments

Comments
 (0)