Refactoring
This commit is contained in:
parent
45a3615816
commit
651db2f858
|
@ -27,7 +27,12 @@ def get_execution_state(_execution_state_root: Bytes32) -> ExecutionState:
|
|||
|
||||
|
||||
def get_pow_chain_head() -> PowBlock:
|
||||
pass"""
|
||||
pass
|
||||
|
||||
|
||||
def validator_is_connected(validator_index: ValidatorIndex) -> bool:
|
||||
# pylint: disable=unused-argument
|
||||
return True"""
|
||||
|
||||
@classmethod
|
||||
def execution_engine_cls(cls) -> str:
|
||||
|
|
|
@ -89,21 +89,12 @@ immediately after the receipt of a new block, so an approximation of those condi
|
|||
used when deciding whether to send or suppress a fork choice notification. The exact conditions
|
||||
used may be implementation-specific, a suggested implementation is below.
|
||||
|
||||
Let `validator_is_connected` be a function that indicates whether the validator with
|
||||
`validator_index` is connected to the node (e.g. has sent an unexpired proposer preparation
|
||||
message).
|
||||
Let `validator_is_connected(validator_index: ValidatorIndex) -> bool` be a function that indicates
|
||||
whether the validator with `validator_index` is connected to the node (e.g. has sent an unexpired
|
||||
proposer preparation message).
|
||||
|
||||
```python
|
||||
def validator_is_connected(_validator_index: ValidatorIndex) -> boolean:
|
||||
...
|
||||
```
|
||||
|
||||
```python
|
||||
def should_override_forkchoice_update(
|
||||
store: Store,
|
||||
head_root: Root,
|
||||
) -> boolean:
|
||||
justified_state = store.checkpoint_states[store.justified_checkpoint]
|
||||
def should_override_forkchoice_update(store: Store, head_root: Root) -> bool:
|
||||
head_block = store.blocks[head_root]
|
||||
parent_root = head_block.parent_root
|
||||
parent_block = store.blocks[parent_root]
|
||||
|
@ -111,7 +102,13 @@ def should_override_forkchoice_update(
|
|||
proposal_slot = head_block.slot + 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
|
||||
head_late = is_head_late(store, head_root)
|
||||
|
||||
# Shuffling stable.
|
||||
shuffling_stable = is_shuffling_stable(proposal_slot)
|
||||
|
||||
# FFG information of the new head_block will be competitive with the current head.
|
||||
ffg_competitive = is_ffg_competitive(store, head_root, parent_root)
|
||||
|
||||
# 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]
|
||||
|
@ -120,45 +117,32 @@ def should_override_forkchoice_update(
|
|||
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
|
||||
finalization_ok = is_finalization_ok(store, proposal_slot)
|
||||
|
||||
# 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
|
||||
(proposal_slot == current_slot and
|
||||
time_into_slot <= SECONDS_PER_SLOT // INTERVALS_PER_SLOT // 2))
|
||||
proposing_on_time = is_proposing_on_time(store)
|
||||
|
||||
# Note that this condition is different from `get_proposer_head`
|
||||
current_time_ok = (head_block.slot == current_slot
|
||||
or (proposal_slot == current_slot and proposing_on_time))
|
||||
single_slot_reorg = parent_slot_ok and current_time_ok
|
||||
|
||||
# Shuffling stable.
|
||||
shuffling_stable = proposal_slot % SLOTS_PER_EPOCH != 0
|
||||
|
||||
# FFG information of the new head_block will be competitive with the current head.
|
||||
ffg_competitive = (store.unrealized_justifications[parent_root] ==
|
||||
store.unrealized_justifications[head_root])
|
||||
|
||||
# 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.
|
||||
if current_slot > head_block.slot:
|
||||
head_weight = get_weight(store, head_root)
|
||||
reorg_threshold = calculate_committee_fraction(justified_state, REORG_WEIGHT_THRESHOLD)
|
||||
head_weak = head_weight < reorg_threshold
|
||||
|
||||
parent_weight = get_weight(store, parent_root)
|
||||
parent_threshold = calculate_committee_fraction(justified_state, REORG_PARENT_WEIGHT_THRESHOLD)
|
||||
parent_strong = parent_weight > parent_threshold
|
||||
head_weak = is_head_weak(store, head_root)
|
||||
parent_strong = is_parent_strong(store, parent_root)
|
||||
else:
|
||||
head_weak = True
|
||||
parent_strong = True
|
||||
|
||||
return all([head_late, proposing_reorg_slot, finalization_ok, single_slot_reorg,
|
||||
shuffling_stable, ffg_competitive, head_weak, parent_strong])
|
||||
return all([head_late, shuffling_stable, proposing_reorg_slot, ffg_competitive, finalization_ok,
|
||||
single_slot_reorg, head_weak, parent_strong])
|
||||
```
|
||||
|
||||
> Note that the ordering of conditions is a suggestion only. Implementations are free to
|
||||
*Note*: The ordering of conditions is a suggestion only. Implementations are free to
|
||||
optimize by re-ordering the conditions from least to most expensive and by returning early if
|
||||
any of the early conditions are `False`.
|
||||
|
||||
|
|
|
@ -25,7 +25,16 @@
|
|||
- [`filter_block_tree`](#filter_block_tree)
|
||||
- [`get_filtered_block_tree`](#get_filtered_block_tree)
|
||||
- [`get_head`](#get_head)
|
||||
- [`get_proposer_head`](#get_proposer_head)
|
||||
- [Proposer head and reorg helpers](#proposer-head-and-reorg-helpers)
|
||||
- [`is_head_late`](#is_head_late)
|
||||
- [`is_shuffling_stable`](#is_shuffling_stable)
|
||||
- [`is_shuffling_stable`](#is_shuffling_stable-1)
|
||||
- [`is_ffg_competitive`](#is_ffg_competitive)
|
||||
- [`is_finalization_ok`](#is_finalization_ok)
|
||||
- [`is_proposing_on_time`](#is_proposing_on_time)
|
||||
- [`is_head_weak`](#is_head_weak)
|
||||
- [`is_parent_strong`](#is_parent_strong)
|
||||
- [`get_proposer_head`](#get_proposer_head)
|
||||
- [`update_checkpoints`](#update_checkpoints)
|
||||
- [`update_unrealized_checkpoints`](#update_unrealized_checkpoints)
|
||||
- [Pull-up tip helpers](#pull-up-tip-helpers)
|
||||
|
@ -356,58 +365,116 @@ def get_head(store: Store) -> Root:
|
|||
head = max(children, key=lambda root: (get_weight(store, root), root))
|
||||
```
|
||||
|
||||
#### `get_proposer_head`
|
||||
#### Proposer head and reorg helpers
|
||||
|
||||
_Implementing `get_proposer_head` is optional_.
|
||||
_Implementing these helpers is optional_.
|
||||
|
||||
##### `is_head_late`
|
||||
```python
|
||||
def is_head_late(store: Store, head_root: Root) -> bool:
|
||||
return not store.block_timeliness.get(head_root)
|
||||
```
|
||||
|
||||
##### `is_shuffling_stable`
|
||||
```python
|
||||
def is_shuffling_stable(slot: Slot) -> bool:
|
||||
return slot % SLOTS_PER_EPOCH != 0
|
||||
```
|
||||
|
||||
##### `is_shuffling_stable`
|
||||
```python
|
||||
def is_shuffling_stable(slot: Slot) -> bool:
|
||||
return slot % SLOTS_PER_EPOCH != 0
|
||||
```
|
||||
|
||||
##### `is_ffg_competitive`
|
||||
|
||||
```python
|
||||
def is_ffg_competitive(store: Store, head_root: Root, parent_root: Root) -> bool:
|
||||
return (store.unrealized_justifications[head_root] == store.unrealized_justifications[parent_root])
|
||||
```
|
||||
|
||||
##### `is_finalization_ok`
|
||||
|
||||
```python
|
||||
def is_finalization_ok(store: Store, slot: Slot) -> bool:
|
||||
epochs_since_finalization = compute_epoch_at_slot(slot) - store.finalized_checkpoint.epoch
|
||||
return epochs_since_finalization <= REORG_MAX_EPOCHS_SINCE_FINALIZATION
|
||||
```
|
||||
|
||||
##### `is_proposing_on_time`
|
||||
|
||||
```python
|
||||
def is_proposing_on_time(store: Store) -> bool:
|
||||
time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT
|
||||
proposer_reorg_cutoff = SECONDS_PER_SLOT // INTERVALS_PER_SLOT // 2
|
||||
return time_into_slot <= proposer_reorg_cutoff
|
||||
```
|
||||
|
||||
##### `is_head_weak`
|
||||
|
||||
```python
|
||||
def is_head_weak(store: Store, head_root: Root) -> bool:
|
||||
justified_state = store.checkpoint_states[store.justified_checkpoint]
|
||||
reorg_threshold = calculate_committee_fraction(justified_state, REORG_WEIGHT_THRESHOLD)
|
||||
head_weight = get_weight(store, head_root)
|
||||
return head_weight < reorg_threshold
|
||||
```
|
||||
|
||||
##### `is_parent_strong`
|
||||
|
||||
```python
|
||||
def is_parent_strong(store: Store, parent_root: Root) -> bool:
|
||||
justified_state = store.checkpoint_states[store.justified_checkpoint]
|
||||
parent_threshold = calculate_committee_fraction(justified_state, REORG_PARENT_WEIGHT_THRESHOLD)
|
||||
parent_weight = get_weight(store, parent_root)
|
||||
return parent_weight > parent_threshold
|
||||
```
|
||||
|
||||
##### `get_proposer_head`
|
||||
|
||||
```python
|
||||
def get_proposer_head(store: Store, head_root: Root, slot: Slot) -> Root:
|
||||
justified_state = store.checkpoint_states[store.justified_checkpoint]
|
||||
head_block = store.blocks[head_root]
|
||||
parent_root = head_block.parent_root
|
||||
parent_block = store.blocks[parent_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
|
||||
|
||||
# Only re-org if we are proposing on-time.
|
||||
time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT
|
||||
proposing_on_time = time_into_slot <= SECONDS_PER_SLOT // INTERVALS_PER_SLOT // 2
|
||||
head_late = is_head_late(store, head_root)
|
||||
|
||||
# Do not re-org on an epoch boundary where the proposer shuffling could change.
|
||||
shuffling_stable = slot % SLOTS_PER_EPOCH != 0
|
||||
shuffling_stable = is_shuffling_stable(slot)
|
||||
|
||||
# Ensure that the FFG information of the new head will be competitive with the current head.
|
||||
ffg_competitive = (store.unrealized_justifications[parent_root] ==
|
||||
store.unrealized_justifications[head_root])
|
||||
ffg_competitive = is_ffg_competitive(store, head_root, parent_root)
|
||||
|
||||
# Do not re-org if the chain is not finalizing with acceptable frequency.
|
||||
finalization_ok = is_finalization_ok(store, slot)
|
||||
|
||||
# Only re-org if we are proposing on-time.
|
||||
proposing_on_time = is_proposing_on_time(store)
|
||||
|
||||
# Only re-org a single slot at most.
|
||||
parent_slot_ok = parent_block.slot + 1 == head_block.slot
|
||||
current_time_ok = head_block.slot + 1 == slot
|
||||
single_slot_reorg = parent_slot_ok and current_time_ok
|
||||
|
||||
# Check that the head has few enough votes to be overpowered by our proposer boost.
|
||||
assert store.proposer_boost_root != head_root # ensure boost has worn off
|
||||
head_weight = get_weight(store, head_root)
|
||||
reorg_threshold = calculate_committee_fraction(justified_state, REORG_WEIGHT_THRESHOLD)
|
||||
head_weak = head_weight < reorg_threshold
|
||||
head_weak = is_head_weak(store, head_root)
|
||||
|
||||
# Check that the missing votes are assigned to the parent and not being hoarded.
|
||||
parent_weight = get_weight(store, parent_root)
|
||||
parent_threshold = calculate_committee_fraction(justified_state, REORG_PARENT_WEIGHT_THRESHOLD)
|
||||
parent_strong = parent_weight > parent_threshold
|
||||
parent_strong = is_parent_strong(store, parent_root)
|
||||
|
||||
if all([head_late, finalization_ok, single_slot_reorg, proposing_on_time, shuffling_stable,
|
||||
ffg_competitive, head_weak, parent_strong]):
|
||||
if all([head_late, shuffling_stable, ffg_competitive, finalization_ok,
|
||||
proposing_on_time, single_slot_reorg, head_weak, parent_strong]):
|
||||
# We can re-org the current head by building upon its parent block.
|
||||
return parent_root
|
||||
else:
|
||||
return head_root
|
||||
```
|
||||
|
||||
> Note that the ordering of conditions is a suggestion only. Implementations are free to
|
||||
*Note*: The ordering of conditions is a suggestion only. Implementations are free to
|
||||
optimize by re-ordering the conditions from least to most expensive and by returning early if
|
||||
any of the early conditions are `False`.
|
||||
|
||||
|
|
Loading…
Reference in New Issue