From 8bf801ecc64aaa3e1b5ba24b80855bb5e7fa1112 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Tue, 6 Dec 2022 17:44:41 +0100 Subject: [PATCH] Add `block_to_light_client_header` helper Introduce `block_to_light_client_header` helper function to enable future forks to override it with additional info (e.g., execution), without having to change the general light client logic. Likewise, update existing light client data creation flow to use `block_to_light_client_header` and default-initialize empty fields. Furthermore, generalize `create_update` helper to streamline test code using `block_to_light_client_header`. Note: In Altair spec, LC header is the same as `BeaconBlockHeader`. however; future forks will extend it with more information. --- specs/altair/light-client/full-node.md | 64 ++++---- .../light_client/test_update_ranking.py | 154 ++++++++---------- .../light_client/test_sync_protocol.py | 135 +++++++-------- .../eth2spec/test/helpers/light_client.py | 52 +++--- 4 files changed, 181 insertions(+), 224 deletions(-) diff --git a/specs/altair/light-client/full-node.md b/specs/altair/light-client/full-node.md index 53ba4dc82..2dd2ded6c 100644 --- a/specs/altair/light-client/full-node.md +++ b/specs/altair/light-client/full-node.md @@ -11,6 +11,7 @@ - [Introduction](#introduction) - [Helper functions](#helper-functions) - [`compute_merkle_proof_for_state`](#compute_merkle_proof_for_state) + - [`block_to_light_client_header`](#block_to_light_client_header) - [Deriving light client data](#deriving-light-client-data) - [`create_light_client_bootstrap`](#create_light_client_bootstrap) - [`create_light_client_update`](#create_light_client_update) @@ -34,6 +35,19 @@ def compute_merkle_proof_for_state(state: BeaconState, ... ``` +### `block_to_light_client_header` + +```python +def block_to_light_client_header(block: SignedBeaconBlock) -> BeaconBlockHeader: + return BeaconBlockHeader( + slot=block.message.slot, + proposer_index=block.message.proposer_index, + parent_root=block.message.parent_root, + state_root=block.message.state_root, + body_root=hash_tree_root(block.message.body), + ) +``` + ## Deriving light client data Full nodes are expected to derive light client data from historic blocks and states and provide it to other clients. @@ -55,13 +69,7 @@ def create_light_client_bootstrap(state: BeaconState, assert hash_tree_root(header) == hash_tree_root(block.message) return LightClientBootstrap( - header=BeaconBlockHeader( - slot=state.latest_block_header.slot, - proposer_index=state.latest_block_header.proposer_index, - parent_root=state.latest_block_header.parent_root, - state_root=hash_tree_root(state), - body_root=state.latest_block_header.body_root, - ), + header=block_to_light_client_header(block), current_sync_committee=state.current_sync_committee, current_sync_committee_branch=compute_merkle_proof_for_state(state, CURRENT_SYNC_COMMITTEE_INDEX), ) @@ -103,42 +111,30 @@ def create_light_client_update(state: BeaconState, assert hash_tree_root(attested_header) == hash_tree_root(attested_block.message) == block.message.parent_root update_attested_period = compute_sync_committee_period_at_slot(attested_block.message.slot) + update = LightClientUpdate() + + update.attested_header = block_to_light_client_header(attested_block) + # `next_sync_committee` is only useful if the message is signed by the current sync committee if update_attested_period == update_signature_period: - next_sync_committee = attested_state.next_sync_committee - next_sync_committee_branch = compute_merkle_proof_for_state(attested_state, NEXT_SYNC_COMMITTEE_INDEX) - else: - next_sync_committee = SyncCommittee() - next_sync_committee_branch = [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))] + update.next_sync_committee = attested_state.next_sync_committee + update.next_sync_committee_branch = \ + compute_merkle_proof_for_state(attested_state, NEXT_SYNC_COMMITTEE_INDEX) # Indicate finality whenever possible if finalized_block is not None: if finalized_block.message.slot != GENESIS_SLOT: - finalized_header = BeaconBlockHeader( - slot=finalized_block.message.slot, - proposer_index=finalized_block.message.proposer_index, - parent_root=finalized_block.message.parent_root, - state_root=finalized_block.message.state_root, - body_root=hash_tree_root(finalized_block.message.body), - ) - assert hash_tree_root(finalized_header) == attested_state.finalized_checkpoint.root + update.finalized_header = block_to_light_client_header(finalized_block) + assert hash_tree_root(update.finalized_header) == attested_state.finalized_checkpoint.root else: assert attested_state.finalized_checkpoint.root == Bytes32() - finalized_header = BeaconBlockHeader() - finality_branch = compute_merkle_proof_for_state(attested_state, FINALIZED_ROOT_INDEX) - else: - finalized_header = BeaconBlockHeader() - finality_branch = [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))] + update.finality_branch = \ + compute_merkle_proof_for_state(attested_state, FINALIZED_ROOT_INDEX) - return LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=block.message.body.sync_aggregate, - signature_slot=block.message.slot, - ) + update.sync_aggregate = block.message.body.sync_aggregate + update.signature_slot = block.message.slot + + return update ``` Full nodes SHOULD provide the best derivable `LightClientUpdate` (according to `is_better_update`) for each sync committee period covering any epochs in range `[max(ALTAIR_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]` where `current_epoch` is defined by the current wall-clock time. Full nodes MAY also provide `LightClientUpdate` for other sync committee periods. diff --git a/tests/core/pyspec/eth2spec/test/altair/light_client/test_update_ranking.py b/tests/core/pyspec/eth2spec/test/altair/light_client/test_update_ranking.py index 23ad79584..bde70a940 100644 --- a/tests/core/pyspec/eth2spec/test/altair/light_client/test_update_ranking.py +++ b/tests/core/pyspec/eth2spec/test/altair/light_client/test_update_ranking.py @@ -9,45 +9,23 @@ from eth2spec.test.helpers.attestations import ( ) from eth2spec.test.helpers.constants import MINIMAL from eth2spec.test.helpers.light_client import ( - get_sync_aggregate, - signed_block_to_header, + create_update, ) from eth2spec.test.helpers.state import ( next_slots, ) -from math import floor -def create_update(spec, test, with_next, with_finality, participation_rate): +def create_test_update(spec, test, with_next, with_finality, participation_rate): attested_state, attested_block, finalized_block = test - num_participants = floor(spec.SYNC_COMMITTEE_SIZE * participation_rate) - - attested_header = signed_block_to_header(spec, attested_block) - - if with_next: - next_sync_committee = attested_state.next_sync_committee - next_sync_committee_branch = spec.compute_merkle_proof_for_state(attested_state, spec.NEXT_SYNC_COMMITTEE_INDEX) - else: - next_sync_committee = spec.SyncCommittee() - next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] - - if with_finality: - finalized_header = signed_block_to_header(spec, finalized_block) - finality_branch = spec.compute_merkle_proof_for_state(attested_state, spec.FINALIZED_ROOT_INDEX) - else: - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - - sync_aggregate, signature_slot = get_sync_aggregate(spec, attested_state, num_participants) - - return spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + return create_update( + spec, + attested_state, + attested_block, + finalized_block, + with_next, + with_finality, + participation_rate, ) @@ -84,76 +62,76 @@ def test_update_ranking(spec, state): # Create updates (in descending order of quality) updates = [ # Updates with sync committee finality - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=1.0), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=1.0), - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.8), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.8), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=1.0), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=1.0), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.8), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.8), # Updates without sync committee finality - create_update(spec, att, with_next=1, with_finality=1, participation_rate=1.0), - create_update(spec, att, with_next=1, with_finality=1, participation_rate=0.8), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=1.0), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=0.8), # Updates without indication of any finality - create_update(spec, att, with_next=1, with_finality=0, participation_rate=1.0), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=1.0), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=1.0), - create_update(spec, att, with_next=1, with_finality=0, participation_rate=0.8), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.8), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.8), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=1.0), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=1.0), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=1.0), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=0.8), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.8), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.8), # Updates with sync committee finality but no `next_sync_committee` - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.8), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.8), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.8), # Updates without sync committee finality and also no `next_sync_committee` - create_update(spec, att, with_next=0, with_finality=1, participation_rate=1.0), - create_update(spec, att, with_next=0, with_finality=1, participation_rate=0.8), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=1.0), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=0.8), # Updates without indication of any finality nor `next_sync_committee` - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=1.0), - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.8), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=0.8), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.8), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=1.0), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.8), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.8), # Updates with low sync committee participation - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.4), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.4), - create_update(spec, att, with_next=1, with_finality=1, participation_rate=0.4), - create_update(spec, att, with_next=1, with_finality=0, participation_rate=0.4), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.4), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.4), - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, att, with_next=0, with_finality=1, participation_rate=0.4), - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.4), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=0.4), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.4), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.4), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.4), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=0.4), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=0.4), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.4), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.4), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=0.4), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.4), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.4), # Updates with very low sync committee participation - create_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.2), - create_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.2), - create_update(spec, att, with_next=1, with_finality=1, participation_rate=0.2), - create_update(spec, att, with_next=1, with_finality=0, participation_rate=0.2), - create_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.2), - create_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.2), - create_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, att, with_next=0, with_finality=1, participation_rate=0.2), - create_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.2), - create_update(spec, att, with_next=0, with_finality=0, participation_rate=0.2), - create_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.2), - create_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, fin, with_next=1, with_finality=1, participation_rate=0.2), + create_test_update(spec, lat, with_next=1, with_finality=1, participation_rate=0.2), + create_test_update(spec, att, with_next=1, with_finality=1, participation_rate=0.2), + create_test_update(spec, att, with_next=1, with_finality=0, participation_rate=0.2), + create_test_update(spec, fin, with_next=1, with_finality=0, participation_rate=0.2), + create_test_update(spec, lat, with_next=1, with_finality=0, participation_rate=0.2), + create_test_update(spec, sig, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, fin, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, lat, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, att, with_next=0, with_finality=1, participation_rate=0.2), + create_test_update(spec, sig, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, att, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, fin, with_next=0, with_finality=0, participation_rate=0.2), + create_test_update(spec, lat, with_next=0, with_finality=0, participation_rate=0.2), ] yield "updates", updates diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py b/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py index bf09cc30e..a72f1980b 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py @@ -11,43 +11,44 @@ from eth2spec.test.helpers.attestations import ( ) from eth2spec.test.helpers.constants import MINIMAL from eth2spec.test.helpers.light_client import ( - get_sync_aggregate, - initialize_light_client_store, - signed_block_to_header, + create_update, ) from eth2spec.test.helpers.state import ( next_slots, ) +def setup_test(spec, state): + trusted_block = spec.SignedBeaconBlock() + trusted_block.message.state_root = state.hash_tree_root() + trusted_block_root = trusted_block.message.hash_tree_root() + bootstrap = spec.create_light_client_bootstrap(state, trusted_block) + store = spec.initialize_light_client_store(trusted_block_root, bootstrap) + store.next_sync_committee = state.next_sync_committee + + return (trusted_block, store) + + @with_altair_and_later @spec_state_test_with_matching_config def test_process_light_client_update_not_timeout(spec, state): - store = initialize_light_client_store(spec, state) + genesis_block, store = setup_test(spec, state) # Block at slot 1 doesn't increase sync committee period, so it won't force update store.finalized_header attested_block = state_transition_with_full_block(spec, state, False, False) - attested_header = signed_block_to_header(spec, attested_block) - - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - next_sync_committee = spec.SyncCommittee() - next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] + signature_slot = state.slot + 1 # Ensure that finality checkpoint is genesis assert state.finalized_checkpoint.epoch == 0 - # Finality is unchanged - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=genesis_block, + with_next=False, + with_finality=False, + participation_rate=1.0, ) pre_store = deepcopy(store) @@ -64,7 +65,7 @@ def test_process_light_client_update_not_timeout(spec, state): @spec_state_test_with_matching_config @with_presets([MINIMAL], reason="too slow") def test_process_light_client_update_at_period_boundary(spec, state): - store = initialize_light_client_store(spec, state) + genesis_block, store = setup_test(spec, state) # Forward to slot before next sync committee period so that next block is final one in period next_slots(spec, state, spec.UPDATE_TIMEOUT - 2) @@ -73,25 +74,16 @@ def test_process_light_client_update_at_period_boundary(spec, state): assert store_period == update_period attested_block = state_transition_with_full_block(spec, state, False, False) - attested_header = signed_block_to_header(spec, attested_block) + signature_slot = state.slot + 1 - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - next_sync_committee = spec.SyncCommittee() - next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] - - # Finality is unchanged - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=genesis_block, + with_next=False, + with_finality=False, + participation_rate=1.0, ) pre_store = deepcopy(store) @@ -108,7 +100,7 @@ def test_process_light_client_update_at_period_boundary(spec, state): @spec_state_test_with_matching_config @with_presets([MINIMAL], reason="too slow") def test_process_light_client_update_timeout(spec, state): - store = initialize_light_client_store(spec, state) + genesis_block, store = setup_test(spec, state) # Forward to next sync committee period next_slots(spec, state, spec.UPDATE_TIMEOUT) @@ -117,26 +109,16 @@ def test_process_light_client_update_timeout(spec, state): assert store_period + 1 == update_period attested_block = state_transition_with_full_block(spec, state, False, False) - attested_header = signed_block_to_header(spec, attested_block) + signature_slot = state.slot + 1 - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - - # Sync committee is updated - next_sync_committee = state.next_sync_committee - next_sync_committee_branch = spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX) - # Finality is unchanged - finalized_header = spec.BeaconBlockHeader() - finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] - - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=genesis_block, + with_next=True, + with_finality=False, + participation_rate=1.0, ) pre_store = deepcopy(store) @@ -153,7 +135,7 @@ def test_process_light_client_update_timeout(spec, state): @spec_state_test_with_matching_config @with_presets([MINIMAL], reason="too slow") def test_process_light_client_update_finality_updated(spec, state): - store = initialize_light_client_store(spec, state) + _, store = setup_test(spec, state) # Change finality blocks = [] @@ -169,28 +151,21 @@ def test_process_light_client_update_finality_updated(spec, state): assert store_period == update_period attested_block = blocks[-1] - attested_header = signed_block_to_header(spec, attested_block) + signature_slot = state.slot + 1 - # Sync committee signing the attested_header - sync_aggregate, signature_slot = get_sync_aggregate(spec, state) - - # 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))] + # Updated finality finalized_block = blocks[spec.SLOTS_PER_EPOCH - 1] - finalized_header = signed_block_to_header(spec, finalized_block) - assert finalized_header.slot == spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) - assert finalized_header.hash_tree_root() == state.finalized_checkpoint.root - finality_branch = spec.compute_merkle_proof_for_state(state, spec.FINALIZED_ROOT_INDEX) + assert finalized_block.message.slot == spec.compute_start_slot_at_epoch(state.finalized_checkpoint.epoch) + assert finalized_block.message.hash_tree_root() == state.finalized_checkpoint.root - update = spec.LightClientUpdate( - attested_header=attested_header, - next_sync_committee=next_sync_committee, - next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finalized_header, - finality_branch=finality_branch, - sync_aggregate=sync_aggregate, - signature_slot=signature_slot, + update = create_update( + spec, + attested_state=state, + attested_block=attested_block, + finalized_block=finalized_block, + with_next=False, + with_finality=True, + participation_rate=1.0, ) spec.process_light_client_update(store, update, signature_slot, state.genesis_validators_root) diff --git a/tests/core/pyspec/eth2spec/test/helpers/light_client.py b/tests/core/pyspec/eth2spec/test/helpers/light_client.py index 8d632b3a1..39175a352 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/light_client.py +++ b/tests/core/pyspec/eth2spec/test/helpers/light_client.py @@ -5,28 +5,7 @@ from eth2spec.test.helpers.sync_committee import ( compute_aggregate_sync_committee_signature, compute_committee_indices, ) - - -def signed_block_to_header(spec, block): - return spec.BeaconBlockHeader( - slot=block.message.slot, - proposer_index=block.message.proposer_index, - parent_root=block.message.parent_root, - state_root=block.message.state_root, - body_root=block.message.body.hash_tree_root(), - ) - - -def initialize_light_client_store(spec, state): - return spec.LightClientStore( - finalized_header=spec.BeaconBlockHeader(), - current_sync_committee=state.current_sync_committee, - next_sync_committee=state.next_sync_committee, - best_valid_update=None, - optimistic_header=spec.BeaconBlockHeader(), - previous_max_active_participants=0, - current_max_active_participants=0, - ) +from math import floor def get_sync_aggregate(spec, state, num_participants=None, signature_slot=None): @@ -60,3 +39,32 @@ def get_sync_aggregate(spec, state, num_participants=None, signature_slot=None): sync_committee_signature=sync_committee_signature, ) return sync_aggregate, signature_slot + + +def create_update(spec, + attested_state, + attested_block, + finalized_block, + with_next, + with_finality, + participation_rate): + num_participants = floor(spec.SYNC_COMMITTEE_SIZE * participation_rate) + + update = spec.LightClientUpdate() + + update.attested_header = spec.block_to_light_client_header(attested_block) + + if with_next: + update.next_sync_committee = attested_state.next_sync_committee + update.next_sync_committee_branch = \ + spec.compute_merkle_proof_for_state(attested_state, spec.NEXT_SYNC_COMMITTEE_INDEX) + + if with_finality: + update.finalized_header = spec.block_to_light_client_header(finalized_block) + update.finality_branch = \ + spec.compute_merkle_proof_for_state(attested_state, spec.FINALIZED_ROOT_INDEX) + + update.sync_aggregate, update.signature_slot = \ + get_sync_aggregate(spec, attested_state, num_participants) + + return update