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

get_attesting_indices set instead of sorted #1225

Merged
merged 5 commits into from
Jun 28, 2019
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
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),
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
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))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Bugfix: The previous accessor here for the responder bit just from the list of attestors would likely correspond to the incorrect bit in the bitfield. Instead it should be the index of the responder within the entire committee.

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