Skip to content

Commit

Permalink
Merge pull request #2947 from etan-status/lc-forceupdate
Browse files Browse the repository at this point in the history
Manually trigger `LightClientStore` force updates
  • Loading branch information
hwwhww authored Jul 22, 2022
2 parents 4370f30 + 8643e28 commit d717f7e
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 30 deletions.
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

0 comments on commit d717f7e

Please sign in to comment.