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

Rework Shard transition processing #1856

Merged
merged 2 commits into from
Jun 1, 2020
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
79 changes: 48 additions & 31 deletions specs/phase1/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@
- [Operations](#operations)
- [New Attestation processing](#new-attestation-processing)
- [`validate_attestation`](#validate_attestation)
- [Updated `process_attestation`](#updated-process_attestation)
- [Shard transition processing](#shard-transition-processing)
- [`apply_shard_transition`](#apply_shard_transition)
- [`process_crosslink_for_shard`](#process_crosslink_for_shard)
- [`process_crosslinks`](#process_crosslinks)
- [`process_attestation`](#process_attestation)
- [`verify_empty_shard_transition`](#verify_empty_shard_transition)
- [`process_shard_transitions`](#process_shard_transitions)
- [New Attester slashing processing](#new-attester-slashing-processing)
- [Verify empty shard transition](#verify-empty-shard-transition)
- [Light client processing](#light-client-processing)
- [Epoch transition](#epoch-transition)
- [Custody game updates](#custody-game-updates)
Expand Down Expand Up @@ -671,7 +673,6 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_eth1_data(state, block.body)
process_light_client_signatures(state, block.body)
process_operations(state, block.body)
verify_empty_shard_transition(state, block.body)
```

#### Operations
Expand All @@ -695,7 +696,7 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
# See custody game spec.
process_custody_game_operations(state, body)

process_crosslinks(state, body.shard_transitions, body.attestations)
process_shard_transitions(state, body.shard_transitions, body.attestations)
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be neat to convert to:
for_ops(body.shard_transitions, body.attestations, process_shard_transitions)
But it doesn't look feasible today 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But for_ops is mainly for executing operation one-by-one, and we need all body.shard_transitions and body.attestations to process crosslinks.

Copy link
Contributor

Choose a reason for hiding this comment

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

Makes sense!


# TODO process_operations(body.shard_receipt_proofs, process_shard_receipt_proofs)
```
Expand Down Expand Up @@ -742,6 +743,27 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None:
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
```

###### Updated `process_attestation`

```python
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
Copy link
Contributor Author

@hwwhww hwwhww May 28, 2020

Choose a reason for hiding this comment

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

To reviewers: only moving the function block to here. Nothing was changed in this function.

validate_attestation(state, attestation)
# Store pending attestation for epoch processing
pending_attestation = PendingAttestation(
aggregation_bits=attestation.aggregation_bits,
data=attestation.data,
inclusion_delay=state.slot - attestation.data.slot,
proposer_index=get_beacon_proposer_index(state),
crosslink_success=False, # To be filled in during process_shard_transitions
)
if attestation.data.target.epoch == get_current_epoch(state):
state.current_epoch_attestations.append(pending_attestation)
else:
state.previous_epoch_attestations.append(pending_attestation)
```

##### Shard transition processing

###### `apply_shard_transition`

```python
Expand Down Expand Up @@ -882,23 +904,30 @@ def process_crosslinks(state: BeaconState,
pending_attestation.crosslink_success = True
```

###### `process_attestation`
###### `verify_empty_shard_transition`

```python
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
validate_attestation(state, attestation)
# Store pending attestation for epoch processing
pending_attestation = PendingAttestation(
aggregation_bits=attestation.aggregation_bits,
data=attestation.data,
inclusion_delay=state.slot - attestation.data.slot,
proposer_index=get_beacon_proposer_index(state),
crosslink_success=False, # To be filled in during process_crosslinks
)
if attestation.data.target.epoch == get_current_epoch(state):
state.current_epoch_attestations.append(pending_attestation)
else:
state.previous_epoch_attestations.append(pending_attestation)
def verify_empty_shard_transition(state: BeaconState, shard_transitions: Sequence[ShardTransition]) -> bool:
"""
Verify that a `shard_transition` in a block is empty if an attestation was not processed for it.
"""
for shard in range(get_active_shard_count(state)):
if state.shard_states[shard].slot != compute_previous_slot(state.slot):
if shard_transitions[shard] != ShardTransition():
return False
return True
```

###### `process_shard_transitions`

```python
def process_shard_transitions(state: BeaconState,
shard_transitions: Sequence[ShardTransition],
attestations: Sequence[Attestation]) -> None:
# Process crosslinks
process_crosslinks(state, shard_transitions, attestations)
# Verify the empty proposal shard states
assert verify_empty_shard_transition(state, shard_transitions)
```

##### New Attester slashing processing
Expand Down Expand Up @@ -943,18 +972,6 @@ def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSla
assert slashed_any
```

#### Verify empty shard transition

```python
def verify_empty_shard_transition(state: BeaconState, block_body: BeaconBlockBody) -> None:
"""
Verify that ``shard_transitions`` are empty if a crosslink was not formed for the associated shard in this slot.
"""
for shard in range(get_active_shard_count(state)):
if state.shard_states[shard].slot != compute_previous_slot(state.slot):
assert block_body.shard_transitions[shard] == ShardTransition()
```

#### Light client processing

```python
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from eth2spec.test.context import expect_assertion_error


def run_crosslinks_processing(spec, state, shard_transitions, attestations, valid=True):
def run_shard_transitions_processing(spec, state, shard_transitions, attestations, valid=True):
"""
Run ``process_attestation``, yielding:
Run ``process_shard_transitions``, yielding:
- pre-state ('pre')
- shard_transitions ('shard_transitions')
- attestations ('attestations')
Expand All @@ -17,12 +17,12 @@ def run_crosslinks_processing(spec, state, shard_transitions, attestations, vali

# If the attestation is invalid, processing is aborted, and there is no post-state.
if not valid:
expect_assertion_error(lambda: spec.process_crosslinks(state, shard_transitions, attestations))
expect_assertion_error(lambda: spec.process_shard_transitions(state, shard_transitions, attestations))
yield 'post', None
return

# process crosslinks
spec.process_crosslinks(state, shard_transitions, attestations)
spec.process_shard_transitions(state, shard_transitions, attestations)

# yield post-state
yield 'post', state
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
spec_state_test,
always_bls,
)
from eth2spec.test.helpers.crosslinks import run_crosslinks_processing
from eth2spec.test.helpers.shard_transitions import run_shard_transitions_processing
from eth2spec.test.helpers.shard_block import (
build_attestation_with_shard_transition,
build_shard_block,
Expand Down Expand Up @@ -46,7 +46,7 @@ def run_basic_crosslink_tests(spec, state, target_len_offset_slot, valid=True):
transition_to(spec, state, state.slot + target_len_offset_slot)
pre_shard_state = state.shard_states[shard]

yield from run_crosslinks_processing(spec, state, shard_transitions, [attestation], valid=valid)
yield from run_shard_transitions_processing(spec, state, shard_transitions, [attestation], valid=valid)

if valid:
# After state transition,
Expand Down
2 changes: 1 addition & 1 deletion tests/generators/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin

if __name__ == "__main__":
gen_runner.run_generator("epoch_processing", [
create_provider('crosslinks', test_process_crosslinks, 'minimal'),
create_provider('final_updates', test_process_final_updates, 'minimal'),
...
])

Expand Down