Merge branch 'dev' into carl-patch-1

This commit is contained in:
Danny Ryan 2019-04-17 11:18:29 -06:00
commit d118eb03c9
No known key found for this signature in database
GPG Key ID: 2765A792E42CE07A
26 changed files with 222 additions and 272 deletions

View File

@ -15,13 +15,13 @@ jobs:
name: Run py-tests
command: make test
- run:
name: Generate YAML tests
command: make gen_yaml_tests
# TODO in future PR (after #851): decide on CI triggering of yaml tests building,
# TODO see #928: decide on CI triggering of yaml tests building,
# and destination of output (new yaml tests LFS-configured repository)
#
# - run:
# name: Generate YAML tests
# command: make gen_yaml_tests
#
# - store_artifacts:
# path: test-reports
# destination: test-reports

View File

@ -2,7 +2,6 @@ SPEC_DIR = ./specs
SCRIPT_DIR = ./scripts
TEST_LIBS_DIR = ./test_libs
PY_SPEC_DIR = $(TEST_LIBS_DIR)/pyspec
PY_TEST_DIR = ./py_tests
YAML_TEST_DIR = ./yaml_tests
GENERATOR_DIR = ./test_generators
CONFIGS_DIR = ./configs
@ -24,7 +23,7 @@ all: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_DIR) $(YAML_TEST_TARGETS)
clean:
rm -rf $(YAML_TEST_DIR)
rm -rf $(GENERATOR_VENVS)
rm -rf $(PY_TEST_DIR)/venv $(PY_TEST_DIR)/.pytest_cache
rm -rf $(PY_SPEC_DIR)/venv $(PY_SPEC_DIR)/.pytest_cache
rm -rf $(PY_SPEC_ALL_TARGETS)
# "make gen_yaml_tests" to run generators
@ -32,7 +31,7 @@ gen_yaml_tests: $(YAML_TEST_DIR) $(YAML_TEST_TARGETS)
# runs a limited set of tests against a minimal config
test: $(PY_SPEC_ALL_TARGETS)
cd $(PY_TEST_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt; pytest -m minimal_config .
cd $(PY_SPEC_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt; python -m pytest -m minimal_config .
# "make pyspec" to create the pyspec for all phases.
pyspec: $(PY_SPEC_ALL_TARGETS)

View File

@ -37,6 +37,5 @@ The following are the broad design goals for Ethereum 2.0:
Documentation on the different components used during spec writing can be found here:
* [YAML Test Generators](test_generators/README.md)
* [Executable Python Spec](test_libs/eth2spec/README.md)
* [Py-tests](py_tests/README.md)
* [Executable Python Spec, with Py-tests](test_libs/pyspec/README.md)

View File

@ -5,16 +5,16 @@
# Misc
# ---------------------------------------------------------------
# 2**10 ` (= 1,024)
# 2**10 (= 1,024)
SHARD_COUNT: 1024
# 2**7 ` (= 128)
# 2**7 (= 128)
TARGET_COMMITTEE_SIZE: 128
# 2**5 ` (= 32)
MAX_BALANCE_CHURN_QUOTIENT: 32
# 2**12 ` (= 4,096)
# 2**12 (= 4,096)
MAX_ATTESTATION_PARTICIPANTS: 4096
# 2**2 ` (= 4)
MAX_EXIT_DEQUEUES_PER_EPOCH: 4
# 2**2 (= 4)
MIN_PER_EPOCH_CHURN_LIMIT: 4
# 2**16 (= 65,536)
CHURN_LIMIT_QUOTIENT: 65536
# See issue 563
SHUFFLE_ROUND_COUNT: 90
@ -23,19 +23,19 @@ SHUFFLE_ROUND_COUNT: 90
# ---------------------------------------------------------------
# **TBD**
DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890
# 2**5 ` (= 32)
# 2**5 (= 32)
DEPOSIT_CONTRACT_TREE_DEPTH: 32
# Gwei values
# ---------------------------------------------------------------
# 2**0 * 10**9 ` (= 1,000,000,000) Gwei
# 2**0 * 10**9 (= 1,000,000,000) Gwei
MIN_DEPOSIT_AMOUNT: 1000000000
# 2**5 * 10**9 ` (= 32,000,000,000) Gwei
# 2**5 * 10**9 (= 32,000,000,000) Gwei
MAX_DEPOSIT_AMOUNT: 32000000000
# 2**4 * 10**9 ` (= 16,000,000,000) Gwei
# 2**4 * 10**9 (= 16,000,000,000) Gwei
EJECTION_BALANCE: 16000000000
# 2**0 * 10**9 ` (= 1,000,000,000) Gwei
# 2**0 * 10**9 (= 1,000,000,000) Gwei
HIGH_BALANCE_INCREMENT: 1000000000
@ -54,63 +54,63 @@ BLS_WITHDRAWAL_PREFIX_BYTE: 0x00
# ---------------------------------------------------------------
# 6 seconds 6 seconds
SECONDS_PER_SLOT: 6
# 2**2 ` (= 4) slots 24 seconds
# 2**2 (= 4) slots 24 seconds
MIN_ATTESTATION_INCLUSION_DELAY: 4
# 2**6 ` (= 64) slots 6.4 minutes
# 2**6 (= 64) slots 6.4 minutes
SLOTS_PER_EPOCH: 64
# 2**0 ` (= 1) epochs 6.4 minutes
# 2**0 (= 1) epochs 6.4 minutes
MIN_SEED_LOOKAHEAD: 1
# 2**2 ` (= 4) epochs 25.6 minutes
# 2**2 (= 4) epochs 25.6 minutes
ACTIVATION_EXIT_DELAY: 4
# 2**4 ` (= 16) epochs ~1.7 hours
EPOCHS_PER_ETH1_VOTING_PERIOD: 16
# 2**13 ` (= 8,192) slots ~13 hours
# 2**10 (= 1,024) slots ~1.7 hours
SLOTS_PER_ETH1_VOTING_PERIOD: 1024
# 2**13 (= 8,192) slots ~13 hours
SLOTS_PER_HISTORICAL_ROOT: 8192
# 2**8 ` (= 256) epochs ~27 hours
# 2**8 (= 256) epochs ~27 hours
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
# 2**11 ` (= 2,048) epochs 9 days
# 2**11 (= 2,048) epochs 9 days
PERSISTENT_COMMITTEE_PERIOD: 2048
# 2**6 ` (= 64)
# 2**6 (= 64) epochs ~7 hours
MAX_CROSSLINK_EPOCHS: 64
# State list lengths
# ---------------------------------------------------------------
# 2**13 ` (= 8,192) epochs ~36 days
# 2**13 (= 8,192) epochs ~36 days
LATEST_RANDAO_MIXES_LENGTH: 8192
# 2**13 ` (= 8,192) epochs ~36 days
# 2**13 (= 8,192) epochs ~36 days
LATEST_ACTIVE_INDEX_ROOTS_LENGTH: 8192
# 2**13 ` (= 8,192) epochs ~36 days
# 2**13 (= 8,192) epochs ~36 days
LATEST_SLASHED_EXIT_LENGTH: 8192
# Reward and penalty quotients
# ---------------------------------------------------------------
# 2**5 ` (= 32)
# 2**5 (= 32)
BASE_REWARD_QUOTIENT: 32
# 2**9 ` (= 512)
# 2**9 (= 512)
WHISTLEBLOWING_REWARD_QUOTIENT: 512
# 2**3 ` (= 8)
# 2**3 (= 8)
PROPOSER_REWARD_QUOTIENT: 8
# 2**24 ` (= 16,777,216)
# 2**24 (= 16,777,216)
INACTIVITY_PENALTY_QUOTIENT: 16777216
# Max operations per block
# ---------------------------------------------------------------
# 2**5 ` (= 32)
# 2**5 (= 32)
MIN_PENALTY_QUOTIENT: 32
# 2**4 ` (= 16)
# 2**4 (= 16)
MAX_PROPOSER_SLASHINGS: 16
# 2**0 ` (= 1)
# 2**0 (= 1)
MAX_ATTESTER_SLASHINGS: 1
# 2**7 ` (= 128)
# 2**7 (= 128)
MAX_ATTESTATIONS: 128
# 2**4 ` (= 16)
# 2**4 (= 16)
MAX_DEPOSITS: 16
# 2**4 ` (= 16)
# 2**4 (= 16)
MAX_VOLUNTARY_EXITS: 16
# 2**4 ` (= 16)
# 2**4 (= 16)
MAX_TRANSFERS: 16

View File

@ -9,13 +9,13 @@ SHARD_COUNT: 8
# [customized] unsecure, but fast
TARGET_COMMITTEE_SIZE: 4
# 2**5 ` (= 32)
MAX_BALANCE_CHURN_QUOTIENT: 32
# 2**12 ` (= 4,096)
# 2**12 (= 4,096)
MAX_ATTESTATION_PARTICIPANTS: 4096
# 2**2 ` (= 4)
MAX_EXIT_DEQUEUES_PER_EPOCH: 4
# See issue 563
# 2**2 (= 4)
MIN_PER_EPOCH_CHURN_LIMIT: 4
# 2**16 (= 65,536)
CHURN_LIMIT_QUOTIENT: 65536
# [customized] Faster, but unsecure.
SHUFFLE_ROUND_COUNT: 10
@ -23,19 +23,19 @@ SHUFFLE_ROUND_COUNT: 10
# ---------------------------------------------------------------
# **TBD**
DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890
# 2**5 ` (= 32)
# 2**5 (= 32)
DEPOSIT_CONTRACT_TREE_DEPTH: 32
# Gwei values
# ---------------------------------------------------------------
# 2**0 * 10**9 ` (= 1,000,000,000) Gwei
# 2**0 * 10**9 (= 1,000,000,000) Gwei
MIN_DEPOSIT_AMOUNT: 1000000000
# 2**5 * 10**9 ` (= 32,000,000,000) Gwei
# 2**5 * 10**9 (= 32,000,000,000) Gwei
MAX_DEPOSIT_AMOUNT: 32000000000
# 2**4 * 10**9 ` (= 16,000,000,000) Gwei
# 2**4 * 10**9 (= 16,000,000,000) Gwei
EJECTION_BALANCE: 16000000000
# 2**0 * 10**9 ` (= 1,000,000,000) Gwei
# 2**0 * 10**9 (= 1,000,000,000) Gwei
HIGH_BALANCE_INCREMENT: 1000000000
@ -58,19 +58,19 @@ SECONDS_PER_SLOT: 6
MIN_ATTESTATION_INCLUSION_DELAY: 2
# [customized] fast epochs
SLOTS_PER_EPOCH: 8
# 2**0 ` (= 1) epochs 6.4 minutes
# 2**0 (= 1) epochs 6.4 minutes
MIN_SEED_LOOKAHEAD: 1
# 2**2 ` (= 4) epochs 25.6 minutes
# 2**2 (= 4) epochs 25.6 minutes
ACTIVATION_EXIT_DELAY: 4
# [customized] higher frequency new deposits from eth1 for testing
EPOCHS_PER_ETH1_VOTING_PERIOD: 2
SLOTS_PER_ETH1_VOTING_PERIOD: 16
# [customized] smaller state
SLOTS_PER_HISTORICAL_ROOT: 64
# 2**8 ` (= 256) epochs ~27 hours
# 2**8 (= 256) epochs ~27 hours
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
# 2**11 ` (= 2,048) epochs 9 days
# 2**11 (= 2,048) epochs 9 days
PERSISTENT_COMMITTEE_PERIOD: 2048
# 2**6 ` (= 64)
# 2**6 (= 64) epochs ~7 hours
MAX_CROSSLINK_EPOCHS: 64
@ -86,31 +86,31 @@ LATEST_SLASHED_EXIT_LENGTH: 64
# Reward and penalty quotients
# ---------------------------------------------------------------
# 2**5 ` (= 32)
# 2**5 (= 32)
BASE_REWARD_QUOTIENT: 32
# 2**9 ` (= 512)
# 2**9 (= 512)
WHISTLEBLOWING_REWARD_QUOTIENT: 512
# 2**3 ` (= 8)
# 2**3 (= 8)
PROPOSER_REWARD_QUOTIENT: 8
# 2**24 ` (= 16,777,216)
# 2**24 (= 16,777,216)
INACTIVITY_PENALTY_QUOTIENT: 16777216
# Max operations per block
# ---------------------------------------------------------------
# 2**5 ` (= 32)
# 2**5 (= 32)
MIN_PENALTY_QUOTIENT: 32
# 2**4 ` (= 16)
# 2**4 (= 16)
MAX_PROPOSER_SLASHINGS: 16
# 2**0 ` (= 1)
# 2**0 (= 1)
MAX_ATTESTER_SLASHINGS: 1
# 2**7 ` (= 128)
# 2**7 (= 128)
MAX_ATTESTATIONS: 128
# 2**4 ` (= 16)
# 2**4 (= 16)
MAX_DEPOSITS: 16
# 2**4 ` (= 16)
# 2**4 (= 16)
MAX_VOLUNTARY_EXITS: 16
# 2**4 ` (= 16)
# 2**4 (= 16)
MAX_TRANSFERS: 16

View File

@ -1,30 +0,0 @@
# ETH 2.0 py-tests
These tests are not intended for client-consumption.
These tests are sanity tests, to verify if the spec itself is consistent.
There are ideas to port these tests to the YAML test suite,
but we are still looking for inputs on how this should work.
## How to run tests
### Automated
Run `make test` from the root of the spec repository.
### Manual
From within the py_tests folder:
Install dependencies:
```bash
python3 -m venv venv
. venv/bin/activate
pip3 install -r requirements.txt
```
Note: make sure to run `make pyspec` from the root of the specs repository, to build the pyspec requirement.
Run the tests:
```
pytest -m minimal_config .
```

View File

@ -1,7 +0,0 @@
eth-utils>=1.3.0,<2
eth-typing>=2.1.0,<3.0.0
oyaml==0.7
pycryptodome==3.7.3
py_ecc>=1.6.0
pytest>=3.6,<3.7
../test_libs/pyspec

View File

@ -25,7 +25,6 @@
- [`Fork`](#fork)
- [`Crosslink`](#crosslink)
- [`Eth1Data`](#eth1data)
- [`Eth1DataVote`](#eth1datavote)
- [`AttestationData`](#attestationdata)
- [`AttestationDataAndCustodyBit`](#attestationdataandcustodybit)
- [`IndexedAttestation`](#indexedattestation)
@ -116,7 +115,6 @@
- [Helper functions](#helper-functions-1)
- [Justification](#justification)
- [Crosslinks](#crosslinks)
- [Eth1 data](#eth1-data)
- [Rewards and penalties](#rewards-and-penalties)
- [Justification and finalization](#justification-and-finalization)
- [Crosslinks](#crosslinks-1)
@ -229,7 +227,7 @@ These configurations are updated for releases, but may be out of sync during `de
| `SLOTS_PER_EPOCH` | `2**6` (= 64) | slots | 6.4 minutes |
| `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes |
| `ACTIVATION_EXIT_DELAY` | `2**2` (= 4) | epochs | 25.6 minutes |
| `EPOCHS_PER_ETH1_VOTING_PERIOD` | `2**4` (= 16) | epochs | ~1.7 hours |
| `SLOTS_PER_ETH1_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours |
| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~13 hours |
| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours |
| `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days |
@ -325,17 +323,6 @@ The types are defined topologically to aid in facilitating an executable version
}
```
#### `Eth1DataVote`
```python
{
# Data being voted for
'eth1_data': Eth1Data,
# Vote count
'vote_count': 'uint64',
}
```
#### `AttestationData`
```python
@ -615,7 +602,7 @@ The types are defined topologically to aid in facilitating an executable version
# Ethereum 1.0 chain data
'latest_eth1_data': Eth1Data,
'eth1_data_votes': [Eth1DataVote],
'eth1_data_votes': [Eth1Data],
'deposit_index': 'uint64',
}
```
@ -984,25 +971,17 @@ def generate_seed(state: BeaconState,
### `get_beacon_proposer_index`
```python
def get_beacon_proposer_index(state: BeaconState,
slot: Slot) -> ValidatorIndex:
def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
"""
Return the beacon proposer index for the ``slot``.
Due to proposer selection being based upon the validator balances during
the epoch in question, this can only be run for the current epoch.
Return the beacon proposer index at ``state.slot``.
"""
current_epoch = get_current_epoch(state)
assert slot_to_epoch(slot) == current_epoch
first_committee, _ = get_crosslink_committees_at_slot(state, slot)[0]
first_committee, _ = get_crosslink_committees_at_slot(state, state.slot)[0]
i = 0
while True:
rand_byte = hash(
generate_seed(state, current_epoch) +
int_to_bytes8(i // 32)
)[i % 32]
candidate = first_committee[(current_epoch + i) % len(first_committee)]
if get_effective_balance(state, candidate) * 256 > MAX_EFFECTIVE_BALANCE * rand_byte:
random_byte = hash(generate_seed(state, current_epoch) + int_to_bytes8(i // 32))[i % 32]
if get_effective_balance(state, candidate) * 256 > MAX_EFFECTIVE_BALANCE * random_byte:
return candidate
i += 1
```
@ -1051,16 +1030,8 @@ def get_attestation_participants(state: BeaconState,
Return the sorted participant indices corresponding to ``attestation_data`` and ``bitfield``.
"""
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data)
assert verify_bitfield(bitfield, len(crosslink_committee))
# Find the participating attesters in the committee
participants = []
for i, validator_index in enumerate(crosslink_committee):
aggregation_bit = get_bitfield_bit(bitfield, i)
if aggregation_bit == 0b1:
participants.append(validator_index)
return sorted(participants)
return sorted([index for i, index in enumerate(crosslink_committee) if get_bitfield_bit(bitfield, i) == 0b1])
```
### `int_to_bytes1`, `int_to_bytes2`, ...
@ -1344,7 +1315,7 @@ def slash_validator(state: BeaconState, slashed_index: ValidatorIndex, whistlebl
slashed_balance = get_effective_balance(state, slashed_index)
state.latest_slashed_balances[get_current_epoch(state) % LATEST_SLASHED_EXIT_LENGTH] += slashed_balance
proposer_index = get_beacon_proposer_index(state, state.slot)
proposer_index = get_beacon_proposer_index(state)
if whistleblower_index is None:
whistleblower_index = proposer_index
whistleblowing_reward = slashed_balance // WHISTLEBLOWING_REWARD_QUOTIENT
@ -1494,9 +1465,9 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
process_deposit(state, deposit)
# Process genesis activations
for validator_index in range(len(state.validator_registry)):
if get_effective_balance(state, validator_index) >= MAX_EFFECTIVE_BALANCE:
activate_validator(state, validator_index, is_genesis=True)
for index in range(len(state.validator_registry)):
if get_effective_balance(state, index) >= MAX_EFFECTIVE_BALANCE:
activate_validator(state, index, is_genesis=True)
genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH))
for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH):
@ -1547,8 +1518,8 @@ def get_ancestor(store: Store, block: BeaconBlock, slot: Slot) -> BeaconBlock:
return get_ancestor(store, store.get_parent(block), slot)
```
* Let `get_latest_attestation(store: Store, validator_index: ValidatorIndex) -> Attestation` be the attestation with the highest slot number in `store` from the validator with the given `validator_index`. If several such attestations exist, use the one the [validator](#dfn-validator) `v` observed first.
* Let `get_latest_attestation_target(store: Store, validator_index: ValidatorIndex) -> BeaconBlock` be the target block in the attestation `get_latest_attestation(store, validator_index)`.
* Let `get_latest_attestation(store: Store, index: ValidatorIndex) -> Attestation` be the attestation with the highest slot number in `store` from the validator with the given `index`. If several such attestations exist, use the one the [validator](#dfn-validator) `v` observed first.
* Let `get_latest_attestation_target(store: Store, index: ValidatorIndex) -> BeaconBlock` be the target block in the attestation `get_latest_attestation(store, index)`.
* Let `get_children(store: Store, block: BeaconBlock) -> List[BeaconBlock]` returns the child blocks of the given `block`.
* Let `justified_head_state` be the resulting `BeaconState` object from processing the chain up to the `justified_head`.
* The `head` is `lmd_ghost(store, justified_head_state, justified_head)` where the function `lmd_ghost` is defined below. Note that the implementation below is suboptimal; there are implementations that compute the head in time logarithmic in slot count.
@ -1560,10 +1531,7 @@ def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock)
"""
validators = start_state.validator_registry
active_validator_indices = get_active_validator_indices(validators, slot_to_epoch(start_state.slot))
attestation_targets = [
(validator_index, get_latest_attestation_target(store, validator_index))
for validator_index in active_validator_indices
]
attestation_targets = [(i, get_latest_attestation_target(store, i)) for i in active_validator_indices]
# Use the rounded-balance-with-hysteresis supplied by the protocol for fork
# choice voting. This reduces the number of recomputations that need to be
@ -1628,7 +1596,7 @@ The steps below happen when `state.slot > GENESIS_SLOT and (state.slot + 1) % SL
#### Helper functions
We define some helper functions utilized when processing an epoch transition:
We define epoch transition helper functions:
```python
def get_current_total_balance(state: BeaconState) -> Gwei:
@ -1702,71 +1670,54 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple
```
```python
def earliest_attestation(state: BeaconState, validator_index: ValidatorIndex) -> PendingAttestation:
def get_earliest_attestation(state: BeaconState, attestations: List[PendingAttestation], index: ValidatorIndex) -> PendingAttestation:
return min([
a for a in state.previous_epoch_attestations if
validator_index in get_attestation_participants(state, a.data, a.aggregation_bitfield)
a for a in attestations if index in get_attestation_participants(state, a.data, a.aggregation_bitfield)
], key=lambda a: a.inclusion_slot)
```
```python
def inclusion_slot(state: BeaconState, validator_index: ValidatorIndex) -> Slot:
return earliest_attestation(state, validator_index).inclusion_slot
```
```python
def inclusion_distance(state: BeaconState, validator_index: ValidatorIndex) -> int:
attestation = earliest_attestation(state, validator_index)
return attestation.inclusion_slot - attestation.data.slot
```
#### Justification
Run the following function:
```python
def update_justification_and_finalization(state: BeaconState) -> None:
new_justified_epoch = state.current_justified_epoch
new_finalized_epoch = state.finalized_epoch
antepenultimate_justified_epoch = state.previous_justified_epoch
# Rotate the justification bitfield up one epoch to make room for the current epoch (and limit to 64 bits)
# Process justifications
state.previous_justified_epoch = state.current_justified_epoch
state.previous_justified_root = state.current_justified_root
state.justification_bitfield = (state.justification_bitfield << 1) % 2**64
# If the previous epoch gets justified, fill the second last bit
previous_boundary_attesting_balance = get_attesting_balance(state, get_previous_epoch_boundary_attestations(state))
if previous_boundary_attesting_balance * 3 >= get_previous_total_balance(state) * 2:
new_justified_epoch = get_current_epoch(state) - 1
state.justification_bitfield |= 2
# If the current epoch gets justified, fill the last bit
state.current_justified_epoch = get_previous_epoch(state)
state.current_justified_root = get_block_root(state, get_epoch_start_slot(state.current_justified_epoch))
state.justification_bitfield |= (1 << 1)
current_boundary_attesting_balance = get_attesting_balance(state, get_current_epoch_boundary_attestations(state))
if current_boundary_attesting_balance * 3 >= get_current_total_balance(state) * 2:
new_justified_epoch = get_current_epoch(state)
state.justification_bitfield |= 1
state.current_justified_epoch = get_current_epoch(state)
state.current_justified_root = get_block_root(state, get_epoch_start_slot(state.current_justified_epoch))
state.justification_bitfield |= (1 << 0)
# Process finalizations
bitfield = state.justification_bitfield
current_epoch = get_current_epoch(state)
# The 2nd/3rd/4th most recent epochs are all justified, the 2nd using the 4th as source
if (bitfield >> 1) % 8 == 0b111 and state.previous_justified_epoch == current_epoch - 3:
new_finalized_epoch = state.previous_justified_epoch
# The 2nd/3rd most recent epochs are both justified, the 2nd using the 3rd as source
if (bitfield >> 1) % 4 == 0b11 and state.previous_justified_epoch == current_epoch - 2:
new_finalized_epoch = state.previous_justified_epoch
# The 1st/2nd/3rd most recent epochs are all justified, the 1st using the 3rd as source
if (bitfield >> 0) % 8 == 0b111 and state.current_justified_epoch == current_epoch - 2:
new_finalized_epoch = state.current_justified_epoch
# The 1st/2nd most recent epochs are both justified, the 1st using the 2nd as source
if (bitfield >> 0) % 4 == 0b11 and state.current_justified_epoch == current_epoch - 1:
new_finalized_epoch = state.current_justified_epoch
# Update state jusification/finality fields
state.previous_justified_epoch = state.current_justified_epoch
state.previous_justified_root = state.current_justified_root
if new_justified_epoch != state.current_justified_epoch:
state.current_justified_epoch = new_justified_epoch
state.current_justified_root = get_block_root(state, get_epoch_start_slot(new_justified_epoch))
if new_finalized_epoch != state.finalized_epoch:
state.finalized_epoch = new_finalized_epoch
state.finalized_root = get_block_root(state, get_epoch_start_slot(new_finalized_epoch))
# The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source
if (bitfield >> 1) % 8 == 0b111 and antepenultimate_justified_epoch == current_epoch - 3:
state.finalized_epoch = antepenultimate_justified_epoch
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
# The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source
if (bitfield >> 1) % 4 == 0b11 and antepenultimate_justified_epoch == current_epoch - 2:
state.finalized_epoch = antepenultimate_justified_epoch
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
# The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source
if (bitfield >> 0) % 8 == 0b111 and state.previous_justified_root == current_epoch - 2:
state.finalized_epoch = state.previous_justified_root
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
# The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source
if (bitfield >> 0) % 4 == 0b11 and state.previous_justified_root == current_epoch - 1:
state.finalized_epoch = state.previous_justified_root
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
```
#### Crosslinks
@ -1790,34 +1741,24 @@ def process_crosslinks(state: BeaconState) -> None:
)
```
#### Eth1 data
Run the following function:
```python
def maybe_reset_eth1_period(state: BeaconState) -> None:
if (get_current_epoch(state) + 1) % EPOCHS_PER_ETH1_VOTING_PERIOD == 0:
for eth1_data_vote in state.eth1_data_votes:
# If a majority of all votes were for a particular eth1_data value,
# then set that as the new canonical value
if eth1_data_vote.vote_count * 2 > EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH:
state.latest_eth1_data = eth1_data_vote.eth1_data
state.eth1_data_votes = []
```
#### Rewards and penalties
First, we define some additional helpers:
```python
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
if get_previous_total_balance(state) == 0:
def get_base_reward_from_total_balance(state: BeaconState, total_balance: Gwei, index: ValidatorIndex) -> Gwei:
if total_balance == 0:
return 0
adjusted_quotient = integer_squareroot(get_previous_total_balance(state)) // BASE_REWARD_QUOTIENT
adjusted_quotient = integer_squareroot(total_balance) // BASE_REWARD_QUOTIENT
return get_effective_balance(state, index) // adjusted_quotient // 5
```
```python
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
return get_base_reward_from_total_balance(state, get_previous_total_balance(state), index)
```
```python
def get_inactivity_penalty(state: BeaconState, index: ValidatorIndex, epochs_since_finality: int) -> Gwei:
if epochs_since_finality <= 4:
@ -1858,10 +1799,9 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[
if index in get_unslashed_attesting_indices(state, state.previous_epoch_attestations):
rewards[index] += base_reward * total_attesting_balance // total_balance
# Inclusion speed bonus
rewards[index] += (
base_reward * MIN_ATTESTATION_INCLUSION_DELAY //
inclusion_distance(state, index)
)
earliest_attestation = get_earliest_attestation(state, state.previous_epoch_attestations, index)
inclusion_delay = earliest_attestation.inclusion_slot - earliest_attestation.data.slot
rewards[index] += base_reward * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_delay
else:
penalties[index] += base_reward
# Expected FFG target
@ -1874,10 +1814,6 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[
rewards[index] += base_reward * matching_head_balance // total_balance
else:
penalties[index] += base_reward
# Proposer bonus
if index in get_unslashed_attesting_indices(state, state.previous_epoch_attestations):
proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index))
rewards[proposer_index] += base_reward // PROPOSER_REWARD_QUOTIENT
# Take away max rewards if we're not finalizing
if epochs_since_finality > 4:
penalties[index] += base_reward * 4
@ -1914,14 +1850,8 @@ def apply_rewards(state: BeaconState) -> None:
rewards1, penalties1 = get_justification_and_finalization_deltas(state)
rewards2, penalties2 = get_crosslink_deltas(state)
for i in range(len(state.validator_registry)):
set_balance(
state,
i,
max(
0,
get_balance(state, i) + rewards1[i] + rewards2[i] - penalties1[i] - penalties2[i],
),
)
increase_balance(state, i, rewards1[i] + rewards2[i])
decrease_balance(state, i, penalties1[i] + penalties2[i])
```
#### Balance-driven status transitions
@ -2000,6 +1930,9 @@ Run the following function:
def finish_epoch_update(state: BeaconState) -> None:
current_epoch = get_current_epoch(state)
next_epoch = current_epoch + 1
# Reset eth1 data votes
if state.slot % SLOTS_PER_ETH1_VOTING_PERIOD == 0:
state.eth1_data_votes = []
# Set active index root
index_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % LATEST_ACTIVE_INDEX_ROOTS_LENGTH
state.latest_active_index_roots[index_root_position] = hash_tree_root(
@ -2047,7 +1980,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
# Save current block as the new latest block
state.latest_block_header = get_temporary_block_header(block)
# Verify proposer is not slashed
proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]
proposer = state.validator_registry[get_beacon_proposer_index(state)]
assert not proposer.slashed
# Verify proposer signature
assert bls_verify(
@ -2062,7 +1995,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
```python
def process_randao(state: BeaconState, block: BeaconBlock) -> None:
proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]
proposer = state.validator_registry[get_beacon_proposer_index(state)]
# Verify that the provided randao value is valid
assert bls_verify(
pubkey=proposer.pubkey,
@ -2081,13 +2014,9 @@ def process_randao(state: BeaconState, block: BeaconBlock) -> None:
```python
def process_eth1_data(state: BeaconState, block: BeaconBlock) -> None:
for eth1_data_vote in state.eth1_data_votes:
# If someone else has already voted for the same hash, add to its counter
if eth1_data_vote.eth1_data == block.body.eth1_data:
eth1_data_vote.vote_count += 1
return
# If we're seeing this hash for the first time, make a new counter
state.eth1_data_votes.append(Eth1DataVote(eth1_data=block.body.eth1_data, vote_count=1))
state.eth1_data_votes.append(block.body.eth1_data)
if state.eth1_data_votes.count(block.body.eth1_data) * 2 > SLOTS_PER_ETH1_VOTING_PERIOD:
state.latest_eth1_data = block.body.eth1_data
```
#### Operations
@ -2210,6 +2139,18 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
state.previous_epoch_attestations.append(pending_attestation)
```
Run `process_proposer_attestation_rewards(state)`.
```python
def process_proposer_attestation_rewards(state: BeaconState) -> None:
proposer_index = get_beacon_proposer_index(state)
for pending_attestations in (state.previous_epoch_attestations, state.current_epoch_attestations):
for index in get_unslashed_attesting_indices(state, pending_attestations):
if get_earliest_attestation(state, pending_attestations, index).inclusion_slot == state.slot:
base_reward = get_base_reward_from_total_balance(state, get_current_total_balance(state), index)
increase_balance(state, proposer_index, base_reward // PROPOSER_REWARD_QUOTIENT)
```
##### Deposits
Verify that `len(block.body.deposits) == min(MAX_DEPOSITS, latest_eth1_data.deposit_count - state.deposit_index)`.
@ -2356,7 +2297,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
# Process the transfer
decrease_balance(state, transfer.sender, transfer.amount + transfer.fee)
increase_balance(state, transfer.recipient, transfer.amount)
increase_balance(state, get_beacon_proposer_index(state, state.slot), transfer.fee)
increase_balance(state, get_beacon_proposer_index(state), transfer.fee)
# Verify balances are not dust
assert not (0 < get_balance(state, transfer.sender) < MIN_DEPOSIT_AMOUNT)
assert not (0 < get_balance(state, transfer.recipient) < MIN_DEPOSIT_AMOUNT)

View File

@ -283,7 +283,7 @@ def process_custody_reveal(state: BeaconState,
assert is_active_validator(revealer, get_current_epoch(state)) or revealer.exit_epoch > get_current_epoch(state)
revealer.custody_reveal_index += 1
revealer.max_reveal_lateness = max(revealer.max_reveal_lateness, current_custody_period - reveal.period)
proposer_index = get_beacon_proposer_index(state, state.slot)
proposer_index = get_beacon_proposer_index(state)
increase_balance(state, proposer_index, base_reward(state, index) // MINOR_REWARD_QUOTIENT)
# Case 2: masked punitive early reveal
@ -323,7 +323,7 @@ def process_chunk_challenge(state: BeaconState,
# Add new chunk challenge record
state.custody_chunk_challenge_records.append(CustodyChunkChallengeRecord(
challenge_index=state.custody_challenge_index,
challenger_index=get_beacon_proposer_index(state, state.slot),
challenger_index=get_beacon_proposer_index(state),
responder_index=challenge.responder_index
deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE,
crosslink_data_root=challenge.attestation.data.crosslink_data_root,
@ -436,7 +436,7 @@ def process_chunk_challenge_response(state: BeaconState,
# Clear the challenge
state.custody_chunk_challenge_records.remove(challenge)
# Reward the proposer
proposer_index = get_beacon_proposer_index(state, state.slot)
proposer_index = get_beacon_proposer_index(state)
increase_balance(state, proposer_index, base_reward(state, index) // MINOR_REWARD_QUOTIENT)
```

View File

@ -406,4 +406,4 @@ def is_valid_beacon_attestation(shard: Shard,
## Shard fork choice rule
The fork choice rule for any shard is LMD GHOST using the shard attestations of the persistent committee and the beacon chain attestations of the crosslink committee currently assigned to that shard, but instead of being rooted in the genesis it is rooted in the block referenced in the most recent accepted crosslink (i.e. `state.crosslinks[shard].shard_block_root`). Only blocks whose `beacon_chain_root` is the block in the main beacon chain at the specified `slot` should be considered. (If the beacon chain skips a slot, then the block at that slot is considered to be the block in the beacon chain at the highest slot lower than a slot.)
The fork choice rule for any shard is LMD GHOST using the shard attestations of the persistent committee and the beacon chain attestations of the crosslink committee currently assigned to that shard, but instead of being rooted in the genesis it is rooted in the block referenced in the most recent accepted crosslink (i.e. `state.crosslinks[shard].shard_block_root`). Only blocks whose `beacon_chain_root` is the block in the main beacon chain at the specified `slot` should be considered. (If the beacon chain skips a slot, then the block at that slot is considered to be the block in the beacon chain at the highest slot lower than that slot.)

View File

@ -369,24 +369,23 @@ def get_committee_assignment(
return assignment
```
A validator can use the following function to see if they are supposed to propose during their assigned committee slot. This function can only be run during the epoch of the slot in question and can not reliably be used to predict an epoch in advance.
A validator can use the following function to see if they are supposed to propose during their assigned committee slot. This function can only be run during the slot in question and can not reliably be used to predict in advance.
```python
def is_proposer_at_slot(state: BeaconState,
slot: Slot,
validator_index: ValidatorIndex) -> bool:
current_epoch = get_current_epoch(state)
assert slot_to_epoch(slot) == current_epoch
assert state.slot == slot
return get_beacon_proposer_index(state, slot) == validator_index
return get_beacon_proposer_index(state) == validator_index
```
_Note_: If a validator is assigned to the 0th slot of an epoch, the validator must run an empty slot transition from the previous epoch into the 0th slot of the epoch to be able to check if they are a proposer at that slot.
_Note_: To see if a validator is assigned to proposer during the slot, the validator must run an empty slot transition from the previous state to the current slot.
### Lookahead
The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing which must checked during the epoch in question.
The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing which must checked during the slot in question.
`get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments which involves noting at which future slot one will have to attest and also which shard one should begin syncing (in phase 1+).

View File

@ -7,6 +7,7 @@ With this executable spec,
test-generators can easily create test-vectors for client implementations,
and the spec itself can be verified to be consistent and coherent, through sanity tests implemented with pytest.
## Building
All the dynamic parts of the spec can be build at once with `make pyspec`.
@ -15,12 +16,42 @@ Alternatively, you can build a sub-set of the pyspec: `make phase0`.
Or, to build a single file, specify the path, e.g. `make test_libs/pyspec/eth2spec/phase0/spec.py`
## Py-tests
These tests are not intended for client-consumption.
These tests are sanity tests, to verify if the spec itself is consistent.
### How to run tests
#### Automated
Run `make test` from the root of the spec repository.
#### Manual
From within the `pyspec` folder:
Install dependencies:
```bash
python3 -m venv venv
. venv/bin/activate
pip3 install -r requirements.txt
```
Note: make sure to run `make pyspec` from the root of the specs repository,
to build the parts of the pyspec module derived from the markdown specs.
Run the tests:
```
pytest -m minimal_config .
```
## Contributing
Contributions are welcome, but consider implementing your idea as part of the spec itself first.
The pyspec is not a replacement.
If you see opportunity to include any of the `pyspec/eth2spec/utils/` code in the spec,
please submit an issue or PR.
## License

View File

@ -10,7 +10,8 @@ from typing import (
from .spec import (
BeaconState,
BeaconBlock,
Slot
Slot,
process_proposer_attestation_rewards,
)
@ -51,6 +52,7 @@ def process_operations(state: BeaconState, block: BeaconBlock) -> None:
spec.MAX_ATTESTATIONS,
spec.process_attestation,
)
process_proposer_attestation_rewards(state)
assert len(block.body.deposits) == expected_deposit_count(state)
process_operation_type(
@ -91,7 +93,6 @@ def process_block(state: BeaconState,
def process_epoch_transition(state: BeaconState) -> None:
spec.update_justification_and_finalization(state)
spec.process_crosslinks(state)
spec.maybe_reset_eth1_period(state)
spec.apply_rewards(state)
spec.process_balance_driven_status_transitions(state)
spec.update_registry(state)
@ -112,4 +113,3 @@ def state_transition(state: BeaconState,
verify_state_root: bool=False) -> BeaconState:
state_transition_to(state, block.slot)
process_block(state, block, verify_state_root)

View File

@ -2,3 +2,4 @@ eth-utils>=1.3.0,<2
eth-typing>=2.1.0,<3.0.0
pycryptodome==3.7.3
py_ecc>=1.6.0
pytest>=3.6,<3.7

View File

@ -3,6 +3,7 @@ from setuptools import setup, find_packages
setup(
name='pyspec',
packages=find_packages(),
tests_require=["pytest"],
install_requires=[
"eth-utils>=1.3.0,<2",
"eth-typing>=2.1.0,<3.0.0",

View File

View File

@ -11,7 +11,7 @@ from eth2spec.phase0.spec import (
process_attestation,
slot_to_epoch,
)
from phase0.helpers import (
from tests.helpers import (
build_empty_block_for_next_slot,
get_valid_attestation,
)

View File

@ -7,7 +7,7 @@ from eth2spec.phase0.spec import (
get_beacon_proposer_index,
process_attester_slashing,
)
from phase0.helpers import (
from tests.helpers import (
get_valid_attester_slashing,
)
@ -39,7 +39,7 @@ def run_attester_slashing_processing(state, attester_slashing, valid=True):
get_balance(post_state, slashed_index) <
get_balance(state, slashed_index)
)
proposer_index = get_beacon_proposer_index(state, state.slot)
proposer_index = get_beacon_proposer_index(state)
# gained whistleblower reward
assert (
get_balance(post_state, proposer_index) >

View File

@ -8,8 +8,9 @@ from eth2spec.phase0.spec import (
advance_slot,
process_block_header,
)
from phase0.helpers import (
from tests.helpers import (
build_empty_block_for_next_slot,
next_slot,
)
# mark entire file as 'header'
@ -61,8 +62,12 @@ def test_invalid_previous_block_root(state):
def test_proposer_slashed(state):
# use stub state to get proposer index of next slot
stub_state = deepcopy(state)
next_slot(stub_state)
proposer_index = get_beacon_proposer_index(stub_state)
# set proposer to slashed
proposer_index = get_beacon_proposer_index(state, state.slot + 1)
state.validator_registry[proposer_index].slashed = True
block = build_empty_block_for_next_slot(state)

View File

@ -8,7 +8,7 @@ from eth2spec.phase0.spec import (
ZERO_HASH,
process_deposit,
)
from phase0.helpers import (
from tests.helpers import (
build_deposit,
privkeys,
pubkeys,

View File

@ -7,7 +7,7 @@ from eth2spec.phase0.spec import (
get_current_epoch,
process_proposer_slashing,
)
from phase0.helpers import (
from tests.helpers import (
get_valid_proposer_slashing,
)

View File

@ -9,7 +9,7 @@ from eth2spec.phase0.spec import (
get_current_epoch,
process_voluntary_exit,
)
from phase0.helpers import (
from tests.helpers import (
build_voluntary_exit,
pubkey_to_privkey,
)

View File

@ -2,6 +2,9 @@ from copy import deepcopy
from py_ecc import bls
from eth2spec.phase0.state_transition import (
state_transition,
)
import eth2spec.phase0.spec as spec
from eth2spec.utils.minimal_ssz import signing_root
from eth2spec.phase0.spec import (
@ -303,3 +306,8 @@ def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0)
domain_type=spec.DOMAIN_ATTESTATION,
)
)
def next_slot(state):
block = build_empty_block_for_next_slot(state)
state_transition(state, block)

View File

@ -160,7 +160,7 @@ def test_attester_slashing(state):
# lost whistleblower reward
assert get_balance(test_state, validator_index) < get_balance(state, validator_index)
proposer_index = get_beacon_proposer_index(test_state, test_state.slot)
proposer_index = get_beacon_proposer_index(test_state)
# gained whistleblower reward
assert (
get_balance(test_state, proposer_index) >
@ -260,6 +260,9 @@ def test_attestation(state):
assert len(test_state.current_epoch_attestations) == len(state.current_epoch_attestations) + 1
proposer_index = get_beacon_proposer_index(test_state)
assert test_state.balances[proposer_index] > state.balances[proposer_index]
#
# Epoch transition should move to previous_epoch_attestations
#