Skip to content

Commit

Permalink
get_attesting_indices set instead of sorted (#1225)
Browse files Browse the repository at this point in the history
  • Loading branch information
djrtwo authored and JustinDrake committed Jun 28, 2019
1 parent 2739767 commit dcb0244
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 69 deletions.
14 changes: 7 additions & 7 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -866,13 +866,13 @@ def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> S
### `get_attesting_indices`

```python
def get_attesting_indices(state: BeaconState, data: AttestationData, bitfield: bytes) -> Sequence[ValidatorIndex]:
def get_attesting_indices(state: BeaconState, data: AttestationData, bitfield: bytes) -> Set[ValidatorIndex]:
"""
Return the sorted attesting indices corresponding to ``data`` and ``bitfield``.
Return the set of attesting indices corresponding to ``data`` and ``bitfield``.
"""
committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard)
assert verify_bitfield(bitfield, len(committee))
return sorted([index for i, index in enumerate(committee) if get_bitfield_bit(bitfield, i) == 0b1])
return set(index for i, index in enumerate(committee) if get_bitfield_bit(bitfield, i) == 0b1)
```

### `int_to_bytes`
Expand Down Expand Up @@ -950,12 +950,12 @@ def convert_to_indexed(state: BeaconState, attestation: Attestation) -> IndexedA
"""
attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
custody_bit_1_indices = get_attesting_indices(state, attestation.data, attestation.custody_bitfield)
assert set(custody_bit_1_indices).issubset(attesting_indices)
custody_bit_0_indices = [index for index in attesting_indices if index not in custody_bit_1_indices]
assert custody_bit_1_indices.issubset(attesting_indices)
custody_bit_0_indices = attesting_indices.difference(custody_bit_1_indices)

return IndexedAttestation(
custody_bit_0_indices=custody_bit_0_indices,
custody_bit_1_indices=custody_bit_1_indices,
custody_bit_0_indices=sorted(custody_bit_0_indices),
custody_bit_1_indices=sorted(custody_bit_1_indices),
data=attestation.data,
signature=attestation.signature,
)
Expand Down
94 changes: 32 additions & 62 deletions specs/core/1_custody-game.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
- [`get_custody_chunk_bit`](#get_custody_chunk_bit)
- [`get_chunk_bits_root`](#get_chunk_bits_root)
- [`get_randao_epoch_for_custody_period`](#get_randao_epoch_for_custody_period)
- [`get_validators_custody_reveal_period`](#get_validators_custody_reveal_period)
- [`get_reveal_period`](#get_reveal_period)
- [`replace_empty_or_append`](#replace_empty_or_append)
- [Per-block processing](#per-block-processing)
- [Operations](#operations)
Expand Down Expand Up @@ -224,7 +224,7 @@ Add the following fields to the end of the specified container objects. Fields w
class Validator(Container):
# next_custody_reveal_period is initialised to the custody period
# (of the particular validator) in which the validator is activated
# = get_validators_custody_reveal_period(...)
# = get_reveal_period(...)
next_custody_reveal_period: uint64
max_reveal_lateness: uint64
```
Expand Down Expand Up @@ -299,17 +299,12 @@ def get_randao_epoch_for_custody_period(period: int, validator_index: ValidatorI
return Epoch(next_period_start + CUSTODY_PERIOD_TO_RANDAO_PADDING)
```

### `get_validators_custody_reveal_period`
### `get_reveal_period`

```python
def get_validators_custody_reveal_period(state: BeaconState,
validator_index: ValidatorIndex,
epoch: Epoch=None) -> int:
def get_reveal_period(state: BeaconState, validator_index: ValidatorIndex, epoch: Epoch=None) -> int:
'''
This function returns the reveal period for a given validator.
If no epoch is supplied, the current epoch is assumed.
Note: This function implicitly requires that validators are not removed from the
validator set in fewer than EPOCHS_PER_CUSTODY_PERIOD epochs
Return the reveal period for a given validator.
'''
epoch = get_current_epoch(state) if epoch is None else epoch
return (epoch + validator_index % EPOCHS_PER_CUSTODY_PERIOD) // EPOCHS_PER_CUSTODY_PERIOD
Expand Down Expand Up @@ -340,17 +335,15 @@ Verify that `len(block.body.custody_key_reveals) <= MAX_CUSTODY_KEY_REVEALS`.
For each `reveal` in `block.body.custody_key_reveals`, run the following function:

```python
def process_custody_key_reveal(state: BeaconState,
reveal: CustodyKeyReveal) -> None:
def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) -> None:
"""
Process ``CustodyKeyReveal`` operation.
Note that this function mutates ``state``.
"""

revealer = state.validators[reveal.revealer_index]
epoch_to_sign = get_randao_epoch_for_custody_period(revealer.next_custody_reveal_period, reveal.revealed_index)

assert revealer.next_custody_reveal_period < get_validators_custody_reveal_period(state, reveal.revealed_index)
assert revealer.next_custody_reveal_period < get_reveal_period(state, reveal.revealed_index)

# Revealed validator is active or exited, but not withdrawn
assert is_slashable_validator(revealer, get_current_epoch(state))
Expand All @@ -368,11 +361,11 @@ def process_custody_key_reveal(state: BeaconState,
)

# Decrement max reveal lateness if response is timely
if revealer.next_custody_reveal_period == get_validators_custody_reveal_period(state, reveal.revealer_index) - 2:
if revealer.next_custody_reveal_period == get_reveal_period(state, reveal.revealer_index) - 2:
revealer.max_reveal_lateness -= MAX_REVEAL_LATENESS_DECREMENT
revealer.max_reveal_lateness = max(
revealer.max_reveal_lateness,
get_validators_custody_reveal_period(state, reveal.revealed_index) - revealer.next_custody_reveal_period
get_reveal_period(state, reveal.revealed_index) - revealer.next_custody_reveal_period
)

# Process reveal
Expand All @@ -394,13 +387,11 @@ Verify that `len(block.body.early_derived_secret_reveals) <= MAX_EARLY_DERIVED_S
For each `reveal` in `block.body.early_derived_secret_reveals`, run the following function:

```python
def process_early_derived_secret_reveal(state: BeaconState,
reveal: EarlyDerivedSecretReveal) -> None:
def process_early_derived_secret_reveal(state: BeaconState, reveal: EarlyDerivedSecretReveal) -> None:
"""
Process ``EarlyDerivedSecretReveal`` operation.
Note that this function mutates ``state``.
"""

revealed_validator = state.validators[reveal.revealed_index]
derived_secret_location = reveal.epoch % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS

Expand Down Expand Up @@ -470,8 +461,7 @@ Verify that `len(block.body.custody_chunk_challenges) <= MAX_CUSTODY_CHUNK_CHALL
For each `challenge` in `block.body.custody_chunk_challenges`, run the following function:

```python
def process_chunk_challenge(state: BeaconState,
challenge: CustodyChunkChallenge) -> None:
def process_chunk_challenge(state: BeaconState, challenge: CustodyChunkChallenge) -> None:
# Verify the attestation
validate_indexed_attestation(state, convert_to_indexed(state, challenge.attestation))
# Verify it is not too late to challenge
Expand Down Expand Up @@ -514,59 +504,41 @@ Verify that `len(block.body.custody_bit_challenges) <= MAX_CUSTODY_BIT_CHALLENGE
For each `challenge` in `block.body.custody_bit_challenges`, run the following function:

```python
def process_bit_challenge(state: BeaconState,
challenge: CustodyBitChallenge) -> None:
def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) -> None:
attestation = challenge.attestation
epoch = slot_to_epoch(attestation.data.slot)
shard = attestation.data.crosslink.shard

# Verify challenge signature
challenger = state.validators[challenge.challenger_index]
assert bls_verify(
pubkey=challenger.pubkey,
message_hash=signing_root(challenge),
signature=challenge.signature,
domain=get_domain(state, DOMAIN_CUSTODY_BIT_CHALLENGE, get_current_epoch(state)),
)
domain = get_domain(state, DOMAIN_CUSTODY_BIT_CHALLENGE, get_current_epoch(state))
assert bls_verify(challenger.pubkey, signing_root(challenge), challenge.signature, domain)
# Verify challenger is slashable
assert is_slashable_validator(challenger, get_current_epoch(state))

# Verify the attestation
attestation = challenge.attestation
# Verify attestation
validate_indexed_attestation(state, convert_to_indexed(state, attestation))
# Verify the attestation is eligible for challenging
# Verify attestation is eligible for challenging
responder = state.validators[challenge.responder_index]
assert (slot_to_epoch(attestation.data.slot) + responder.max_reveal_lateness <=
get_validators_custody_reveal_period(state, challenge.responder_index))

# Verify the responder participated in the attestation
assert epoch + responder.max_reveal_lateness <= get_reveal_period(state, challenge.responder_index)
# Verify responder participated in the attestation
attesters = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
assert challenge.responder_index in attesters

# A validator can be the challenger for at most one challenge at a time
# Verifier challenger is not already challenging
for record in state.custody_bit_challenge_records:
assert record.challenger_index != challenge.challenger_index

# Verify the responder is a valid custody key
# Verify the responder custody key
epoch_to_sign = get_randao_epoch_for_custody_period(
get_validators_custody_reveal_period(
state,
challenge.responder_index,
epoch=slot_to_epoch(attestation.data.slot)),
challenge.responder_index
get_reveal_period(state, challenge.responder_index, epoch),
challenge.responder_index,
)
assert bls_verify(
pubkey=responder.pubkey,
message_hash=hash_tree_root(epoch_to_sign),
signature=challenge.responder_key,
domain=get_domain(
state=state,
domain_type=DOMAIN_RANDAO,
message_epoch=epoch_to_sign,
),
)

domain = get_domain(state, DOMAIN_RANDAO, epoch_to_sign)
assert bls_verify(responder.pubkey, hash_tree_root(epoch_to_sign), challenge.responder_key, domain)
# Verify the chunk count
chunk_count = get_custody_chunk_count(attestation.data.crosslink)
assert verify_bitfield(challenge.chunk_bits, chunk_count)
# Verify the first bit of the hash of the chunk bits does not equal the custody bit
custody_bit = get_bitfield_bit(attestation.custody_bitfield, attesters.index(challenge.responder_index))
committee = get_crosslink_committee(state, epoch, shard)
custody_bit = get_bitfield_bit(attestation.custody_bitfield, committee.index(challenge.responder_index))
assert custody_bit != get_bitfield_bit(get_chunk_bits_root(challenge.chunk_bits), 0)
# Add new bit challenge record
new_record = CustodyBitChallengeRecord(
Expand All @@ -581,7 +553,6 @@ def process_bit_challenge(state: BeaconState,
)
replace_empty_or_append(state.custody_bit_challenge_records, new_record)
state.custody_challenge_index += 1

# Postpone responder withdrawability
responder.withdrawable_epoch = FAR_FUTURE_EPOCH
```
Expand All @@ -593,8 +564,7 @@ Verify that `len(block.body.custody_responses) <= MAX_CUSTODY_RESPONSES`.
For each `response` in `block.body.custody_responses`, run the following function:

```python
def process_custody_response(state: BeaconState,
response: CustodyResponse) -> None:
def process_custody_response(state: BeaconState, response: CustodyResponse) -> None:
chunk_challenge = next((record for record in state.custody_chunk_challenge_records
if record.challenge_index == response.challenge_index), None)
if chunk_challenge is not None:
Expand Down Expand Up @@ -682,7 +652,7 @@ Run `process_reveal_deadlines(state)` immediately after `process_registry_update
def process_reveal_deadlines(state: BeaconState) -> None:
for index, validator in enumerate(state.validators):
deadline = validator.next_custody_reveal_period + (CUSTODY_RESPONSE_DEADLINE // EPOCHS_PER_CUSTODY_PERIOD)
if get_validators_custody_reveal_period(state, ValidatorIndex(index)) > deadline:
if get_reveal_period(state, ValidatorIndex(index)) > deadline:
slash_validator(state, ValidatorIndex(index))
```

Expand Down

0 comments on commit dcb0244

Please sign in to comment.