Allow multiple bit challenges, and recover withdrawability

Resolves #864 items 4, 7, 14
This commit is contained in:
vbuterin 2019-05-03 05:07:11 -05:00 committed by GitHub
parent c0f3453093
commit 66b152f79e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 24 additions and 8 deletions

View File

@ -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',
@ -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,
@ -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,
@ -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(
@ -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))),
@ -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),
@ -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[record.responder_index]
assert not responder.slashed
# Verify the chunk matches the crosslink data root
assert verify_merkle_branch(
leaf=hash_tree_root(response.chunk),
@ -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()
@ -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
if eligible:
if validator.exit_epoch == FAR_FUTURE_EPOCH:
validator.withdrawable_epoch = FAR_FUTURE_EPOCH
else:
validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
```
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):