diff --git a/.gitignore b/.gitignore index c6b39955f..16d39a434 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,10 @@ eth2.0-spec-tests/ # Dynamically built from Markdown spec test_libs/pyspec/eth2spec/phase0/spec.py test_libs/pyspec/eth2spec/phase1/spec.py + +# coverage reports +.htmlcov +.coverage + +# local CI testing output +test_libs/pyspec/test-reports diff --git a/Makefile b/Makefile index 7e2025418..30173e8e2 100644 --- a/Makefile +++ b/Makefile @@ -21,8 +21,11 @@ PY_SPEC_PHASE_1_DEPS = $(SPEC_DIR)/core/1_*.md PY_SPEC_ALL_TARGETS = $(PY_SPEC_PHASE_0_TARGETS) $(PY_SPEC_PHASE_1_TARGETS) +COV_HTML_OUT=.htmlcov +COV_INDEX_FILE=$(PY_SPEC_DIR)/$(COV_HTML_OUT)/index.html -.PHONY: clean all test citest lint gen_yaml_tests pyspec phase0 phase1 install_test install_deposit_contract_test test_deposit_contract compile_deposit_contract +.PHONY: clean all test citest lint gen_yaml_tests pyspec phase0 phase1 install_test open_cov \ + install_deposit_contract_test test_deposit_contract compile_deposit_contract all: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_DIR) $(YAML_TEST_TARGETS) @@ -32,6 +35,9 @@ clean: rm -rf $(PY_SPEC_DIR)/venv $(PY_SPEC_DIR)/.pytest_cache rm -rf $(PY_SPEC_ALL_TARGETS) rm -rf $(DEPOSIT_CONTRACT_DIR)/venv $(DEPOSIT_CONTRACT_DIR)/.pytest_cache + rm -rf $(PY_SPEC_DIR)/$(COV_HTML_OUT) + rm -rf $(PY_SPEC_DIR)/.coverage + rm -rf $(PY_SPEC_DIR)/test-reports # "make gen_yaml_tests" to run generators gen_yaml_tests: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_TARGETS) @@ -41,11 +47,15 @@ install_test: cd $(PY_SPEC_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements-testing.txt; test: $(PY_SPEC_ALL_TARGETS) - cd $(PY_SPEC_DIR); . venv/bin/activate; python -m pytest eth2spec + cd $(PY_SPEC_DIR); . venv/bin/activate; export PYTHONPATH="./"; \ + python -m pytest --cov=eth2spec.phase0.spec --cov=eth2spec.phase1.spec --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec citest: $(PY_SPEC_ALL_TARGETS) - cd $(PY_SPEC_DIR); mkdir -p test-reports/eth2spec; . venv/bin/activate; \ - python -m pytest --junitxml=test-reports/eth2spec/test_results_phase0.xml eth2spec + cd $(PY_SPEC_DIR); mkdir -p test-reports/eth2spec; . venv/bin/activate; \ + python -m pytest --junitxml=test-reports/eth2spec/test_results.xml eth2spec + +open_cov: + ((open "$(COV_INDEX_FILE)" || xdg-open "$(COV_INDEX_FILE)") &> /dev/null) & lint: $(PY_SPEC_ALL_TARGETS) cd $(PY_SPEC_DIR); . venv/bin/activate; \ diff --git a/configs/constant_presets/mainnet.yaml b/configs/constant_presets/mainnet.yaml index dca2a69ba..ed839fc7d 100644 --- a/configs/constant_presets/mainnet.yaml +++ b/configs/constant_presets/mainnet.yaml @@ -15,8 +15,6 @@ MAX_INDICES_PER_ATTESTATION: 4096 MIN_PER_EPOCH_CHURN_LIMIT: 4 # 2**16 (= 65,536) CHURN_LIMIT_QUOTIENT: 65536 -# Normalizes base rewards -BASE_REWARDS_PER_EPOCH: 5 # See issue 563 SHUFFLE_ROUND_COUNT: 90 @@ -25,8 +23,6 @@ SHUFFLE_ROUND_COUNT: 90 # --------------------------------------------------------------- # **TBD** DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890 -# 2**5 (= 32) -DEPOSIT_CONTRACT_TREE_DEPTH: 32 # Gwei values @@ -46,8 +42,6 @@ EFFECTIVE_BALANCE_INCREMENT: 1000000000 GENESIS_FORK_VERSION: 0x00000000 # 0, GENESIS_EPOCH is derived from this constant GENESIS_SLOT: 0 -# 2**64 - 1 -FAR_FUTURE_EPOCH: 18446744073709551615 BLS_WITHDRAWAL_PREFIX: 0 @@ -80,11 +74,11 @@ MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4 # State list lengths # --------------------------------------------------------------- # 2**13 (= 8,192) epochs ~36 days -LATEST_RANDAO_MIXES_LENGTH: 8192 +RANDAO_MIXES_LENGTH: 8192 # 2**13 (= 8,192) epochs ~36 days -LATEST_ACTIVE_INDEX_ROOTS_LENGTH: 8192 +ACTIVE_INDEX_ROOTS_LENGTH: 8192 # 2**13 (= 8,192) epochs ~36 days -LATEST_SLASHED_EXIT_LENGTH: 8192 +SLASHED_EXIT_LENGTH: 8192 # Reward and penalty quotients diff --git a/configs/constant_presets/minimal.yaml b/configs/constant_presets/minimal.yaml index fa337cfbf..d631300e2 100644 --- a/configs/constant_presets/minimal.yaml +++ b/configs/constant_presets/minimal.yaml @@ -14,8 +14,6 @@ MAX_INDICES_PER_ATTESTATION: 4096 MIN_PER_EPOCH_CHURN_LIMIT: 4 # 2**16 (= 65,536) CHURN_LIMIT_QUOTIENT: 65536 -# Normalizes base rewards -BASE_REWARDS_PER_EPOCH: 5 # [customized] Faster, but unsecure. SHUFFLE_ROUND_COUNT: 10 @@ -24,8 +22,6 @@ SHUFFLE_ROUND_COUNT: 10 # --------------------------------------------------------------- # **TBD** DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890 -# 2**5 (= 32) -DEPOSIT_CONTRACT_TREE_DEPTH: 32 # Gwei values @@ -45,8 +41,6 @@ EFFECTIVE_BALANCE_INCREMENT: 1000000000 GENESIS_FORK_VERSION: 0x00000000 # 0, GENESIS_EPOCH is derived from this constant GENESIS_SLOT: 0 -# 2**64 - 1 -FAR_FUTURE_EPOCH: 18446744073709551615 BLS_WITHDRAWAL_PREFIX: 0 @@ -81,11 +75,11 @@ EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 4096 # State list lengths # --------------------------------------------------------------- # [customized] smaller state -LATEST_RANDAO_MIXES_LENGTH: 64 +RANDAO_MIXES_LENGTH: 64 # [customized] smaller state -LATEST_ACTIVE_INDEX_ROOTS_LENGTH: 64 +ACTIVE_INDEX_ROOTS_LENGTH: 64 # [customized] smaller state -LATEST_SLASHED_EXIT_LENGTH: 64 +SLASHED_EXIT_LENGTH: 64 # Reward and penalty quotients diff --git a/scripts/build_spec.py b/scripts/build_spec.py index 1091e7bed..e20c82de3 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -144,9 +144,10 @@ def objects_to_spec(functions: Dict[str, str], """ Given all the objects that constitute a spec, combine them into a single pyfile. """ - new_type_definitions = \ - '\n'.join(['''%s = NewType('%s', %s)''' % (key, key, value) for key, value in new_types.items()]) - new_type_definitions += '\n' + '\n'.join(['Bytes%s = BytesN[%s]' % (n, n) for n in byte_types]) + new_type_definitions = '\n'.join(['Bytes%s = BytesN[%s]' % (n, n) for n in byte_types]) + new_type_definitions += '\n' + '\n'.join(['Hash = Bytes32', 'BLSPubkey = Bytes48', 'BLSSignature = Bytes96']) + new_type_definitions += \ + '\n' + '\n'.join(['''%s = NewType('%s', %s)''' % (key, key, value) for key, value in new_types.items()]) functions_spec = '\n\n'.join(functions.values()) constants_spec = '\n'.join(map(lambda x: '%s = %s' % (x, constants[x]), constants)) ssz_objects_instantiation_spec = '\n\n'.join(ssz_objects.values()) @@ -191,7 +192,7 @@ def dependency_order_ssz_objects(objects: Dict[str, str]) -> None: items = list(objects.items()) for key, value in items: dependencies = re.findall(r'(: [A-Z][\w[]*)', value) - dependencies = map(lambda x: re.sub(r'\W|Vector|List|Container|Dict|uint\d+|Bytes\d+|bytes', '', x), dependencies) + dependencies = map(lambda x: re.sub(r'\W|Vector|List|Container|Hash|BLSPubkey|BLSSignature|uint\d+|Bytes\d+|bytes', '', x), dependencies) for dep in dependencies: if dep in NEW_TYPES or len(dep) == 0: continue diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 0725f5211..b92e6b869 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -11,6 +11,7 @@ - [Notation](#notation) - [Terminology](#terminology) - [Constants](#constants) + - [Configuration](#configuration) - [Misc](#misc) - [Deposit contract](#deposit-contract) - [Gwei values](#gwei-values) @@ -20,7 +21,8 @@ - [Rewards and penalties](#rewards-and-penalties) - [Max operations per block](#max-operations-per-block) - [Signature domains](#signature-domains) - - [Data structures](#data-structures) + - [Custom types](#custom-types) + - [Containers](#containers) - [Misc dependencies](#misc-dependencies) - [`Fork`](#fork) - [`Validator`](#validator) @@ -45,7 +47,6 @@ - [`BeaconBlock`](#beaconblock) - [Beacon state](#beacon-state) - [`BeaconState`](#beaconstate) - - [Custom types](#custom-types) - [Helper functions](#helper-functions) - [`xor`](#xor) - [`hash`](#hash) @@ -152,7 +153,18 @@ Code snippets appearing in `this style` are to be interpreted as Python code. ## Constants -*Note*: The default mainnet values for the constants are included here for spec-design purposes. +The following values are (non-configurable) constants used throughout the specification. + +| Name | Value | +| - | - | +| `FAR_FUTURE_EPOCH` | `2**64 - 1` | +| `ZERO_HASH` | `b'\x00' * 32` | +| `BASE_REWARDS_PER_EPOCH` | `5` | +| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) | + +## Configuration + +*Note*: The default mainnet configuration values are included here for spec-design purposes. The different configurations for mainnet, testnets, and YAML-based testing can be found in the `configs/constant_presets/` directory. These configurations are updated for releases, but may be out of sync during `dev` changes. @@ -165,17 +177,10 @@ These configurations are updated for releases, but may be out of sync during `de | `MAX_INDICES_PER_ATTESTATION` | `2**12` (= 4,096) | | `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) | | `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) | -| `BASE_REWARDS_PER_EPOCH` | `5` | | `SHUFFLE_ROUND_COUNT` | `90` | * For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](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.) -### Deposit contract - -| Name | Value | -| - | - | -| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) | - ### Gwei values | Name | Value | Unit | @@ -191,15 +196,13 @@ These configurations are updated for releases, but may be out of sync during `de | - | - | | `GENESIS_SLOT` | `0` | | `GENESIS_EPOCH` | `0` | -| `FAR_FUTURE_EPOCH` | `2**64 - 1` | -| `ZERO_HASH` | `b'\x00' * 32` | | `BLS_WITHDRAWAL_PREFIX` | `0` | ### Time parameters | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `MIN_ATTESTATION_INCLUSION_DELAY` | `2**2` (= 4) | slots | 24 seconds | +| `MIN_ATTESTATION_INCLUSION_DELAY` | `2**0` (= 1) | slots | 6 seconds | | `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 | @@ -216,22 +219,21 @@ These configurations are updated for releases, but may be out of sync during `de | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | -| `LATEST_ACTIVE_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | -| `LATEST_SLASHED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | +| `RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | +| `ACTIVE_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | +| `SLASHED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | ### Rewards and penalties | Name | Value | | - | - | -| `BASE_REWARD_FACTOR` | `2**5` (= 32) | +| `BASE_REWARD_FACTOR` | `2**6` (= 64) | | `WHISTLEBLOWING_REWARD_QUOTIENT` | `2**9` (= 512) | | `PROPOSER_REWARD_QUOTIENT` | `2**3` (= 8) | | `INACTIVITY_PENALTY_QUOTIENT` | `2**25` (= 33,554,432) | | `MIN_SLASHING_PENALTY_QUOTIENT` | `2**5` (= 32) | -* **The `BASE_REWARD_FACTOR` is NOT final. Once all other protocol details are finalized, it will be adjusted to target a theoretical maximum total issuance of `2**21` ETH per year if `2**27` ETH is validating (and therefore `2**20` per year if `2**25` ETH is validating, etc.)** -* The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (~18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating [validators](#dfn-validator) to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline [validators](#dfn-validator) 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)`. +* 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](#dfn-validator) to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline [validators](#dfn-validator) 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)`. ### Max operations per block @@ -255,314 +257,6 @@ These configurations are updated for releases, but may be out of sync during `de | `DOMAIN_VOLUNTARY_EXIT` | `4` | | `DOMAIN_TRANSFER` | `5` | -## Data structures - -The following data structures are defined as [SimpleSerialize (SSZ)](../simple-serialize.md) objects. - -The types are defined topologically to aid in facilitating an executable version of the spec. - -### Misc dependencies - -#### `Fork` - -```python -class Fork(Container): - # Previous fork version - previous_version: Bytes4 - # Current fork version - current_version: Bytes4 - # Fork epoch number - epoch: uint64 -``` - -#### `Validator` - -```python -class Validator(Container): - # BLS public key - pubkey: Bytes48 - # Withdrawal credentials - withdrawal_credentials: Bytes32 - # Epoch when became eligible for activation - activation_eligibility_epoch: uint64 - # Epoch when validator activated - activation_epoch: uint64 - # Epoch when validator exited - exit_epoch: uint64 - # Epoch when validator is eligible to withdraw - withdrawable_epoch: uint64 - # Was the validator slashed - slashed: bool - # Effective balance - effective_balance: uint64 -``` - -#### `Crosslink` - -```python -class Crosslink(Container): - # Shard number - shard: uint64 - # Crosslinking data from epochs [start....end-1] - start_epoch: uint64 - end_epoch: uint64 - # Root of the previous crosslink - parent_root: Bytes32 - # Root of the crosslinked shard data since the previous crosslink - data_root: Bytes32 -``` - -#### `AttestationData` - -```python -class AttestationData(Container): - # LMD GHOST vote - beacon_block_root: Bytes32 - - # FFG vote - source_epoch: uint64 - source_root: Bytes32 - target_epoch: uint64 - target_root: Bytes32 - - # Crosslink vote - crosslink: Crosslink -``` - -#### `AttestationDataAndCustodyBit` - -```python -class AttestationDataAndCustodyBit(Container): - # Attestation data - data: AttestationData - # Custody bit - custody_bit: bool -``` - -#### `IndexedAttestation` - -```python -class IndexedAttestation(Container): - # Validator indices - custody_bit_0_indices: List[uint64] - custody_bit_1_indices: List[uint64] - # Attestation data - data: AttestationData - # Aggregate signature - signature: Bytes96 -``` - -#### `PendingAttestation` - -```python -class PendingAttestation(Container): - # Attester aggregation bitfield - aggregation_bitfield: bytes - # Attestation data - data: AttestationData - # Inclusion delay - inclusion_delay: uint64 - # Proposer index - proposer_index: uint64 -``` - -#### `Eth1Data` - -```python -class Eth1Data(Container): - # Block hash - block_hash: Bytes32 - # Root of the deposit tree - deposit_root: Bytes32 - # Total number of deposits - deposit_count: uint64 -``` - -#### `HistoricalBatch` - -```python -class HistoricalBatch(Container): - # Block roots - block_roots: Vector[Bytes32, SLOTS_PER_HISTORICAL_ROOT] - # State roots - state_roots: Vector[Bytes32, SLOTS_PER_HISTORICAL_ROOT] -``` - -#### `DepositData` - -```python -class DepositData(Container): - # BLS pubkey - pubkey: Bytes48 - # Withdrawal credentials - withdrawal_credentials: Bytes32 - # Amount in Gwei - amount: uint64 - # Container self-signature - signature: Bytes96 -``` - -#### `BeaconBlockHeader` - -```python -class BeaconBlockHeader(Container): - slot: uint64 - parent_root: Bytes32 - state_root: Bytes32 - body_root: Bytes32 - signature: Bytes96 -``` - -### Beacon operations - -#### `ProposerSlashing` - -```python -class ProposerSlashing(Container): - # Proposer index - proposer_index: uint64 - # First block header - header_1: BeaconBlockHeader - # Second block header - header_2: BeaconBlockHeader -``` - -#### `AttesterSlashing` - -```python -class AttesterSlashing(Container): - # First attestation - attestation_1: IndexedAttestation - # Second attestation - attestation_2: IndexedAttestation -``` - -#### `Attestation` - -```python -class Attestation(Container): - # Attester aggregation bitfield - aggregation_bitfield: bytes - # Attestation data - data: AttestationData - # Custody bitfield - custody_bitfield: bytes - # BLS aggregate signature - signature: Bytes96 -``` - -#### `Deposit` - -```python -class Deposit(Container): - # Branch in the deposit tree - proof: Vector[Bytes32, DEPOSIT_CONTRACT_TREE_DEPTH] - # Data - data: DepositData -``` - -#### `VoluntaryExit` - -```python -class VoluntaryExit(Container): - # Minimum epoch for processing exit - epoch: uint64 - # Index of the exiting validator - validator_index: uint64 - # Validator signature - signature: Bytes96 -``` - -#### `Transfer` - -```python -class Transfer(Container): - # Sender index - sender: uint64 - # Recipient index - recipient: uint64 - # Amount in Gwei - amount: uint64 - # Fee in Gwei for block proposer - fee: uint64 - # Inclusion slot - slot: uint64 - # Sender withdrawal pubkey - pubkey: Bytes48 - # Sender signature - signature: Bytes96 -``` - -### Beacon blocks - -#### `BeaconBlockBody` - -```python -class BeaconBlockBody(Container): - randao_reveal: Bytes96 - eth1_data: Eth1Data - graffiti: Bytes32 - proposer_slashings: List[ProposerSlashing] - attester_slashings: List[AttesterSlashing] - attestations: List[Attestation] - deposits: List[Deposit] - voluntary_exits: List[VoluntaryExit] - transfers: List[Transfer] -``` - -#### `BeaconBlock` - -```python -class BeaconBlock(Container): - # Header - slot: uint64 - parent_root: Bytes32 - state_root: Bytes32 - body: BeaconBlockBody - signature: Bytes96 -``` - -### Beacon state - -#### `BeaconState` - -```python -class BeaconState(Container): - # Misc - slot: uint64 - genesis_time: uint64 - fork: Fork # For versioning hard forks - # Validator registry - validator_registry: List[Validator] - balances: List[uint64] - # Randomness and committees - latest_randao_mixes: Vector[Bytes32, LATEST_RANDAO_MIXES_LENGTH] - latest_start_shard: uint64 - # Finality - previous_epoch_attestations: List[PendingAttestation] - current_epoch_attestations: List[PendingAttestation] - previous_justified_epoch: uint64 - current_justified_epoch: uint64 - previous_justified_root: Bytes32 - current_justified_root: Bytes32 - justification_bitfield: uint64 - finalized_epoch: uint64 - finalized_root: Bytes32 - # Recent state - current_crosslinks: Vector[Crosslink, SHARD_COUNT] - previous_crosslinks: Vector[Crosslink, SHARD_COUNT] - latest_block_roots: Vector[Bytes32, SLOTS_PER_HISTORICAL_ROOT] - latest_state_roots: Vector[Bytes32, SLOTS_PER_HISTORICAL_ROOT] - latest_active_index_roots: Vector[Bytes32, LATEST_ACTIVE_INDEX_ROOTS_LENGTH] - latest_slashed_balances: Vector[uint64, LATEST_SLASHED_EXIT_LENGTH] - latest_block_header: BeaconBlockHeader - historical_roots: List[Bytes32] - # Ethereum 1.0 chain data - latest_eth1_data: Eth1Data - eth1_data_votes: List[Eth1Data] - deposit_index: uint64 -``` - ## Custom types We define the following Python custom types for type hinting and readability: @@ -577,6 +271,264 @@ We define the following Python custom types for type hinting and readability: | `BLSPubkey` | `Bytes48` | a BLS12-381 public key | | `BLSSignature` | `Bytes96` | a BLS12-381 signature | +## Containers + +The following types are [SimpleSerialize (SSZ)](../simple-serialize.md) containers. + +*Note*: The definitions are ordered topologically to facilitate execution of the spec. + +### Misc dependencies + +#### `Fork` + +```python +class Fork(Container): + previous_version: Bytes4 + current_version: Bytes4 + epoch: Epoch # Epoch of latest fork +``` + +#### `Validator` + +```python +class Validator(Container): + pubkey: BLSPubkey + withdrawal_credentials: Hash # Commitment to pubkey for withdrawals and transfers + effective_balance: Gwei # Balance at stake + slashed: bool + # Status epochs + activation_eligibility_epoch: Epoch # When criteria for activation were met + activation_epoch: Epoch + exit_epoch: Epoch + withdrawable_epoch: Epoch # When validator can withdraw or transfer funds +``` + +#### `Crosslink` + +```python +class Crosslink(Container): + shard: Shard + parent_root: Hash + # Crosslinking data + start_epoch: Epoch + end_epoch: Epoch + data_root: Hash +``` + +#### `AttestationData` + +```python +class AttestationData(Container): + # LMD GHOST vote + beacon_block_root: Hash + # FFG vote + source_epoch: Epoch + source_root: Hash + target_epoch: Epoch + target_root: Hash + # Crosslink vote + crosslink: Crosslink +``` + +#### `AttestationDataAndCustodyBit` + +```python +class AttestationDataAndCustodyBit(Container): + data: AttestationData + custody_bit: bool # Challengeable bit for the custody of crosslink data +``` + +#### `IndexedAttestation` + +```python +class IndexedAttestation(Container): + custody_bit_0_indices: List[ValidatorIndex] # Indices with custody bit equal to 0 + custody_bit_1_indices: List[ValidatorIndex] # Indices with custody bit equal to 1 + data: AttestationData + signature: BLSSignature +``` + +#### `PendingAttestation` + +```python +class PendingAttestation(Container): + aggregation_bitfield: bytes # Bit set for every attesting participant within a committee + data: AttestationData + inclusion_delay: Slot + proposer_index: ValidatorIndex +``` + +#### `Eth1Data` + +```python +class Eth1Data(Container): + deposit_root: Hash + deposit_count: uint64 + block_hash: Hash +``` + +#### `HistoricalBatch` + +```python +class HistoricalBatch(Container): + block_roots: Vector[Hash, SLOTS_PER_HISTORICAL_ROOT] + state_roots: Vector[Hash, SLOTS_PER_HISTORICAL_ROOT] +``` + +#### `DepositData` + +```python +class DepositData(Container): + pubkey: BLSPubkey + withdrawal_credentials: Hash + amount: Gwei + signature: BLSSignature +``` + +#### `BeaconBlockHeader` + +```python +class BeaconBlockHeader(Container): + slot: Slot + parent_root: Hash + state_root: Hash + body_root: Hash + signature: BLSSignature +``` + +### Beacon operations + +#### `ProposerSlashing` + +```python +class ProposerSlashing(Container): + proposer_index: ValidatorIndex + header_1: BeaconBlockHeader + header_2: BeaconBlockHeader +``` + +#### `AttesterSlashing` + +```python +class AttesterSlashing(Container): + attestation_1: IndexedAttestation + attestation_2: IndexedAttestation +``` + +#### `Attestation` + +```python +class Attestation(Container): + aggregation_bitfield: bytes + data: AttestationData + custody_bitfield: bytes + signature: BLSSignature +``` + +#### `Deposit` + +```python +class Deposit(Container): + proof: Vector[Hash, DEPOSIT_CONTRACT_TREE_DEPTH] # Merkle path to deposit root + data: DepositData +``` + +#### `VoluntaryExit` + +```python +class VoluntaryExit(Container): + epoch: Epoch # Earliest epoch when voluntary exit can be processed + validator_index: ValidatorIndex + signature: BLSSignature +``` + +#### `Transfer` + +```python +class Transfer(Container): + sender: ValidatorIndex + recipient: ValidatorIndex + amount: Gwei + fee: Gwei + slot: Slot # Slot at which transfer must be processed + pubkey: BLSPubkey # Withdrawal pubkey + signature: BLSSignature # Signature checked against withdrawal pubkey +``` + +### Beacon blocks + +#### `BeaconBlockBody` + +```python +class BeaconBlockBody(Container): + randao_reveal: BLSSignature + eth1_data: Eth1Data # Eth1 data vote + graffiti: Bytes32 # Arbitrary data + # Operations + proposer_slashings: List[ProposerSlashing] + attester_slashings: List[AttesterSlashing] + attestations: List[Attestation] + deposits: List[Deposit] + voluntary_exits: List[VoluntaryExit] + transfers: List[Transfer] +``` + +#### `BeaconBlock` + +```python +class BeaconBlock(Container): + slot: Slot + parent_root: Hash + state_root: Hash + body: BeaconBlockBody + signature: BLSSignature +``` + +### Beacon state + +#### `BeaconState` + +```python +class BeaconState(Container): + # Versioning + genesis_time: uint64 + slot: Slot + fork: Fork + # History + latest_block_header: BeaconBlockHeader + block_roots: Vector[Hash, SLOTS_PER_HISTORICAL_ROOT] + state_roots: Vector[Hash, SLOTS_PER_HISTORICAL_ROOT] + historical_roots: List[Hash] + # Eth1 + eth1_data: Eth1Data + eth1_data_votes: List[Eth1Data] + eth1_deposit_index: uint64 + # Registry + validators: List[Validator] + balances: List[Gwei] + # Shuffling + start_shard: Shard + randao_mixes: Vector[Hash, RANDAO_MIXES_LENGTH] + active_index_roots: Vector[Hash, ACTIVE_INDEX_ROOTS_LENGTH] # Digests of the active registry, for light clients + # Slashings + slashed_balances: Vector[Gwei, SLASHED_EXIT_LENGTH] # Sums of the effective balances of slashed validators + # Attestations + previous_epoch_attestations: List[PendingAttestation] + current_epoch_attestations: List[PendingAttestation] + # Crosslinks + previous_crosslinks: Vector[Crosslink, SHARD_COUNT] # Previous epoch snapshot + current_crosslinks: Vector[Crosslink, SHARD_COUNT] + # Justification + previous_justified_epoch: Epoch # Previous epoch snapshot + previous_justified_root: Hash # Previous epoch snapshot + current_justified_epoch: Epoch + current_justified_root: Hash + justification_bitfield: uint64 # Bit set for every recent justified epoch + # Finality + finalized_epoch: Epoch + finalized_root: Hash +``` + ## Helper functions *Note*: The definitions below are for specification purposes and are not necessarily optimal implementations. @@ -596,11 +548,11 @@ The `hash` function is SHA256. ### `hash_tree_root` -`def hash_tree_root(object: SSZSerializable) -> Bytes32` is a function for hashing objects into a single root utilizing a hash tree structure. `hash_tree_root` is defined in the [SimpleSerialize spec](../simple-serialize.md#merkleization). +`def hash_tree_root(object: SSZSerializable) -> Hash` is a function for hashing objects into a single root utilizing a hash tree structure. `hash_tree_root` is defined in the [SimpleSerialize spec](../simple-serialize.md#merkleization). ### `signing_root` -`def signing_root(object: Container) -> Bytes32` is a function defined in the [SimpleSerialize spec](../simple-serialize.md#self-signed-containers) to compute signing messages. +`def signing_root(object: Container) -> Hash` is a function defined in the [SimpleSerialize spec](../simple-serialize.md#self-signed-containers) to compute signing messages. ### `bls_domain` @@ -681,7 +633,7 @@ def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> List[Valid """ Get active validator indices at ``epoch``. """ - return [i for i, v in enumerate(state.validator_registry) if is_active_validator(v, epoch)] + return [i for i, v in enumerate(state.validators) if is_active_validator(v, epoch)] ``` ### `increase_balance` @@ -726,7 +678,7 @@ def get_epoch_committee_count(state: BeaconState, epoch: Epoch) -> int: ```python def get_shard_delta(state: BeaconState, epoch: Epoch) -> int: """ - Return the number of shards to increment ``state.latest_start_shard`` during ``epoch``. + Return the number of shards to increment ``state.start_shard`` during ``epoch``. """ return min(get_epoch_committee_count(state, epoch), SHARD_COUNT - SHARD_COUNT // SLOTS_PER_EPOCH) ``` @@ -737,7 +689,7 @@ def get_shard_delta(state: BeaconState, epoch: Epoch) -> int: def get_epoch_start_shard(state: BeaconState, epoch: Epoch) -> Shard: assert epoch <= get_current_epoch(state) + 1 check_epoch = get_current_epoch(state) + 1 - shard = (state.latest_start_shard + get_shard_delta(state, get_current_epoch(state))) % SHARD_COUNT + shard = (state.start_shard + get_shard_delta(state, get_current_epoch(state))) % SHARD_COUNT while check_epoch > epoch: check_epoch -= 1 shard = (shard + SHARD_COUNT - get_shard_delta(state, check_epoch)) % SHARD_COUNT @@ -757,19 +709,19 @@ def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot ```python def get_block_root_at_slot(state: BeaconState, - slot: Slot) -> Bytes32: + slot: Slot) -> Hash: """ Return the block root at a recent ``slot``. """ assert slot < state.slot <= slot + SLOTS_PER_HISTORICAL_ROOT - return state.latest_block_roots[slot % SLOTS_PER_HISTORICAL_ROOT] + return state.block_roots[slot % SLOTS_PER_HISTORICAL_ROOT] ``` ### `get_block_root` ```python def get_block_root(state: BeaconState, - epoch: Epoch) -> Bytes32: + epoch: Epoch) -> Hash: """ Return the block root at a recent ``epoch``. """ @@ -780,37 +732,37 @@ def get_block_root(state: BeaconState, ```python def get_randao_mix(state: BeaconState, - epoch: Epoch) -> Bytes32: + epoch: Epoch) -> Hash: """ Return the randao mix at a recent ``epoch``. - ``epoch`` expected to be between (current_epoch - LATEST_RANDAO_MIXES_LENGTH, current_epoch]. + ``epoch`` expected to be between (current_epoch - RANDAO_MIXES_LENGTH, current_epoch]. """ - return state.latest_randao_mixes[epoch % LATEST_RANDAO_MIXES_LENGTH] + return state.randao_mixes[epoch % RANDAO_MIXES_LENGTH] ``` ### `get_active_index_root` ```python def get_active_index_root(state: BeaconState, - epoch: Epoch) -> Bytes32: + epoch: Epoch) -> Hash: """ Return the index root at a recent ``epoch``. ``epoch`` expected to be between - (current_epoch - LATEST_ACTIVE_INDEX_ROOTS_LENGTH + ACTIVATION_EXIT_DELAY, current_epoch + ACTIVATION_EXIT_DELAY]. + (current_epoch - ACTIVE_INDEX_ROOTS_LENGTH + ACTIVATION_EXIT_DELAY, current_epoch + ACTIVATION_EXIT_DELAY]. """ - return state.latest_active_index_roots[epoch % LATEST_ACTIVE_INDEX_ROOTS_LENGTH] + return state.active_index_roots[epoch % ACTIVE_INDEX_ROOTS_LENGTH] ``` ### `generate_seed` ```python def generate_seed(state: BeaconState, - epoch: Epoch) -> Bytes32: + epoch: Epoch) -> Hash: """ Generate a seed for the given ``epoch``. """ return hash( - get_randao_mix(state, epoch + LATEST_RANDAO_MIXES_LENGTH - MIN_SEED_LOOKAHEAD) + + get_randao_mix(state, epoch + RANDAO_MIXES_LENGTH - MIN_SEED_LOOKAHEAD) + get_active_index_root(state, epoch) + int_to_bytes(epoch, length=32) ) @@ -834,7 +786,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: while True: candidate_index = first_committee[(epoch + i) % len(first_committee)] random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32] - effective_balance = state.validator_registry[candidate_index].effective_balance + effective_balance = state.validators[candidate_index].effective_balance if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: return candidate_index i += 1 @@ -843,7 +795,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: ### `verify_merkle_branch` ```python -def verify_merkle_branch(leaf: Bytes32, proof: List[Bytes32], depth: int, index: int, root: Bytes32) -> bool: +def verify_merkle_branch(leaf: Hash, proof: List[Hash], depth: int, index: int, root: Hash) -> bool: """ Verify that the given ``leaf`` is on the merkle branch ``proof`` starting with the given ``root``. @@ -860,7 +812,7 @@ def verify_merkle_branch(leaf: Bytes32, proof: List[Bytes32], depth: int, index: ### `get_shuffled_index` ```python -def get_shuffled_index(index: ValidatorIndex, index_count: int, seed: Bytes32) -> ValidatorIndex: +def get_shuffled_index(index: ValidatorIndex, index_count: int, seed: Hash) -> ValidatorIndex: """ Return the shuffled validator index corresponding to ``seed`` (and ``index_count``). """ @@ -884,7 +836,7 @@ def get_shuffled_index(index: ValidatorIndex, index_count: int, seed: Bytes32) - ### `compute_committee` ```python -def compute_committee(indices: List[ValidatorIndex], seed: Bytes32, index: int, count: int) -> List[ValidatorIndex]: +def compute_committee(indices: List[ValidatorIndex], seed: Hash, index: int, count: int) -> List[ValidatorIndex]: start = (len(indices) * index) // count end = (len(indices) * (index + 1)) // count return [indices[get_shuffled_index(i, len(indices), seed)] for i in range(start, end)] @@ -937,7 +889,7 @@ def get_total_balance(state: BeaconState, indices: List[ValidatorIndex]) -> Gwei """ Return the combined effective balance of the ``indices``. (1 Gwei minimum to avoid divisions by zero.) """ - return max(sum([state.validator_registry[index].effective_balance for index in indices]), 1) + return max(sum([state.validators[index].effective_balance for index in indices]), 1) ``` ### `get_domain` @@ -1023,8 +975,8 @@ def validate_indexed_attestation(state: BeaconState, indexed_attestation: Indexe # Verify aggregate signature assert bls_verify_multiple( pubkeys=[ - bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in bit_0_indices]), - bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in bit_1_indices]), + bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_0_indices]), + bls_aggregate_pubkeys([state.validators[i].pubkey for i in bit_1_indices]), ], message_hashes=[ hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)), @@ -1113,14 +1065,14 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: Initiate the exit of the validator of the given ``index``. """ # Return if validator already initiated exit - validator = state.validator_registry[index] + validator = state.validators[index] if validator.exit_epoch != FAR_FUTURE_EPOCH: return # Compute exit queue epoch - exit_epochs = [v.exit_epoch for v in state.validator_registry if v.exit_epoch != FAR_FUTURE_EPOCH] + exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH] exit_queue_epoch = max(exit_epochs + [get_delayed_activation_exit_epoch(get_current_epoch(state))]) - exit_queue_churn = len([v for v in state.validator_registry if v.exit_epoch == exit_queue_epoch]) + exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_churn_limit(state): exit_queue_epoch += 1 @@ -1140,10 +1092,10 @@ def slash_validator(state: BeaconState, """ current_epoch = get_current_epoch(state) initiate_validator_exit(state, slashed_index) - state.validator_registry[slashed_index].slashed = True - state.validator_registry[slashed_index].withdrawable_epoch = current_epoch + LATEST_SLASHED_EXIT_LENGTH - slashed_balance = state.validator_registry[slashed_index].effective_balance - state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] += slashed_balance + state.validators[slashed_index].slashed = True + state.validators[slashed_index].withdrawable_epoch = current_epoch + SLASHED_EXIT_LENGTH + slashed_balance = state.validators[slashed_index].effective_balance + state.slashed_balances[current_epoch % SLASHED_EXIT_LENGTH] += slashed_balance proposer_index = get_beacon_proposer_index(state) if whistleblower_index is None: @@ -1201,7 +1153,7 @@ Let `genesis_state = get_genesis_beacon_state(genesis_deposits, genesis_time, ge def get_genesis_beacon_state(deposits: List[Deposit], genesis_time: int, genesis_eth1_data: Eth1Data) -> BeaconState: state = BeaconState( genesis_time=genesis_time, - latest_eth1_data=genesis_eth1_data, + eth1_data=genesis_eth1_data, latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())), ) @@ -1210,15 +1162,15 @@ def get_genesis_beacon_state(deposits: List[Deposit], genesis_time: int, genesis process_deposit(state, deposit) # Process genesis activations - for validator in state.validator_registry: + for validator in state.validators: if validator.effective_balance >= MAX_EFFECTIVE_BALANCE: validator.activation_eligibility_epoch = GENESIS_EPOCH validator.activation_epoch = GENESIS_EPOCH - # Populate latest_active_index_roots + # Populate active_index_roots genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH)) - for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH): - state.latest_active_index_roots[index] = genesis_active_index_root + for index in range(ACTIVE_INDEX_ROOTS_LENGTH): + state.active_index_roots[index] = genesis_active_index_root return state ``` @@ -1259,7 +1211,7 @@ def process_slots(state: BeaconState, slot: Slot) -> None: def process_slot(state: BeaconState) -> None: # Cache state root previous_state_root = hash_tree_root(state) - state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root + state.state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root # Cache latest block header state root if state.latest_block_header.state_root == ZERO_HASH: @@ -1267,7 +1219,7 @@ def process_slot(state: BeaconState) -> None: # Cache block root previous_block_root = signing_root(state.latest_block_header) - state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root + state.block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root ``` ### Epoch processing @@ -1322,7 +1274,7 @@ def get_unslashed_attesting_indices(state: BeaconState, output = set() for a in attestations: output = output.union(get_attesting_indices(state, a.data, a.aggregation_bitfield)) - return sorted(filter(lambda index: not state.validator_registry[index].slashed, list(output))) + return sorted(filter(lambda index: not state.validators[index].slashed, list(output))) ``` ```python @@ -1417,7 +1369,7 @@ def process_crosslinks(state: BeaconState) -> None: ```python def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei: total_balance = get_total_active_balance(state) - effective_balance = state.validator_registry[index].effective_balance + effective_balance = state.validators[index].effective_balance return effective_balance * BASE_REWARD_FACTOR // integer_squareroot(total_balance) // BASE_REWARDS_PER_EPOCH ``` @@ -1425,10 +1377,10 @@ def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei: def get_attestation_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: previous_epoch = get_previous_epoch(state) total_balance = get_total_active_balance(state) - rewards = [0 for _ in range(len(state.validator_registry))] - penalties = [0 for _ in range(len(state.validator_registry))] + rewards = [0 for _ in range(len(state.validators))] + penalties = [0 for _ in range(len(state.validators))] eligible_validator_indices = [ - index for index, v in enumerate(state.validator_registry) + index for index, v in enumerate(state.validators) if is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch) ] @@ -1451,8 +1403,10 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: a for a in matching_source_attestations if index in get_attesting_indices(state, a.data, a.aggregation_bitfield) ], key=lambda a: a.inclusion_delay) - rewards[attestation.proposer_index] += get_base_reward(state, index) // PROPOSER_REWARD_QUOTIENT - rewards[index] += get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // attestation.inclusion_delay + proposer_reward = get_base_reward(state, index) // PROPOSER_REWARD_QUOTIENT + rewards[attestation.proposer_index] += proposer_reward + max_attester_reward = get_base_reward(state, index) - proposer_reward + rewards[index] += max_attester_reward * MIN_ATTESTATION_INCLUSION_DELAY // attestation.inclusion_delay # Inactivity penalty finality_delay = previous_epoch - state.finalized_epoch @@ -1462,7 +1416,7 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: penalties[index] += BASE_REWARDS_PER_EPOCH * get_base_reward(state, index) if index not in matching_target_attesting_indices: penalties[index] += ( - state.validator_registry[index].effective_balance * finality_delay // INACTIVITY_PENALTY_QUOTIENT + state.validators[index].effective_balance * finality_delay // INACTIVITY_PENALTY_QUOTIENT ) return rewards, penalties @@ -1470,8 +1424,8 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: ```python def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: - rewards = [0 for index in range(len(state.validator_registry))] - penalties = [0 for index in range(len(state.validator_registry))] + rewards = [0 for index in range(len(state.validators))] + penalties = [0 for index in range(len(state.validators))] epoch = get_previous_epoch(state) for offset in range(get_epoch_committee_count(state, epoch)): shard = (get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT @@ -1495,7 +1449,7 @@ def process_rewards_and_penalties(state: BeaconState) -> None: rewards1, penalties1 = get_attestation_deltas(state) rewards2, penalties2 = get_crosslink_deltas(state) - for i in range(len(state.validator_registry)): + for i in range(len(state.validators)): increase_balance(state, i, rewards1[i] + rewards2[i]) decrease_balance(state, i, penalties1[i] + penalties2[i]) ``` @@ -1505,7 +1459,7 @@ def process_rewards_and_penalties(state: BeaconState) -> None: ```python def process_registry_updates(state: BeaconState) -> None: # Process activation eligibility and ejections - for index, validator in enumerate(state.validator_registry): + for index, validator in enumerate(state.validators): if ( validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and validator.effective_balance >= MAX_EFFECTIVE_BALANCE @@ -1517,13 +1471,13 @@ def process_registry_updates(state: BeaconState) -> None: # Queue validators eligible for activation and not dequeued for activation prior to finalized epoch activation_queue = sorted([ - index for index, validator in enumerate(state.validator_registry) if + index for index, validator in enumerate(state.validators) if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and validator.activation_epoch >= get_delayed_activation_exit_epoch(state.finalized_epoch) - ], key=lambda index: state.validator_registry[index].activation_eligibility_epoch) + ], key=lambda index: state.validators[index].activation_eligibility_epoch) # Dequeued validators for activation up to churn limit (without resetting activation epoch) for index in activation_queue[:get_churn_limit(state)]: - validator = state.validator_registry[index] + validator = state.validators[index] if validator.activation_epoch == FAR_FUTURE_EPOCH: validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) ``` @@ -1536,12 +1490,12 @@ def process_slashings(state: BeaconState) -> None: total_balance = get_total_active_balance(state) # Compute slashed balances in the current epoch - total_at_start = state.latest_slashed_balances[(current_epoch + 1) % LATEST_SLASHED_EXIT_LENGTH] - total_at_end = state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] + total_at_start = state.slashed_balances[(current_epoch + 1) % SLASHED_EXIT_LENGTH] + total_at_end = state.slashed_balances[current_epoch % SLASHED_EXIT_LENGTH] total_penalties = total_at_end - total_at_start - for index, validator in enumerate(state.validator_registry): - if validator.slashed and current_epoch == validator.withdrawable_epoch - LATEST_SLASHED_EXIT_LENGTH // 2: + for index, validator in enumerate(state.validators): + if validator.slashed and current_epoch == validator.withdrawable_epoch - SLASHED_EXIT_LENGTH // 2: penalty = max( validator.effective_balance * min(total_penalties * 3, total_balance) // total_balance, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT @@ -1559,29 +1513,29 @@ def process_final_updates(state: BeaconState) -> None: if (state.slot + 1) % SLOTS_PER_ETH1_VOTING_PERIOD == 0: state.eth1_data_votes = [] # Update effective balances with hysteresis - for index, validator in enumerate(state.validator_registry): + for index, validator in enumerate(state.validators): balance = state.balances[index] HALF_INCREMENT = EFFECTIVE_BALANCE_INCREMENT // 2 if balance < validator.effective_balance or validator.effective_balance + 3 * HALF_INCREMENT < balance: validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) # Update start shard - state.latest_start_shard = (state.latest_start_shard + get_shard_delta(state, current_epoch)) % SHARD_COUNT + state.start_shard = (state.start_shard + get_shard_delta(state, current_epoch)) % SHARD_COUNT # 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( + index_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % ACTIVE_INDEX_ROOTS_LENGTH + state.active_index_roots[index_root_position] = hash_tree_root( get_active_validator_indices(state, next_epoch + ACTIVATION_EXIT_DELAY) ) # Set total slashed balances - state.latest_slashed_balances[next_epoch % LATEST_SLASHED_EXIT_LENGTH] = ( - state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] + state.slashed_balances[next_epoch % SLASHED_EXIT_LENGTH] = ( + state.slashed_balances[current_epoch % SLASHED_EXIT_LENGTH] ) # Set randao mix - state.latest_randao_mixes[next_epoch % LATEST_RANDAO_MIXES_LENGTH] = get_randao_mix(state, current_epoch) + state.randao_mixes[next_epoch % RANDAO_MIXES_LENGTH] = get_randao_mix(state, current_epoch) # Set historical root accumulator if next_epoch % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0: historical_batch = HistoricalBatch( - block_roots=state.latest_block_roots, - state_roots=state.latest_state_roots, + block_roots=state.block_roots, + state_roots=state.state_roots, ) state.historical_roots.append(hash_tree_root(historical_batch)) # Rotate current/previous epoch attestations @@ -1614,7 +1568,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: body_root=hash_tree_root(block.body), ) # Verify proposer is not slashed - proposer = state.validator_registry[get_beacon_proposer_index(state)] + proposer = state.validators[get_beacon_proposer_index(state)] assert not proposer.slashed # Verify proposer signature assert bls_verify(proposer.pubkey, signing_root(block), block.signature, get_domain(state, DOMAIN_BEACON_PROPOSER)) @@ -1624,7 +1578,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None: ```python def process_randao(state: BeaconState, body: BeaconBlockBody) -> None: - proposer = state.validator_registry[get_beacon_proposer_index(state)] + proposer = state.validators[get_beacon_proposer_index(state)] # Verify that the provided randao value is valid assert bls_verify( proposer.pubkey, @@ -1633,7 +1587,7 @@ def process_randao(state: BeaconState, body: BeaconBlockBody) -> None: get_domain(state, DOMAIN_RANDAO), ) # Mix it in - state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = ( + state.randao_mixes[get_current_epoch(state) % RANDAO_MIXES_LENGTH] = ( xor(get_randao_mix(state, get_current_epoch(state)), hash(body.randao_reveal)) ) @@ -1645,7 +1599,7 @@ def process_randao(state: BeaconState, body: BeaconBlockBody) -> None: def process_eth1_data(state: BeaconState, body: BeaconBlockBody) -> None: state.eth1_data_votes.append(body.eth1_data) if state.eth1_data_votes.count(body.eth1_data) * 2 > SLOTS_PER_ETH1_VOTING_PERIOD: - state.latest_eth1_data = body.eth1_data + state.eth1_data = body.eth1_data ``` #### Operations @@ -1653,7 +1607,7 @@ def process_eth1_data(state: BeaconState, body: BeaconBlockBody) -> None: ```python def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: # Verify that outstanding deposits are processed up to the maximum number of deposits - assert len(body.deposits) == min(MAX_DEPOSITS, state.latest_eth1_data.deposit_count - state.deposit_index) + assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index) # Verify that there are no duplicate transfers assert len(body.transfers) == len(set(body.transfers)) @@ -1677,7 +1631,7 @@ def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSla """ Process ``ProposerSlashing`` operation. """ - proposer = state.validator_registry[proposer_slashing.proposer_index] + proposer = state.validators[proposer_slashing.proposer_index] # Verify that the epoch is the same assert slot_to_epoch(proposer_slashing.header_1.slot) == slot_to_epoch(proposer_slashing.header_2.slot) # But the headers are different @@ -1709,7 +1663,7 @@ def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSla attesting_indices_1 = attestation_1.custody_bit_0_indices + attestation_1.custody_bit_1_indices attesting_indices_2 = attestation_2.custody_bit_0_indices + attestation_2.custody_bit_1_indices for index in sorted(set(attesting_indices_1).intersection(attesting_indices_2)): - if is_slashable_validator(state.validator_registry[index], get_current_epoch(state)): + if is_slashable_validator(state.validators[index], get_current_epoch(state)): slash_validator(state, index) slashed_any = True assert slashed_any @@ -1767,16 +1721,16 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: leaf=hash_tree_root(deposit.data), proof=deposit.proof, depth=DEPOSIT_CONTRACT_TREE_DEPTH, - index=state.deposit_index, - root=state.latest_eth1_data.deposit_root, + index=state.eth1_deposit_index, + root=state.eth1_data.deposit_root, ) # Deposits must be processed in order - state.deposit_index += 1 + state.eth1_deposit_index += 1 pubkey = deposit.data.pubkey amount = deposit.data.amount - validator_pubkeys = [v.pubkey for v in state.validator_registry] + validator_pubkeys = [v.pubkey for v in state.validators] if pubkey not in validator_pubkeys: # Verify the deposit signature (proof of possession). # Invalid signatures are allowed by the deposit contract, @@ -1788,7 +1742,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: return # Add validator and balance entries - state.validator_registry.append(Validator( + state.validators.append(Validator( pubkey=pubkey, withdrawal_credentials=deposit.data.withdrawal_credentials, activation_eligibility_epoch=FAR_FUTURE_EPOCH, @@ -1811,7 +1765,7 @@ def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None: """ Process ``VoluntaryExit`` operation. """ - validator = state.validator_registry[exit.validator_index] + validator = state.validators[exit.validator_index] # Verify the validator is active assert is_active_validator(validator, get_current_epoch(state)) # Verify the validator has not yet exited @@ -1840,13 +1794,13 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: assert state.slot == transfer.slot # Sender must be not yet eligible for activation, withdrawn, or transfer balance over MAX_EFFECTIVE_BALANCE assert ( - state.validator_registry[transfer.sender].activation_eligibility_epoch == FAR_FUTURE_EPOCH or - get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or + state.validators[transfer.sender].activation_eligibility_epoch == FAR_FUTURE_EPOCH or + get_current_epoch(state) >= state.validators[transfer.sender].withdrawable_epoch or transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= state.balances[transfer.sender] ) # Verify that the pubkey is valid assert ( - state.validator_registry[transfer.sender].withdrawal_credentials == + state.validators[transfer.sender].withdrawal_credentials == int_to_bytes(BLS_WITHDRAWAL_PREFIX, length=1) + hash(transfer.pubkey)[1:] ) # Verify that the signature is valid diff --git a/specs/core/0_fork-choice.md b/specs/core/0_fork-choice.md index 1a1465d25..05735e081 100644 --- a/specs/core/0_fork-choice.md +++ b/specs/core/0_fork-choice.md @@ -8,7 +8,12 @@ - [Ethereum 2.0 Phase 0 -- Beacon Chain Fork Choice](#ethereum-20-phase-0----beacon-chain-fork-choice) - [Table of contents](#table-of-contents) - [Introduction](#introduction) +<<<<<<< HEAD - [Constants](#constants) +======= + - [Prerequisites](#prerequisites) + - [Configuration](#configuration) +>>>>>>> dev - [Time parameters](#time-parameters) - [Fork choice](#fork-choice) - [Containers](#containers) @@ -30,7 +35,7 @@ This document is the beacon chain fork choice spec, part of Ethereum 2.0 Phase 0. It assumes the [beacon chain state transition function spec](./0_beacon-chain.md). -## Constants +## Configuration ### Time parameters @@ -77,7 +82,13 @@ class Store(object): finalized_root: Bytes32 = ZERO_HASH ``` +<<<<<<< HEAD ### Helpers +======= +* The parent block with root `block.parent_root` has been processed and accepted. +* An Ethereum 1.0 block pointed to by the `state.eth1_data.block_hash` has been processed and accepted. +* The node's Unix time is greater than or equal to `state.genesis_time + block.slot * SECONDS_PER_SLOT`. +>>>>>>> dev #### `get_genesis_store` diff --git a/specs/core/1_custody-game.md b/specs/core/1_custody-game.md index 6c89ef853..75ab36609 100644 --- a/specs/core/1_custody-game.md +++ b/specs/core/1_custody-game.md @@ -339,7 +339,7 @@ def process_custody_key_reveal(state: BeaconState, Note that this function mutates ``state``. """ - revealer = state.validator_registry[reveal.revealer_index] + revealer = state.validators[reveal.revealer_index] epoch_to_sign = get_randao_epoch_for_custody_period(revealer.next_custody_reveal_period, reveal.revealed_index) assert revealer.next_custody_reveal_period < get_validators_custody_reveal_period(state, reveal.revealed_index) @@ -389,8 +389,8 @@ def process_early_derived_secret_reveal(state: BeaconState, Note that this function mutates ``state``. """ - revealed_validator = state.validator_registry[reveal.revealed_index] - masker = state.validator_registry[reveal.masker_index] + revealed_validator = state.validators[reveal.revealed_index] + masker = state.validators[reveal.masker_index] derived_secret_location = reveal.epoch % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS assert reveal.epoch >= get_current_epoch(state) + RANDAO_PENALTY_EPOCHS @@ -399,7 +399,7 @@ def process_early_derived_secret_reveal(state: BeaconState, assert reveal.revealed_index not in state.exposed_derived_secrets[derived_secret_location] # Verify signature correctness - masker = state.validator_registry[reveal.masker_index] + masker = state.validators[reveal.masker_index] pubkeys = [revealed_validator.pubkey, masker.pubkey] message_hashes = [ hash_tree_root(reveal.epoch), @@ -465,7 +465,7 @@ def process_chunk_challenge(state: BeaconState, validate_indexed_attestation(state, convert_to_indexed(state, challenge.attestation)) # Verify it is not too late to challenge assert slot_to_epoch(challenge.attestation.data.slot) >= get_current_epoch(state) - MAX_CHUNK_CHALLENGE_DELAY - responder = state.validator_registry[challenge.responder_index] + responder = state.validators[challenge.responder_index] assert responder.exit_epoch >= get_current_epoch(state) - MAX_CHUNK_CHALLENGE_DELAY # Verify the responder participated in the attestation attesters = get_attesting_indices(state, challenge.attestation.data, challenge.attestation.aggregation_bitfield) @@ -507,7 +507,7 @@ def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) -> None: # Verify challenge signature - challenger = state.validator_registry[challenge.challenger_index] + challenger = state.validators[challenge.challenger_index] assert bls_verify( pubkey=challenger.pubkey, message_hash=signing_root(challenge), @@ -520,7 +520,7 @@ def process_bit_challenge(state: BeaconState, attestation = challenge.attestation validate_indexed_attestation(state, convert_to_indexed(state, attestation)) # Verify the attestation is eligible for challenging - responder = state.validator_registry[challenge.responder_index] + responder = state.validators[challenge.responder_index] assert (slot_to_epoch(attestation.data.slot) + responder.max_reveal_lateness <= get_validators_custody_reveal_period(state, challenge.responder_index)) @@ -630,7 +630,7 @@ def process_bit_challenge_response(state: BeaconState, # Verify chunk index assert response.chunk_index < challenge.chunk_count # Verify responder has not been slashed - responder = state.validator_registry[challenge.responder_index] + responder = state.validators[challenge.responder_index] assert not responder.slashed # Verify the chunk matches the crosslink data root assert verify_merkle_branch( @@ -669,7 +669,7 @@ Run `process_reveal_deadlines(state)` immediately after `process_registry_update process_reveal_deadlines(state) # end insert @process_reveal_deadlines def process_reveal_deadlines(state: BeaconState) -> None: - for index, validator in enumerate(state.validator_registry): + for index, validator in enumerate(state.validators): deadline = validator.next_custody_reveal_period + (CUSTODY_RESPONSE_DEADLINE // EPOCHS_PER_CUSTODY_PERIOD) if get_validators_custody_reveal_period(state, index) > deadline: slash_validator(state, index) @@ -710,7 +710,7 @@ def after_process_final_updates(state: BeaconState) -> None: validator_indices_in_records = set( [record.challenger_index for record in records] + [record.responder_index for record in records] ) - for index, validator in enumerate(state.validator_registry): + for index, validator in enumerate(state.validators): if index not in validator_indices_in_records: if validator.exit_epoch != FAR_FUTURE_EPOCH and validator.withdrawable_epoch == FAR_FUTURE_EPOCH: validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 84af66ffb..99332f6ec 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -9,8 +9,9 @@ - [Ethereum 2.0 Phase 1 -- Shard Data Chains](#ethereum-20-phase-1----shard-data-chains) - [Table of contents](#table-of-contents) - [Introduction](#introduction) - - [Constants](#constants) + - [Configuration](#configuration) - [Misc](#misc) + - [Initial values](#initial-values) - [Time parameters](#time-parameters) - [Signature domains](#signature-domains) - [Data structures](#data-structures) @@ -38,7 +39,7 @@ This document describes the shard data layer and the shard fork choice rule in Phase 1 of Ethereum 2.0. -## Constants +## Configuration ### Misc @@ -46,6 +47,10 @@ This document describes the shard data layer and the shard fork choice rule in P | - | - | | `BYTES_PER_SHARD_BLOCK_BODY` | `2**14` (= 16,384) | | `MAX_SHARD_ATTESTIONS` | `2**4` (= 16) | + +### Initial values + +| Name | Value | | `PHASE_1_FORK_EPOCH` | **TBD** | | `PHASE_1_FORK_SLOT` | **TBD** | | `GENESIS_SHARD_SLOT` | 0 | @@ -157,9 +162,9 @@ def get_persistent_committee(state: BeaconState, later_start_epoch = epoch - (epoch % PERSISTENT_COMMITTEE_PERIOD) - PERSISTENT_COMMITTEE_PERIOD committee_count = max( - len(get_active_validator_indices(state.validator_registry, earlier_start_epoch)) // + len(get_active_validator_indices(state.validators, earlier_start_epoch)) // (SHARD_COUNT * TARGET_COMMITTEE_SIZE), - len(get_active_validator_indices(state.validator_registry, later_start_epoch)) // + len(get_active_validator_indices(state.validators, later_start_epoch)) // (SHARD_COUNT * TARGET_COMMITTEE_SIZE), ) + 1 @@ -189,7 +194,7 @@ def get_shard_proposer_index(state: BeaconState, # Search for an active proposer for index in persistent_committee: - if is_active_validator(state.validator_registry[index], get_current_epoch(state)): + if is_active_validator(state.validators[index], get_current_epoch(state)): return index # No block can be proposed if no validator is active @@ -223,7 +228,7 @@ def verify_shard_attestation_signature(state: BeaconState, pubkeys = [] for i, index in enumerate(persistent_committee): if get_bitfield_bit(attestation.aggregation_bitfield, i) == 0b1: - validator = state.validator_registry[index] + validator = state.validators[index] assert is_active_validator(validator, get_current_epoch(state)) pubkeys.append(validator.pubkey) assert bls_verify( @@ -324,7 +329,7 @@ def is_valid_shard_block(beacon_blocks: List[BeaconBlock], proposer_index = get_shard_proposer_index(beacon_state, candidate.shard, candidate.slot) assert proposer_index is not None assert bls_verify( - pubkey=beacon_state.validator_registry[proposer_index].pubkey, + pubkey=beacon_state.validators[proposer_index].pubkey, message_hash=signing_root(block), signature=candidate.signature, domain=get_domain(beacon_state, slot_to_epoch(candidate.slot), DOMAIN_SHARD_PROPOSER), @@ -394,7 +399,7 @@ def is_valid_beacon_attestation(shard: Shard, assert candidate.data.previous_attestation.epoch < slot_to_epoch(candidate.data.slot) # Check crosslink data root - start_epoch = beacon_state.latest_crosslinks[shard].epoch + start_epoch = beacon_state.crosslinks[shard].epoch end_epoch = min(slot_to_epoch(candidate.data.slot) - CROSSLINK_LOOKBACK, start_epoch + MAX_EPOCHS_PER_CROSSLINK) blocks = [] for slot in range(start_epoch * SLOTS_PER_EPOCH, end_epoch * SLOTS_PER_EPOCH): diff --git a/specs/light_client/sync_protocol.md b/specs/light_client/sync_protocol.md index 8501c5869..eb4bf09eb 100644 --- a/specs/light_client/sync_protocol.md +++ b/specs/light_client/sync_protocol.md @@ -31,7 +31,7 @@ We define an "expansion" of an object as an object where a field in an object th We define two expansions: -* `ExtendedBeaconState`, which is identical to a `BeaconState` except `latest_active_index_roots: List[Bytes32]` is replaced by `latest_active_indices: List[List[ValidatorIndex]]`, where `BeaconState.latest_active_index_roots[i] = hash_tree_root(ExtendedBeaconState.latest_active_indices[i])`. +* `ExtendedBeaconState`, which is identical to a `BeaconState` except `active_index_roots: List[Bytes32]` is replaced by `active_indices: List[List[ValidatorIndex]]`, where `BeaconState.active_index_roots[i] = hash_tree_root(ExtendedBeaconState.active_indices[i])`. * `ExtendedBeaconBlock`, which is identical to a `BeaconBlock` except `state_root` is replaced with the corresponding `state: ExtendedBeaconState`. ### `get_active_validator_indices` @@ -40,10 +40,10 @@ Note that there is now a new way to compute `get_active_validator_indices`: ```python def get_active_validator_indices(state: ExtendedBeaconState, epoch: Epoch) -> List[ValidatorIndex]: - return state.latest_active_indices[epoch % LATEST_ACTIVE_INDEX_ROOTS_LENGTH] + return state.active_indices[epoch % ACTIVE_INDEX_ROOTS_LENGTH] ``` -Note that it takes `state` instead of `state.validator_registry` as an argument. This does not affect its use in `get_shuffled_committee`, because `get_shuffled_committee` has access to the full `state` as one of its arguments. +Note that it takes `state` instead of `state.validators` as an argument. This does not affect its use in `get_shuffled_committee`, because `get_shuffled_committee` has access to the full `state` as one of its arguments. ### `MerklePartial` @@ -85,7 +85,7 @@ def get_period_data(block: ExtendedBeaconBlock, shard_id: Shard, later: bool) -> return PeriodData( validator_count, generate_seed(block.state, period_start), - [block.state.validator_registry[i] for i in indices], + [block.state.validators[i] for i in indices], ) ``` diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index b81f78408..be154075c 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -95,8 +95,8 @@ Since some clients are waiting for `libp2p` implementations in their respective ( network_id: uint8 chain_id: uint64 - latest_finalized_root: bytes32 - latest_finalized_epoch: uint64 + finalized_root: bytes32 + finalized_epoch: uint64 best_root: bytes32 best_slot: uint64 ) @@ -107,7 +107,7 @@ Clients exchange `hello` messages upon connection, forming a two-phase handshake Clients SHOULD immediately disconnect from one another following the handshake above under the following conditions: 1. If `network_id` belongs to a different chain, since the client definitionally cannot sync with this client. -2. If the `latest_finalized_root` shared by the peer is not in the client's chain at the expected epoch. For example, if Peer 1 in the diagram below has `(root, epoch)` of `(A, 5)` and Peer 2 has `(B, 3)`, Peer 1 would disconnect because it knows that `B` is not the root in their chain at epoch 3: +2. If the `finalized_root` shared by the peer is not in the client's chain at the expected epoch. For example, if Peer 1 in the diagram below has `(root, epoch)` of `(A, 5)` and Peer 2 has `(B, 3)`, Peer 1 would disconnect because it knows that `B` is not the root in their chain at epoch 3: ``` Root A @@ -136,7 +136,7 @@ Root B ^ +---+ ``` -Once the handshake completes, the client with the higher `latest_finalized_epoch` or `best_slot` (if the clients have equal `latest_finalized_epoch`s) SHOULD request beacon block roots from its counterparty via `beacon_block_roots` (i.e. RPC method `10`). +Once the handshake completes, the client with the higher `finalized_epoch` or `best_slot` (if the clients have equal `finalized_epoch`s) SHOULD request beacon block roots from its counterparty via `beacon_block_roots` (i.e. RPC method `10`). ### Goodbye diff --git a/specs/test_formats/operations/README.md b/specs/test_formats/operations/README.md index 32cf880b3..cc7e43f3f 100644 --- a/specs/test_formats/operations/README.md +++ b/specs/test_formats/operations/README.md @@ -14,7 +14,7 @@ post: BeaconState -- state after applying the operation. No ## Condition -A handler of the `operations` test-runner should process these cases, +A handler of the `operations` test-runner should process these cases, calling the corresponding processing implementation. Operations: @@ -29,7 +29,7 @@ Operations: | `transfer` | `Transfer` | `transfer` | `process_transfer(state, transfer)` | | `voluntary_exit` | `VoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` | -Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here. +Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here. The resulting state should match the expected `post` state, or if the `post` state is left blank, the handler should reject the input operation as invalid. diff --git a/specs/test_formats/sanity/slots.md b/specs/test_formats/sanity/slots.md index 81866d47b..eb0f336b4 100644 --- a/specs/test_formats/sanity/slots.md +++ b/specs/test_formats/sanity/slots.md @@ -8,7 +8,7 @@ Sanity tests to cover a series of one or more empty-slot transitions being proce description: string -- description of test case, purely for debugging purposes bls_setting: int -- see general test-format spec. pre: BeaconState -- state before running through the transitions. -slots: N -- amount of slots to process, N being a positive numer. +slots: N -- amount of slots to process, N being a positive number. post: BeaconState -- state after applying all the transitions. ``` diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 29724da51..ecbc5af27 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -101,15 +101,15 @@ To submit a deposit: * Let `signature` be the result of `bls_sign` of the `signing_root(deposit_data)` with `domain=bls_domain(DOMAIN_DEPOSIT)`. (Deposits are valid regardless of fork version, `bls_domain` will default to zeroes there). * Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `def deposit(pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96])` along with a deposit of `amount` Gwei. -*Note*: Deposits made for the same `pubkey` are treated as for the same validator. A singular `Validator` will be added to `state.validator_registry` with each additional deposit amount added to the validator's balance. A validator can only be activated when total deposits for the validator pubkey meet or exceed `MAX_EFFECTIVE_BALANCE`. +*Note*: Deposits made for the same `pubkey` are treated as for the same validator. A singular `Validator` will be added to `state.validators` with each additional deposit amount added to the validator's balance. A validator can only be activated when total deposits for the validator pubkey meet or exceed `MAX_EFFECTIVE_BALANCE`. ### Process deposit -Deposits cannot be processed into the beacon chain until the Eth 1.0 block in which they were deposited or any of its descendants is added to the beacon chain `state.eth1_data`. This takes _a minimum_ of `ETH1_FOLLOW_DISTANCE` Eth 1.0 blocks (~4 hours) plus `ETH1_DATA_VOTING_PERIOD` epochs (~1.7 hours). Once the requisite Eth 1.0 data is added, the deposit will normally be added to a beacon chain block and processed into the `state.validator_registry` within an epoch or two. The validator is then in a queue to be activated. +Deposits cannot be processed into the beacon chain until the Eth 1.0 block in which they were deposited or any of its descendants is added to the beacon chain `state.eth1_data`. This takes _a minimum_ of `ETH1_FOLLOW_DISTANCE` Eth 1.0 blocks (~4 hours) plus `ETH1_DATA_VOTING_PERIOD` epochs (~1.7 hours). Once the requisite Eth 1.0 data is added, the deposit will normally be added to a beacon chain block and processed into the `state.validators` within an epoch or two. The validator is then in a queue to be activated. ### Validator index -Once a validator has been processed and added to the beacon state's `validator_registry`, the validator's `validator_index` is defined by the index into the registry at which the [`ValidatorRecord`](../core/0_beacon-chain.md#validator) contains the `pubkey` specified in the validator's deposit. A validator's `validator_index` is guaranteed to not change from the time of initial deposit until the validator exits and fully withdraws. This `validator_index` is used throughout the specification to dictate validator roles and responsibilities at any point and should be stored locally. +Once a validator has been processed and added to the beacon state's `validators`, the validator's `validator_index` is defined by the index into the registry at which the [`ValidatorRecord`](../core/0_beacon-chain.md#validator) contains the `pubkey` specified in the validator's deposit. A validator's `validator_index` is guaranteed to not change from the time of initial deposit until the validator exits and fully withdraws. This `validator_index` is used throughout the specification to dictate validator roles and responsibilities at any point and should be stored locally. ### Activation @@ -118,7 +118,7 @@ In normal operation, the validator is quickly activated at which point the valid The function [`is_active_validator`](../core/0_beacon-chain.md#is_active_validator) can be used to check if a validator is active during a given epoch. Usage is as follows: ```python -validator = state.validator_registry[validator_index] +validator = state.validators[validator_index] is_active = is_active_validator(validator, get_current_epoch(state)) ``` @@ -221,10 +221,10 @@ epoch_signature = bls_sign( ##### Eth1 Data -`block.eth1_data` is a mechanism used by block proposers vote on a recent Ethereum 1.0 block hash and an associated deposit root found in the Ethereum 1.0 deposit contract. When consensus is formed, `state.latest_eth1_data` is updated, and validator deposits up to this root can be processed. The deposit root can be calculated by calling the `get_deposit_root()` function of the deposit contract using the post-state of the block hash. +`block.eth1_data` is a mechanism used by block proposers vote on a recent Ethereum 1.0 block hash and an associated deposit root found in the Ethereum 1.0 deposit contract. When consensus is formed, `state.eth1_data` is updated, and validator deposits up to this root can be processed. The deposit root can be calculated by calling the `get_deposit_root()` function of the deposit contract using the post-state of the block hash. * Let `D` be the list of `Eth1DataVote` objects `vote` in `state.eth1_data_votes` where: - * `vote.eth1_data.block_hash` is the hash of an Eth 1.0 block that is (i) part of the canonical chain, (ii) >= `ETH1_FOLLOW_DISTANCE` blocks behind the head, and (iii) newer than `state.latest_eth1_data.block_hash`. + * `vote.eth1_data.block_hash` is the hash of an Eth 1.0 block that is (i) part of the canonical chain, (ii) >= `ETH1_FOLLOW_DISTANCE` blocks behind the head, and (iii) newer than `state.eth1_data.block_hash`. * `vote.eth1_data.deposit_count` is the deposit count of the Eth 1.0 deposit contract at the block defined by `vote.eth1_data.block_hash`. * `vote.eth1_data.deposit_root` is the deposit root of the Eth 1.0 deposit contract at the block defined by `vote.eth1_data.block_hash`. * If `D` is empty: @@ -267,9 +267,9 @@ Up to `MAX_ATTESTATIONS` aggregate attestations can be included in the `block`. ##### Deposits -If there are any unprocessed deposits for the existing `state.latest_eth1_data` (i.e. `state.latest_eth1_data.deposit_count > state.deposit_index`), then pending deposits _must_ be added to the block. The expected number of deposits is exactly `min(MAX_DEPOSITS, latest_eth1_data.deposit_count - state.deposit_index)`. These [`deposits`](../core/0_beacon-chain.md#deposit) are constructed from the `Deposit` logs from the [Eth 1.0 deposit contract](../core/0_deposit-contract) and must be processed in sequential order. The deposits included in the `block` must satisfy the verification conditions found in [deposits processing](../core/0_beacon-chain.md#deposits). +If there are any unprocessed deposits for the existing `state.eth1_data` (i.e. `state.eth1_data.deposit_count > state.eth1_deposit_index`), then pending deposits _must_ be added to the block. The expected number of deposits is exactly `min(MAX_DEPOSITS, eth1_data.deposit_count - state.eth1_deposit_index)`. These [`deposits`](../core/0_beacon-chain.md#deposit) are constructed from the `Deposit` logs from the [Eth 1.0 deposit contract](../core/0_deposit-contract) and must be processed in sequential order. The deposits included in the `block` must satisfy the verification conditions found in [deposits processing](../core/0_beacon-chain.md#deposits). -The `proof` for each deposit must be constructed against the deposit root contained in `state.latest_eth1_data` rather than the deposit root at the time the deposit was initially logged from the 1.0 chain. This entails storing a full deposit merkle tree locally and computing updated proofs against the `latest_eth1_data.deposit_root` as needed. See [`minimal_merkle.py`](https://github.com/ethereum/research/blob/master/spec_pythonizer/utils/merkle_minimal.py) for a sample implementation. +The `proof` for each deposit must be constructed against the deposit root contained in `state.eth1_data` rather than the deposit root at the time the deposit was initially logged from the 1.0 chain. This entails storing a full deposit merkle tree locally and computing updated proofs against the `eth1_data.deposit_root` as needed. See [`minimal_merkle.py`](https://github.com/ethereum/research/blob/master/spec_pythonizer/utils/merkle_minimal.py) for a sample implementation. ##### Voluntary exits diff --git a/test_generators/bls/main.py b/test_generators/bls/main.py index ef80635de..8a6a7dafe 100644 --- a/test_generators/bls/main.py +++ b/test_generators/bls/main.py @@ -12,8 +12,15 @@ from gen_base import gen_runner, gen_suite, gen_typing from py_ecc import bls -def int_to_hex(n: int) -> str: - return '0x' + int_to_big_endian(n).hex() +F2Q_COEFF_LEN = 48 +G2_COMPRESSED_Z_LEN = 48 + + +def int_to_hex(n: int, byte_length: int=None) -> str: + byte_value = int_to_big_endian(n) + if byte_length: + byte_value = byte_value.rjust(byte_length, b'\x00') + return '0x' + byte_value.hex() def hex_to_int(x: str) -> int: @@ -58,8 +65,8 @@ def hash_message(msg: bytes, """ return [ [ - int_to_hex(fq2.coeffs[0]), - int_to_hex(fq2.coeffs[1]), + int_to_hex(fq2.coeffs[0], F2Q_COEFF_LEN), + int_to_hex(fq2.coeffs[1], F2Q_COEFF_LEN), ] for fq2 in bls.utils.hash_to_G2(msg, domain) ] @@ -75,8 +82,7 @@ def hash_message_compressed(msg: bytes, domain: int) -> Tuple[str, str]: - Message hash as a compressed G2 point """ z1, z2 = bls.utils.compress_G2(bls.utils.hash_to_G2(msg, domain)) - return [int_to_hex(z1), int_to_hex(z2)] - + return [int_to_hex(z1, G2_COMPRESSED_Z_LEN), int_to_hex(z2, G2_COMPRESSED_Z_LEN)] @to_tuple diff --git a/test_libs/pyspec/README.md b/test_libs/pyspec/README.md index 2c2226ee7..53750517d 100644 --- a/test_libs/pyspec/README.md +++ b/test_libs/pyspec/README.md @@ -28,7 +28,7 @@ These tests are sanity tests, to verify if the spec itself is consistent. #### Automated -Run `make test` from the root of the specs repository. +Run `make test` from the root of the specs repository (after running `make install_test` if have not before). #### Manual @@ -50,6 +50,10 @@ pytest --config=minimal eth2spec ``` Note the package-name, this is to locate the tests. +### How to view code coverage report + +Run `make open_cov` from the root of the specs repository after running `make test` to open the html code coverage report. + ## Contributing diff --git a/test_libs/pyspec/eth2spec/fuzzing/__init__.py b/test_libs/pyspec/eth2spec/fuzzing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_libs/pyspec/eth2spec/fuzzing/decoder.py b/test_libs/pyspec/eth2spec/fuzzing/decoder.py new file mode 100644 index 000000000..a5d3dfd97 --- /dev/null +++ b/test_libs/pyspec/eth2spec/fuzzing/decoder.py @@ -0,0 +1,84 @@ +from eth2spec.utils.ssz import ssz_typing as spec_ssz +import ssz + + +def translate_typ(typ) -> ssz.BaseSedes: + """ + Translates a spec type to a Py-SSZ type description (sedes). + :param typ: The spec type, a class. + :return: The Py-SSZ equivalent. + """ + if spec_ssz.is_container_type(typ): + return ssz.Container( + [translate_typ(field_typ) for (field_name, field_typ) in typ.get_fields()]) + elif spec_ssz.is_bytesn_type(typ): + return ssz.ByteVector(typ.length) + elif spec_ssz.is_bytes_type(typ): + return ssz.ByteList() + elif spec_ssz.is_vector_type(typ): + return ssz.Vector(translate_typ(spec_ssz.read_vector_elem_type(typ)), typ.length) + elif spec_ssz.is_list_type(typ): + return ssz.List(translate_typ(spec_ssz.read_list_elem_type(typ))) + elif spec_ssz.is_bool_type(typ): + return ssz.boolean + elif spec_ssz.is_uint_type(typ): + size = spec_ssz.uint_byte_size(typ) + if size == 1: + return ssz.uint8 + elif size == 2: + return ssz.uint16 + elif size == 4: + return ssz.uint32 + elif size == 8: + return ssz.uint64 + elif size == 16: + return ssz.uint128 + elif size == 32: + return ssz.uint256 + else: + raise TypeError("invalid uint size") + else: + raise TypeError("Type not supported: {}".format(typ)) + + +def translate_value(value, typ): + """ + Translate a value output from Py-SSZ deserialization into the given spec type. + :param value: The PySSZ value + :param typ: The type from the spec to translate into + :return: the translated value + """ + if spec_ssz.is_uint_type(typ): + size = spec_ssz.uint_byte_size(typ) + if size == 1: + return spec_ssz.uint8(value) + elif size == 2: + return spec_ssz.uint16(value) + elif size == 4: + return spec_ssz.uint32(value) + elif size == 8: + # uint64 is default (TODO this is changing soon) + return value + elif size == 16: + return spec_ssz.uint128(value) + elif size == 32: + return spec_ssz.uint256(value) + else: + raise TypeError("invalid uint size") + elif spec_ssz.is_list_type(typ): + elem_typ = spec_ssz.read_elem_type(typ) + return [translate_value(elem, elem_typ) for elem in value] + elif spec_ssz.is_bool_type(typ): + return value + elif spec_ssz.is_vector_type(typ): + elem_typ = spec_ssz.read_elem_type(typ) + return typ(*(translate_value(elem, elem_typ) for elem in value)) + elif spec_ssz.is_bytesn_type(typ): + return typ(value) + elif spec_ssz.is_bytes_type(typ): + return value + elif spec_ssz.is_container_type(typ): + return typ(**{f_name: translate_value(f_val, f_typ) for (f_name, f_val, f_typ) + in zip(typ.get_field_names(), value, typ.get_field_types())}) + else: + raise TypeError("Type not supported: {}".format(typ)) diff --git a/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py b/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py new file mode 100644 index 000000000..26ee6e913 --- /dev/null +++ b/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py @@ -0,0 +1,33 @@ +from eth2spec.fuzzing.decoder import translate_typ, translate_value +from eth2spec.phase0 import spec +from eth2spec.utils.ssz import ssz_impl as spec_ssz_impl +from random import Random +from eth2spec.debug import random_value + + +def test_decoder(): + rng = Random(123) + + # check these types only, Block covers a lot of operation types already. + for typ in [spec.BeaconBlock, spec.BeaconState, spec.IndexedAttestation, spec.AttestationDataAndCustodyBit]: + # create a random pyspec value + original = random_value.get_random_ssz_object(rng, typ, 100, 10, + mode=random_value.RandomizationMode.mode_random, + chaos=True) + # serialize it, using pyspec + pyspec_data = spec_ssz_impl.serialize(original) + # get the py-ssz type for it + block_sedes = translate_typ(typ) + # try decoding using the py-ssz type + raw_value = block_sedes.deserialize(pyspec_data) + + # serialize it using py-ssz + pyssz_data = block_sedes.serialize(raw_value) + # now check if the serialized form is equal. If so, we confirmed decoding and encoding to work. + assert pyspec_data == pyssz_data + + # now translate the py-ssz value in a pyspec-value + block = translate_value(raw_value, typ) + + # and see if the hash-tree-root of the original matches the hash-tree-root of the decoded & translated value. + assert spec_ssz_impl.hash_tree_root(original) == spec_ssz_impl.hash_tree_root(block) diff --git a/test_libs/pyspec/eth2spec/test/helpers/block.py b/test_libs/pyspec/eth2spec/test/helpers/block.py index 5c7cb02a0..16249fe93 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/block.py @@ -57,7 +57,7 @@ def build_empty_block(spec, state, slot=None, signed=False): slot = state.slot empty_block = spec.BeaconBlock() empty_block.slot = slot - empty_block.body.eth1_data.deposit_count = state.deposit_index + empty_block.body.eth1_data.deposit_count = state.eth1_deposit_index previous_block_header = deepcopy(state.latest_block_header) if previous_block_header.state_root == spec.ZERO_HASH: previous_block_header.state_root = state.hash_tree_root() diff --git a/test_libs/pyspec/eth2spec/test/helpers/deposits.py b/test_libs/pyspec/eth2spec/test/helpers/deposits.py index 1d4761753..20ff7440f 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/deposits.py +++ b/test_libs/pyspec/eth2spec/test/helpers/deposits.py @@ -58,7 +58,7 @@ def prepare_state_and_deposit(spec, state, validator_index, amount, withdrawal_c """ Prepare the state for the deposit, and create a deposit for the given validator, depositing the given amount. """ - pre_validator_count = len(state.validator_registry) + pre_validator_count = len(state.validators) # fill previous deposits with zero-hash deposit_data_leaves = [spec.ZERO_HASH] * pre_validator_count @@ -80,6 +80,6 @@ def prepare_state_and_deposit(spec, state, validator_index, amount, withdrawal_c signed, ) - state.latest_eth1_data.deposit_root = root - state.latest_eth1_data.deposit_count = len(deposit_data_leaves) + state.eth1_data.deposit_root = root + state.eth1_data.deposit_count = len(deposit_data_leaves) return deposit diff --git a/test_libs/pyspec/eth2spec/test/helpers/genesis.py b/test_libs/pyspec/eth2spec/test/helpers/genesis.py index a3ca915ef..87b0446af 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/genesis.py +++ b/test_libs/pyspec/eth2spec/test/helpers/genesis.py @@ -22,8 +22,8 @@ def create_genesis_state(spec, num_validators): state = spec.BeaconState( genesis_time=0, - deposit_index=num_validators, - latest_eth1_data=spec.Eth1Data( + eth1_deposit_index=num_validators, + eth1_data=spec.Eth1Data( deposit_root=deposit_root, deposit_count=num_validators, block_hash=spec.ZERO_HASH, @@ -32,16 +32,16 @@ def create_genesis_state(spec, num_validators): # We "hack" in the initial validators, # as it is much faster than creating and processing genesis deposits for every single test case. state.balances = [spec.MAX_EFFECTIVE_BALANCE] * num_validators - state.validator_registry = [build_mock_validator(spec, i, state.balances[i]) for i in range(num_validators)] + state.validators = [build_mock_validator(spec, i, state.balances[i]) for i in range(num_validators)] # Process genesis activations - for validator in state.validator_registry: + for validator in state.validators: if validator.effective_balance >= spec.MAX_EFFECTIVE_BALANCE: validator.activation_eligibility_epoch = spec.GENESIS_EPOCH validator.activation_epoch = spec.GENESIS_EPOCH genesis_active_index_root = hash_tree_root(spec.get_active_validator_indices(state, spec.GENESIS_EPOCH)) - for index in range(spec.LATEST_ACTIVE_INDEX_ROOTS_LENGTH): - state.latest_active_index_roots[index] = genesis_active_index_root + for index in range(spec.ACTIVE_INDEX_ROOTS_LENGTH): + state.active_index_roots[index] = genesis_active_index_root return state diff --git a/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py b/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py index 86c6acf47..d5b7f7b7f 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py +++ b/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py @@ -7,7 +7,7 @@ from eth2spec.test.helpers.keys import pubkey_to_privkey def get_valid_proposer_slashing(spec, state, signed_1=False, signed_2=False): current_epoch = spec.get_current_epoch(state) validator_index = spec.get_active_validator_indices(state, current_epoch)[-1] - privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[state.validators[validator_index].pubkey] slot = state.slot header_1 = spec.BeaconBlockHeader( diff --git a/test_libs/pyspec/eth2spec/test/helpers/state.py b/test_libs/pyspec/eth2spec/test/helpers/state.py index 63aa27d70..3708a354e 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/state.py +++ b/test_libs/pyspec/eth2spec/test/helpers/state.py @@ -1,3 +1,6 @@ +from eth2spec.test.helpers.block import sign_block + + def get_balance(state, index): return state.balances[index] @@ -22,4 +25,14 @@ def get_state_root(spec, state, slot) -> bytes: Return the state root at a recent ``slot``. """ assert slot < state.slot <= slot + spec.SLOTS_PER_HISTORICAL_ROOT - return state.latest_state_roots[slot % spec.SLOTS_PER_HISTORICAL_ROOT] + return state.state_roots[slot % spec.SLOTS_PER_HISTORICAL_ROOT] + + +def state_transition_and_sign_block(spec, state, block): + """ + State transition via the provided ``block`` + then package the block with the state root and signature. + """ + spec.state_transition(state, block) + block.state_root = state.hash_tree_root() + sign_block(spec, state, block) diff --git a/test_libs/pyspec/eth2spec/test/helpers/transfers.py b/test_libs/pyspec/eth2spec/test/helpers/transfers.py index 4d40deee7..acc6a35c5 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/transfers.py +++ b/test_libs/pyspec/eth2spec/test/helpers/transfers.py @@ -31,7 +31,7 @@ def get_valid_transfer(spec, state, slot=None, sender_index=None, amount=None, f sign_transfer(spec, state, transfer, transfer_privkey) # ensure withdrawal_credentials reproducible - state.validator_registry[transfer.sender].withdrawal_credentials = ( + state.validators[transfer.sender].withdrawal_credentials = ( spec.int_to_bytes(spec.BLS_WITHDRAWAL_PREFIX, length=1) + spec.hash(transfer.pubkey)[1:] ) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py index 6c7637d59..c51f5a8a9 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py @@ -34,7 +34,7 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True) # Process slashing spec.process_attester_slashing(state, attester_slashing) - slashed_validator = state.validator_registry[slashed_index] + slashed_validator = state.validators[slashed_index] # Check slashing assert slashed_validator.slashed @@ -135,7 +135,7 @@ def test_participants_already_slashed(spec, state): attestation_1 = attester_slashing.attestation_1 validator_indices = attestation_1.custody_bit_0_indices + attestation_1.custody_bit_1_indices for index in validator_indices: - state.validator_registry[index].slashed = True + state.validators[index].slashed = True yield from run_attester_slashing_processing(spec, state, attester_slashing, False) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py index f3c017982..a2306ef4d 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py @@ -78,7 +78,7 @@ def test_proposer_slashed(spec, state): proposer_index = spec.get_beacon_proposer_index(stub_state) # set proposer to slashed - state.validator_registry[proposer_index].slashed = True + state.validators[proposer_index].slashed = True block = build_empty_block_for_next_slot(spec, state, signed=True) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py index 603a07c3d..63b94c638 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py @@ -16,7 +16,7 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ - pre_validator_count = len(state.validator_registry) + pre_validator_count = len(state.validators) pre_balance = 0 if validator_index < pre_validator_count: pre_balance = get_balance(state, validator_index) @@ -34,29 +34,29 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef yield 'post', state if not effective: - assert len(state.validator_registry) == pre_validator_count + assert len(state.validators) == pre_validator_count assert len(state.balances) == pre_validator_count if validator_index < pre_validator_count: assert get_balance(state, validator_index) == pre_balance else: if validator_index < pre_validator_count: # top-up - assert len(state.validator_registry) == pre_validator_count + assert len(state.validators) == pre_validator_count assert len(state.balances) == pre_validator_count else: # new validator - assert len(state.validator_registry) == pre_validator_count + 1 + assert len(state.validators) == pre_validator_count + 1 assert len(state.balances) == pre_validator_count + 1 assert get_balance(state, validator_index) == pre_balance + deposit.data.amount - assert state.deposit_index == state.latest_eth1_data.deposit_count + assert state.eth1_deposit_index == state.eth1_data.deposit_count @with_all_phases @spec_state_test def test_new_deposit(spec, state): # fresh deposit = next validator index = validator appended to registry - validator_index = len(state.validator_registry) + validator_index = len(state.validators) amount = spec.MAX_EFFECTIVE_BALANCE deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) @@ -68,7 +68,7 @@ def test_new_deposit(spec, state): @spec_state_test def test_invalid_sig_new_deposit(spec, state): # fresh deposit = next validator index = validator appended to registry - validator_index = len(state.validator_registry) + validator_index = len(state.validators) amount = spec.MAX_EFFECTIVE_BALANCE deposit = prepare_state_and_deposit(spec, state, validator_index, amount) yield from run_deposit_processing(spec, state, deposit, validator_index, valid=True, effective=False) @@ -117,12 +117,12 @@ def test_invalid_withdrawal_credentials_top_up(spec, state): @with_all_phases @spec_state_test def test_wrong_index(spec, state): - validator_index = len(state.validator_registry) + validator_index = len(state.validators) amount = spec.MAX_EFFECTIVE_BALANCE deposit = prepare_state_and_deposit(spec, state, validator_index, amount) - # mess up deposit_index - deposit.index = state.deposit_index + 1 + # mess up eth1_deposit_index + deposit.index = state.eth1_deposit_index + 1 sign_deposit_data(spec, state, deposit.data, privkeys[validator_index]) @@ -132,7 +132,7 @@ def test_wrong_index(spec, state): @with_all_phases @spec_state_test def test_wrong_deposit_for_deposit_count(spec, state): - deposit_data_leaves = [spec.ZERO_HASH] * len(state.validator_registry) + deposit_data_leaves = [spec.ZERO_HASH] * len(state.validators) # build root for deposit_1 index_1 = len(deposit_data_leaves) @@ -166,8 +166,8 @@ def test_wrong_deposit_for_deposit_count(spec, state): ) # state has root for deposit_2 but is at deposit_count for deposit_1 - state.latest_eth1_data.deposit_root = root_2 - state.latest_eth1_data.deposit_count = deposit_count_1 + state.eth1_data.deposit_root = root_2 + state.eth1_data.deposit_count = deposit_count_1 yield from run_deposit_processing(spec, state, deposit_2, index_2, valid=False) @@ -178,7 +178,7 @@ def test_wrong_deposit_for_deposit_count(spec, state): @with_all_phases @spec_state_test def test_bad_merkle_proof(spec, state): - validator_index = len(state.validator_registry) + validator_index = len(state.validators) amount = spec.MAX_EFFECTIVE_BALANCE deposit = prepare_state_and_deposit(spec, state, validator_index, amount) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py index b35241859..af34ea709 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py @@ -28,7 +28,7 @@ def run_proposer_slashing_processing(spec, state, proposer_slashing, valid=True) yield 'post', state # check if slashed - slashed_validator = state.validator_registry[proposer_slashing.proposer_index] + slashed_validator = state.validators[proposer_slashing.proposer_index] assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH @@ -77,7 +77,7 @@ def test_invalid_sig_1_and_2(spec, state): def test_invalid_proposer_index(spec, state): proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True) # Index just too high (by 1) - proposer_slashing.proposer_index = len(state.validator_registry) + proposer_slashing.proposer_index = len(state.validators) yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False) @@ -111,7 +111,7 @@ def test_proposer_is_not_activated(spec, state): proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True) # set proposer to be not active yet - state.validator_registry[proposer_slashing.proposer_index].activation_epoch = spec.get_current_epoch(state) + 1 + state.validators[proposer_slashing.proposer_index].activation_epoch = spec.get_current_epoch(state) + 1 yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False) @@ -122,7 +122,7 @@ def test_proposer_is_slashed(spec, state): proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True) # set proposer to slashed - state.validator_registry[proposer_slashing.proposer_index].slashed = True + state.validators[proposer_slashing.proposer_index].slashed = True yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False) @@ -137,6 +137,6 @@ def test_proposer_is_withdrawn(spec, state): # set proposer withdrawable_epoch in past current_epoch = spec.get_current_epoch(state) proposer_index = proposer_slashing.proposer_index - state.validator_registry[proposer_index].withdrawable_epoch = current_epoch - 1 + state.validators[proposer_index].withdrawable_epoch = current_epoch - 1 yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py index 1294ca84a..e9d282b3a 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py @@ -41,7 +41,7 @@ def run_transfer_processing(spec, state, transfer, valid=True): def test_success_non_activated(spec, state): transfer = get_valid_transfer(spec, state, signed=True) # un-activate so validator can transfer - state.validator_registry[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer) @@ -55,7 +55,7 @@ def test_success_withdrawable(spec, state): transfer = get_valid_transfer(spec, state, signed=True) # withdrawable_epoch in past so can transfer - state.validator_registry[transfer.sender].withdrawable_epoch = spec.get_current_epoch(state) - 1 + state.validators[transfer.sender].withdrawable_epoch = spec.get_current_epoch(state) - 1 yield from run_transfer_processing(spec, state, transfer) @@ -86,7 +86,7 @@ def test_success_active_above_max_effective_fee(spec, state): def test_invalid_signature(spec, state): transfer = get_valid_transfer(spec, state) # un-activate so validator can transfer - state.validator_registry[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) @@ -107,7 +107,7 @@ def test_active_but_transfer_past_effective_balance(spec, state): def test_incorrect_slot(spec, state): transfer = get_valid_transfer(spec, state, slot=state.slot + 1, signed=True) # un-activate so validator can transfer - state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) @@ -120,7 +120,7 @@ def test_insufficient_balance_for_fee(spec, state): transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=0, fee=1, signed=True) # un-activate so validator can transfer - state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) @@ -133,7 +133,7 @@ def test_insufficient_balance(spec, state): transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=1, fee=0, signed=True) # un-activate so validator can transfer - state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) @@ -153,7 +153,7 @@ def test_no_dust_sender(spec, state): ) # un-activate so validator can transfer - state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) @@ -167,7 +167,7 @@ def test_no_dust_recipient(spec, state): state.balances[transfer.recipient] = 0 # un-activate so validator can transfer - state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) @@ -176,9 +176,9 @@ def test_no_dust_recipient(spec, state): @spec_state_test def test_invalid_pubkey(spec, state): transfer = get_valid_transfer(spec, state, signed=True) - state.validator_registry[transfer.sender].withdrawal_credentials = spec.ZERO_HASH + state.validators[transfer.sender].withdrawal_credentials = spec.ZERO_HASH # un-activate so validator can transfer - state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py index 3359c5e78..33cacc4e2 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py @@ -21,14 +21,14 @@ def run_voluntary_exit_processing(spec, state, voluntary_exit, valid=True): yield 'post', None return - pre_exit_epoch = state.validator_registry[validator_index].exit_epoch + pre_exit_epoch = state.validators[validator_index].exit_epoch spec.process_voluntary_exit(state, voluntary_exit) yield 'post', state assert pre_exit_epoch == spec.FAR_FUTURE_EPOCH - assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH + assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH @with_all_phases @@ -39,7 +39,7 @@ def test_success(spec, state): current_epoch = spec.get_current_epoch(state) validator_index = spec.get_active_validator_indices(state, current_epoch)[0] - privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[state.validators[validator_index].pubkey] voluntary_exit = build_voluntary_exit(spec, state, current_epoch, validator_index, privkey, signed=True) @@ -55,7 +55,7 @@ def test_invalid_signature(spec, state): current_epoch = spec.get_current_epoch(state) validator_index = spec.get_active_validator_indices(state, current_epoch)[0] - privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[state.validators[validator_index].pubkey] voluntary_exit = build_voluntary_exit(spec, state, current_epoch, validator_index, privkey) @@ -76,7 +76,7 @@ def test_success_exit_queue(spec, state): # Prepare a bunch of exits, based on the current state exit_queue = [] for index in initial_indices: - privkey = pubkey_to_privkey[state.validator_registry[index].pubkey] + privkey = pubkey_to_privkey[state.validators[index].pubkey] exit_queue.append(build_voluntary_exit( spec, state, @@ -94,7 +94,7 @@ def test_success_exit_queue(spec, state): # exit an additional validator validator_index = spec.get_active_validator_indices(state, current_epoch)[-1] - privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[state.validators[validator_index].pubkey] voluntary_exit = build_voluntary_exit( spec, state, @@ -109,8 +109,8 @@ def test_success_exit_queue(spec, state): yield from run_voluntary_exit_processing(spec, state, voluntary_exit) assert ( - state.validator_registry[validator_index].exit_epoch == - state.validator_registry[initial_indices[0]].exit_epoch + 1 + state.validators[validator_index].exit_epoch == + state.validators[initial_indices[0]].exit_epoch + 1 ) @@ -122,7 +122,7 @@ def test_validator_exit_in_future(spec, state): current_epoch = spec.get_current_epoch(state) validator_index = spec.get_active_validator_indices(state, current_epoch)[0] - privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[state.validators[validator_index].pubkey] voluntary_exit = build_voluntary_exit( spec, @@ -146,7 +146,7 @@ def test_validator_invalid_validator_index(spec, state): current_epoch = spec.get_current_epoch(state) validator_index = spec.get_active_validator_indices(state, current_epoch)[0] - privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[state.validators[validator_index].pubkey] voluntary_exit = build_voluntary_exit( spec, @@ -156,7 +156,7 @@ def test_validator_invalid_validator_index(spec, state): privkey, signed=False, ) - voluntary_exit.validator_index = len(state.validator_registry) + voluntary_exit.validator_index = len(state.validators) sign_voluntary_exit(spec, state, voluntary_exit, privkey) yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False) @@ -167,9 +167,9 @@ def test_validator_invalid_validator_index(spec, state): def test_validator_not_active(spec, state): current_epoch = spec.get_current_epoch(state) validator_index = spec.get_active_validator_indices(state, current_epoch)[0] - privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[state.validators[validator_index].pubkey] - state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH # build and test voluntary exit voluntary_exit = build_voluntary_exit( @@ -192,10 +192,10 @@ def test_validator_already_exited(spec, state): current_epoch = spec.get_current_epoch(state) validator_index = spec.get_active_validator_indices(state, current_epoch)[0] - privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[state.validators[validator_index].pubkey] # but validator already has exited - state.validator_registry[validator_index].exit_epoch = current_epoch + 2 + state.validators[validator_index].exit_epoch = current_epoch + 2 voluntary_exit = build_voluntary_exit( spec, @@ -214,7 +214,7 @@ def test_validator_already_exited(spec, state): def test_validator_not_active_long_enough(spec, state): current_epoch = spec.get_current_epoch(state) validator_index = spec.get_active_validator_indices(state, current_epoch)[0] - privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] + privkey = pubkey_to_privkey[state.validators[validator_index].pubkey] voluntary_exit = build_voluntary_exit( spec, @@ -226,7 +226,7 @@ def test_validator_not_active_long_enough(spec, state): ) assert ( - current_epoch - state.validator_registry[validator_index].activation_epoch < + current_epoch - state.validators[validator_index].activation_epoch < spec.PERSISTENT_COMMITTEE_PERIOD ) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py index 65d958678..d51191efb 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py @@ -3,7 +3,8 @@ from copy import deepcopy from eth2spec.test.context import spec_state_test, with_all_phases from eth2spec.test.helpers.state import ( next_epoch, - next_slot + next_slot, + state_transition_and_sign_block, ) from eth2spec.test.helpers.block import apply_empty_block, sign_block from eth2spec.test.helpers.attestations import ( @@ -27,11 +28,14 @@ def run_process_crosslinks(spec, state, valid=True): block = build_empty_block_for_next_slot(spec, state) block.slot = slot sign_block(spec, state, block) - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) # cache state before epoch transition spec.process_slot(state) + # process components of epoch transition before processing crosslinks + spec.process_justification_and_finalization(state) + yield 'pre', state spec.process_crosslinks(state) yield 'post', state diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py index e6679f844..d92220910 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py @@ -1,6 +1,5 @@ -from eth2spec.phase0.spec import state_transition from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block -from eth2spec.test.helpers.state import next_epoch +from eth2spec.test.helpers.state import next_epoch, state_transition_and_sign_block from eth2spec.test.context import spec_state_test, with_all_phases @@ -16,7 +15,7 @@ def run_process_registry_updates(spec, state, valid=True): block = build_empty_block_for_next_slot(spec, state) block.slot = slot sign_block(spec, state, block) - state_transition(state, block) + state_transition_and_sign_block(spec, state, block) # cache state before epoch transition spec.process_slot(state) @@ -35,23 +34,23 @@ def run_process_registry_updates(spec, state, valid=True): @spec_state_test def test_activation(spec, state): index = 0 - assert spec.is_active_validator(state.validator_registry[index], spec.get_current_epoch(state)) + assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state)) # Mock a new deposit - state.validator_registry[index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH - state.validator_registry[index].activation_epoch = spec.FAR_FUTURE_EPOCH - state.validator_registry[index].effective_balance = spec.MAX_EFFECTIVE_BALANCE - assert not spec.is_active_validator(state.validator_registry[index], spec.get_current_epoch(state)) + state.validators[index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH + state.validators[index].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[index].effective_balance = spec.MAX_EFFECTIVE_BALANCE + assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state)) for _ in range(spec.ACTIVATION_EXIT_DELAY + 1): next_epoch(spec, state) yield from run_process_registry_updates(spec, state) - assert state.validator_registry[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH - assert state.validator_registry[index].activation_epoch != spec.FAR_FUTURE_EPOCH + assert state.validators[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH + assert state.validators[index].activation_epoch != spec.FAR_FUTURE_EPOCH assert spec.is_active_validator( - state.validator_registry[index], + state.validators[index], spec.get_current_epoch(state), ) @@ -60,19 +59,19 @@ def test_activation(spec, state): @spec_state_test def test_ejection(spec, state): index = 0 - assert spec.is_active_validator(state.validator_registry[index], spec.get_current_epoch(state)) - assert state.validator_registry[index].exit_epoch == spec.FAR_FUTURE_EPOCH + assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state)) + assert state.validators[index].exit_epoch == spec.FAR_FUTURE_EPOCH # Mock an ejection - state.validator_registry[index].effective_balance = spec.EJECTION_BALANCE + state.validators[index].effective_balance = spec.EJECTION_BALANCE for _ in range(spec.ACTIVATION_EXIT_DELAY + 1): next_epoch(spec, state) yield from run_process_registry_updates(spec, state) - assert state.validator_registry[index].exit_epoch != spec.FAR_FUTURE_EPOCH + assert state.validators[index].exit_epoch != spec.FAR_FUTURE_EPOCH assert not spec.is_active_validator( - state.validator_registry[index], + state.validators[index], spec.get_current_epoch(state), ) diff --git a/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py b/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py index 110231d77..87297d443 100644 --- a/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py +++ b/test_libs/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py @@ -24,7 +24,7 @@ def run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal, v spec.process_early_derived_secret_reveal(state, randao_key_reveal) - slashed_validator = state.validator_registry[randao_key_reveal.revealed_index] + slashed_validator = state.validators[randao_key_reveal.revealed_index] if randao_key_reveal.epoch >= spec.get_current_epoch(state) + spec.CUSTODY_PERIOD_TO_RANDAO_PADDING: assert slashed_validator.slashed @@ -111,7 +111,7 @@ def test_double_reveal(spec, state): @spec_state_test def test_revealer_is_slashed(spec, state): randao_key_reveal = get_valid_early_derived_secret_reveal(spec, state, spec.get_current_epoch(state)) - state.validator_registry[randao_key_reveal.revealed_index].slashed = True + state.validators[randao_key_reveal.revealed_index].slashed = True yield from run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal, False) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index 2275161c8..f8590005e 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -4,7 +4,7 @@ from typing import List from eth2spec.utils.ssz.ssz_impl import signing_root from eth2spec.utils.bls import bls_sign -from eth2spec.test.helpers.state import get_balance +from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block # from eth2spec.test.helpers.transfers import get_valid_transfer from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block from eth2spec.test.helpers.keys import privkeys, pubkeys @@ -13,11 +13,10 @@ from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing from eth2spec.test.helpers.attestations import get_valid_attestation from eth2spec.test.helpers.deposits import prepare_state_and_deposit -from eth2spec.test.context import spec_state_test, never_bls, with_all_phases +from eth2spec.test.context import spec_state_test, with_all_phases @with_all_phases -@never_bls @spec_state_test def test_empty_block_transition(spec, state): pre_slot = state.slot @@ -26,17 +25,18 @@ def test_empty_block_transition(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state, signed=True) - yield 'blocks', [block], List[spec.BeaconBlock] - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state assert len(state.eth1_data_votes) == pre_eth1_votes + 1 assert spec.get_block_root_at_slot(state, pre_slot) == block.parent_root + assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.ZERO_HASH @with_all_phases -@never_bls @spec_state_test def test_skipped_slots(spec, state): pre_slot = state.slot @@ -45,12 +45,14 @@ def test_skipped_slots(spec, state): block = build_empty_block_for_next_slot(spec, state) block.slot += 3 sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state assert state.slot == block.slot + assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.ZERO_HASH for slot in range(pre_slot, state.slot): assert spec.get_block_root_at_slot(state, slot) == block.parent_root @@ -64,9 +66,10 @@ def test_empty_epoch_transition(spec, state): block = build_empty_block_for_next_slot(spec, state) block.slot += spec.SLOTS_PER_EPOCH sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state assert state.slot == block.slot @@ -84,14 +87,15 @@ def test_empty_epoch_transition(spec, state): # block = build_empty_block_for_next_slot(spec, state) # block.slot += spec.SLOTS_PER_EPOCH * 5 # sign_block(spec, state, block, proposer_index=0) -# yield 'blocks', [block], List[spec.BeaconBlock] -# spec.state_transition(state, block) +# state_transition_and_sign_block(spec, state, block) + +# yield 'blocks', [block], List[spec.BeaconBlock] # yield 'post', state # assert state.slot == block.slot # assert state.finalized_epoch < spec.get_current_epoch(state) - 4 -# for index in range(len(state.validator_registry)): +# for index in range(len(state.validators)): # assert get_balance(state, index) < get_balance(pre_state, index) @@ -103,7 +107,7 @@ def test_proposer_slashing(spec, state): proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True) validator_index = proposer_slashing.proposer_index - assert not state.validator_registry[validator_index].slashed + assert not state.validators[validator_index].slashed yield 'pre', state @@ -113,13 +117,14 @@ def test_proposer_slashing(spec, state): block = build_empty_block_for_next_slot(spec, state) block.body.proposer_slashings.append(proposer_slashing) sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state # check if slashed - slashed_validator = state.validator_registry[validator_index] + slashed_validator = state.validators[validator_index] assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH @@ -137,7 +142,7 @@ def test_attester_slashing(spec, state): validator_index = (attester_slashing.attestation_1.custody_bit_0_indices + attester_slashing.attestation_1.custody_bit_1_indices)[0] - assert not state.validator_registry[validator_index].slashed + assert not state.validators[validator_index].slashed yield 'pre', state @@ -147,12 +152,13 @@ def test_attester_slashing(spec, state): block = build_empty_block_for_next_slot(spec, state) block.body.attester_slashings.append(attester_slashing) sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state - slashed_validator = state.validator_registry[validator_index] + slashed_validator = state.validators[validator_index] assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH @@ -172,10 +178,10 @@ def test_attester_slashing(spec, state): @with_all_phases @spec_state_test def test_deposit_in_block(spec, state): - initial_registry_len = len(state.validator_registry) + initial_registry_len = len(state.validators) initial_balances_len = len(state.balances) - validator_index = len(state.validator_registry) + validator_index = len(state.validators) amount = spec.MAX_EFFECTIVE_BALANCE deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) @@ -185,15 +191,15 @@ def test_deposit_in_block(spec, state): block.body.deposits.append(deposit) sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] + state_transition_and_sign_block(spec, state, block) - spec.state_transition(state, block) + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state - assert len(state.validator_registry) == initial_registry_len + 1 + assert len(state.validators) == initial_registry_len + 1 assert len(state.balances) == initial_balances_len + 1 assert get_balance(state, validator_index) == spec.MAX_EFFECTIVE_BALANCE - assert state.validator_registry[validator_index].pubkey == pubkeys[validator_index] + assert state.validators[validator_index].pubkey == pubkeys[validator_index] @with_all_phases @@ -203,7 +209,7 @@ def test_deposit_top_up(spec, state): amount = spec.MAX_EFFECTIVE_BALANCE // 4 deposit = prepare_state_and_deposit(spec, state, validator_index, amount) - initial_registry_len = len(state.validator_registry) + initial_registry_len = len(state.validators) initial_balances_len = len(state.balances) validator_pre_balance = get_balance(state, validator_index) @@ -213,12 +219,12 @@ def test_deposit_top_up(spec, state): block.body.deposits.append(deposit) sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] + state_transition_and_sign_block(spec, state, block) - spec.state_transition(state, block) + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state - assert len(state.validator_registry) == initial_registry_len + assert len(state.validators) == initial_registry_len assert len(state.balances) == initial_balances_len assert get_balance(state, validator_index) == validator_pre_balance + amount @@ -238,7 +244,7 @@ def test_attestation(spec, state): attestation_block.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY attestation_block.body.attestations.append(attestation) sign_block(spec, state, attestation_block) - spec.state_transition(state, attestation_block) + state_transition_and_sign_block(spec, state, attestation_block) assert len(state.current_epoch_attestations) == pre_current_attestations_len + 1 @@ -248,7 +254,7 @@ def test_attestation(spec, state): epoch_block = build_empty_block_for_next_slot(spec, state) epoch_block.slot += spec.SLOTS_PER_EPOCH sign_block(spec, state, epoch_block) - spec.state_transition(state, epoch_block) + state_transition_and_sign_block(spec, state, epoch_block) yield 'blocks', [attestation_block, epoch_block], List[spec.BeaconBlock] yield 'post', state @@ -287,20 +293,20 @@ def test_voluntary_exit(spec, state): initiate_exit_block = build_empty_block_for_next_slot(spec, state) initiate_exit_block.body.voluntary_exits.append(voluntary_exit) sign_block(spec, state, initiate_exit_block) - spec.state_transition(state, initiate_exit_block) + state_transition_and_sign_block(spec, state, initiate_exit_block) - assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH + assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH # Process within epoch transition exit_block = build_empty_block_for_next_slot(spec, state) exit_block.slot += spec.SLOTS_PER_EPOCH sign_block(spec, state, exit_block) - spec.state_transition(state, exit_block) + state_transition_and_sign_block(spec, state, exit_block) yield 'blocks', [initiate_exit_block, exit_block], List[spec.BeaconBlock] yield 'post', state - assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH + assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH # @with_all_phases @@ -317,7 +323,7 @@ def test_voluntary_exit(spec, state): # pre_transfer_recipient_balance = get_balance(state, recipient_index) # un-activate so validator can transfer - # state.validator_registry[sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH + # state.validators[sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH # yield 'pre', state @@ -326,9 +332,9 @@ def test_voluntary_exit(spec, state): # block.body.transfers.append(transfer) # sign_block(spec, state, block) - # yield 'blocks', [block], List[spec.BeaconBlock] + # state_transition_and_sign_block(spec, state, block) - # spec.state_transition(state, block) + # yield 'blocks', [block], List[spec.BeaconBlock] # yield 'post', state # sender_balance = get_balance(state, sender_index) @@ -343,10 +349,10 @@ def test_balance_driven_status_transitions(spec, state): current_epoch = spec.get_current_epoch(state) validator_index = spec.get_active_validator_indices(state, current_epoch)[-1] - assert state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH + assert state.validators[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH # set validator balance to below ejection threshold - state.validator_registry[validator_index].effective_balance = spec.EJECTION_BALANCE + state.validators[validator_index].effective_balance = spec.EJECTION_BALANCE yield 'pre', state @@ -354,12 +360,12 @@ def test_balance_driven_status_transitions(spec, state): block = build_empty_block_for_next_slot(spec, state) block.slot += spec.SLOTS_PER_EPOCH sign_block(spec, state, block) - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state - assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH + assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH @with_all_phases @@ -371,7 +377,7 @@ def test_historical_batch(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state, signed=True) - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state @@ -392,7 +398,7 @@ def test_historical_batch(spec, state): # blocks = [] # for _ in range(spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1): # block = build_empty_block_for_next_slot(spec, state) -# spec.state_transition(state, block) +# state_transition_and_sign_block(spec, state, block) # expected_votes += 1 # assert len(state.eth1_data_votes) == expected_votes # blocks.append(block) @@ -400,7 +406,7 @@ def test_historical_batch(spec, state): # block = build_empty_block_for_next_slot(spec, state) # blocks.append(block) -# spec.state_transition(state, block) +# state_transition_and_sign_block(spec, state, block) # yield 'blocks', [block], List[spec.BeaconBlock] # yield 'post', state diff --git a/test_libs/pyspec/eth2spec/test/test_finality.py b/test_libs/pyspec/eth2spec/test/test_finality.py index 801e8b4fd..5e81f52c8 100644 --- a/test_libs/pyspec/eth2spec/test/test_finality.py +++ b/test_libs/pyspec/eth2spec/test/test_finality.py @@ -2,7 +2,7 @@ from copy import deepcopy from typing import List from eth2spec.test.context import spec_state_test, never_bls, with_all_phases -from eth2spec.test.helpers.state import next_epoch +from eth2spec.test.helpers.state import next_epoch, state_transition_and_sign_block from eth2spec.test.helpers.block import build_empty_block_for_next_slot, apply_empty_block from eth2spec.test.helpers.attestations import get_valid_attestation @@ -54,7 +54,7 @@ def next_epoch_with_attestations(spec, prev_attestation = get_valid_attestation(spec, post_state, slot_to_attest) block.body.attestations.append(prev_attestation) - spec.state_transition(post_state, block) + state_transition_and_sign_block(spec, post_state, block) blocks.append(block) return state, blocks, post_state diff --git a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py index d4b481ef2..80c344c8a 100644 --- a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py +++ b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py @@ -1,3 +1,4 @@ +from types import GeneratorType from typing import ( List, Iterable, @@ -362,6 +363,8 @@ def parse_bytes(val): return val elif isinstance(val, int): return bytes([val]) + elif isinstance(val, (list, GeneratorType)): + return bytes(val) else: return None diff --git a/test_libs/pyspec/requirements-testing.txt b/test_libs/pyspec/requirements-testing.txt index 331d0fa28..edd141650 100644 --- a/test_libs/pyspec/requirements-testing.txt +++ b/test_libs/pyspec/requirements-testing.txt @@ -2,3 +2,4 @@ pytest>=3.6,<3.7 ../config_helpers flake8==3.7.7 +pytest-cov diff --git a/test_libs/pyspec/requirements.txt b/test_libs/pyspec/requirements.txt index 18e5d2a4c..b96b0a80f 100644 --- a/test_libs/pyspec/requirements.txt +++ b/test_libs/pyspec/requirements.txt @@ -4,3 +4,4 @@ pycryptodome==3.7.3 py_ecc>=1.6.0 typing_inspect==0.4.0 dataclasses==0.6 +ssz==0.1.0a10 diff --git a/test_libs/pyspec/setup.py b/test_libs/pyspec/setup.py index e99b911ee..3856640ab 100644 --- a/test_libs/pyspec/setup.py +++ b/test_libs/pyspec/setup.py @@ -9,6 +9,7 @@ setup( "eth-typing>=2.1.0,<3.0.0", "pycryptodome==3.7.3", "py_ecc>=1.6.0", - "typing_inspect==0.4.0" + "typing_inspect==0.4.0", + "ssz==0.1.0a10" ] )