---
# System prepended metadata

title: EPF Week 10 Updates - Ptc Cache

---

## EPF Week 10 Updates

Lighthouse epbs related PR's this week:
- [PR](https://github.com/shane-moore/lighthouse/pull/9) updating `per_slot_processing` and `per_epoch_processing` per the [spec](https://ethereum.github.io/consensus-specs/specs/gloas/beacon-chain/)
- draft [PR](https://github.com/shane-moore/lighthouse/pull/10) for building out a `ptc_cache.rs` so that way we can save on the shuffling computation during various calls to `get_ptc` during block proposal and validation, which we discuss later
- reviewed the `gloas` boilerplate [PR](https://github.com/sigp/lighthouse/pull/7728) for lighthouse, which we're trying to get into the `unstable` branch

### `ptc_cache`
We want to build out a cache for `get_ptc`:
```rust
    pub fn get_ptc(&self, slot: Slot, spec: &ChainSpec) -> Result<PTC<E>, Error> {
        let committee_cache = self.committee_cache_at_slot(slot)?;
        let committees = committee_cache.get_beacon_committees_at_slot(slot)?;

        let seed = self.get_ptc_attester_seed(slot, spec)?;

        let committee_indices: Vec<usize> = committees
            .iter()
            .flat_map(|committee| committee.committee.iter().copied())
            .collect();
        let selected_indices = self.compute_balance_weighted_selection(
            &committee_indices,
            &seed,
            E::ptc_size(),
            false,
            spec,
        )?;

        Ok(PTC(FixedVector::from(selected_indices)))
    }
```

`get_ptc` should be deterministic per slot and return a list of validators who are assigned to a PTC committee for a given slot and should be a subset of the total validators who are assigned to an attestation committee for that slot per the [spec](https://github.com/shane-moore/lighthouse/pull/10).  We can luckily save the computation of attestation committee members by grabbing them from the `committee_cache`, but without a ptc committee cache itself, we'd still have to calculate if a given validator from the current slot's  attestation committee has been selected for the ptc committee by running a balance weighted selection algorythm, and it would be nice to not have to do this heavy computation on each call to `get_ptc`.

This is where a `ptc_cache` would come in handy.  Let's look at how `get_ptc` is used as to understand the requirements.

### Use Case 1: Proposer - Block Proposal
When creating `PayloadAttestation` in the `BeaconBlockBody`, each `PayloadAttestation` should be an aggregate of PTC member's who voted for `payload present` and `blob_data_available`.  Therefore, there are four variations of `PayloadAttestation` to store in the `BeaconBlockBody`.  Each `PayloadAttestation` contains a bitlist, `payload_attestation::aggregation_bits`, indicating which committee members voted for that variation. Essentially, a `0` bit indicates a ptc member did not contribute a signature to this aggregate.  Each block proposer will need to fill the `payload_attestation::aggregation_bits` bitlist in a deterministic order derived from `get_ptc(slot-1)`.

### Use Case 2: Validator - Block Validation
Once a block is received over the network, a validator will want to validate that the `BeaconBlockBody::payload_attestations` are legit, so they will need to grab the pubkeys of each validator who actually participated in the PTC last slot for signature verification. To get the pubkeys, the `process_operations` flow will call `process_payload_attestation` and use `get_indexed_payload_attestation(payload_attestation)` to find the validator indices that voted for that `PayloadAttestation` variant by looping over the ptc validator indices derived from `get_ptc(slot)` and checking which members had a bit `1` set in  `payload_attestations::aggregation_bits` at the corresponding index. The resulting validator indices list is sorted and returned for aggregated BLS signature verification.

## Use Case 3: Validator PTC Assignment
`get_ptc_assignment` will be used at the start of each epoch to get the ptc assignments for the next epoch for a given validator index.  While looping over each slot in the next epoch during `get_ptc_assignment`, `get_ptc` will run to check whether the validator's index is included in any of the upcoming ptc committees. If so, mark the slot.

### `ptc_cache` Requirements
With these use cases in mind, we can see that the requirements of the `ptc_cache` should be to store a list of ptc assignments per slot per epoch for the previous, current, and next epoch.  We can model this similar to the `committee_cache` where we store the shuffling and position lookups for efficient access.

### `PTCCache` Architecture
The `PTCCache` struct follows the same pattern as `CommitteeCache` but optimized for PTC operations. Here's how we structured it in [PR](https://github.com/shane-moore/lighthouse/pull/10):
```rust
pub struct PTCCache {
    initialized_epoch: Option<Epoch>,
    ptc_shuffling: Vec<usize>,               // Flat list of all PTC members
    ptc_positions: Vec<NonZeroUsizeOption>,  // Position lookup for each validator
    ptc_size: usize,                         // Constant 512 for Gloas+
    slots_per_epoch: u64,                    // Usually 32
}
```

### `Design Considerations`:
- Flat Shuffling Storage: Instead of a nested `Vec<Vec<usize>>`, we use a single flat vector `ptc_shuffling` that stores all PTC members for an entire epoch. The layout is [slot0_members..., slot1_members..., slot2_members...]. This gives us O(1) slot lookups by calculating `start_index = slot_offset * ptc_size` and slicing the appropriate range since we know an epoch is 32 slots.

- Position Lookup Optimization: The `ptc_positions` vector maps each validator index to their position in the flat shuffling. This enables O(1) lookup for [get_ptc_assignment](https://ethereum.github.io/consensus-specs/specs/gloas/validator/#validator-assignment) instead of having to linearly search through all slots.
- Per-Epoch Initialization: Like `CommitteeCache`, we initialize the entire epoch's worth of PTC assignments at once during epoch transitions instead of doing it on-demand as to offload the expensive balance weighted selection computation.