diff --git a/specs/altair/sync-protocol.md b/specs/altair/sync-protocol.md index 5340a2015..fdfa68da8 100644 --- a/specs/altair/sync-protocol.md +++ b/specs/altair/sync-protocol.md @@ -16,6 +16,7 @@ - [`LightClientUpdate`](#lightclientupdate) - [`LightClientStore`](#lightclientstore) - [Helper functions](#helper-functions) + - [`is_sync_committee_update`](#is_sync_committee_update) - [`is_finality_update`](#is_finality_update) - [`get_subtree_index`](#get_subtree_index) - [`get_active_header`](#get_active_header) @@ -96,6 +97,13 @@ class LightClientStore(object): ## Helper functions +### `is_sync_committee_update` + +```python +def is_sync_committee_update(update: LightClientUpdate) -> bool: + return update.next_sync_committee_branch != [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))] +``` + ### `is_finality_update` ```python @@ -189,10 +197,14 @@ def validate_light_client_update(store: LightClientStore, root=update.attested_header.state_root, ) - # Verify update next sync committee if the update period incremented - if update_period == finalized_period: - assert update.next_sync_committee_branch == [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))] + # Verify that the `next_sync_committee`, if present, actually is the next sync committee saved in the + # state of the `active_header` + if not is_sync_committee_update(update): + assert update_period == finalized_period + assert update.next_sync_committee == SyncCommittee() else: + if update_period == finalized_period: + assert update.next_sync_committee == store.next_sync_committee assert is_valid_merkle_branch( leaf=hash_tree_root(update.next_sync_committee), branch=update.next_sync_committee_branch, diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py b/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py index 69d9db0f2..a252f0e68 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py @@ -42,6 +42,7 @@ def test_process_light_client_update_not_timeout(spec, state): # Sync committee signing the block_header sync_aggregate, signature_slot = get_sync_aggregate(spec, state, block_header) + next_sync_committee = spec.SyncCommittee() next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] # Ensure that finality checkpoint is genesis @@ -52,7 +53,7 @@ def test_process_light_client_update_not_timeout(spec, state): update = spec.LightClientUpdate( attested_header=block_header, - next_sync_committee=state.next_sync_committee, + next_sync_committee=next_sync_committee, next_sync_committee_branch=next_sync_committee_branch, finalized_header=finality_header, finality_branch=finality_branch, @@ -94,6 +95,7 @@ def test_process_light_client_update_at_period_boundary(spec, state): # Sync committee signing the block_header sync_aggregate, signature_slot = get_sync_aggregate(spec, state, block_header) + next_sync_committee = spec.SyncCommittee() next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] # Finality is unchanged @@ -102,7 +104,7 @@ def test_process_light_client_update_at_period_boundary(spec, state): update = spec.LightClientUpdate( attested_header=block_header, - next_sync_committee=state.next_sync_committee, + next_sync_committee=next_sync_committee, next_sync_committee_branch=next_sync_committee_branch, finalized_header=finality_header, finality_branch=finality_branch, @@ -146,6 +148,7 @@ def test_process_light_client_update_timeout(spec, state): sync_aggregate, signature_slot = get_sync_aggregate(spec, state, block_header) # Sync committee is updated + next_sync_committee = state.next_sync_committee next_sync_committee_branch = build_proof(state.get_backing(), spec.NEXT_SYNC_COMMITTEE_INDEX) # Finality is unchanged finality_header = spec.BeaconBlockHeader() @@ -153,7 +156,7 @@ def test_process_light_client_update_timeout(spec, state): update = spec.LightClientUpdate( attested_header=block_header, - next_sync_committee=state.next_sync_committee, + next_sync_committee=next_sync_committee, next_sync_committee_branch=next_sync_committee_branch, finalized_header=finality_header, finality_branch=finality_branch, @@ -191,6 +194,7 @@ def test_process_light_client_update_finality_updated(spec, state): assert snapshot_period == update_period # Updated sync_committee and finality + next_sync_committee = spec.SyncCommittee() next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] finalized_block_header = blocks[spec.SLOTS_PER_EPOCH - 1].message assert finalized_block_header.slot == spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) @@ -212,7 +216,7 @@ def test_process_light_client_update_finality_updated(spec, state): update = spec.LightClientUpdate( attested_header=block_header, - next_sync_committee=state.next_sync_committee, + next_sync_committee=next_sync_committee, next_sync_committee_branch=next_sync_committee_branch, finalized_header=finalized_block_header, finality_branch=finality_branch,