Skip to content

Commit

Permalink
Add epochs since finalization check
Browse files Browse the repository at this point in the history
Remove adhoc participation check
  • Loading branch information
michaelsproul committed Nov 28, 2022
1 parent 1c8cc82 commit 64c7231
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 28 deletions.
24 changes: 11 additions & 13 deletions specs/bellatrix/fork-choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,26 +108,32 @@ def should_override_forkchoice_update(
parent_root = head_block.parent_root
parent_block = store.blocks[parent_root]
current_slot = get_current_slot(store)
proposal_slot = head_block.slot + 1

# Only re-org the head_block block if it arrived later than the attestation deadline.
head_late = store.block_timeliness.get(head_root) is False

# Only suppress the fork choice update if we are confident that we will propose the next block.
parent_state_advanced = store.block_states[parent_root]
process_slots(parent_state_advanced, head_block.slot + 1)
process_slots(parent_state_advanced, proposal_slot)
proposer_index = get_beacon_proposer_index(parent_state_advanced)
proposing_reorg_slot = validator_is_connected(proposer_index)

# Do not re-org if the chain is not finalizing with acceptable frequency.
proposal_epoch = compute_epoch_at_slot(proposal_slot)
epochs_since_finalization = proposal_epoch - store.finalized_checkpoint.epoch
finalization_ok = epochs_since_finalization <= REORG_MAX_EPOCHS_SINCE_FINALIZATION

# Single slot re-org.
parent_slot_ok = parent_block.slot + 1 == head_block.slot
time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT
current_time_ok = (head_block.slot == current_slot or
(head_block.slot + 1 == current_slot and
(proposal_slot == current_slot and
time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT))
single_slot_reorg = parent_slot_ok and current_time_ok

# Shuffling stable.
shuffling_stable = (head_block.slot + 1) % SLOTS_PER_EPOCH != 0
shuffling_stable = proposal_slot % SLOTS_PER_EPOCH != 0

# FFG information of the new head_block will be competitive with the current head.
# FIXME: needs more justification/finalization information in the store
Expand All @@ -138,14 +144,6 @@ def should_override_forkchoice_update(
# store.realized_finalization[head_root]
ffg_competitive = True

# Ensure that total participation meets a minimum threshold.
# The participation multiplier is set to the number of slots for which the parent_block block has
# been known.
participation_multiplier = current_slot - parent_block.slot
parent_weight = get_latest_attesting_balance(store, parent_root)
participation_threshold = calculate_committee_fraction(justified_state, PARTICIPATION_THRESHOLD)
participation_ok = parent_weight >= participation_multiplier * participation_threshold

# Check the head weight only if the attestations from the head slot have already been applied.
# Implementations may want to do this in different ways, e.g. by advancing
# `store.time` early, or by counting queued attestations during the head block's slot.
Expand All @@ -157,8 +155,8 @@ def should_override_forkchoice_update(
else:
head_weak = True

return all([head_late, proposing_reorg_slot, single_slot_reorg, shuffling_stable,
ffg_competitive, participation_ok, head_weak])
return all([head_late, proposing_reorg_slot, finalization_ok, single_slot_reorg,
shuffling_stable, ffg_competitive, head_weak])
```

> Note that the ordering of conditions is a suggestion only. Implementations are free to
Expand Down
26 changes: 11 additions & 15 deletions specs/phase0/fork-choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ Any of the above handlers that trigger an unhandled exception (e.g. a failed ass

### Configuration

| Name | Value |
| ------------------------------- | ------------ |
| `PROPOSER_SCORE_BOOST` | `uint64(40)` |
| `REORG_WEIGHT_THRESHOLD` | `uint64(20)` |
| `PARTICIPATION_THRESHOLD` | `uint64(70)` |
| Name | Value |
| ------------------------------------- | ------------ |
| `PROPOSER_SCORE_BOOST` | `uint64(40)` |
| `REORG_WEIGHT_THRESHOLD` | `uint64(20)` |
| `REORG_MAX_EPOCHS_SINCE_FINALIZATION` | `Epoch(2)` |

- The proposer score boost, re-org weight threshold and participation threshold are percentage
- The proposer score boost and re-org weight threshold are percentage
values that are measured with respect to the weight of a single committee. See
`calculate_committee_fraction`.

Expand Down Expand Up @@ -301,6 +301,10 @@ def get_proposer_head(store: Store, head_root: Root, slot: Slot) -> Root:
# Only re-org the head block if it arrived later than the attestation deadline.
head_late = store.block_timeliness.get(head_root) is False

# Do not re-org if the chain is not finalizing with acceptable frequency.
epochs_since_finalization = compute_epoch_at_slot(slot) - store.finalized_checkpoint.epoch
finalization_ok = epochs_since_finalization <= REORG_MAX_EPOCHS_SINCE_FINALIZATION

# Only re-org a single slot at most.
single_slot_reorg = parent_block.slot + 1 == head_block.slot and head_block.slot + 1 == slot

Expand All @@ -316,21 +320,13 @@ def get_proposer_head(store: Store, head_root: Root, slot: Slot) -> Root:
# store.realized_finalization[head_root]
ffg_competitive = True

# Ensure that total participation meets a minimum threshold. The factor of 2 accounts for votes
# from the head slot which should flow to the parent either via the head block or votes for
# emptiness.
participation_multiplier = 2
parent_weight = get_latest_attesting_balance(store, parent_root)
participation_threshold = calculate_committee_fraction(justified_state, PARTICIPATION_THRESHOLD)
participation_ok = parent_weight >= participation_multiplier * participation_threshold

# Check that the head has few enough votes to be overpowered by our proposer boost.
assert store.proposer_boost_root == Root()
head_weight = get_latest_attesting_balance(store, head_root)
reorg_threshold = calculate_committee_fraction(justified_state, REORG_WEIGHT_THRESHOLD)
head_weak = head_weight < reorg_threshold

if all([head_late, single_slot_reorg, shuffling_stable, ffg_competitive, participation_ok,
if all([head_late, finalization_ok, single_slot_reorg, shuffling_stable, ffg_competitive,
head_weak]):
# We can re-org the current head by building upon its parent block.
return parent_root
Expand Down

0 comments on commit 64c7231

Please sign in to comment.