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

Pass blocks to LC data creation functions #3070

Merged
merged 4 commits into from
Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
21 changes: 16 additions & 5 deletions specs/altair/light-client/full-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,19 @@ Full nodes are expected to derive light client data from historic blocks and sta

### `create_light_client_bootstrap`

To form a `LightClientBootstrap`, the following objects are needed:
- `state`: the post state of any post-Altair block
- `block`: the corresponding block

```python
def create_light_client_bootstrap(state: BeaconState) -> LightClientBootstrap:
def create_light_client_bootstrap(state: BeaconState,
block: SignedBeaconBlock) -> LightClientBootstrap:
assert compute_epoch_at_slot(state.slot) >= ALTAIR_FORK_EPOCH

assert state.slot == state.latest_block_header.slot
header = state.latest_block_header.copy()
header.state_root = hash_tree_root(state)
assert hash_tree_root(header) == hash_tree_root(block.message)

return LightClientBootstrap(
header=BeaconBlockHeader(
Expand All @@ -54,7 +63,7 @@ def create_light_client_bootstrap(state: BeaconState) -> LightClientBootstrap:
body_root=state.latest_block_header.body_root,
),
current_sync_committee=state.current_sync_committee,
current_sync_committee_branch=compute_merkle_proof_for_state(state, CURRENT_SYNC_COMMITTEE_INDEX)
current_sync_committee_branch=compute_merkle_proof_for_state(state, CURRENT_SYNC_COMMITTEE_INDEX),
)
```

Expand All @@ -69,13 +78,15 @@ Blocks are considered to be epoch boundary blocks if their block root can occur
To form a `LightClientUpdate`, the following historical states and blocks are needed:
- `state`: the post state of any block with a post-Altair parent block
- `block`: the corresponding block
- `attested_state`: the post state of the block referred to by `block.parent_root`
- `attested_state`: the post state of `attested_block`
- `attested_block`: the block referred to by `block.parent_root`
- `finalized_block`: the block referred to by `attested_state.finalized_checkpoint.root`, if locally available (may be unavailable, e.g., when using checkpoint sync, or if it was pruned locally)

