From dcb0244a4f99f50dfa82884a81bd14e1eabb316e Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 28 Jun 2019 10:19:59 -0500 Subject: [PATCH] get_attesting_indices set instead of sorted (#1225) --- specs/core/0_beacon-chain.md | 14 +++--- specs/core/1_custody-game.md | 94 ++++++++++++------------------------ 2 files changed, 39 insertions(+), 69 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 61ef8b7421..abca72ff84 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -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` @@ -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, ) diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index c29033fe3c..9b17b39ed6 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -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) @@ -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 ``` @@ -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 @@ -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)) @@ -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 @@ -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 @@ -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 @@ -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( @@ -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 ``` @@ -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: @@ -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)) ```