# Whisk: Induced missed initial slots
> **Whisk** is a single secret leader election protocol, providing proposers privacy at the protocol level. Checkout _the_ [ethresearch post](https://ethresear.ch/t/whisk-a-practical-shuffle-based-ssle-protocol-for-ethereum/11763) for background
Whisk on its current spec will induce ~2% missed slot rate on the initial epochs after the fork, and over 12,000 cumulative missed slots during the first year post-fork. This doc explains the cause of the issue, its likelihood, and a spec patch to solve it.
## Why Whisk induces missed slots
Any secret shuffling protocol requires all participants to submit entropy. Whisk collects the entropy from each validator's first proposal post-fork.
In Whisk, this entropy is a tracker created with a secret value `k`. To bootstrap the system all validators are initialized with a public deterministic `k_initial` value. Then, progressively, each validator submits a tracker created with a secret `k` on their first proposal post-fork. These trackers are shuffled and a subset of them are elected as a sorted list of `whisk_proposer_trackers`.
For a proposer to claim its proposer slot, it must submit a valid opening proof against:
1. the current slot elected proposer tracker
2. the validator `k` commitment in the parent block's state
```py
assert IsValidWhiskOpeningProof(
state.whisk_proposer_trackers[state.slot % WHISK_PROPOSER_TRACKERS_COUNT],
state.whisk_k_commitments[block.proposer_index],
block.body.whisk_opening_proof
)
```
That block will **mutate** `state.whisk_k_commitments[block.proposer_index]` with the new secret `k` such that subsequent proposals cannot be proven. Specifically, if a validator has additional proposals during the same or next shuffling round, they will fail. Its first block proposer registers both a new k_commitment and a tracker. However, only trackers registered at least 2 shuffle rounds ago can be elected into the proposer trackers.
![](https://hackmd.io/_uploads/rJxSJyh22.png)
## Likelihood of missed slots
In the previous section we have established that:
> If a validator is elected more than once in two adjacent shuffle rounds, it will result in missed slots
So what's the probability of that event happening? The chart below plots the probability of X validators being part of the proposers' list more than once in two adjacent whisk rounds with a monte carlo simulation. At current Whisk parameters and a network size of 650,000 indexes, the expected value is ~200.
![](https://hackmd.io/_uploads/Bk0jrJ22n.jpg)
_[Figure notebook source](https://app.noteable.io/f/0a837858-b50a-403e-9fb8-663a35b4bb17/whisk_double_selection.ipynb)_
Let's explore how the expected value changes with network size. Note Ethereum index count is expected to grow significantly, and other networks may adopt this feature with a much smaller index count. Note that for smaller networks this issue becomes very significant.
![](https://hackmd.io/_uploads/Sk6EDJnn2.jpg)
_[Figure notebook source](https://app.noteable.io/f/0a837858-b50a-403e-9fb8-663a35b4bb17/whisk_double_selection.ipynb)_
Now let's simulate the actual count of missed slots accounting only for the first proposals. Assuming a network size of 650,000 simulate for 1 year (320 rounds).
![](https://hackmd.io/_uploads/SkXGdk3n3.jpg)
_[Figure notebook source](https://app.noteable.io/f/d3618f03-bbec-4395-b408-47b3c3072796/whisk_initial_period.ipynb)_
On the initial epochs, 160 slots per shuffle round represent ~2% missed slot rate. This is quite significant and not acceptable IMO.
## Solution
Proposals on a slot with an elected proposer tracker created with a deterministic known initial `k` (first proposal + adjacent proposals) are not secret. There is no reason for a validator to have to submit an opening proof since any network participant knows who is the elected proposer for that slot.
So the elected proposer can just signal that the elected tracker belongs to itself, and is created with its initial_k by setting the opening proof to zero.
```python
def process_whisk_opening_proof(state: BeaconState, block: BeaconBlock) -> None:
tracker = state.whisk_proposer_trackers[state.slot % WHISK_PROPOSER_TRACKERS_COUNT]
if body.whisk_opening_proof == WhiskOpeningProof():
assert tracker.k_r_G == tracker.r_G * get_initial_whisk_k(block.proposer_index)
else:
k_commitment = state.whisk_k_commitments[block.proposer_index]
assert IsValidWhiskOpeningProof(tracker, k_commitment, block.body.whisk_opening_proof)
```
This patch fixes the issue but makes both branches viable during the first proposal post-whisk. A validator can choose to submit a valid proof with `k_initial` or to set the proof to zero. Both are correct all will pass validation. We may choose to restrict this behavior by forcing proposers to zero out the first proposal's proof, but it's not necessary.
```python
def process_whisk_opening_proof(state: BeaconState, block: BeaconBlock) -> None:
tracker = state.whisk_proposer_trackers[state.slot % WHISK_PROPOSER_TRACKERS_COUNT]
if body.whisk_opening_proof == WhiskOpeningProof():
assert tracker.k_r_G == tracker.r_G * get_initial_whisk_k(block.proposer_index)
else:
k_commitment = state.whisk_k_commitments[block.proposer_index]
assert IsValidWhiskOpeningProof(tracker, k_commitment, block.body.whisk_opening_proof)
# Assert no first proposal
assert state.whisk_trackers[block.proposer_index].r_G != BLS_G1_GENERATOR
```
## Conclusion
The current whisk bootstrapping strategy causes a significant number of cumulative missed slots. A simple fix guards against this edge case, spec PR -> [ethereum/consensus-specs#3481](https://github.com/ethereum/consensus-specs/pull/3481). However, other possible boostraping strategies can fix the bigger problem of significantly exposing validators until their first proposal.