From 2bf020d49d4f3f7cbc052b2a88a98c2d15ce12e9 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 23 Apr 2020 23:46:27 +0800 Subject: [PATCH 01/10] Apply strict uint64 casting --- specs/phase0/beacon-chain.md | 112 ++++++++++++++++++----------------- specs/phase0/fork-choice.md | 8 +-- specs/phase0/validator.md | 2 +- 3 files changed, 63 insertions(+), 59 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 142cf3b02..d1f97a506 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -170,9 +170,9 @@ The following values are (non-configurable) constants used throughout the specif | `GENESIS_SLOT` | `Slot(0)` | | `GENESIS_EPOCH` | `Epoch(0)` | | `FAR_FUTURE_EPOCH` | `Epoch(2**64 - 1)` | -| `BASE_REWARDS_PER_EPOCH` | `4` | -| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) | -| `JUSTIFICATION_BITS_LENGTH` | `4` | +| `BASE_REWARDS_PER_EPOCH` | `uint64(4)` | +| `DEPOSIT_CONTRACT_TREE_DEPTH` | `uint64(2**5)` (= 32) | +| `JUSTIFICATION_BITS_LENGTH` | `uint64(4)` | | `ENDIANNESS` | `'little'` | ## Configuration @@ -183,17 +183,17 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | | - | - | -| `MAX_COMMITTEES_PER_SLOT` | `2**6` (= 64) | -| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | -| `MAX_VALIDATORS_PER_COMMITTEE` | `2**11` (= 2,048) | -| `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) | -| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) | -| `SHUFFLE_ROUND_COUNT` | `90` | -| `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `2**14` (= 16,384) | -| `MIN_GENESIS_TIME` | `1578009600` (Jan 3, 2020) | -| `HYSTERESIS_QUOTIENT` | `4` | -| `HYSTERESIS_DOWNWARD_MULTIPLIER` | `1` | -| `HYSTERESIS_UPWARD_MULTIPLIER` | `5` | +| `MAX_COMMITTEES_PER_SLOT` | `uint64(2**6)` (= 64) | +| `TARGET_COMMITTEE_SIZE` | `uint64(2**7)` (= 128) | +| `MAX_VALIDATORS_PER_COMMITTEE` | `uint64(2**11)` (= 2,048) | +| `MIN_PER_EPOCH_CHURN_LIMIT` | `uint64(2**2)` (= 4) | +| `CHURN_LIMIT_QUOTIENT` | `uint64(2**16)` (= 65,536) | +| `SHUFFLE_ROUND_COUNT` | `uint64(90)` | +| `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `uint64(2**14)` (= 16,384) | +| `MIN_GENESIS_TIME` | `uint64(1578009600)` (Jan 3, 2020) | +| `HYSTERESIS_QUOTIENT` | `uint64(4)` | +| `HYSTERESIS_DOWNWARD_MULTIPLIER` | `uint64(1)` | +| `HYSTERESIS_UPWARD_MULTIPLIER` | `uint64(5)` | - For the safety of committees, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](http://web.archive.org/web/20190504131341/https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -217,37 +217,37 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `MIN_GENESIS_DELAY` | `86400` | seconds | 1 day | -| `SECONDS_PER_SLOT` | `12` | seconds | 12 seconds | -| `MIN_ATTESTATION_INCLUSION_DELAY` | `2**0` (= 1) | slots | 12 seconds | -| `SLOTS_PER_EPOCH` | `2**5` (= 32) | slots | 6.4 minutes | -| `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | -| `MAX_SEED_LOOKAHEAD` | `2**2` (= 4) | epochs | 25.6 minutes | -| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes | -| `EPOCHS_PER_ETH1_VOTING_PERIOD` | `2**5` (= 32) | epochs | ~3.4 hours | -| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~27 hours | -| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | +<<<<<<< HEAD +| `MIN_GENESIS_DELAY` | `uint64(86400)` | seconds | 1 day | +| `SECONDS_PER_SLOT` | `uint64(12)` | seconds | 12 seconds | +| `MIN_ATTESTATION_INCLUSION_DELAY` | `uint64(2**0)` (= 1) | slots | 12 seconds | +| `SLOTS_PER_EPOCH` | `uint64(2**5)` (= 32) | slots | 6.4 minutes | +| `MIN_SEED_LOOKAHEAD` | `uint64(2**0)` (= 1) | epochs | 6.4 minutes | +| `MAX_SEED_LOOKAHEAD` | `uint64(2**2)` (= 4) | epochs | 25.6 minutes | +| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `uint64(2**2)` (= 4) | epochs | 25.6 minutes | +| `EPOCHS_PER_ETH1_VOTING_PERIOD` | `uint64(2**5)` (= 32) | epochs | ~3.4 hours | +| `SLOTS_PER_HISTORICAL_ROOT` | `uint64(2**13)` (= 8,192) | slots | ~27 hours | +| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `uint64(2**8)` (= 256) | epochs | ~27 hours | | `SHARD_COMMITTEE_PERIOD` | `Epoch(2**8)` (= 256) | epochs | ~27 hours | - ### State list lengths | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `EPOCHS_PER_HISTORICAL_VECTOR` | `2**16` (= 65,536) | epochs | ~0.8 years | -| `EPOCHS_PER_SLASHINGS_VECTOR` | `2**13` (= 8,192) | epochs | ~36 days | -| `HISTORICAL_ROOTS_LIMIT` | `2**24` (= 16,777,216) | historical roots | ~52,262 years | -| `VALIDATOR_REGISTRY_LIMIT` | `2**40` (= 1,099,511,627,776) | validators | +| `EPOCHS_PER_HISTORICAL_VECTOR` | `uint64(2**16)` (= 65,536) | epochs | ~0.8 years | +| `EPOCHS_PER_SLASHINGS_VECTOR` | `uint64(2**13)` (= 8,192) | epochs | ~36 days | +| `HISTORICAL_ROOTS_LIMIT` | `uint64(2**24)` (= 16,777,216) | historical roots | ~52,262 years | +| `VALIDATOR_REGISTRY_LIMIT` | `uint64(2**40)` (= 1,099,511,627,776) | validators | ### Rewards and penalties | Name | Value | | - | - | -| `BASE_REWARD_FACTOR` | `2**6` (= 64) | -| `WHISTLEBLOWER_REWARD_QUOTIENT` | `2**9` (= 512) | -| `PROPOSER_REWARD_QUOTIENT` | `2**3` (= 8) | -| `INACTIVITY_PENALTY_QUOTIENT` | `2**24` (= 16,777,216) | -| `MIN_SLASHING_PENALTY_QUOTIENT` | `2**5` (= 32) | +| `BASE_REWARD_FACTOR` | `uint64(2**6)` (= 64) | +| `WHISTLEBLOWER_REWARD_QUOTIENT` | `uint64(2**9)` (= 512) | +| `PROPOSER_REWARD_QUOTIENT` | `uint64(2**3)` (= 8) | +| `INACTIVITY_PENALTY_QUOTIENT` | `uint64(2**24)` (= 16,777,216) | +| `MIN_SLASHING_PENALTY_QUOTIENT` | `uint64(2**5)` (= 32) | - The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12` epochs (about 18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating validators to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline validators after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)`; so after `INVERSE_SQRT_E_DROP_TIME` epochs, it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`. @@ -560,9 +560,9 @@ def integer_squareroot(n: uint64) -> uint64: x = n y = (x + 1) // 2 while y < x: - x = y + x = uint64(y) y = (x + n // x) // 2 - return x + return uint64(x) ``` #### `xor` @@ -592,7 +592,7 @@ def bytes_to_int(data: bytes) -> uint64: """ Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian. """ - return int.from_bytes(data, ENDIANNESS) + return uint64(int.from_bytes(data, ENDIANNESS)) ``` ### Crypto @@ -731,14 +731,18 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) # See the 'generalized domain' algorithm on page 3 - for current_round in range(SHUFFLE_ROUND_COUNT): - pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count + for current_round in map(uint64, range(SHUFFLE_ROUND_COUNT)): + pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=uint64(1)))[0:8]) % index_count flip = (pivot + index_count - index) % index_count position = max(index, flip) - source = hash(seed + int_to_bytes(current_round, length=1) + int_to_bytes(position // 256, length=4)) + source = hash( + seed + + int_to_bytes(current_round, length=uint64(1)) + + int_to_bytes(uint64(position // 256), length=uint64(4)) + ) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 - index = flip if bit else index + index = uint64(flip) if bit else index return index ``` @@ -754,8 +758,8 @@ def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex] MAX_RANDOM_BYTE = 2**8 - 1 i = 0 while True: - candidate_index = indices[compute_shuffled_index(i % len(indices), len(indices), seed)] - random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32] + candidate_index = indices[compute_shuffled_index(uint64(i % len(indices)), uint64(len(indices)), seed)] + random_byte = hash(seed + int_to_bytes(uint64(i // 32), length=uint64(8)))[i % 32] effective_balance = state.validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: return candidate_index @@ -774,7 +778,7 @@ def compute_committee(indices: Sequence[ValidatorIndex], """ start = (len(indices) * index) // count end = (len(indices) * (index + 1)) // count - return [indices[compute_shuffled_index(i, len(indices), seed)] for i in range(start, end)] + return [indices[compute_shuffled_index(uint64(i), uint64(len(indices)), seed)] for i in range(start, end)] ``` #### `compute_epoch_at_slot` @@ -933,7 +937,7 @@ def get_validator_churn_limit(state: BeaconState) -> uint64: Return the validator churn limit for the current epoch. """ active_validator_indices = get_active_validator_indices(state, get_current_epoch(state)) - return max(MIN_PER_EPOCH_CHURN_LIMIT, len(active_validator_indices) // CHURN_LIMIT_QUOTIENT) + return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices) // CHURN_LIMIT_QUOTIENT)) ``` #### `get_seed` @@ -944,7 +948,7 @@ def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes Return the seed at ``epoch``. """ mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow - return hash(domain_type + int_to_bytes(epoch, length=8) + mix) + return hash(domain_type + int_to_bytes(epoch, length=uint64(8)) + mix) ``` #### `get_committee_count_at_slot` @@ -955,9 +959,9 @@ def get_committee_count_at_slot(state: BeaconState, slot: Slot) -> uint64: Return the number of committees at ``slot``. """ epoch = compute_epoch_at_slot(slot) - return max(1, min( + return max(uint64(1), min( MAX_COMMITTEES_PER_SLOT, - len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, + uint64(len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE), )) ``` @@ -973,8 +977,8 @@ def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) return compute_committee( indices=get_active_validator_indices(state, epoch), seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER), - index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index, - count=committees_per_slot * SLOTS_PER_EPOCH, + index=uint64((slot % SLOTS_PER_EPOCH) * committees_per_slot + index), + count=uint64(committees_per_slot * SLOTS_PER_EPOCH), ) ``` @@ -986,7 +990,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: Return the beacon proposer index at the current slot. """ epoch = get_current_epoch(state) - seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=8)) + seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=uint64(8))) indices = get_active_validator_indices(state, epoch) return compute_proposer_index(state, indices, seed) ``` @@ -1094,7 +1098,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))]) exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_validator_churn_limit(state): - exit_queue_epoch += Epoch(1) + exit_queue_epoch = Epoch(exit_queue_epoch + 1) # Set validator exit epoch and withdrawable epoch validator.exit_epoch = exit_queue_epoch @@ -1728,13 +1732,13 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: assert is_valid_merkle_branch( leaf=hash_tree_root(deposit.data), branch=deposit.proof, - depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in + depth=uint64(DEPOSIT_CONTRACT_TREE_DEPTH + 1), # Add 1 for the List length mix-in index=state.eth1_deposit_index, root=state.eth1_data.deposit_root, ) # Deposits must be processed in order - state.eth1_deposit_index += 1 + state.eth1_deposit_index = uint64(state.eth1_deposit_index + 1) pubkey = deposit.data.pubkey amount = deposit.data.amount diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index 4ed2733e1..fb5740ae5 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -100,7 +100,7 @@ _The block for `anchor_root` is incorrectly initialized to the block header, rat ```python def get_forkchoice_store(anchor_state: BeaconState) -> Store: - anchor_block_header = anchor_state.latest_block_header.copy() + anchor_block_header: BeaconBlockHeader = anchor_state.latest_block_header.copy() if anchor_block_header.state_root == Bytes32(): anchor_block_header.state_root = hash_tree_root(anchor_state) anchor_root = hash_tree_root(anchor_block_header) @@ -108,7 +108,7 @@ def get_forkchoice_store(anchor_state: BeaconState) -> Store: justified_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root) finalized_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root) return Store( - time=anchor_state.genesis_time + SECONDS_PER_SLOT * anchor_state.slot, + time=uint64(anchor_state.genesis_time + SECONDS_PER_SLOT * anchor_state.slot), genesis_time=anchor_state.genesis_time, justified_checkpoint=justified_checkpoint, finalized_checkpoint=finalized_checkpoint, @@ -300,7 +300,7 @@ def validate_on_attestation(store: Store, attestation: Attestation) -> None: def store_target_checkpoint_state(store: Store, target: Checkpoint) -> None: # Store target checkpoint state if not yet seen if target not in store.checkpoint_states: - base_state = store.block_states[target.root].copy() + base_state: BeaconState = store.block_states[target.root].copy() process_slots(base_state, compute_start_slot_at_epoch(target.epoch)) store.checkpoint_states[target] = base_state ``` @@ -344,7 +344,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: block = signed_block.message # Make a copy of the state to avoid mutability issues assert block.parent_root in store.block_states - pre_state = store.block_states[block.parent_root].copy() + pre_state: BeaconState = store.block_states[block.parent_root].copy() # Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past. assert get_current_slot(store) >= block.slot # Add new block to the store diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index adf23c840..630d42950 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -270,7 +270,7 @@ An honest block proposer sets `block.body.eth1_data = get_eth1_vote(state)` wher ```python def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64: - return state.genesis_time + slot * SECONDS_PER_SLOT + return uint64(state.genesis_time + slot * SECONDS_PER_SLOT) ``` ```python From 88d7315739b475f3297151f27866f8bef39cc7f5 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 May 2020 21:49:17 +0800 Subject: [PATCH 02/10] Fix typing --- specs/phase0/beacon-chain.md | 4 ++-- specs/phase0/validator.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index d1f97a506..2678a379f 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -1476,12 +1476,12 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence _, inactivity_penalties = get_inactivity_penalty_deltas(state) rewards = [ - source_rewards[i] + target_rewards[i] + head_rewards[i] + inclusion_delay_rewards[i] + Gwei(source_rewards[i] + target_rewards[i] + head_rewards[i] + inclusion_delay_rewards[i]) for i in range(len(state.validators)) ] penalties = [ - source_penalties[i] + target_penalties[i] + head_penalties[i] + inactivity_penalties[i] + Gwei(source_penalties[i] + target_penalties[i] + head_penalties[i] + inactivity_penalties[i]) for i in range(len(state.validators)) ] diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 630d42950..805498250 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -430,7 +430,7 @@ def compute_subnet_for_attestation(state: BeaconState, attestation: Attestation) slots_since_epoch_start = attestation.data.slot % SLOTS_PER_EPOCH committees_since_epoch_start = get_committee_count_at_slot(state, attestation.data.slot) * slots_since_epoch_start - return (committees_since_epoch_start + attestation.data.index) % ATTESTATION_SUBNET_COUNT + return uint64((committees_since_epoch_start + attestation.data.index) % ATTESTATION_SUBNET_COUNT) ``` ### Attestation aggregation From 9f340d5fd1b0b2e0b7c014e24a9c0787b433baea Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 May 2020 21:53:14 +0800 Subject: [PATCH 03/10] (i) Fix leftover (ii) `SHARD_COMMITTEE_PERIOD` should be uint64 --- specs/phase0/beacon-chain.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 2678a379f..60ed518f6 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -217,7 +217,6 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | Unit | Duration | | - | - | :-: | :-: | -<<<<<<< HEAD | `MIN_GENESIS_DELAY` | `uint64(86400)` | seconds | 1 day | | `SECONDS_PER_SLOT` | `uint64(12)` | seconds | 12 seconds | | `MIN_ATTESTATION_INCLUSION_DELAY` | `uint64(2**0)` (= 1) | slots | 12 seconds | @@ -228,7 +227,7 @@ The following values are (non-configurable) constants used throughout the specif | `EPOCHS_PER_ETH1_VOTING_PERIOD` | `uint64(2**5)` (= 32) | epochs | ~3.4 hours | | `SLOTS_PER_HISTORICAL_ROOT` | `uint64(2**13)` (= 8,192) | slots | ~27 hours | | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `uint64(2**8)` (= 256) | epochs | ~27 hours | -| `SHARD_COMMITTEE_PERIOD` | `Epoch(2**8)` (= 256) | epochs | ~27 hours | +| `SHARD_COMMITTEE_PERIOD` | `uint64(2**8)` (= 256) | epochs | ~27 hours | ### State list lengths From 8f70453aeff73b37304abc77983af6b4d4dac1e1 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 15 May 2020 23:09:07 +0800 Subject: [PATCH 04/10] PR feedback from Proto --- specs/phase0/beacon-chain.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 60ed518f6..4f2f98458 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -732,7 +732,7 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # See the 'generalized domain' algorithm on page 3 for current_round in map(uint64, range(SHUFFLE_ROUND_COUNT)): pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=uint64(1)))[0:8]) % index_count - flip = (pivot + index_count - index) % index_count + flip = uint64((pivot + index_count - index) % index_count) position = max(index, flip) source = hash( seed @@ -741,7 +741,7 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> ) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 - index = uint64(flip) if bit else index + index = flip if bit else index return index ``` @@ -1097,7 +1097,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))]) exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_validator_churn_limit(state): - exit_queue_epoch = Epoch(exit_queue_epoch + 1) + exit_queue_epoch += Epoch(1) # Set validator exit epoch and withdrawable epoch validator.exit_epoch = exit_queue_epoch From cd91380d8056cf86ef98690a5ee7192db5a4f824 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 18 May 2020 14:34:12 +0800 Subject: [PATCH 05/10] PR feedback from proto: set `length` back to `int` --- specs/phase0/beacon-chain.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 4f2f98458..8e212ba13 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -577,7 +577,7 @@ def xor(bytes_1: Bytes32, bytes_2: Bytes32) -> Bytes32: #### `int_to_bytes` ```python -def int_to_bytes(n: uint64, length: uint64) -> bytes: +def int_to_bytes(n: uint64, length: int) -> bytes: """ Return the ``length``-byte serialization of ``n`` in ``ENDIANNESS``-endian. """ @@ -731,13 +731,13 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) # See the 'generalized domain' algorithm on page 3 for current_round in map(uint64, range(SHUFFLE_ROUND_COUNT)): - pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=uint64(1)))[0:8]) % index_count + pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count flip = uint64((pivot + index_count - index) % index_count) position = max(index, flip) source = hash( seed - + int_to_bytes(current_round, length=uint64(1)) - + int_to_bytes(uint64(position // 256), length=uint64(4)) + + int_to_bytes(current_round, length=1) + + int_to_bytes(uint64(position // 256), length=4) ) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 @@ -758,7 +758,7 @@ def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex] i = 0 while True: candidate_index = indices[compute_shuffled_index(uint64(i % len(indices)), uint64(len(indices)), seed)] - random_byte = hash(seed + int_to_bytes(uint64(i // 32), length=uint64(8)))[i % 32] + random_byte = hash(seed + int_to_bytes(uint64(i // 32), length=8))[i % 32] effective_balance = state.validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: return candidate_index @@ -947,7 +947,7 @@ def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes Return the seed at ``epoch``. """ mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow - return hash(domain_type + int_to_bytes(epoch, length=uint64(8)) + mix) + return hash(domain_type + int_to_bytes(epoch, length=8) + mix) ``` #### `get_committee_count_at_slot` @@ -989,7 +989,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: Return the beacon proposer index at the current slot. """ epoch = get_current_epoch(state) - seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=uint64(8))) + seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=8)) indices = get_active_validator_indices(state, epoch) return compute_proposer_index(state, indices, seed) ``` From 4428a6aedffc66162edec92431ce0b127b5874d5 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 18 Jun 2020 13:45:32 +0800 Subject: [PATCH 06/10] Fix table --- specs/phase0/beacon-chain.md | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index 74c09a0c5..f8357be96 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -218,9 +218,9 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | Unit | Duration | | - | - | :-: | :-: | -<<<<<<< HEAD -| `MIN_GENESIS_DELAY` | `uint64(86400)` | seconds | 1 day | +| `GENESIS_DELAY` | `uint64(172800)` | seconds | 2 days | | `SECONDS_PER_SLOT` | `uint64(12)` | seconds | 12 seconds | +| `SECONDS_PER_ETH1_BLOCK` | `uint64(14)` | seconds | 14 seconds | | `MIN_ATTESTATION_INCLUSION_DELAY` | `uint64(2**0)` (= 1) | slots | 12 seconds | | `SLOTS_PER_EPOCH` | `uint64(2**5)` (= 32) | slots | 6.4 minutes | | `MIN_SEED_LOOKAHEAD` | `uint64(2**0)` (= 1) | epochs | 6.4 minutes | @@ -230,20 +230,6 @@ The following values are (non-configurable) constants used throughout the specif | `SLOTS_PER_HISTORICAL_ROOT` | `uint64(2**13)` (= 8,192) | slots | ~27 hours | | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `uint64(2**8)` (= 256) | epochs | ~27 hours | | `SHARD_COMMITTEE_PERIOD` | `uint64(2**8)` (= 256) | epochs | ~27 hours | -======= -| `GENESIS_DELAY` | `172800` | seconds | 2 days | -| `SECONDS_PER_SLOT` | `12` | seconds | 12 seconds | -| `SECONDS_PER_ETH1_BLOCK` | `14` | seconds | 14 seconds | -| `MIN_ATTESTATION_INCLUSION_DELAY` | `2**0` (= 1) | slots | 12 seconds | -| `SLOTS_PER_EPOCH` | `2**5` (= 32) | slots | 6.4 minutes | -| `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | -| `MAX_SEED_LOOKAHEAD` | `2**2` (= 4) | epochs | 25.6 minutes | -| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes | -| `EPOCHS_PER_ETH1_VOTING_PERIOD` | `2**5` (= 32) | epochs | ~3.4 hours | -| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~27 hours | -| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | -| `SHARD_COMMITTEE_PERIOD` | `Epoch(2**8)` (= 256) | epochs | ~27 hours | ->>>>>>> dev ### State list lengths From 26c540fbf9fa0dceb7b09210f82af6e7a8b71c84 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 18 Jun 2020 14:07:59 +0800 Subject: [PATCH 07/10] Fix `get_min_new_period_epochs` helper --- .../eth2spec/test/validator/test_validator_unittest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/validator/test_validator_unittest.py b/tests/core/pyspec/eth2spec/test/validator/test_validator_unittest.py index e749b1e3d..f5346e4e1 100644 --- a/tests/core/pyspec/eth2spec/test/validator/test_validator_unittest.py +++ b/tests/core/pyspec/eth2spec/test/validator/test_validator_unittest.py @@ -40,9 +40,9 @@ def run_is_candidate_block(spec, eth1_block, period_start, success=True): def get_min_new_period_epochs(spec): - return int( - spec.SECONDS_PER_ETH1_BLOCK * spec.ETH1_FOLLOW_DISTANCE * 2 # to seconds - / spec.SECONDS_PER_SLOT / spec.SLOTS_PER_EPOCH + return ( + (spec.SECONDS_PER_ETH1_BLOCK * spec.ETH1_FOLLOW_DISTANCE * 2) # to seconds + // spec.SECONDS_PER_SLOT // spec.SLOTS_PER_EPOCH ) From 531184f42bbda5a5d2e8244b583014bbd2f2225b Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 26 Jun 2020 15:41:47 +0200 Subject: [PATCH 08/10] Infer types where possible, e.g. uint64+uint64=uint64 --- specs/phase0/beacon-chain.md | 37 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index ec4751fc3..df9390964 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -561,9 +561,9 @@ def integer_squareroot(n: uint64) -> uint64: x = n y = (x + 1) // 2 while y < x: - x = uint64(y) + x = y y = (x + n // x) // 2 - return uint64(x) + return x ``` #### `xor` @@ -732,15 +732,11 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> # Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf) # See the 'generalized domain' algorithm on page 3 - for current_round in map(uint64, range(SHUFFLE_ROUND_COUNT)): + for current_round in range(SHUFFLE_ROUND_COUNT): pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count - flip = uint64((pivot + index_count - index) % index_count) + flip = (pivot + index_count - index) % index_count position = max(index, flip) - source = hash( - seed - + int_to_bytes(current_round, length=1) - + int_to_bytes(uint64(position // 256), length=4) - ) + source = hash(seed + int_to_bytes(current_round, length=1) + int_to_bytes(position // 256, length=4)) byte = source[(position % 256) // 8] bit = (byte >> (position % 8)) % 2 index = flip if bit else index @@ -757,10 +753,11 @@ def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex] """ assert len(indices) > 0 MAX_RANDOM_BYTE = 2**8 - 1 - i = 0 + i = uint64(0) + total = uint64(len(indices)) while True: - candidate_index = indices[compute_shuffled_index(uint64(i % len(indices)), uint64(len(indices)), seed)] - random_byte = hash(seed + int_to_bytes(uint64(i // 32), length=8))[i % 32] + candidate_index = indices[compute_shuffled_index(i % total, total, seed)] + random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32] effective_balance = state.validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: return candidate_index @@ -938,7 +935,7 @@ def get_validator_churn_limit(state: BeaconState) -> uint64: Return the validator churn limit for the current epoch. """ active_validator_indices = get_active_validator_indices(state, get_current_epoch(state)) - return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices) // CHURN_LIMIT_QUOTIENT)) + return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT) ``` #### `get_seed` @@ -961,7 +958,7 @@ def get_committee_count_per_slot(state: BeaconState, epoch: Epoch) -> uint64: """ return max(uint64(1), min( MAX_COMMITTEES_PER_SLOT, - uint64(len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE), + uint64(len(get_active_validator_indices(state, epoch))) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE, )) ``` @@ -977,8 +974,8 @@ def get_beacon_committee(state: BeaconState, slot: Slot, index: CommitteeIndex) return compute_committee( indices=get_active_validator_indices(state, epoch), seed=get_seed(state, epoch, DOMAIN_BEACON_ATTESTER), - index=uint64((slot % SLOTS_PER_EPOCH) * committees_per_slot + index), - count=uint64(committees_per_slot * SLOTS_PER_EPOCH), + index=(slot % SLOTS_PER_EPOCH) * committees_per_slot + index, + count=committees_per_slot * SLOTS_PER_EPOCH, ) ``` @@ -1501,12 +1498,12 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence _, inactivity_penalties = get_inactivity_penalty_deltas(state) rewards = [ - Gwei(source_rewards[i] + target_rewards[i] + head_rewards[i] + inclusion_delay_rewards[i]) + source_rewards[i] + target_rewards[i] + head_rewards[i] + inclusion_delay_rewards[i] for i in range(len(state.validators)) ] penalties = [ - Gwei(source_penalties[i] + target_penalties[i] + head_penalties[i] + inactivity_penalties[i]) + source_penalties[i] + target_penalties[i] + head_penalties[i] + inactivity_penalties[i] for i in range(len(state.validators)) ] @@ -1773,13 +1770,13 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: assert is_valid_merkle_branch( leaf=hash_tree_root(deposit.data), branch=deposit.proof, - depth=uint64(DEPOSIT_CONTRACT_TREE_DEPTH + 1), # Add 1 for the List length mix-in + depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in index=state.eth1_deposit_index, root=state.eth1_data.deposit_root, ) # Deposits must be processed in order - state.eth1_deposit_index = uint64(state.eth1_deposit_index + 1) + state.eth1_deposit_index += 1 pubkey = deposit.data.pubkey amount = deposit.data.amount From 3fb0257cbb8cd95336d4b57fb42fa5e02a74baf4 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 26 Jun 2020 15:55:07 +0200 Subject: [PATCH 09/10] update remerkleable for uint and mypy improvements --- setup.py | 2 +- tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 85d1a95ea..801f215d0 100644 --- a/setup.py +++ b/setup.py @@ -519,7 +519,7 @@ setup( "py_ecc==4.0.0", "milagro_bls_binding==1.3.0", "dataclasses==0.6", - "remerkleable==0.1.16", + "remerkleable==0.1.17", "ruamel.yaml==0.16.5", "lru-dict==1.1.6" ] diff --git a/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py b/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py index e6536c748..b3a0b9962 100644 --- a/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py +++ b/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py @@ -5,4 +5,4 @@ from remerkleable.complex import Container, Vector, List from remerkleable.basic import boolean, bit, uint, byte, uint8, uint16, uint32, uint64, uint128, uint256 from remerkleable.bitfields import Bitvector, Bitlist from remerkleable.byte_arrays import ByteVector, Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96, ByteList -from remerkleable.core import BasicView, View, TypeDef +from remerkleable.core import BasicView, View From 3b7617f51ace8161b5fc89ea1eaf2ee102f200c5 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 26 Jun 2020 16:13:38 +0200 Subject: [PATCH 10/10] make extracted byte uint8 for bitshift, do not use negative slice indexing, avoid negative comparison in test --- setup.py | 2 +- specs/phase0/beacon-chain.md | 4 ++-- .../epoch_processing/test_process_rewards_and_penalties.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 801f215d0..3a4fd1e18 100644 --- a/setup.py +++ b/setup.py @@ -96,7 +96,7 @@ from lru import LRU from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy from eth2spec.utils.ssz.ssz_typing import ( - View, boolean, Container, List, Vector, uint64, + View, boolean, Container, List, Vector, uint64, uint8, Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector, ) from eth2spec.utils import bls diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index df9390964..c7dd25d3a 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -737,7 +737,7 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) -> flip = (pivot + index_count - index) % index_count position = max(index, flip) source = hash(seed + int_to_bytes(current_round, length=1) + int_to_bytes(position // 256, length=4)) - byte = source[(position % 256) // 8] + byte = uint8(source[(position % 256) // 8]) bit = (byte >> (position % 8)) % 2 index = flip if bit else index @@ -1317,7 +1317,7 @@ def process_justification_and_finalization(state: BeaconState) -> None: # Process justifications state.previous_justified_checkpoint = state.current_justified_checkpoint - state.justification_bits[1:] = state.justification_bits[:-1] + state.justification_bits[1:] = state.justification_bits[:JUSTIFICATION_BITS_LENGTH - 1] state.justification_bits[0] = 0b0 matching_target_attestations = get_matching_target_attestations(state, previous_epoch) # Previous epoch if get_attesting_balance(state, matching_target_attestations) * 3 >= get_total_active_balance(state) * 2: diff --git a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py index d3744f897..72bdd3e92 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py +++ b/tests/core/pyspec/eth2spec/test/phase0/epoch_processing/test_process_rewards_and_penalties.py @@ -48,7 +48,7 @@ def test_genesis_epoch_full_attestations_no_rewards(spec, state): attestation = get_valid_attestation(spec, state, signed=True) attestations.append(attestation) # fill each created slot in state after inclusion delay - if slot - spec.MIN_ATTESTATION_INCLUSION_DELAY >= 0: + if slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY: include_att = attestations[slot - spec.MIN_ATTESTATION_INCLUSION_DELAY] add_attestations_to_state(spec, state, [include_att], state.slot) next_slot(spec, state)