From 48e8164e28aeb73d7bc9108a89c40bff60ef2e42 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 12 Jun 2019 20:08:19 -0400 Subject: [PATCH] Add phase1 type hinting checks and fix many bugs --- scripts/build_spec.py | 1 + specs/core/1_custody-game.md | 60 ++++++++++++++++--------------- specs/core/1_shard-data-chains.md | 34 +++++++++--------- 3 files changed, 50 insertions(+), 45 deletions(-) diff --git a/scripts/build_spec.py b/scripts/build_spec.py index a4a97ab57..3fb1dda7e 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -43,6 +43,7 @@ PHASE1_IMPORTS = '''from typing import ( Callable, Dict, List, + Optional, Set, Tuple, ) diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index 6c89ef853..9e8414a42 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -184,7 +184,7 @@ class CustodyResponse(Container): ```python class CustodyKeyReveal(Container): # Index of the validator whose key is being revealed - revealer_index: uint64 + revealer_index: ValidatorIndex # Reveal (masked signature) reveal: Bytes96 ``` @@ -198,7 +198,7 @@ class EarlyDerivedSecretReveal(Container): # Index of the validator whose key is being revealed revealed_index: uint64 # RANDAO epoch of the key that is being revealed - epoch: uint64 + epoch: Epoch # Reveal (masked signature) reveal: Bytes96 # Index of the validator who revealed (whistleblower) @@ -251,7 +251,7 @@ class BeaconBlockBody(Container): ### `ceillog2` ```python -def ceillog2(x): +def ceillog2(x: int) -> int: return x.bit_length() ``` @@ -269,7 +269,7 @@ def get_custody_chunk_count(crosslink: Crosslink) -> int: ```python def get_custody_chunk_bit(key: Bytes96, chunk: bytes) -> bool: # TODO: Replace with something MPC-friendly, e.g. the Legendre symbol - return get_bitfield_bit(hash(key + chunk), 0) + return bool(get_bitfield_bit(hash(key + chunk), 0)) ``` ### `get_chunk_bits_root` @@ -288,7 +288,7 @@ def get_chunk_bits_root(chunk_bitfield: bytes) -> Bytes32: ```python def get_randao_epoch_for_custody_period(period: int, validator_index: ValidatorIndex) -> Epoch: next_period_start = (period + 1) * EPOCHS_PER_CUSTODY_PERIOD - validator_index % EPOCHS_PER_CUSTODY_PERIOD - return next_period_start + CUSTODY_PERIOD_TO_RANDAO_PADDING + return Epoch(next_period_start + CUSTODY_PERIOD_TO_RANDAO_PADDING) ``` ### `get_validators_custody_reveal_period` @@ -372,7 +372,11 @@ def process_custody_key_reveal(state: BeaconState, # Reward Block Preposer proposer_index = get_beacon_proposer_index(state) - increase_balance(state, proposer_index, get_base_reward(state, reveal.revealer_index) // MINOR_REWARD_QUOTIENT) + increase_balance( + state, + proposer_index, + Gwei(get_base_reward(state, reveal.revealer_index) // MINOR_REWARD_QUOTIENT) + ) ``` #### Early derived secret reveals @@ -433,7 +437,7 @@ def process_early_derived_secret_reveal(state: BeaconState, // len(get_active_validator_indices(state, get_current_epoch(state))) // PROPOSER_REWARD_QUOTIENT ) - penalty = ( + penalty = Gwei( max_proposer_slot_reward * EARLY_DERIVED_SECRET_REVEAL_SLOT_REWARD_MULTIPLE * (len(state.exposed_derived_secrets[derived_secret_location]) + 1) @@ -442,8 +446,8 @@ def process_early_derived_secret_reveal(state: BeaconState, # Apply penalty proposer_index = get_beacon_proposer_index(state) whistleblower_index = reveal.masker_index - whistleblowing_reward = penalty // WHISTLEBLOWING_REWARD_QUOTIENT - proposer_reward = whistleblowing_reward // PROPOSER_REWARD_QUOTIENT + whistleblowing_reward = Gwei(penalty // WHISTLEBLOWING_REWARD_QUOTIENT) + proposer_reward = Gwei(whistleblowing_reward // PROPOSER_REWARD_QUOTIENT) increase_balance(state, proposer_index, proposer_reward) increase_balance(state, whistleblower_index, whistleblowing_reward - proposer_reward) decrease_balance(state, reveal.revealed_index, penalty) @@ -512,7 +516,7 @@ def process_bit_challenge(state: BeaconState, pubkey=challenger.pubkey, message_hash=signing_root(challenge), signature=challenge.signature, - domain=get_domain(state, get_current_epoch(state), DOMAIN_CUSTODY_BIT_CHALLENGE), + domain=get_domain(state, DOMAIN_CUSTODY_BIT_CHALLENGE, get_current_epoch(state)), ) assert is_slashable_validator(challenger, get_current_epoch(state)) @@ -535,8 +539,8 @@ def process_bit_challenge(state: BeaconState, # Verify the responder is a valid custody key epoch_to_sign = get_randao_epoch_for_custody_period( get_validators_custody_reveal_period( - state=state, - index=challenge.responder_index, + state, + challenge.responder_index, epoch=slot_to_epoch(attestation.data.slot)), challenge.responder_index ) @@ -610,7 +614,7 @@ def process_chunk_challenge_response(state: BeaconState, # Verify the chunk matches the crosslink data root assert verify_merkle_branch( leaf=hash_tree_root(response.chunk), - branch=response.data_branch, + proof=response.data_branch, depth=challenge.depth, index=response.chunk_index, root=challenge.data_root, @@ -620,7 +624,7 @@ def process_chunk_challenge_response(state: BeaconState, records[records.index(challenge)] = CustodyChunkChallengeRecord() # Reward the proposer proposer_index = get_beacon_proposer_index(state) - increase_balance(state, proposer_index, get_base_reward(state, proposer_index) // MINOR_REWARD_QUOTIENT) + increase_balance(state, proposer_index, Gwei(get_base_reward(state, proposer_index) // MINOR_REWARD_QUOTIENT)) ``` ```python @@ -635,7 +639,7 @@ def process_bit_challenge_response(state: BeaconState, # Verify the chunk matches the crosslink data root assert verify_merkle_branch( leaf=hash_tree_root(response.chunk), - branch=response.data_branch, + proof=response.data_branch, depth=ceillog2(challenge.chunk_count), index=response.chunk_index, root=challenge.data_root, @@ -643,7 +647,7 @@ def process_bit_challenge_response(state: BeaconState, # Verify the chunk bit leaf matches the challenge data assert verify_merkle_branch( leaf=response.chunk_bits_leaf, - branch=response.chunk_bits_branch, + proof=response.chunk_bits_branch, depth=ceillog2(challenge.chunk_count) >> 8, index=response.chunk_index // 256, root=challenge.chunk_bits_merkle_root @@ -671,8 +675,8 @@ Run `process_reveal_deadlines(state)` immediately after `process_registry_update def process_reveal_deadlines(state: BeaconState) -> None: for index, validator in enumerate(state.validator_registry): deadline = validator.next_custody_reveal_period + (CUSTODY_RESPONSE_DEADLINE // EPOCHS_PER_CUSTODY_PERIOD) - if get_validators_custody_reveal_period(state, index) > deadline: - slash_validator(state, index) + if get_validators_custody_reveal_period(state, ValidatorIndex(index)) > deadline: + slash_validator(state, ValidatorIndex(index)) ``` Run `process_challenge_deadlines(state)` immediately after `process_reveal_deadlines(state)`: @@ -682,17 +686,17 @@ Run `process_challenge_deadlines(state)` immediately after `process_reveal_deadl process_challenge_deadlines(state) # end insert @process_challenge_deadlines def process_challenge_deadlines(state: BeaconState) -> None: - for challenge in state.custody_chunk_challenge_records: - 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 custody_chunk_challenge in state.custody_chunk_challenge_records: + if get_current_epoch(state) > custody_chunk_challenge.inclusion_epoch + CUSTODY_RESPONSE_DEADLINE: + slash_validator(state, custody_chunk_challenge.responder_index, custody_chunk_challenge.challenger_index) + records = state.custody_chunk_challenge + records[records.index(custody_chunk_challenge)] = CustodyChunkChallengeRecord() - for challenge in state.custody_bit_challenge_records: - if get_current_epoch(state) > challenge.inclusion_epoch + CUSTODY_RESPONSE_DEADLINE: - slash_validator(state, challenge.responder_index, challenge.challenger_index) + for custody_bit_challenge in state.custody_bit_challenge_records: + if get_current_epoch(state) > custody_bit_challenge.inclusion_epoch + CUSTODY_RESPONSE_DEADLINE: + slash_validator(state, custody_bit_challenge.responder_index, custody_bit_challenge.challenger_index) records = state.custody_bit_challenge_records - records[records.index(challenge)] = CustodyBitChallengeRecord() + records[records.index(custody_bit_challenge)] = CustodyBitChallengeRecord() ``` Append this to `process_final_updates(state)`: @@ -713,5 +717,5 @@ def after_process_final_updates(state: BeaconState) -> None: for index, validator in enumerate(state.validator_registry): if index not in validator_indices_in_records: if validator.exit_epoch != FAR_FUTURE_EPOCH and validator.withdrawable_epoch == FAR_FUTURE_EPOCH: - validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY + validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY) ``` diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 21e08e7c9..ce6f0a6c2 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -79,8 +79,8 @@ class ShardBlockBody(Container): ```python class ShardAttestation(Container): class data(Container): - slot: uint64 - shard: uint64 + slot: Slot + shard: Shard shard_block_root: Bytes32 aggregation_bitfield: bytes aggregate_signature: Bytes96 @@ -90,8 +90,8 @@ class ShardAttestation(Container): ```python class ShardBlock(Container): - slot: uint64 - shard: uint64 + slot: Slot + shard: Shard beacon_chain_root: Bytes32 parent_root: Bytes32 data: ShardBlockBody @@ -104,8 +104,8 @@ class ShardBlock(Container): ```python class ShardBlockHeader(Container): - slot: uint64 - shard: uint64 + slot: Slot + shard: Shard beacon_chain_root: Bytes32 parent_root: Bytes32 body_root: Bytes32 @@ -138,8 +138,8 @@ def get_period_committee(state: BeaconState, ### `get_switchover_epoch` ```python -def get_switchover_epoch(state: BeaconState, epoch: Epoch, index: ValidatorIndex): - earlier_start_epoch = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2 +def get_switchover_epoch(state: BeaconState, epoch: Epoch, index: ValidatorIndex) -> int: + earlier_start_epoch = Epoch(epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2) return (bytes_to_int(hash(generate_seed(state, earlier_start_epoch) + int_to_bytes(index, length=3)[0:8])) % PERSISTENT_COMMITTEE_PERIOD) ``` @@ -154,19 +154,19 @@ def get_persistent_committee(state: BeaconState, Return the persistent committee for the given ``shard`` at the given ``slot``. """ epoch = slot_to_epoch(slot) - earlier_start_epoch = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2 - later_start_epoch = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD + earlier_start_epoch = Epoch(epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD * 2) + later_start_epoch = Epoch(epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD) committee_count = max( - len(get_active_validator_indices(state.validator_registry, earlier_start_epoch)) // + len(get_active_validator_indices(state, earlier_start_epoch)) // (SHARD_COUNT * TARGET_COMMITTEE_SIZE), - len(get_active_validator_indices(state.validator_registry, later_start_epoch)) // + len(get_active_validator_indices(state, later_start_epoch)) // (SHARD_COUNT * TARGET_COMMITTEE_SIZE), ) + 1 index = slot % committee_count - earlier_committee = get_period_committee(state, shard, earlier_start_epoch, index, committee_count) - later_committee = get_period_committee(state, shard, later_start_epoch, index, committee_count) + earlier_committee = get_period_committee(state, earlier_start_epoch, shard, index, committee_count) + later_committee = get_period_committee(state, later_start_epoch, shard, index, committee_count) # Take not-yet-cycled-out validators from earlier committee and already-cycled-in validators from # later committee; return a sorted list of the union of the two, deduplicated @@ -181,7 +181,7 @@ def get_persistent_committee(state: BeaconState, ```python def get_shard_proposer_index(state: BeaconState, shard: Shard, - slot: Slot) -> ValidatorIndex: + slot: Slot) -> Optional[ValidatorIndex]: # Randomly shift persistent committee persistent_committee = get_persistent_committee(state, shard, slot) seed = hash(state.current_shuffling_seed + int_to_bytes(shard, length=8) + int_to_bytes(slot, length=8)) @@ -231,7 +231,7 @@ def verify_shard_attestation_signature(state: BeaconState, pubkey=bls_aggregate_pubkeys(pubkeys), message_hash=data.shard_block_root, signature=attestation.aggregate_signature, - domain=get_domain(state, slot_to_epoch(data.slot), DOMAIN_SHARD_ATTESTER) + domain=get_domain(state, DOMAIN_SHARD_ATTESTER, slot_to_epoch(data.slot)) ) ``` @@ -328,7 +328,7 @@ def is_valid_shard_block(beacon_blocks: List[BeaconBlock], pubkey=beacon_state.validator_registry[proposer_index].pubkey, message_hash=signing_root(block), signature=candidate.signature, - domain=get_domain(beacon_state, slot_to_epoch(candidate.slot), DOMAIN_SHARD_PROPOSER), + domain=get_domain(beacon_state, DOMAIN_SHARD_PROPOSER, slot_to_epoch(candidate.slot)), ) return True