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

Add block_to_light_client_header helper #3149

Merged
merged 2 commits into from
Jan 5, 2023
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
66 changes: 31 additions & 35 deletions specs/altair/light-client/full-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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:
etan-status marked this conversation as resolved.
Show resolved Hide resolved
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.
Expand All @@ -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),
)
Expand Down Expand Up @@ -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))]
Comment on lines -129 to -131
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewers: removed because they are the default value of update.finalized_header and update.finality_branch.


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,
)
Comment on lines -133 to -141
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel the old return LightClientUpdate(...) is more mistake-proofing, i.e., less likely to miss one field. However, I like how your refactoring reduced the variables. 👍

update.finality_branch = compute_merkle_proof_for_state(
attested_state, FINALIZED_ROOT_INDEX)

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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
@spec_state_test
def test_current_sync_committee_merkle_proof(spec, state):
yield "object", state
current_sync_committee_branch = \
spec.compute_merkle_proof_for_state(state, spec.CURRENT_SYNC_COMMITTEE_INDEX)
current_sync_committee_branch = spec.compute_merkle_proof_for_state(
state, spec.CURRENT_SYNC_COMMITTEE_INDEX)
yield "proof", {
"leaf": "0x" + state.current_sync_committee.hash_tree_root().hex(),
"leaf_index": spec.CURRENT_SYNC_COMMITTEE_INDEX,
Expand All @@ -31,8 +31,8 @@ def test_current_sync_committee_merkle_proof(spec, state):
@spec_state_test
def test_next_sync_committee_merkle_proof(spec, state):
yield "object", state
next_sync_committee_branch = \
spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX)
next_sync_committee_branch = spec.compute_merkle_proof_for_state(
state, spec.NEXT_SYNC_COMMITTEE_INDEX)
yield "proof", {
"leaf": "0x" + state.next_sync_committee.hash_tree_root().hex(),
"leaf_index": spec.NEXT_SYNC_COMMITTEE_INDEX,
Expand All @@ -52,8 +52,8 @@ def test_next_sync_committee_merkle_proof(spec, state):
@spec_state_test
def test_finality_root_merkle_proof(spec, state):
yield "object", state
finality_branch = \
spec.compute_merkle_proof_for_state(state, spec.FINALIZED_ROOT_INDEX)
finality_branch = spec.compute_merkle_proof_for_state(
state, spec.FINALIZED_ROOT_INDEX)
yield "proof", {
"leaf": "0x" + state.finalized_checkpoint.root.hex(),
"leaf_index": spec.FINALIZED_ROOT_INDEX,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,23 @@
)
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,
)


Expand Down Expand Up @@ -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

Expand Down
Loading