Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manually trigger LightClientStore force updates #2947

Merged
merged 2 commits into from
Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 22 additions & 23 deletions specs/altair/light-client/sync-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
- [Light client initialization](#light-client-initialization)
- [`initialize_light_client_store`](#initialize_light_client_store)
- [Light client state updates](#light-client-state-updates)
- [`process_slot_for_light_client_store`](#process_slot_for_light_client_store)
- [`validate_light_client_update`](#validate_light_client_update)
- [`apply_light_client_update`](#apply_light_client_update)
- [`process_light_client_store_force_update`](#process_light_client_store_force_update)
- [`process_light_client_update`](#process_light_client_update)
- [`process_light_client_finality_update`](#process_light_client_finality_update)
- [`process_light_client_optimistic_update`](#process_light_client_optimistic_update)
Expand Down Expand Up @@ -285,28 +285,7 @@ def initialize_light_client_store(trusted_block_root: Root,
- **`update: LightClientUpdate`**: Every `update` triggers `process_light_client_update(store, update, current_slot, genesis_validators_root)` where `current_slot` is the current slot based on a local clock.
- **`finality_update: LightClientFinalityUpdate`**: Every `finality_update` triggers `process_light_client_finality_update(store, finality_update, current_slot, genesis_validators_root)`.
- **`optimistic_update: LightClientOptimisticUpdate`**: Every `optimistic_update` triggers `process_light_client_optimistic_update(store, optimistic_update, current_slot, genesis_validators_root)`.
- `process_slot_for_light_client_store` is triggered every time the current slot increments.

### `process_slot_for_light_client_store`

```python
def process_slot_for_light_client_store(store: LightClientStore, current_slot: Slot) -> None:
if current_slot % UPDATE_TIMEOUT == 0:
store.previous_max_active_participants = store.current_max_active_participants
store.current_max_active_participants = 0
if (
current_slot > store.finalized_header.slot + UPDATE_TIMEOUT
and store.best_valid_update is not None
):
# Forced best update when the update timeout has elapsed.
# Because the apply logic waits for `finalized_header.slot` to indicate sync committee finality,
# the `attested_header` may be treated as `finalized_header` in extended periods of non-finality
# to guarantee progression into later sync committee periods according to `is_better_update`.
if store.best_valid_update.finalized_header.slot <= store.finalized_header.slot:
store.best_valid_update.finalized_header = store.best_valid_update.attested_header
apply_light_client_update(store, store.best_valid_update)
store.best_valid_update = None
```
- `process_light_client_store_force_update` MAY be called based on use case dependent heuristics if light client sync appears stuck.

### `validate_light_client_update`

Expand Down Expand Up @@ -399,12 +378,32 @@ def apply_light_client_update(store: LightClientStore, update: LightClientUpdate
elif update_finalized_period == store_period + 1:
store.current_sync_committee = store.next_sync_committee
store.next_sync_committee = update.next_sync_committee
store.previous_max_active_participants = store.current_max_active_participants
store.current_max_active_participants = 0
if update.finalized_header.slot > store.finalized_header.slot:
store.finalized_header = update.finalized_header
if store.finalized_header.slot > store.optimistic_header.slot:
store.optimistic_header = store.finalized_header
```

### `process_light_client_store_force_update`

```python
def process_light_client_store_force_update(store: LightClientStore, current_slot: Slot) -> None:
if (
current_slot > store.finalized_header.slot + UPDATE_TIMEOUT
and store.best_valid_update is not None
):
# Forced best update when the update timeout has elapsed.
# Because the apply logic waits for `finalized_header.slot` to indicate sync committee finality,
# the `attested_header` may be treated as `finalized_header` in extended periods of non-finality
# to guarantee progression into later sync committee periods according to `is_better_update`.
if store.best_valid_update.finalized_header.slot <= store.finalized_header.slot:
store.best_valid_update.finalized_header = store.best_valid_update.attested_header
apply_light_client_update(store, store.best_valid_update)
store.best_valid_update = None
```

### `process_light_client_update`

```python
Expand Down
10 changes: 5 additions & 5 deletions tests/core/pyspec/eth2spec/test/altair/light_client/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ def get_checks(store):
}


def emit_slot(test, spec, state):
def emit_force_update(test, spec, state):
current_slot = state.slot
spec.process_slot_for_light_client_store(test.store, current_slot)
spec.process_light_client_store_force_update(test.store, current_slot)

yield from [] # Consistently enable `yield from` syntax in calling tests
test.steps.append({
"process_slot": {
"force_update": {
"current_slot": int(current_slot),
"checks": get_checks(test.store),
}
Expand Down Expand Up @@ -249,7 +249,7 @@ def test_light_client_sync(spec, state):
# ```
attested_state = state.copy()
next_slots(spec, state, spec.UPDATE_TIMEOUT - 1)
yield from emit_slot(test, spec, state)
yield from emit_force_update(test, spec, state)
assert test.store.finalized_header.slot == store_state.slot
assert test.store.next_sync_committee == store_state.next_sync_committee
assert test.store.best_valid_update is None
Expand Down Expand Up @@ -293,7 +293,7 @@ def test_light_client_sync(spec, state):
assert test.store.next_sync_committee == store_state.next_sync_committee
assert test.store.best_valid_update == update
assert test.store.optimistic_header.slot == attested_state.slot
yield from emit_slot(test, spec, state)
yield from emit_force_update(test, spec, state)
assert test.store.finalized_header.slot == attested_state.slot
assert test.store.next_sync_committee == attested_state.next_sync_committee
assert test.store.best_valid_update is None
Expand Down
4 changes: 2 additions & 2 deletions tests/formats/light_client/sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ optimistic_header: {
}
```

#### `process_slot` execution step
#### `force_update` execution step

The function `process_slot_for_light_client_store(store, current_slot)`
The function `process_light_client_store_force_update(store, current_slot)`
should be executed with the specified parameters:

```yaml
Expand Down