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

Allow multiple bit challenges, and recover withdrawability #1035

Merged
merged 5 commits into from
May 17, 2019
Merged
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
32 changes: 24 additions & 8 deletions specs/core/1_custody-game.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
'challenge_index': 'uint64',
'challenger_index': ValidatorIndex,
'responder_index': ValidatorIndex,
'deadline': Epoch,
'inclusion_epoch': Epoch,
'crosslink_data_root': Hash,
'depth': 'uint64',
'chunk_index': 'uint64',
Expand All @@ -160,7 +160,7 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
'challenge_index': 'uint64',
'challenger_index': ValidatorIndex,
'responder_index': ValidatorIndex,
'deadline': Epoch,
'inclusion_epoch': Epoch,
'crosslink_data_root': Hash,
'chunk_count': 'uint64',
'chunk_bits_merkle_root': Hash,
Expand Down Expand Up @@ -485,7 +485,7 @@ def process_chunk_challenge(state: BeaconState,
challenge_index=state.custody_challenge_index,
challenger_index=get_beacon_proposer_index(state),
responder_index=challenge.responder_index
deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE,
inclusion_epoch=get_current_epoch(state),
crosslink_data_root=challenge.attestation.data.crosslink_data_root,
depth=depth,
chunk_index=challenge.chunk_index,
Expand Down Expand Up @@ -528,10 +528,9 @@ def process_bit_challenge(state: BeaconState,
attesters = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
assert challenge.responder_index in attesters

# A validator can be the challenger or responder for at most one challenge at a time
# A validator can be the challenger for at most one challenge at a time
for record in state.custody_bit_challenge_records:
assert record.challenger_index != challenge.challenger_index
assert record.responder_index != challenge.responder_index

# Verify the responder is a valid custody key
epoch_to_sign = get_randao_epoch_for_custody_period(
Expand Down Expand Up @@ -563,7 +562,7 @@ def process_bit_challenge(state: BeaconState,
challenge_index=state.custody_challenge_index,
challenger_index=challenge.challenger_index,
responder_index=challenge.responder_index,
deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE,
inclusion_epoch=get_current_epoch(state),
crosslink_data_root=challenge.attestation.data.crosslink_data_root,
chunk_count=chunk_count,
chunk_bits_merkle_root=merkle_root(pad_to_power_of_2((challenge.chunk_bits))),
Expand Down Expand Up @@ -604,6 +603,8 @@ def process_chunk_challenge_response(state: BeaconState,
assert response.chunk_index == challenge.chunk_index
# Verify bit challenge data is null
assert response.chunk_bits_branch == [] and response.chunk_bits_leaf == ZERO_HASH
# Verify minimum delay
assert get_current_epoch(state) >= challenge.inclusion_epoch + ACTIVATION_EXIT_DELAY
# Verify the chunk matches the crosslink data root
assert verify_merkle_branch(
leaf=hash_tree_root(response.chunk),
Expand All @@ -626,6 +627,9 @@ def process_bit_challenge_response(state: BeaconState,
challenge: CustodyBitChallengeRecord) -> None:
# Verify chunk index
assert response.chunk_index < challenge.chunk_count
# Verify responder has not been slashed
responder = state.validator_registry[challenge.responder_index]
assert not responder.slashed
# Verify the chunk matches the crosslink data root
assert verify_merkle_branch(
leaf=hash_tree_root(response.chunk),
Expand Down Expand Up @@ -671,13 +675,13 @@ Run `process_challenge_deadlines(state)` immediately after `process_reveal_deadl
```python
def process_challenge_deadlines(state: BeaconState) -> None:
for challenge in state.custody_chunk_challenge_records:
if get_current_epoch(state) > challenge.deadline:
if get_current_epoch(state) > challenge.inclusion_epoch + CUSTODY_RESPONSE_DEADLINE:
slash_validator(state, challenge.responder_index, challenge.challenger_index)
records = state.custody_chunk_challenge_records
records[records.index(challenge)] = CustodyChunkChallengeRecord()

for challenge in state.custody_bit_challenge_records:
if get_current_epoch(state) > challenge.deadline:
if get_current_epoch(state) > challenge.inclusion_epoch + CUSTODY_RESPONSE_DEADLINE:
slash_validator(state, challenge.responder_index, challenge.challenger_index)
records = state.custody_bit_challenge_records
records[records.index(challenge)] = CustodyBitChallengeRecord()
Expand All @@ -688,6 +692,18 @@ Append this to `process_final_updates(state)`:
```python
# Clean up exposed RANDAO key reveals
state.exposed_derived_secrets[current_epoch % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS] = []
# Reset withdrawable epochs if challenge records are empty
for index, validator in enumerate(state.validator_registry):
eligible = True
for records in (state.custody_chunk_challenge_records, state.bit_challenge_records):
for filter_func in (lambda rec: rec.challenger_index == index, lambda rec: rec.responder_index == index):
if len(list(filter(filter_func, records))) > 0:
eligible = False
hwwhww marked this conversation as resolved.
Show resolved Hide resolved
if eligible:
if validator.exit_epoch == FAR_FUTURE_EPOCH:
validator.withdrawable_epoch = FAR_FUTURE_EPOCH
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
else:
validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
hwwhww marked this conversation as resolved.
Show resolved Hide resolved
```

In `process_penalties_and_exits`, change the definition of `eligible` to the following (note that it is not a pure function because `state` is declared in the surrounding scope):
hwwhww marked this conversation as resolved.
Show resolved Hide resolved
Expand Down