# Takeaways
- Lighthouse stores forkchoice to disk, and during block import, it will revert atomically to the last version on disk in case there was a problem:
```rust!
pub struct PersistedForkChoiceStore {
pub balances_cache: BalancesCacheV8,
pub time: Slot,
pub finalized_checkpoint: Checkpoint,
pub justified_checkpoint: Checkpoint,
pub justified_balances: Vec<u64>,
pub best_justified_checkpoint: Checkpoint,
pub unrealized_justified_checkpoint: Checkpoint,
pub unrealized_finalized_checkpoint: Checkpoint,
pub proposer_boost_root: Hash256,
pub equivocating_indices: BTreeSet<u64>,
}
```
- Slot processing is entirely separate task from block importing
- Lighthouse uses the "token" pattern in software engineer to represent blocks at different stages in the pipeline. Given there are so many background tasks and related components, knowing that a function operates on a "**GossipVerifiedBeaconBlock**" makes a big difference in terms of code safety. Each conversion from one block time to another performs the necessary validation until a **FullyVerifiedBeaconBlock** is ready for chain import
- Lighthouse only has a single, block processing pipeline (no onBlockBatch), which follows stages:
```
//!
//! START
//! |
//! ▼
//! SignedBeaconBlock
//! |---------------
//! | |
//! | ▼
//! | GossipVerifiedBlock
//! | |
//! |---------------
//! |
//! ▼
//! SignatureVerifiedBlock
//! |
//! ▼
//! ExecutionPendingBlock
//! |
//! await
//! |
//! ▼
//! END
//!
````
* When it comes to slot processing, Lighthouse provides a timer which runs in the tail-end of each slot and maybe advances the state of the head block forward a single slot.This is known as the "state advance timer" and seems to be part of their secret sauce
* This provides an optimization with the following benefits:
* 1. Removes the burden of a single, mandatory `per_slot_processing` call from the leading-edge of block processing. This helps import blocks faster.
* 2. Allows the node to learn of the shuffling for the next epoch, before the first block from that epoch has arrived. This helps reduce gossip block propagation times.
* The downsides to this optimization are:
1. Required to store an additional `BeaconState` for the head block. This consumes
memory.
2. There's a possibility that the head block is never built upon, causing wasted CPU cycles.
# Single Block Processing Pipeline
Once a block is received from gossip, it is sent to to a processor of network messages:
It enters into beacon_node/network/src/beacon_processor/worker/gossip_methods.rs
```rust!
pub async fn process_gossip_block(...)
```
- Pipelines for blocks, gossip unverified, gossip verified
## Gossip Verification Steps
- **Pipeline**
- correct structure for fork epoch
- do not gossip from future slots
- disallow those that conflict with the anchor weak subjectivity checkpoint
- do not gossip from a finalized slot
- check if already known. If post-finalization, enough to check forkchoice
- check we have not received another with valid sig for this slot
- must descent from finalized root
- parent block slot must < block slot
- reject any that exceed limit on skip slots
- check the expected proposer, deal with caching in case we learn of a new shuffling value
- verify sig
- store in cache to prevent processing another gossip with valid sig for same slot
- validate payload
- return GossipVerifiedBlock
- Post gossip verify
- Check if we can import at the current slot or later
- Process block
## Post Gossip Signature Verification
Next, Lighthouse turns a GossipVerifiedBlock into a SignatureVerifiedBlock doing the following:
- **Pipeline**
- Cheap state advancement to obtain a committee
- Get a pubkey cache and signature verifier
- Verifies the signature
The cheap state advancement function is interesting:
```rust!
/// Performs a cheap (time-efficient) state advancement so the committees and proposer shuffling for
/// `slot` can be obtained from `state`.
///
/// The state advancement is "cheap" since it does not generate state roots. As a result, the
/// returned state might be holistically invalid but the committees/proposers will be correct (since
/// they do not rely upon state roots).
///
/// If the given `state` can already serve the `slot`, the committees will be built on the `state`
/// and `Cow::Borrowed(state)` will be returned. Otherwise, the state will be cloned, cheaply
/// advanced and then returned as a `Cow::Owned`. The end result is that the given `state` is never
/// mutated to be invalid (in fact, it is never changed beyond a simple committee cache build).
```
## Post Gossip, Blockchain Processing
- **Process SignatureVerifiedBlock**
- Turn the block into an "ExecutionPending" block
- In the process, it does the following:
- reject parent with invalid payload
- reject if parent unknown to fork choice
- reject if exceeds limit on skipped slots
- check basic relevance:
- not from future
- not genesis
- not finalized slot
- check if already known
- validate merge transition
- check if optimistic
- spawn payload verification task, but do not wait for it to complete
- block must be higher slot than its parent
- sanity check on prestate
- for state slot < block slot
- Compute state root:
- If parent_block.slot == curr_state.slot, then just parent_block.state_root
- else, tree hash state and save state in DB if epoch boundary. Else, just save summary
- Run per slot processing on curr_state, block
- Build committee caches
- Run per block processing, without sig verification
- Calculate post_state state root
- Ensure matches the one we calculated in our block
- Apply attestations to fork choice
- Apply each attester slashing to fork choice
- Now we have an ExecutionPending block
- Next, await the payload verification task we spawned earlier and receive a FullyVerifiedSignedBlock
## Chain Import
At this point, lighthouse makes it clear the block is NOT yet attestable
```rust
// ----------------------------- BLOCK NOT YET ATTESTABLE ----------------------------------
// Everything in this initial section is on the hot path between processing the block and
// being able to attest to it. DO NOT add any extra processing in this initial section
// unless it must run before fork choice.
```
- **Import FullyVerifiedSignedBlock**
- Check the weak subjectivity checkpoint against the block
- Update public keys cache if there are new validators in the block
- Apply the state to the attester cache only if it is from prev epoch or earlier
- Take an exclusive fork choice lock
- Do not import if not descendant of finalized root
- Call forkchoice onBlock
- Next, check optimistic status
```
// If the block is recent enough and it was not optimistically imported, check to see if it
// becomes the head block. If so, apply it to the early attester cache. This will allow
// attestations to the block without waiting for the block and state to be inserted to the
// database.
//
// Only performing this check on recent blocks avoids slowing down sync with lots of calls
// to fork choice `get_head`.
//
// Optimistically imported blocks are not added to the cache since the cache is only useful
// for a small window of time and the complexity of keeping track of the optimistic status
// is not worth it.
```
Then, lighthouse states:
*Most blocks are now capable of being attested to thanks to the `earlyattestercache`
cache above. Resume non-essential processing.*
*It is important NOT to return errors here before the database commit, because the block
has already been added to fork choice and the database would be left in an inconsistent
state if we returned early without committing. In other words, an error here would
corrupt the node's database permanently.*
- **Import Continued**
- Update shuffling cache
- Observe attestations
- Update slasher
- Begin an atomic operation:
- store the block and state to DB
- If operation fails, *revert* forkchoice to the version that exists in DB and clear attester cache
- Drops the forkchoice lock
- Store the post state, block, and block root to a snapshot cache for block processing
```rust
snapshot_cache.insert(
BeaconSnapshot {
beacon_state: state,
beacon_block: signed_block.clone(),
beacon_block_root: block_root,
},
None,
&self.spec,
)
```
- Finally, update metrics and fire events
Here is the data about forkchoice reloaded from disk in case there is a failure in block import:
```rust!
let fork_choice =
<BeaconChain<T>>::load_fork_choice(store.clone(), reset_payload_statuses, spec, log)?
.ok_or(Error::MissingPersistedForkChoice)?;
let fork_choice_view = fork_choice.cached_fork_choice_view();
let beacon_block_root = fork_choice_view.head_block_root;
let beacon_block = store
.get_full_block(&beacon_block_root)?
.ok_or(Error::MissingBeaconBlock(beacon_block_root))?;
let beacon_state_root = beacon_block.state_root();
let beacon_state = store
.get_state(&beacon_state_root, Some(beacon_block.slot()))?
.ok_or(Error::MissingBeaconState(beacon_state_root))?;
let snapshot = BeaconSnapshot {
beacon_block_root,
beacon_block: Arc::new(beacon_block),
beacon_state,
};
let forkchoice_update_params = fork_choice.get_forkchoice_update_parameters();
let cached_head = CachedHead {
snapshot: Arc::new(snapshot),
justified_checkpoint: fork_choice_view.justified_checkpoint,
finalized_checkpoint: fork_choice_view.finalized_checkpoint,
head_hash: forkchoice_update_params.head_hash,
justified_hash: forkchoice_update_params.justified_hash,
finalized_hash: forkchoice_update_params.finalized_hash,
};
```
# State Advance Timer
Lighthouse, in parallel to block processing, has a background task known as the "state advance timer", which runs at the tail-end of a slot and attempts to advance the state of the head block by a single slot, if possible. This is meant to speed up per slot processing from the core, block processing and import paths.
It:
1. Tries to run the state advance 3/4 of the way through the slot (9s on mainnet)
2. Tries to run fork choice 23/24s of the way through the slot (11.5s on mainnet), to not conflict exactly with the slot boundary
"State advance" tries to fetch a value from the snapshot cache of block importing, and if it obtains it, it will then try to advance the slot by 1, update all caches, and stop.
"Forkchoice update" will recompute the head block.