```python
def create_light_client_update(state: BeaconState,
block: SignedBeaconBlock,
attested_state: BeaconState,
attested_block: SignedBeaconBlock,
finalized_block: Optional[SignedBeaconBlock]) -> LightClientUpdate:
assert compute_epoch_at_slot(attested_state.slot) >= ALTAIR_FORK_EPOCH
assert sum(block.message.body.sync_aggregate.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
Expand All @@ -89,8 +100,8 @@ def create_light_client_update(state: BeaconState,
assert attested_state.slot == attested_state.latest_block_header.slot
attested_header = attested_state.latest_block_header.copy()
attested_header.state_root = hash_tree_root(attested_state)
assert hash_tree_root(attested_header) == block.message.parent_root
update_attested_period = compute_sync_committee_period_at_slot(attested_header.slot)
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)

# `next_sync_committee` is only useful if the message is signed by the current sync committee
if update_attested_period == update_signature_period:
Expand Down
2 changes: 1 addition & 1 deletion specs/altair/light-client/p2p-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Requests the `LightClientBootstrap` structure corresponding to a given post-Alta

The request MUST be encoded as an SSZ-field.

Peers SHOULD provide results as defined in [`create_light_client_bootstrap`](./full-node.md#create_light_client_bootstrap). To fulfill a request, the requested block's post state needs to be known.
Peers SHOULD provide results as defined in [`create_light_client_bootstrap`](./full-node.md#create_light_client_bootstrap). To fulfill a request, the requested block and its post state need to be known.

When a `LightClientBootstrap` instance cannot be produced for a given block root, peers SHOULD respond with error code `3: ResourceUnavailable`.

Expand Down
64 changes: 38 additions & 26 deletions tests/core/pyspec/eth2spec/test/altair/light_client/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class LightClientSyncTest(object):
next_slots(spec, state, spec.SLOTS_PER_EPOCH * 2 - 1)
trusted_block = state_transition_with_full_block(spec, state, True, True)
trusted_block_root = trusted_block.message.hash_tree_root()
bootstrap = spec.create_light_client_bootstrap(state)
bootstrap = spec.create_light_client_bootstrap(state, trusted_block)
yield "trusted_block_root", "meta", "0x" + trusted_block_root.hex()
yield "bootstrap", bootstrap
test.store = spec.initialize_light_client_store(trusted_block_root, bootstrap)
Expand Down Expand Up @@ -85,9 +85,9 @@ def emit_force_update(test, spec, state):
})


def emit_update(test, spec, state, block, attested_state, finalized_block, with_next_sync_committee=True):
update = spec.create_light_client_update(state, block, attested_state, finalized_block)
if not with_next_sync_committee:
def emit_update(test, spec, state, block, attested_state, attested_block, finalized_block, with_next=True):
update = spec.create_light_client_update(state, block, attested_state, attested_block, finalized_block)
if not with_next:
update.next_sync_committee = spec.SyncCommittee()
update.next_sync_committee_branch = \
[spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))]
Expand Down Expand Up @@ -135,11 +135,12 @@ def test_light_client_sync(spec, state):
next_slots(spec, state, spec.SLOTS_PER_EPOCH - 1)
finalized_block = state_transition_with_full_block(spec, state, True, True)
finalized_state = state.copy()
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH, True, True)
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH - 1, True, True)
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
yield from emit_update(test, spec, state, block, attested_state, finalized_block)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block)
assert test.store.finalized_header.slot == finalized_state.slot
assert test.store.next_sync_committee == finalized_state.next_sync_committee
assert test.store.best_valid_update is None
Expand All @@ -160,11 +161,12 @@ def test_light_client_sync(spec, state):
next_slots(spec, state, spec.SLOTS_PER_EPOCH - 1)
finalized_block = state_transition_with_full_block(spec, state, True, True)
finalized_state = state.copy()
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH, True, True)
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH - 1, True, True)
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
yield from emit_update(test, spec, state, block, attested_state, finalized_block)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block)
assert test.store.finalized_header.slot == finalized_state.slot
assert test.store.next_sync_committee == finalized_state.next_sync_committee
assert test.store.best_valid_update is None
Expand All @@ -184,12 +186,13 @@ def test_light_client_sync(spec, state):
next_slots(spec, state, spec.SLOTS_PER_EPOCH - 2)
finalized_block = state_transition_with_full_block(spec, state, True, True)
finalized_state = state.copy()
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH, True, True)
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH - 1, True, True)
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
transition_to(spec, state, compute_start_slot_at_next_sync_committee_period(spec, state))
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
yield from emit_update(test, spec, state, block, attested_state, finalized_block)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block)
assert test.store.finalized_header.slot == finalized_state.slot
assert test.store.next_sync_committee == finalized_state.next_sync_committee
assert test.store.best_valid_update is None
Expand All @@ -206,10 +209,11 @@ def test_light_client_sync(spec, state):
# sync committee
# period boundary
# ```
attested_block = block.copy()
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
update = yield from emit_update(test, spec, state, block, attested_state, finalized_block=None)
update = yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block=None)
assert test.store.finalized_header.slot == finalized_state.slot
assert test.store.next_sync_committee == finalized_state.next_sync_committee
assert test.store.best_valid_update == update
Expand All @@ -226,11 +230,12 @@ def test_light_client_sync(spec, state):
# sync committee
# period boundary
# ```
attested_block = block.copy()
attested_state = state.copy()
store_state = attested_state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
update = yield from emit_update(test, spec, state, block, attested_state, finalized_block)
update = yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block)
assert test.store.finalized_header.slot == finalized_state.slot
assert test.store.next_sync_committee == finalized_state.next_sync_committee
assert test.store.best_valid_update == update
Expand All @@ -247,6 +252,7 @@ def test_light_client_sync(spec, state):
# sync committee `--- store.finalized_header
# period boundary
# ```
attested_block = block.copy()
attested_state = state.copy()
next_slots(spec, state, spec.UPDATE_TIMEOUT - 1)
yield from emit_force_update(test, spec, state)
Expand All @@ -268,7 +274,7 @@ def test_light_client_sync(spec, state):
# ```
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
update = yield from emit_update(test, spec, state, block, attested_state, finalized_block=None)
update = yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block=None)
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 == update
Expand All @@ -285,10 +291,11 @@ def test_light_client_sync(spec, state):
# sync committee sync committee
# period boundary period boundary
# ```
attested_block = block.copy()
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
update = yield from emit_update(test, spec, state, block, attested_state, finalized_block)
update = yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block)
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 == update
Expand All @@ -314,11 +321,12 @@ def test_light_client_sync(spec, state):
next_slots(spec, state, spec.SLOTS_PER_EPOCH - 1)
finalized_block = state_transition_with_full_block(spec, state, True, True)
finalized_state = state.copy()
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH, True, True)
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH - 1, True, True)
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
yield from emit_update(test, spec, state, block, attested_state, finalized_block)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block)
assert test.store.finalized_header.slot == finalized_state.slot
assert test.store.next_sync_committee == finalized_state.next_sync_committee
assert test.store.best_valid_update is None
Expand All @@ -336,7 +344,8 @@ def test_supply_sync_committee_from_past_update(spec, state):
next_slots(spec, state, spec.SLOTS_PER_EPOCH * 2 - 1)
finalized_block = state_transition_with_full_block(spec, state, True, True)
finalized_state = state.copy()
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH, True, True)
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH - 1, True, True)
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
Expand All @@ -347,7 +356,7 @@ def test_supply_sync_committee_from_past_update(spec, state):
assert not spec.is_next_sync_committee_known(test.store)

