diff --git a/.circleci/config.yml b/.circleci/config.yml index d40fd467f..5be6ed500 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,13 +15,13 @@ jobs: name: Run py-tests command: make test - - run: - name: Generate YAML tests - command: make gen_yaml_tests - -# TODO in future PR (after #851): decide on CI triggering of yaml tests building, +# TODO see #928: decide on CI triggering of yaml tests building, # and destination of output (new yaml tests LFS-configured repository) # +# - run: +# name: Generate YAML tests +# command: make gen_yaml_tests +# # - store_artifacts: # path: test-reports # destination: test-reports diff --git a/Makefile b/Makefile index 7ca2cc909..b39538791 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,6 @@ SPEC_DIR = ./specs SCRIPT_DIR = ./scripts TEST_LIBS_DIR = ./test_libs PY_SPEC_DIR = $(TEST_LIBS_DIR)/pyspec -PY_TEST_DIR = ./py_tests YAML_TEST_DIR = ./yaml_tests GENERATOR_DIR = ./test_generators CONFIGS_DIR = ./configs @@ -24,7 +23,7 @@ all: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_DIR) $(YAML_TEST_TARGETS) clean: rm -rf $(YAML_TEST_DIR) rm -rf $(GENERATOR_VENVS) - rm -rf $(PY_TEST_DIR)/venv $(PY_TEST_DIR)/.pytest_cache + rm -rf $(PY_SPEC_DIR)/venv $(PY_SPEC_DIR)/.pytest_cache rm -rf $(PY_SPEC_ALL_TARGETS) # "make gen_yaml_tests" to run generators @@ -32,7 +31,7 @@ gen_yaml_tests: $(YAML_TEST_DIR) $(YAML_TEST_TARGETS) # runs a limited set of tests against a minimal config test: $(PY_SPEC_ALL_TARGETS) - cd $(PY_TEST_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt; pytest -m minimal_config . + cd $(PY_SPEC_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt; python -m pytest -m minimal_config . # "make pyspec" to create the pyspec for all phases. pyspec: $(PY_SPEC_ALL_TARGETS) diff --git a/README.md b/README.md index 12536070d..aa5b7e302 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,5 @@ The following are the broad design goals for Ethereum 2.0: Documentation on the different components used during spec writing can be found here: * [YAML Test Generators](test_generators/README.md) -* [Executable Python Spec](test_libs/eth2spec/README.md) -* [Py-tests](py_tests/README.md) +* [Executable Python Spec, with Py-tests](test_libs/pyspec/README.md) diff --git a/configs/constant_presets/mainnet.yaml b/configs/constant_presets/mainnet.yaml index d2d91d02c..d06febb77 100644 --- a/configs/constant_presets/mainnet.yaml +++ b/configs/constant_presets/mainnet.yaml @@ -5,16 +5,16 @@ # Misc # --------------------------------------------------------------- -# 2**10 ` (= 1,024) +# 2**10 (= 1,024) SHARD_COUNT: 1024 -# 2**7 ` (= 128) +# 2**7 (= 128) TARGET_COMMITTEE_SIZE: 128 -# 2**5 ` (= 32) -MAX_BALANCE_CHURN_QUOTIENT: 32 -# 2**12 ` (= 4,096) +# 2**12 (= 4,096) MAX_ATTESTATION_PARTICIPANTS: 4096 -# 2**2 ` (= 4) -MAX_EXIT_DEQUEUES_PER_EPOCH: 4 +# 2**2 (= 4) +MIN_PER_EPOCH_CHURN_LIMIT: 4 +# 2**16 (= 65,536) +CHURN_LIMIT_QUOTIENT: 65536 # See issue 563 SHUFFLE_ROUND_COUNT: 90 @@ -23,19 +23,19 @@ SHUFFLE_ROUND_COUNT: 90 # --------------------------------------------------------------- # **TBD** DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890 -# 2**5 ` (= 32) +# 2**5 (= 32) DEPOSIT_CONTRACT_TREE_DEPTH: 32 # Gwei values # --------------------------------------------------------------- -# 2**0 * 10**9 ` (= 1,000,000,000) Gwei +# 2**0 * 10**9 (= 1,000,000,000) Gwei MIN_DEPOSIT_AMOUNT: 1000000000 -# 2**5 * 10**9 ` (= 32,000,000,000) Gwei +# 2**5 * 10**9 (= 32,000,000,000) Gwei MAX_DEPOSIT_AMOUNT: 32000000000 -# 2**4 * 10**9 ` (= 16,000,000,000) Gwei +# 2**4 * 10**9 (= 16,000,000,000) Gwei EJECTION_BALANCE: 16000000000 -# 2**0 * 10**9 ` (= 1,000,000,000) Gwei +# 2**0 * 10**9 (= 1,000,000,000) Gwei HIGH_BALANCE_INCREMENT: 1000000000 @@ -54,63 +54,63 @@ BLS_WITHDRAWAL_PREFIX_BYTE: 0x00 # --------------------------------------------------------------- # 6 seconds 6 seconds SECONDS_PER_SLOT: 6 -# 2**2 ` (= 4) slots 24 seconds +# 2**2 (= 4) slots 24 seconds MIN_ATTESTATION_INCLUSION_DELAY: 4 -# 2**6 ` (= 64) slots 6.4 minutes +# 2**6 (= 64) slots 6.4 minutes SLOTS_PER_EPOCH: 64 -# 2**0 ` (= 1) epochs 6.4 minutes +# 2**0 (= 1) epochs 6.4 minutes MIN_SEED_LOOKAHEAD: 1 -# 2**2 ` (= 4) epochs 25.6 minutes +# 2**2 (= 4) epochs 25.6 minutes ACTIVATION_EXIT_DELAY: 4 -# 2**4 ` (= 16) epochs ~1.7 hours -EPOCHS_PER_ETH1_VOTING_PERIOD: 16 -# 2**13 ` (= 8,192) slots ~13 hours +# 2**10 (= 1,024) slots ~1.7 hours +SLOTS_PER_ETH1_VOTING_PERIOD: 1024 +# 2**13 (= 8,192) slots ~13 hours SLOTS_PER_HISTORICAL_ROOT: 8192 -# 2**8 ` (= 256) epochs ~27 hours +# 2**8 (= 256) epochs ~27 hours MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 -# 2**11 ` (= 2,048) epochs 9 days +# 2**11 (= 2,048) epochs 9 days PERSISTENT_COMMITTEE_PERIOD: 2048 -# 2**6 ` (= 64) +# 2**6 (= 64) epochs ~7 hours MAX_CROSSLINK_EPOCHS: 64 # State list lengths # --------------------------------------------------------------- -# 2**13 ` (= 8,192) epochs ~36 days +# 2**13 (= 8,192) epochs ~36 days LATEST_RANDAO_MIXES_LENGTH: 8192 -# 2**13 ` (= 8,192) epochs ~36 days +# 2**13 (= 8,192) epochs ~36 days LATEST_ACTIVE_INDEX_ROOTS_LENGTH: 8192 -# 2**13 ` (= 8,192) epochs ~36 days +# 2**13 (= 8,192) epochs ~36 days LATEST_SLASHED_EXIT_LENGTH: 8192 # Reward and penalty quotients # --------------------------------------------------------------- -# 2**5 ` (= 32) +# 2**5 (= 32) BASE_REWARD_QUOTIENT: 32 -# 2**9 ` (= 512) +# 2**9 (= 512) WHISTLEBLOWING_REWARD_QUOTIENT: 512 -# 2**3 ` (= 8) +# 2**3 (= 8) PROPOSER_REWARD_QUOTIENT: 8 -# 2**24 ` (= 16,777,216) +# 2**24 (= 16,777,216) INACTIVITY_PENALTY_QUOTIENT: 16777216 # Max operations per block # --------------------------------------------------------------- -# 2**5 ` (= 32) +# 2**5 (= 32) MIN_PENALTY_QUOTIENT: 32 -# 2**4 ` (= 16) +# 2**4 (= 16) MAX_PROPOSER_SLASHINGS: 16 -# 2**0 ` (= 1) +# 2**0 (= 1) MAX_ATTESTER_SLASHINGS: 1 -# 2**7 ` (= 128) +# 2**7 (= 128) MAX_ATTESTATIONS: 128 -# 2**4 ` (= 16) +# 2**4 (= 16) MAX_DEPOSITS: 16 -# 2**4 ` (= 16) +# 2**4 (= 16) MAX_VOLUNTARY_EXITS: 16 -# 2**4 ` (= 16) +# 2**4 (= 16) MAX_TRANSFERS: 16 diff --git a/configs/constant_presets/minimal.yaml b/configs/constant_presets/minimal.yaml index c1d69bcd4..80af5398c 100644 --- a/configs/constant_presets/minimal.yaml +++ b/configs/constant_presets/minimal.yaml @@ -9,13 +9,13 @@ SHARD_COUNT: 8 # [customized] unsecure, but fast TARGET_COMMITTEE_SIZE: 4 -# 2**5 ` (= 32) -MAX_BALANCE_CHURN_QUOTIENT: 32 -# 2**12 ` (= 4,096) +# 2**12 (= 4,096) MAX_ATTESTATION_PARTICIPANTS: 4096 -# 2**2 ` (= 4) -MAX_EXIT_DEQUEUES_PER_EPOCH: 4 -# See issue 563 +# 2**2 (= 4) +MIN_PER_EPOCH_CHURN_LIMIT: 4 +# 2**16 (= 65,536) +CHURN_LIMIT_QUOTIENT: 65536 +# [customized] Faster, but unsecure. SHUFFLE_ROUND_COUNT: 10 @@ -23,19 +23,19 @@ SHUFFLE_ROUND_COUNT: 10 # --------------------------------------------------------------- # **TBD** DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890 -# 2**5 ` (= 32) +# 2**5 (= 32) DEPOSIT_CONTRACT_TREE_DEPTH: 32 # Gwei values # --------------------------------------------------------------- -# 2**0 * 10**9 ` (= 1,000,000,000) Gwei +# 2**0 * 10**9 (= 1,000,000,000) Gwei MIN_DEPOSIT_AMOUNT: 1000000000 -# 2**5 * 10**9 ` (= 32,000,000,000) Gwei +# 2**5 * 10**9 (= 32,000,000,000) Gwei MAX_DEPOSIT_AMOUNT: 32000000000 -# 2**4 * 10**9 ` (= 16,000,000,000) Gwei +# 2**4 * 10**9 (= 16,000,000,000) Gwei EJECTION_BALANCE: 16000000000 -# 2**0 * 10**9 ` (= 1,000,000,000) Gwei +# 2**0 * 10**9 (= 1,000,000,000) Gwei HIGH_BALANCE_INCREMENT: 1000000000 @@ -58,19 +58,19 @@ SECONDS_PER_SLOT: 6 MIN_ATTESTATION_INCLUSION_DELAY: 2 # [customized] fast epochs SLOTS_PER_EPOCH: 8 -# 2**0 ` (= 1) epochs 6.4 minutes +# 2**0 (= 1) epochs 6.4 minutes MIN_SEED_LOOKAHEAD: 1 -# 2**2 ` (= 4) epochs 25.6 minutes +# 2**2 (= 4) epochs 25.6 minutes ACTIVATION_EXIT_DELAY: 4 # [customized] higher frequency new deposits from eth1 for testing -EPOCHS_PER_ETH1_VOTING_PERIOD: 2 +SLOTS_PER_ETH1_VOTING_PERIOD: 16 # [customized] smaller state SLOTS_PER_HISTORICAL_ROOT: 64 -# 2**8 ` (= 256) epochs ~27 hours +# 2**8 (= 256) epochs ~27 hours MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256 -# 2**11 ` (= 2,048) epochs 9 days +# 2**11 (= 2,048) epochs 9 days PERSISTENT_COMMITTEE_PERIOD: 2048 -# 2**6 ` (= 64) +# 2**6 (= 64) epochs ~7 hours MAX_CROSSLINK_EPOCHS: 64 @@ -86,31 +86,31 @@ LATEST_SLASHED_EXIT_LENGTH: 64 # Reward and penalty quotients # --------------------------------------------------------------- -# 2**5 ` (= 32) +# 2**5 (= 32) BASE_REWARD_QUOTIENT: 32 -# 2**9 ` (= 512) +# 2**9 (= 512) WHISTLEBLOWING_REWARD_QUOTIENT: 512 -# 2**3 ` (= 8) +# 2**3 (= 8) PROPOSER_REWARD_QUOTIENT: 8 -# 2**24 ` (= 16,777,216) +# 2**24 (= 16,777,216) INACTIVITY_PENALTY_QUOTIENT: 16777216 # Max operations per block # --------------------------------------------------------------- -# 2**5 ` (= 32) +# 2**5 (= 32) MIN_PENALTY_QUOTIENT: 32 -# 2**4 ` (= 16) +# 2**4 (= 16) MAX_PROPOSER_SLASHINGS: 16 -# 2**0 ` (= 1) +# 2**0 (= 1) MAX_ATTESTER_SLASHINGS: 1 -# 2**7 ` (= 128) +# 2**7 (= 128) MAX_ATTESTATIONS: 128 -# 2**4 ` (= 16) +# 2**4 (= 16) MAX_DEPOSITS: 16 -# 2**4 ` (= 16) +# 2**4 (= 16) MAX_VOLUNTARY_EXITS: 16 -# 2**4 ` (= 16) +# 2**4 (= 16) MAX_TRANSFERS: 16 diff --git a/py_tests/README.md b/py_tests/README.md deleted file mode 100644 index ca2bed4cc..000000000 --- a/py_tests/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# ETH 2.0 py-tests - -These tests are not intended for client-consumption. -These tests are sanity tests, to verify if the spec itself is consistent. - -There are ideas to port these tests to the YAML test suite, - but we are still looking for inputs on how this should work. - -## How to run tests - -### Automated - -Run `make test` from the root of the spec repository. - -### Manual - -From within the py_tests folder: - -Install dependencies: -```bash -python3 -m venv venv -. venv/bin/activate -pip3 install -r requirements.txt -``` -Note: make sure to run `make pyspec` from the root of the specs repository, to build the pyspec requirement. - -Run the tests: -``` -pytest -m minimal_config . -``` diff --git a/py_tests/requirements.txt b/py_tests/requirements.txt deleted file mode 100644 index 27b3f22d8..000000000 --- a/py_tests/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -eth-utils>=1.3.0,<2 -eth-typing>=2.1.0,<3.0.0 -oyaml==0.7 -pycryptodome==3.7.3 -py_ecc>=1.6.0 -pytest>=3.6,<3.7 -../test_libs/pyspec diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 974131027..6aa562ca4 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -25,7 +25,6 @@ - [`Fork`](#fork) - [`Crosslink`](#crosslink) - [`Eth1Data`](#eth1data) - - [`Eth1DataVote`](#eth1datavote) - [`AttestationData`](#attestationdata) - [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit) - [`IndexedAttestation`](#indexedattestation) @@ -116,7 +115,6 @@ - [Helper functions](#helper-functions-1) - [Justification](#justification) - [Crosslinks](#crosslinks) - - [Eth1 data](#eth1-data) - [Rewards and penalties](#rewards-and-penalties) - [Justification and finalization](#justification-and-finalization) - [Crosslinks](#crosslinks-1) @@ -229,7 +227,7 @@ These configurations are updated for releases, but may be out of sync during `de | `SLOTS_PER_EPOCH` | `2**6` (= 64) | slots | 6.4 minutes | | `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes | | `ACTIVATION_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes | -| `EPOCHS_PER_ETH1_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours | +| `SLOTS_PER_ETH1_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | | `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~13 hours | | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | | `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days | @@ -325,17 +323,6 @@ The types are defined topologically to aid in facilitating an executable version } ``` -#### `Eth1DataVote` - -```python -{ - # Data being voted for - 'eth1_data': Eth1Data, - # Vote count - 'vote_count': 'uint64', -} -``` - #### `AttestationData` ```python @@ -615,7 +602,7 @@ The types are defined topologically to aid in facilitating an executable version # Ethereum 1.0 chain data 'latest_eth1_data': Eth1Data, - 'eth1_data_votes': [Eth1DataVote], + 'eth1_data_votes': [Eth1Data], 'deposit_index': 'uint64', } ``` @@ -984,25 +971,17 @@ def generate_seed(state: BeaconState, ### `get_beacon_proposer_index` ```python -def get_beacon_proposer_index(state: BeaconState, - slot: Slot) -> ValidatorIndex: +def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: """ - Return the beacon proposer index for the ``slot``. - Due to proposer selection being based upon the validator balances during - the epoch in question, this can only be run for the current epoch. + Return the beacon proposer index at ``state.slot``. """ current_epoch = get_current_epoch(state) - assert slot_to_epoch(slot) == current_epoch - - first_committee, _ = get_crosslink_committees_at_slot(state, slot)[0] + first_committee, _ = get_crosslink_committees_at_slot(state, state.slot)[0] i = 0 while True: - rand_byte = hash( - generate_seed(state, current_epoch) + - int_to_bytes8(i // 32) - )[i % 32] candidate = first_committee[(current_epoch + i) % len(first_committee)] - if get_effective_balance(state, candidate) * 256 > MAX_EFFECTIVE_BALANCE * rand_byte: + random_byte = hash(generate_seed(state, current_epoch) + int_to_bytes8(i // 32))[i % 32] + if get_effective_balance(state, candidate) * 256 > MAX_EFFECTIVE_BALANCE * random_byte: return candidate i += 1 ``` @@ -1051,16 +1030,8 @@ def get_attestation_participants(state: BeaconState, Return the sorted participant indices corresponding to ``attestation_data`` and ``bitfield``. """ crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data) - assert verify_bitfield(bitfield, len(crosslink_committee)) - - # Find the participating attesters in the committee - participants = [] - for i, validator_index in enumerate(crosslink_committee): - aggregation_bit = get_bitfield_bit(bitfield, i) - if aggregation_bit == 0b1: - participants.append(validator_index) - return sorted(participants) + return sorted([index for i, index in enumerate(crosslink_committee) if get_bitfield_bit(bitfield, i) == 0b1]) ``` ### `int_to_bytes1`, `int_to_bytes2`, ... @@ -1344,7 +1315,7 @@ def slash_validator(state: BeaconState, slashed_index: ValidatorIndex, whistlebl slashed_balance = get_effective_balance(state, slashed_index) state.latest_slashed_balances[get_current_epoch(state) % LATEST_SLASHED_EXIT_LENGTH] += slashed_balance - proposer_index = get_beacon_proposer_index(state, state.slot) + proposer_index = get_beacon_proposer_index(state) if whistleblower_index is None: whistleblower_index = proposer_index whistleblowing_reward = slashed_balance // WHISTLEBLOWING_REWARD_QUOTIENT @@ -1494,9 +1465,9 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], process_deposit(state, deposit) # Process genesis activations - for validator_index in range(len(state.validator_registry)): - if get_effective_balance(state, validator_index) >= MAX_EFFECTIVE_BALANCE: - activate_validator(state, validator_index, is_genesis=True) + for index in range(len(state.validator_registry)): + if get_effective_balance(state, index) >= MAX_EFFECTIVE_BALANCE: + activate_validator(state, index, is_genesis=True) genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH)) for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH): @@ -1547,8 +1518,8 @@ def get_ancestor(store: Store, block: BeaconBlock, slot: Slot) -> BeaconBlock: return get_ancestor(store, store.get_parent(block), slot) ``` -* Let `get_latest_attestation(store: Store, validator_index: ValidatorIndex) -> Attestation` be the attestation with the highest slot number in `store` from the validator with the given `validator_index`. If several such attestations exist, use the one the [validator](#dfn-validator) `v` observed first. -* Let `get_latest_attestation_target(store: Store, validator_index: ValidatorIndex) -> BeaconBlock` be the target block in the attestation `get_latest_attestation(store, validator_index)`. +* Let `get_latest_attestation(store: Store, index: ValidatorIndex) -> Attestation` be the attestation with the highest slot number in `store` from the validator with the given `index`. If several such attestations exist, use the one the [validator](#dfn-validator) `v` observed first. +* Let `get_latest_attestation_target(store: Store, index: ValidatorIndex) -> BeaconBlock` be the target block in the attestation `get_latest_attestation(store, index)`. * Let `get_children(store: Store, block: BeaconBlock) -> List[BeaconBlock]` returns the child blocks of the given `block`. * Let `justified_head_state` be the resulting `BeaconState` object from processing the chain up to the `justified_head`. * The `head` is `lmd_ghost(store, justified_head_state, justified_head)` where the function `lmd_ghost` is defined below. Note that the implementation below is suboptimal; there are implementations that compute the head in time logarithmic in slot count. @@ -1560,10 +1531,7 @@ def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock) """ validators = start_state.validator_registry active_validator_indices = get_active_validator_indices(validators, slot_to_epoch(start_state.slot)) - attestation_targets = [ - (validator_index, get_latest_attestation_target(store, validator_index)) - for validator_index in active_validator_indices - ] + attestation_targets = [(i, get_latest_attestation_target(store, i)) for i in active_validator_indices] # Use the rounded-balance-with-hysteresis supplied by the protocol for fork # choice voting. This reduces the number of recomputations that need to be @@ -1628,7 +1596,7 @@ The steps below happen when `state.slot > GENESIS_SLOT and (state.slot + 1) % SL #### Helper functions -We define some helper functions utilized when processing an epoch transition: +We define epoch transition helper functions: ```python def get_current_total_balance(state: BeaconState) -> Gwei: @@ -1702,71 +1670,54 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple ``` ```python -def earliest_attestation(state: BeaconState, validator_index: ValidatorIndex) -> PendingAttestation: +def get_earliest_attestation(state: BeaconState, attestations: List[PendingAttestation], index: ValidatorIndex) -> PendingAttestation: return min([ - a for a in state.previous_epoch_attestations if - validator_index in get_attestation_participants(state, a.data, a.aggregation_bitfield) + a for a in attestations if index in get_attestation_participants(state, a.data, a.aggregation_bitfield) ], key=lambda a: a.inclusion_slot) ``` -```python -def inclusion_slot(state: BeaconState, validator_index: ValidatorIndex) -> Slot: - return earliest_attestation(state, validator_index).inclusion_slot -``` - -```python -def inclusion_distance(state: BeaconState, validator_index: ValidatorIndex) -> int: - attestation = earliest_attestation(state, validator_index) - return attestation.inclusion_slot - attestation.data.slot -``` - #### Justification Run the following function: ```python def update_justification_and_finalization(state: BeaconState) -> None: - new_justified_epoch = state.current_justified_epoch - new_finalized_epoch = state.finalized_epoch + antepenultimate_justified_epoch = state.previous_justified_epoch - # Rotate the justification bitfield up one epoch to make room for the current epoch (and limit to 64 bits) + # Process justifications + state.previous_justified_epoch = state.current_justified_epoch + state.previous_justified_root = state.current_justified_root state.justification_bitfield = (state.justification_bitfield << 1) % 2**64 - # If the previous epoch gets justified, fill the second last bit previous_boundary_attesting_balance = get_attesting_balance(state, get_previous_epoch_boundary_attestations(state)) if previous_boundary_attesting_balance * 3 >= get_previous_total_balance(state) * 2: - new_justified_epoch = get_current_epoch(state) - 1 - state.justification_bitfield |= 2 - # If the current epoch gets justified, fill the last bit + state.current_justified_epoch = get_previous_epoch(state) + state.current_justified_root = get_block_root(state, get_epoch_start_slot(state.current_justified_epoch)) + state.justification_bitfield |= (1 << 1) current_boundary_attesting_balance = get_attesting_balance(state, get_current_epoch_boundary_attestations(state)) if current_boundary_attesting_balance * 3 >= get_current_total_balance(state) * 2: - new_justified_epoch = get_current_epoch(state) - state.justification_bitfield |= 1 + state.current_justified_epoch = get_current_epoch(state) + state.current_justified_root = get_block_root(state, get_epoch_start_slot(state.current_justified_epoch)) + state.justification_bitfield |= (1 << 0) # Process finalizations bitfield = state.justification_bitfield current_epoch = get_current_epoch(state) - # The 2nd/3rd/4th most recent epochs are all justified, the 2nd using the 4th as source - if (bitfield >> 1) % 8 == 0b111 and state.previous_justified_epoch == current_epoch - 3: - new_finalized_epoch = state.previous_justified_epoch - # The 2nd/3rd most recent epochs are both justified, the 2nd using the 3rd as source - if (bitfield >> 1) % 4 == 0b11 and state.previous_justified_epoch == current_epoch - 2: - new_finalized_epoch = state.previous_justified_epoch - # The 1st/2nd/3rd most recent epochs are all justified, the 1st using the 3rd as source - if (bitfield >> 0) % 8 == 0b111 and state.current_justified_epoch == current_epoch - 2: - new_finalized_epoch = state.current_justified_epoch - # The 1st/2nd most recent epochs are both justified, the 1st using the 2nd as source - if (bitfield >> 0) % 4 == 0b11 and state.current_justified_epoch == current_epoch - 1: - new_finalized_epoch = state.current_justified_epoch - - # Update state jusification/finality fields - state.previous_justified_epoch = state.current_justified_epoch - state.previous_justified_root = state.current_justified_root - if new_justified_epoch != state.current_justified_epoch: - state.current_justified_epoch = new_justified_epoch - state.current_justified_root = get_block_root(state, get_epoch_start_slot(new_justified_epoch)) - if new_finalized_epoch != state.finalized_epoch: - state.finalized_epoch = new_finalized_epoch - state.finalized_root = get_block_root(state, get_epoch_start_slot(new_finalized_epoch)) + # The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source + if (bitfield >> 1) % 8 == 0b111 and antepenultimate_justified_epoch == current_epoch - 3: + state.finalized_epoch = antepenultimate_justified_epoch + state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch)) + # The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source + if (bitfield >> 1) % 4 == 0b11 and antepenultimate_justified_epoch == current_epoch - 2: + state.finalized_epoch = antepenultimate_justified_epoch + state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch)) + # The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source + if (bitfield >> 0) % 8 == 0b111 and state.previous_justified_root == current_epoch - 2: + state.finalized_epoch = state.previous_justified_root + state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch)) + # The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source + if (bitfield >> 0) % 4 == 0b11 and state.previous_justified_root == current_epoch - 1: + state.finalized_epoch = state.previous_justified_root + state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch)) ``` #### Crosslinks @@ -1790,34 +1741,24 @@ def process_crosslinks(state: BeaconState) -> None: ) ``` -#### Eth1 data - -Run the following function: - -```python -def maybe_reset_eth1_period(state: BeaconState) -> None: - if (get_current_epoch(state) + 1) % EPOCHS_PER_ETH1_VOTING_PERIOD == 0: - for eth1_data_vote in state.eth1_data_votes: - # If a majority of all votes were for a particular eth1_data value, - # then set that as the new canonical value - if eth1_data_vote.vote_count * 2 > EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH: - state.latest_eth1_data = eth1_data_vote.eth1_data - state.eth1_data_votes = [] -``` - #### Rewards and penalties First, we define some additional helpers: ```python -def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei: - if get_previous_total_balance(state) == 0: +def get_base_reward_from_total_balance(state: BeaconState, total_balance: Gwei, index: ValidatorIndex) -> Gwei: + if total_balance == 0: return 0 - adjusted_quotient = integer_squareroot(get_previous_total_balance(state)) // BASE_REWARD_QUOTIENT + adjusted_quotient = integer_squareroot(total_balance) // BASE_REWARD_QUOTIENT return get_effective_balance(state, index) // adjusted_quotient // 5 ``` +```python +def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei: + return get_base_reward_from_total_balance(state, get_previous_total_balance(state), index) +``` + ```python def get_inactivity_penalty(state: BeaconState, index: ValidatorIndex, epochs_since_finality: int) -> Gwei: if epochs_since_finality <= 4: @@ -1858,10 +1799,9 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[ if index in get_unslashed_attesting_indices(state, state.previous_epoch_attestations): rewards[index] += base_reward * total_attesting_balance // total_balance # Inclusion speed bonus - rewards[index] += ( - base_reward * MIN_ATTESTATION_INCLUSION_DELAY // - inclusion_distance(state, index) - ) + earliest_attestation = get_earliest_attestation(state, state.previous_epoch_attestations, index) + inclusion_delay = earliest_attestation.inclusion_slot - earliest_attestation.data.slot + rewards[index] += base_reward * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_delay else: penalties[index] += base_reward # Expected FFG target @@ -1874,10 +1814,6 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[ rewards[index] += base_reward * matching_head_balance // total_balance else: penalties[index] += base_reward - # Proposer bonus - if index in get_unslashed_attesting_indices(state, state.previous_epoch_attestations): - proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index)) - rewards[proposer_index] += base_reward // PROPOSER_REWARD_QUOTIENT # Take away max rewards if we're not finalizing if epochs_since_finality > 4: penalties[index] += base_reward * 4 @@ -1914,14 +1850,8 @@ def apply_rewards(state: BeaconState) -> None: rewards1, penalties1 = get_justification_and_finalization_deltas(state) rewards2, penalties2 = get_crosslink_deltas(state) for i in range(len(state.validator_registry)): - set_balance( - state, - i, - max( - 0, - get_balance(state, i) + rewards1[i] + rewards2[i] - penalties1[i] - penalties2[i], - ), - ) + increase_balance(state, i, rewards1[i] + rewards2[i]) + decrease_balance(state, i, penalties1[i] + penalties2[i]) ``` #### Balance-driven status transitions @@ -2000,6 +1930,9 @@ Run the following function: def finish_epoch_update(state: BeaconState) -> None: current_epoch = get_current_epoch(state) next_epoch = current_epoch + 1 + # Reset eth1 data votes + if state.slot % SLOTS_PER_ETH1_VOTING_PERIOD == 0: + state.eth1_data_votes = [] # Set active index root index_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % LATEST_ACTIVE_INDEX_ROOTS_LENGTH state.latest_active_index_roots[index_root_position] = hash_tree_root( @@ -2047,7 +1980,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: # Save current block as the new latest block state.latest_block_header = get_temporary_block_header(block) # Verify proposer is not slashed - proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)] + proposer = state.validator_registry[get_beacon_proposer_index(state)] assert not proposer.slashed # Verify proposer signature assert bls_verify( @@ -2062,7 +1995,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: ```python def process_randao(state: BeaconState, block: BeaconBlock) -> None: - proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)] + proposer = state.validator_registry[get_beacon_proposer_index(state)] # Verify that the provided randao value is valid assert bls_verify( pubkey=proposer.pubkey, @@ -2081,13 +2014,9 @@ def process_randao(state: BeaconState, block: BeaconBlock) -> None: ```python def process_eth1_data(state: BeaconState, block: BeaconBlock) -> None: - for eth1_data_vote in state.eth1_data_votes: - # If someone else has already voted for the same hash, add to its counter - if eth1_data_vote.eth1_data == block.body.eth1_data: - eth1_data_vote.vote_count += 1 - return - # If we're seeing this hash for the first time, make a new counter - state.eth1_data_votes.append(Eth1DataVote(eth1_data=block.body.eth1_data, vote_count=1)) + state.eth1_data_votes.append(block.body.eth1_data) + if state.eth1_data_votes.count(block.body.eth1_data) * 2 > SLOTS_PER_ETH1_VOTING_PERIOD: + state.latest_eth1_data = block.body.eth1_data ``` #### Operations @@ -2210,6 +2139,18 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: state.previous_epoch_attestations.append(pending_attestation) ``` +Run `process_proposer_attestation_rewards(state)`. + +```python +def process_proposer_attestation_rewards(state: BeaconState) -> None: + proposer_index = get_beacon_proposer_index(state) + for pending_attestations in (state.previous_epoch_attestations, state.current_epoch_attestations): + for index in get_unslashed_attesting_indices(state, pending_attestations): + if get_earliest_attestation(state, pending_attestations, index).inclusion_slot == state.slot: + base_reward = get_base_reward_from_total_balance(state, get_current_total_balance(state), index) + increase_balance(state, proposer_index, base_reward // PROPOSER_REWARD_QUOTIENT) +``` + ##### Deposits Verify that `len(block.body.deposits) == min(MAX_DEPOSITS, latest_eth1_data.deposit_count - state.deposit_index)`. @@ -2356,7 +2297,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: # Process the transfer decrease_balance(state, transfer.sender, transfer.amount + transfer.fee) increase_balance(state, transfer.recipient, transfer.amount) - increase_balance(state, get_beacon_proposer_index(state, state.slot), transfer.fee) + increase_balance(state, get_beacon_proposer_index(state), transfer.fee) # Verify balances are not dust assert not (0 < get_balance(state, transfer.sender) < MIN_DEPOSIT_AMOUNT) assert not (0 < get_balance(state, transfer.recipient) < MIN_DEPOSIT_AMOUNT) diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index 6399a13c9..138e69fee 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -283,7 +283,7 @@ def process_custody_reveal(state: BeaconState, assert is_active_validator(revealer, get_current_epoch(state)) or revealer.exit_epoch > get_current_epoch(state) revealer.custody_reveal_index += 1 revealer.max_reveal_lateness = max(revealer.max_reveal_lateness, current_custody_period - reveal.period) - proposer_index = get_beacon_proposer_index(state, state.slot) + proposer_index = get_beacon_proposer_index(state) increase_balance(state, proposer_index, base_reward(state, index) // MINOR_REWARD_QUOTIENT) # Case 2: masked punitive early reveal @@ -323,7 +323,7 @@ def process_chunk_challenge(state: BeaconState, # Add new chunk challenge record state.custody_chunk_challenge_records.append(CustodyChunkChallengeRecord( challenge_index=state.custody_challenge_index, - challenger_index=get_beacon_proposer_index(state, state.slot), + challenger_index=get_beacon_proposer_index(state), responder_index=challenge.responder_index deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE, crosslink_data_root=challenge.attestation.data.crosslink_data_root, @@ -436,7 +436,7 @@ def process_chunk_challenge_response(state: BeaconState, # Clear the challenge state.custody_chunk_challenge_records.remove(challenge) # Reward the proposer - proposer_index = get_beacon_proposer_index(state, state.slot) + proposer_index = get_beacon_proposer_index(state) increase_balance(state, proposer_index, base_reward(state, index) // MINOR_REWARD_QUOTIENT) ``` diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 6a2094688..1e1a232fe 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -406,4 +406,4 @@ def is_valid_beacon_attestation(shard: Shard, ## Shard fork choice rule -The fork choice rule for any shard is LMD GHOST using the shard attestations of the persistent committee and the beacon chain attestations of the crosslink committee currently assigned to that shard, but instead of being rooted in the genesis it is rooted in the block referenced in the most recent accepted crosslink (i.e. `state.crosslinks[shard].shard_block_root`). Only blocks whose `beacon_chain_root` is the block in the main beacon chain at the specified `slot` should be considered. (If the beacon chain skips a slot, then the block at that slot is considered to be the block in the beacon chain at the highest slot lower than a slot.) +The fork choice rule for any shard is LMD GHOST using the shard attestations of the persistent committee and the beacon chain attestations of the crosslink committee currently assigned to that shard, but instead of being rooted in the genesis it is rooted in the block referenced in the most recent accepted crosslink (i.e. `state.crosslinks[shard].shard_block_root`). Only blocks whose `beacon_chain_root` is the block in the main beacon chain at the specified `slot` should be considered. (If the beacon chain skips a slot, then the block at that slot is considered to be the block in the beacon chain at the highest slot lower than that slot.) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 60d283664..f29e23390 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -369,24 +369,23 @@ def get_committee_assignment( return assignment ``` -A validator can use the following function to see if they are supposed to propose during their assigned committee slot. This function can only be run during the epoch of the slot in question and can not reliably be used to predict an epoch in advance. +A validator can use the following function to see if they are supposed to propose during their assigned committee slot. This function can only be run during the slot in question and can not reliably be used to predict in advance. ```python def is_proposer_at_slot(state: BeaconState, slot: Slot, validator_index: ValidatorIndex) -> bool: - current_epoch = get_current_epoch(state) - assert slot_to_epoch(slot) == current_epoch + assert state.slot == slot - return get_beacon_proposer_index(state, slot) == validator_index + return get_beacon_proposer_index(state) == validator_index ``` -_Note_: If a validator is assigned to the 0th slot of an epoch, the validator must run an empty slot transition from the previous epoch into the 0th slot of the epoch to be able to check if they are a proposer at that slot. +_Note_: To see if a validator is assigned to proposer during the slot, the validator must run an empty slot transition from the previous state to the current slot. ### Lookahead -The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing which must checked during the epoch in question. +The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing which must checked during the slot in question. `get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments which involves noting at which future slot one will have to attest and also which shard one should begin syncing (in phase 1+). diff --git a/test_libs/pyspec/README.md b/test_libs/pyspec/README.md index 08042e746..b3cab11d2 100644 --- a/test_libs/pyspec/README.md +++ b/test_libs/pyspec/README.md @@ -7,6 +7,7 @@ With this executable spec, test-generators can easily create test-vectors for client implementations, and the spec itself can be verified to be consistent and coherent, through sanity tests implemented with pytest. + ## Building All the dynamic parts of the spec can be build at once with `make pyspec`. @@ -15,12 +16,42 @@ Alternatively, you can build a sub-set of the pyspec: `make phase0`. Or, to build a single file, specify the path, e.g. `make test_libs/pyspec/eth2spec/phase0/spec.py` + +## Py-tests + +These tests are not intended for client-consumption. +These tests are sanity tests, to verify if the spec itself is consistent. + +### How to run tests + +#### Automated + +Run `make test` from the root of the spec repository. + +#### Manual + +From within the `pyspec` folder: + +Install dependencies: +```bash +python3 -m venv venv +. venv/bin/activate +pip3 install -r requirements.txt +``` +Note: make sure to run `make pyspec` from the root of the specs repository, + to build the parts of the pyspec module derived from the markdown specs. + +Run the tests: +``` +pytest -m minimal_config . +``` + + ## Contributing Contributions are welcome, but consider implementing your idea as part of the spec itself first. The pyspec is not a replacement. -If you see opportunity to include any of the `pyspec/eth2spec/utils/` code in the spec, - please submit an issue or PR. + ## License diff --git a/test_libs/pyspec/eth2spec/phase0/state_transition.py b/test_libs/pyspec/eth2spec/phase0/state_transition.py index 9be192c1f..f43e2791b 100644 --- a/test_libs/pyspec/eth2spec/phase0/state_transition.py +++ b/test_libs/pyspec/eth2spec/phase0/state_transition.py @@ -10,7 +10,8 @@ from typing import ( from .spec import ( BeaconState, BeaconBlock, - Slot + Slot, + process_proposer_attestation_rewards, ) @@ -51,6 +52,7 @@ def process_operations(state: BeaconState, block: BeaconBlock) -> None: spec.MAX_ATTESTATIONS, spec.process_attestation, ) + process_proposer_attestation_rewards(state) assert len(block.body.deposits) == expected_deposit_count(state) process_operation_type( @@ -91,7 +93,6 @@ def process_block(state: BeaconState, def process_epoch_transition(state: BeaconState) -> None: spec.update_justification_and_finalization(state) spec.process_crosslinks(state) - spec.maybe_reset_eth1_period(state) spec.apply_rewards(state) spec.process_balance_driven_status_transitions(state) spec.update_registry(state) @@ -112,4 +113,3 @@ def state_transition(state: BeaconState, verify_state_root: bool=False) -> BeaconState: state_transition_to(state, block.slot) process_block(state, block, verify_state_root) - diff --git a/test_libs/pyspec/requirements.txt b/test_libs/pyspec/requirements.txt index 78d41708d..3296ef807 100644 --- a/test_libs/pyspec/requirements.txt +++ b/test_libs/pyspec/requirements.txt @@ -2,3 +2,4 @@ eth-utils>=1.3.0,<2 eth-typing>=2.1.0,<3.0.0 pycryptodome==3.7.3 py_ecc>=1.6.0 +pytest>=3.6,<3.7 diff --git a/test_libs/pyspec/setup.py b/test_libs/pyspec/setup.py index b04847d37..1a131a417 100644 --- a/test_libs/pyspec/setup.py +++ b/test_libs/pyspec/setup.py @@ -3,6 +3,7 @@ from setuptools import setup, find_packages setup( name='pyspec', packages=find_packages(), + tests_require=["pytest"], install_requires=[ "eth-utils>=1.3.0,<2", "eth-typing>=2.1.0,<3.0.0", diff --git a/py_tests/phase0/__init__.py b/test_libs/pyspec/tests/README.md similarity index 100% rename from py_tests/phase0/__init__.py rename to test_libs/pyspec/tests/README.md diff --git a/test_libs/pyspec/tests/__init__.py b/test_libs/pyspec/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/py_tests/phase0/block_processing/test_process_attestation.py b/test_libs/pyspec/tests/block_processing/test_process_attestation.py similarity index 99% rename from py_tests/phase0/block_processing/test_process_attestation.py rename to test_libs/pyspec/tests/block_processing/test_process_attestation.py index 2e3f24dd6..d66434c2c 100644 --- a/py_tests/phase0/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/tests/block_processing/test_process_attestation.py @@ -11,7 +11,7 @@ from eth2spec.phase0.spec import ( process_attestation, slot_to_epoch, ) -from phase0.helpers import ( +from tests.helpers import ( build_empty_block_for_next_slot, get_valid_attestation, ) diff --git a/py_tests/phase0/block_processing/test_process_attester_slashing.py b/test_libs/pyspec/tests/block_processing/test_process_attester_slashing.py similarity index 97% rename from py_tests/phase0/block_processing/test_process_attester_slashing.py rename to test_libs/pyspec/tests/block_processing/test_process_attester_slashing.py index 4008e38a2..9417a6ffa 100644 --- a/py_tests/phase0/block_processing/test_process_attester_slashing.py +++ b/test_libs/pyspec/tests/block_processing/test_process_attester_slashing.py @@ -7,7 +7,7 @@ from eth2spec.phase0.spec import ( get_beacon_proposer_index, process_attester_slashing, ) -from phase0.helpers import ( +from tests.helpers import ( get_valid_attester_slashing, ) @@ -39,7 +39,7 @@ def run_attester_slashing_processing(state, attester_slashing, valid=True): get_balance(post_state, slashed_index) < get_balance(state, slashed_index) ) - proposer_index = get_beacon_proposer_index(state, state.slot) + proposer_index = get_beacon_proposer_index(state) # gained whistleblower reward assert ( get_balance(post_state, proposer_index) > diff --git a/py_tests/phase0/block_processing/test_process_block_header.py b/test_libs/pyspec/tests/block_processing/test_process_block_header.py similarity index 89% rename from py_tests/phase0/block_processing/test_process_block_header.py rename to test_libs/pyspec/tests/block_processing/test_process_block_header.py index a02cca656..b35b0a9c1 100644 --- a/py_tests/phase0/block_processing/test_process_block_header.py +++ b/test_libs/pyspec/tests/block_processing/test_process_block_header.py @@ -8,8 +8,9 @@ from eth2spec.phase0.spec import ( advance_slot, process_block_header, ) -from phase0.helpers import ( +from tests.helpers import ( build_empty_block_for_next_slot, + next_slot, ) # mark entire file as 'header' @@ -61,8 +62,12 @@ def test_invalid_previous_block_root(state): def test_proposer_slashed(state): + # use stub state to get proposer index of next slot + stub_state = deepcopy(state) + next_slot(stub_state) + proposer_index = get_beacon_proposer_index(stub_state) + # set proposer to slashed - proposer_index = get_beacon_proposer_index(state, state.slot + 1) state.validator_registry[proposer_index].slashed = True block = build_empty_block_for_next_slot(state) diff --git a/py_tests/phase0/block_processing/test_process_deposit.py b/test_libs/pyspec/tests/block_processing/test_process_deposit.py similarity index 99% rename from py_tests/phase0/block_processing/test_process_deposit.py rename to test_libs/pyspec/tests/block_processing/test_process_deposit.py index 31862c75d..2e6f4cd95 100644 --- a/py_tests/phase0/block_processing/test_process_deposit.py +++ b/test_libs/pyspec/tests/block_processing/test_process_deposit.py @@ -8,7 +8,7 @@ from eth2spec.phase0.spec import ( ZERO_HASH, process_deposit, ) -from phase0.helpers import ( +from tests.helpers import ( build_deposit, privkeys, pubkeys, diff --git a/py_tests/phase0/block_processing/test_process_proposer_slashing.py b/test_libs/pyspec/tests/block_processing/test_process_proposer_slashing.py similarity index 99% rename from py_tests/phase0/block_processing/test_process_proposer_slashing.py rename to test_libs/pyspec/tests/block_processing/test_process_proposer_slashing.py index d7afd2750..6f9dee262 100644 --- a/py_tests/phase0/block_processing/test_process_proposer_slashing.py +++ b/test_libs/pyspec/tests/block_processing/test_process_proposer_slashing.py @@ -7,7 +7,7 @@ from eth2spec.phase0.spec import ( get_current_epoch, process_proposer_slashing, ) -from phase0.helpers import ( +from tests.helpers import ( get_valid_proposer_slashing, ) diff --git a/py_tests/phase0/block_processing/test_voluntary_exit.py b/test_libs/pyspec/tests/block_processing/test_voluntary_exit.py similarity index 99% rename from py_tests/phase0/block_processing/test_voluntary_exit.py rename to test_libs/pyspec/tests/block_processing/test_voluntary_exit.py index 4404e7255..c58c5238a 100644 --- a/py_tests/phase0/block_processing/test_voluntary_exit.py +++ b/test_libs/pyspec/tests/block_processing/test_voluntary_exit.py @@ -9,7 +9,7 @@ from eth2spec.phase0.spec import ( get_current_epoch, process_voluntary_exit, ) -from phase0.helpers import ( +from tests.helpers import ( build_voluntary_exit, pubkey_to_privkey, ) diff --git a/py_tests/phase0/conftest.py b/test_libs/pyspec/tests/conftest.py similarity index 100% rename from py_tests/phase0/conftest.py rename to test_libs/pyspec/tests/conftest.py diff --git a/py_tests/phase0/helpers.py b/test_libs/pyspec/tests/helpers.py similarity index 98% rename from py_tests/phase0/helpers.py rename to test_libs/pyspec/tests/helpers.py index cef4f4827..9ef891219 100644 --- a/py_tests/phase0/helpers.py +++ b/test_libs/pyspec/tests/helpers.py @@ -2,6 +2,9 @@ from copy import deepcopy from py_ecc import bls +from eth2spec.phase0.state_transition import ( + state_transition, +) import eth2spec.phase0.spec as spec from eth2spec.utils.minimal_ssz import signing_root from eth2spec.phase0.spec import ( @@ -303,3 +306,8 @@ def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0) domain_type=spec.DOMAIN_ATTESTATION, ) ) + + +def next_slot(state): + block = build_empty_block_for_next_slot(state) + state_transition(state, block) diff --git a/py_tests/phase0/test_sanity.py b/test_libs/pyspec/tests/test_sanity.py similarity index 98% rename from py_tests/phase0/test_sanity.py rename to test_libs/pyspec/tests/test_sanity.py index cc987002e..b50d4a812 100644 --- a/py_tests/phase0/test_sanity.py +++ b/test_libs/pyspec/tests/test_sanity.py @@ -160,7 +160,7 @@ def test_attester_slashing(state): # lost whistleblower reward assert get_balance(test_state, validator_index) < get_balance(state, validator_index) - proposer_index = get_beacon_proposer_index(test_state, test_state.slot) + proposer_index = get_beacon_proposer_index(test_state) # gained whistleblower reward assert ( get_balance(test_state, proposer_index) > @@ -260,6 +260,9 @@ def test_attestation(state): assert len(test_state.current_epoch_attestations) == len(state.current_epoch_attestations) + 1 + proposer_index = get_beacon_proposer_index(test_state) + assert test_state.balances[proposer_index] > state.balances[proposer_index] + # # Epoch transition should move to previous_epoch_attestations #