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

Restrict best LC update collection to canonical blocks #3553

Merged
merged 27 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d82fa3d
Restrict best LC update collection to canonical blocks
etan-status Nov 21, 2023
940f45b
Merge branch 'dev' into lc-canonical
etan-status Feb 6, 2024
be29841
Add canonical data collection test infrastructure
etan-status Feb 6, 2024
2154298
Typo
etan-status Feb 6, 2024
248f32b
Lint
etan-status Feb 6, 2024
c0d037f
Fix missing `optimistc_update` in new tests
etan-status Feb 23, 2024
3f1a8fc
Merge branch 'dev' into lc-canonical
etan-status Feb 23, 2024
b8f0ddc
Add more tests for multi-period reorgs
etan-status Mar 3, 2024
bacba34
Merge branch 'dev' into lc-canonical
etan-status Sep 20, 2024
c9c21e9
Merge branch 'dev' into lc-canonical
etan-status Oct 9, 2024
9468496
Fix nits in data_collection format
jtraglia Nov 22, 2024
5639ca6
Rename two classes for consistency
jtraglia Nov 22, 2024
aff4e34
Move bellatrix/capella tests into respective dirs
jtraglia Nov 22, 2024
b6259a9
Revert "Move bellatrix/capella tests into respective dirs"
jtraglia Nov 22, 2024
e00e866
Synchronise capitalization change request across files
etan-status Nov 27, 2024
0e29f20
Merge branch 'dev' into lc-canonical
etan-status Nov 27, 2024
84bef3c
Split LC sync test into multiple files
etan-status Nov 27, 2024
75c65e6
Split LC data collection test into multiple files
etan-status Nov 27, 2024
24dffad
Link tests with generator
etan-status Nov 27, 2024
eaed600
Lint
etan-status Nov 27, 2024
531a0b0
Fix module list
etan-status Nov 27, 2024
12401a5
Move fork tests to origin rather than destination to fix issues
etan-status Nov 28, 2024
30bed61
Add missing mod
etan-status Nov 28, 2024
a52a82c
Extend decorator factory to support `other_phases`
etan-status Nov 28, 2024
09e8f01
Make `from` -> `to` bounds explicit
etan-status Nov 28, 2024
51ad00a
Merge branch 'dev' into lc-canonical
etan-status Jan 6, 2025
5565b2b
Merge branch 'dev' into lc-canonical
etan-status Jan 6, 2025
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
2 changes: 1 addition & 1 deletion specs/altair/light-client/full-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ Full nodes SHOULD provide the best derivable `LightClientUpdate` (according to `

- `LightClientUpdate` are assigned to sync committee periods based on their `attested_header.beacon.slot`
- `LightClientUpdate` are only considered if `compute_sync_committee_period_at_slot(update.attested_header.beacon.slot) == compute_sync_committee_period_at_slot(update.signature_slot)`
- Only `LightClientUpdate` with `next_sync_committee` as selected by fork choice are provided, regardless of ranking by `is_better_update`. To uniquely identify a non-finalized sync committee fork, all of `period`, `current_sync_committee` and `next_sync_committee` need to be incorporated, as sync committees may reappear over time.
- Only `LightClientUpdate` with `sync_aggregate` from blocks on the canonical chain as selected by fork choice are considered, regardless of ranking by `is_better_update`. `LightClientUpdate` referring to orphaned blocks SHOULD NOT be provided.

### `create_light_client_finality_update`

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
from eth2spec.test.context import (
spec_state_test_with_matching_config,
with_presets,
with_light_client,
)
from eth2spec.test.helpers.constants import (
MINIMAL,
)
from eth2spec.test.helpers.light_client_data_collection import (
add_new_block,
finish_lc_data_collection_test,
get_lc_bootstrap_block_id,
get_lc_update_attested_block_id,
get_light_client_bootstrap,
get_light_client_finality_update,
get_light_client_optimistic_update,
get_light_client_update_for_period,
select_new_head,
setup_lc_data_collection_test,
BlockID,
)


@with_light_client
@spec_state_test_with_matching_config
@with_presets([MINIMAL], reason="too slow")
def test_light_client_data_collection(spec, state):
# Start test
test = yield from setup_lc_data_collection_test(spec, state)

# Genesis block is post Altair and is finalized, so can be used as bootstrap
genesis_bid = BlockID(slot=state.slot, root=spec.BeaconBlock(state_root=state.hash_tree_root()).hash_tree_root())
assert get_lc_bootstrap_block_id(get_light_client_bootstrap(test, genesis_bid.root).data) == genesis_bid

# No blocks have been imported, so no other light client data is available
period = spec.compute_sync_committee_period_at_slot(state.slot)
assert get_light_client_update_for_period(test, period).spec is None
assert get_light_client_finality_update(test).spec is None
assert get_light_client_optimistic_update(test).spec is None

# Start branch A with a block that has an empty sync aggregate
spec_a, state_a, bid_1 = yield from add_new_block(test, spec, state, slot=1)
yield from select_new_head(test, spec_a, bid_1)
period = spec_a.compute_sync_committee_period_at_slot(state_a.slot)
assert get_light_client_update_for_period(test, period).spec is None
assert get_light_client_finality_update(test).spec is None
assert get_light_client_optimistic_update(test).spec is None

# Start branch B with a block that has 1 participant
spec_b, state_b, bid_2 = yield from add_new_block(test, spec, state, slot=2, num_sync_participants=1)
yield from select_new_head(test, spec_b, bid_2)
period = spec_b.compute_sync_committee_period_at_slot(state_b.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == genesis_bid
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == genesis_bid
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == genesis_bid

# Build on branch A, once more with an empty sync aggregate
spec_a, state_a, bid_3 = yield from add_new_block(test, spec_a, state_a, slot=3)
yield from select_new_head(test, spec_a, bid_3)
period = spec_a.compute_sync_committee_period_at_slot(state_a.slot)
assert get_light_client_update_for_period(test, period).spec is None
assert get_light_client_finality_update(test).spec is None
assert get_light_client_optimistic_update(test).spec is None

# Build on branch B, this time with an empty sync aggregate
spec_b, state_b, bid_4 = yield from add_new_block(test, spec_b, state_b, slot=4)
yield from select_new_head(test, spec_b, bid_4)
period = spec_b.compute_sync_committee_period_at_slot(state_b.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == genesis_bid
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == genesis_bid
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == genesis_bid

# Build on branch B, once more with 1 participant
spec_b, state_b, bid_5 = yield from add_new_block(test, spec_b, state_b, slot=5, num_sync_participants=1)
yield from select_new_head(test, spec_b, bid_5)
period = spec_b.compute_sync_committee_period_at_slot(state_b.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == genesis_bid
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == bid_4
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == bid_4

# Build on branch B, this time with 3 participants
spec_b, state_b, bid_6 = yield from add_new_block(test, spec_b, state_b, slot=6, num_sync_participants=3)
yield from select_new_head(test, spec_b, bid_6)
period = spec_b.compute_sync_committee_period_at_slot(state_b.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == bid_5
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == bid_5
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == bid_5

# Build on branch A, with 2 participants
spec_a, state_a, bid_7 = yield from add_new_block(test, spec_a, state_a, slot=7, num_sync_participants=2)
yield from select_new_head(test, spec_a, bid_7)
period = spec_a.compute_sync_committee_period_at_slot(state_a.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == bid_3
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == bid_3
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == bid_3

# Branch A: epoch 1, slot 5
slot = spec_a.compute_start_slot_at_epoch(1) + 5
spec_a, state_a, bid_1_5 = yield from add_new_block(test, spec_a, state_a, slot=slot, num_sync_participants=4)
yield from select_new_head(test, spec_a, bid_1_5)
assert get_light_client_bootstrap(test, bid_7.root).spec is None
assert get_light_client_bootstrap(test, bid_1_5.root).spec is None
period = spec_a.compute_sync_committee_period_at_slot(state_a.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == bid_7
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == bid_7
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == bid_7

# Branch B: epoch 2, slot 4
slot = spec_b.compute_start_slot_at_epoch(2) + 4
spec_b, state_b, bid_2_4 = yield from add_new_block(test, spec_b, state_b, slot=slot, num_sync_participants=5)
yield from select_new_head(test, spec_b, bid_2_4)
assert get_light_client_bootstrap(test, bid_7.root).spec is None
assert get_light_client_bootstrap(test, bid_1_5.root).spec is None
assert get_light_client_bootstrap(test, bid_2_4.root).spec is None
period = spec_b.compute_sync_committee_period_at_slot(state_b.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == bid_6
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == bid_6
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == bid_6

# Branch A: epoch 3, slot 0
slot = spec_a.compute_start_slot_at_epoch(3) + 0
spec_a, state_a, bid_3_0 = yield from add_new_block(test, spec_a, state_a, slot=slot, num_sync_participants=6)
yield from select_new_head(test, spec_a, bid_3_0)
assert get_light_client_bootstrap(test, bid_7.root).spec is None
assert get_light_client_bootstrap(test, bid_1_5.root).spec is None
assert get_light_client_bootstrap(test, bid_2_4.root).spec is None
assert get_light_client_bootstrap(test, bid_3_0.root).spec is None
period = spec_a.compute_sync_committee_period_at_slot(state_a.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == bid_1_5
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == bid_1_5
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == bid_1_5

# Branch A: fill epoch
for i in range(1, spec_a.SLOTS_PER_EPOCH):
spec_a, state_a, bid_a = yield from add_new_block(test, spec_a, state_a)
yield from select_new_head(test, spec_a, bid_a)
assert get_light_client_bootstrap(test, bid_7.root).spec is None
assert get_light_client_bootstrap(test, bid_1_5.root).spec is None
assert get_light_client_bootstrap(test, bid_2_4.root).spec is None
assert get_light_client_bootstrap(test, bid_3_0.root).spec is None
period = spec_a.compute_sync_committee_period_at_slot(state_a.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == bid_1_5
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == bid_1_5
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == bid_1_5
assert state_a.slot == spec_a.compute_start_slot_at_epoch(4) - 1
bid_3_n = bid_a

# Branch A: epoch 4, slot 0
slot = spec_a.compute_start_slot_at_epoch(4) + 0
spec_a, state_a, bid_4_0 = yield from add_new_block(test, spec_a, state_a, slot=slot, num_sync_participants=6)
yield from select_new_head(test, spec_a, bid_4_0)
assert get_light_client_bootstrap(test, bid_7.root).spec is None
assert get_light_client_bootstrap(test, bid_1_5.root).spec is None
assert get_light_client_bootstrap(test, bid_2_4.root).spec is None
assert get_light_client_bootstrap(test, bid_3_0.root).spec is None
assert get_light_client_bootstrap(test, bid_4_0.root).spec is None
period = spec_a.compute_sync_committee_period_at_slot(state_a.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == bid_1_5
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == bid_3_n
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == bid_3_n

# Branch A: fill epoch
for i in range(1, spec_a.SLOTS_PER_EPOCH):
spec_a, state_a, bid_a = yield from add_new_block(test, spec_a, state_a)
yield from select_new_head(test, spec_a, bid_a)
assert get_light_client_bootstrap(test, bid_7.root).spec is None
assert get_light_client_bootstrap(test, bid_1_5.root).spec is None
assert get_light_client_bootstrap(test, bid_2_4.root).spec is None
assert get_light_client_bootstrap(test, bid_3_0.root).spec is None
assert get_light_client_bootstrap(test, bid_4_0.root).spec is None
period = spec_a.compute_sync_committee_period_at_slot(state_a.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == bid_1_5
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == bid_3_n
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == bid_3_n
assert state_a.slot == spec_a.compute_start_slot_at_epoch(5) - 1
bid_4_n = bid_a

# Branch A: epoch 6, slot 2
slot = spec_a.compute_start_slot_at_epoch(6) + 2
spec_a, state_a, bid_6_2 = yield from add_new_block(test, spec_a, state_a, slot=slot, num_sync_participants=6)
yield from select_new_head(test, spec_a, bid_6_2)
assert get_lc_bootstrap_block_id(get_light_client_bootstrap(test, bid_7.root).data) == bid_7
assert get_lc_bootstrap_block_id(get_light_client_bootstrap(test, bid_1_5.root).data) == bid_1_5
assert get_light_client_bootstrap(test, bid_2_4.root).spec is None
assert get_lc_bootstrap_block_id(get_light_client_bootstrap(test, bid_3_0.root).data) == bid_3_0
assert get_light_client_bootstrap(test, bid_4_0.root).spec is None
period = spec_a.compute_sync_committee_period_at_slot(state_a.slot)
assert get_lc_update_attested_block_id(get_light_client_update_for_period(test, period).data) == bid_1_5
assert get_lc_update_attested_block_id(get_light_client_finality_update(test).data) == bid_4_n
assert get_lc_update_attested_block_id(get_light_client_optimistic_update(test).data) == bid_4_n

# Finish test
yield from finish_lc_data_collection_test(test)
Loading