# Apply `LightClientUpdate` from the past, populating `store.next_sync_committee`
yield from emit_update(test, spec, past_state, block, attested_state, finalized_block)
yield from emit_update(test, spec, past_state, block, attested_state, attested_block, finalized_block)
assert test.store.finalized_header.slot == state.slot
assert test.store.next_sync_committee == finalized_state.next_sync_committee
assert test.store.best_valid_update is None
Expand All @@ -368,11 +377,12 @@ def test_advance_finality_without_sync_committee(spec, state):
next_slots(spec, state, spec.SLOTS_PER_EPOCH - 1)
finalized_block = state_transition_with_full_block(spec, state, True, True)
finalized_state = state.copy()
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH, True, True)
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH - 1, True, True)
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
yield from emit_update(test, spec, state, block, attested_state, finalized_block)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block)
assert test.store.finalized_header.slot == finalized_state.slot
assert test.store.next_sync_committee == finalized_state.next_sync_committee
assert test.store.best_valid_update is None
Expand All @@ -386,11 +396,12 @@ def test_advance_finality_without_sync_committee(spec, state):
_, _, state = next_slots_with_attestations(spec, state, spec.SLOTS_PER_EPOCH - 1, True, True)
justified_block = state_transition_with_full_block(spec, state, True, True)
justified_state = state.copy()
_, _, state = next_slots_with_attestations(spec, state, spec.SLOTS_PER_EPOCH, True, True)
_, _, state = next_slots_with_attestations(spec, state, spec.SLOTS_PER_EPOCH - 1, True, True)
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
yield from emit_update(test, spec, state, block, attested_state, finalized_block, with_next_sync_committee=False)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block, with_next=False)
assert test.store.finalized_header.slot == finalized_state.slot
assert not spec.is_next_sync_committee_known(test.store)
assert test.store.best_valid_update is None
Expand All @@ -400,27 +411,28 @@ def test_advance_finality_without_sync_committee(spec, state):
past_state = finalized_state
finalized_block = justified_block
finalized_state = justified_state
_, _, state = next_slots_with_attestations(spec, state, spec.SLOTS_PER_EPOCH - 1, True, True)
_, _, state = next_slots_with_attestations(spec, state, spec.SLOTS_PER_EPOCH - 2, True, True)
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)

# Apply `LightClientUpdate` without `finalized_header` nor `next_sync_committee`
update = yield from emit_update(test, spec, state, block, attested_state, None, with_next_sync_committee=False)
update = yield from emit_update(test, spec, state, block, attested_state, attested_block, None, with_next=False)
assert test.store.finalized_header.slot == past_state.slot
assert not spec.is_next_sync_committee_known(test.store)
assert test.store.best_valid_update == update
assert test.store.optimistic_header.slot == attested_state.slot

# Apply `LightClientUpdate` with `finalized_header` but no `next_sync_committee`
yield from emit_update(test, spec, state, block, attested_state, finalized_block, with_next_sync_committee=False)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block, with_next=False)
assert test.store.finalized_header.slot == finalized_state.slot
assert not spec.is_next_sync_committee_known(test.store)
assert test.store.best_valid_update is None
assert test.store.optimistic_header.slot == attested_state.slot

# Apply full `LightClientUpdate`, supplying `next_sync_committee`
yield from emit_update(test, spec, state, block, attested_state, finalized_block)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block)
assert test.store.finalized_header.slot == finalized_state.slot
assert test.store.next_sync_committee == finalized_state.next_sync_committee
assert test.store.best_valid_update is None
Expand Down
Loading