diff --git a/setup.py b/setup.py index 214559d22..1f88c31dc 100644 --- a/setup.py +++ b/setup.py @@ -94,9 +94,9 @@ from dataclasses import ( from lru import LRU -from eth2spec.utils.ssz.ssz_impl import hash_tree_root +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 @@ -118,7 +118,7 @@ from dataclasses import ( from lru import LRU -from eth2spec.utils.ssz.ssz_impl import hash_tree_root +from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy from eth2spec.utils.ssz.ssz_typing import ( View, boolean, Container, List, Vector, uint64, uint8, bit, ByteList, ByteVector, Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector, @@ -529,7 +529,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/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index cd52762d5..c7dd25d3a 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,18 +183,18 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | | - | - | -| `ETH1_FOLLOW_DISTANCE` | `2**10` (= 1,024) | -| `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` | +| `ETH1_FOLLOW_DISTANCE` | `uint64(2**10)` (= 1,024) | +| `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.) @@ -218,37 +218,37 @@ The following values are (non-configurable) constants used throughout the specif | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `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 | +| `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 | +| `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` | `uint64(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)`. @@ -579,7 +579,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. """ @@ -593,7 +593,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 @@ -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 @@ -753,9 +753,10 @@ 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(i % len(indices), len(indices), seed)] + 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: @@ -775,7 +776,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` @@ -934,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, len(active_validator_indices) // CHURN_LIMIT_QUOTIENT) + return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT) ``` #### `get_seed` @@ -955,9 +956,9 @@ def get_committee_count_per_slot(state: BeaconState, epoch: Epoch) -> uint64: """ Return the number of committees in each slot for the given ``epoch``. """ - 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, )) ``` @@ -1316,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/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index a9b1b2eee..8e1f65121 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -102,7 +102,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 = copy(anchor_state.latest_block_header) 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) @@ -110,14 +110,14 @@ 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, best_justified_checkpoint=justified_checkpoint, blocks={anchor_root: anchor_block_header}, - block_states={anchor_root: anchor_state.copy()}, - checkpoint_states={justified_checkpoint: anchor_state.copy()}, + block_states={anchor_root: copy(anchor_state)}, + checkpoint_states={justified_checkpoint: copy(anchor_state)}, ) ``` @@ -302,7 +302,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 = copy(store.block_states[target.root]) if base_state.slot < compute_start_slot_at_epoch(target.epoch): process_slots(base_state, compute_start_slot_at_epoch(target.epoch)) store.checkpoint_states[target] = base_state @@ -348,7 +348,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Parent block must be known assert block.parent_root in store.block_states # Make a copy of the state to avoid mutability issues - pre_state = store.block_states[block.parent_root].copy() + pre_state = copy(store.block_states[block.parent_root]) # 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 diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index bdd6f170d..d0055b8b3 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -273,7 +273,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 @@ -443,7 +443,7 @@ def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, comm slots_since_epoch_start = slot % SLOTS_PER_EPOCH committees_since_epoch_start = committees_per_slot * slots_since_epoch_start - return (committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT + return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT) ``` ### Attestation aggregation 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) 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 84c4b9fe5..d793db789 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 ) diff --git a/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py b/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py index 34e6c4ee8..ad5a0fedf 100644 --- a/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py +++ b/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py @@ -1,3 +1,4 @@ +from typing import TypeVar from remerkleable.core import View from remerkleable.byte_arrays import Bytes32 @@ -8,3 +9,11 @@ def serialize(obj: View) -> bytes: def hash_tree_root(obj: View) -> Bytes32: return Bytes32(obj.get_backing().merkle_root()) + + +V = TypeVar('V', bound=View) + + +# Helper method for typing copies, and avoiding a example_input.copy() method call, instead of copy(example_input) +def copy(obj: V) -> V: + return obj.copy() 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