Merge branch 'dev' into fix-deposit-forkv

This commit is contained in:
Danny Ryan 2019-05-21 11:04:13 -06:00
commit 7a9097ec23
No known key found for this signature in database
GPG Key ID: 2765A792E42CE07A
28 changed files with 538 additions and 586 deletions

View File

@ -52,7 +52,7 @@ jobs:
key: v1-specs-repo-{{ .Branch }}-{{ .Revision }}
paths:
- ~/specs-repo
install_test:
install_env:
docker:
- image: circleci/python:3.6
working_directory: ~/specs-repo
@ -60,13 +60,13 @@ jobs:
- restore_cache:
key: v1-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_cached_venv:
venv_name: v1-pyspec
venv_name: v1-pyspec-03
reqs_checksum: '{{ checksum "test_libs/pyspec/requirements.txt" }}-{{ checksum "test_libs/pyspec/requirements-testing.txt" }}'
- run:
name: Install pyspec requirements
command: make install_test
command: make install_test && make install_lint
- save_cached_venv:
venv_name: v1-pyspec
venv_name: v1-pyspec-03
reqs_checksum: '{{ checksum "test_libs/pyspec/requirements.txt" }}-{{ checksum "test_libs/pyspec/requirements-testing.txt" }}'
venv_path: ./test_libs/pyspec/venv
test:
@ -77,21 +77,37 @@ jobs:
- restore_cache:
key: v1-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_cached_venv:
venv_name: v1-pyspec
venv_name: v1-pyspec-03
reqs_checksum: '{{ checksum "test_libs/pyspec/requirements.txt" }}-{{ checksum "test_libs/pyspec/requirements-testing.txt" }}'
- run:
name: Run py-tests
command: make citest
- store_test_results:
path: test_libs/pyspec/test-reports
lint:
docker:
- image: circleci/python:3.6
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v1-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_cached_venv:
venv_name: v1-pyspec-03
reqs_checksum: '{{ checksum "test_libs/pyspec/requirements.txt" }}-{{ checksum "test_libs/pyspec/requirements-testing.txt" }}'
- run:
name: Run linter
command: make lint
workflows:
version: 2.1
test_spec:
jobs:
- checkout_specs
- install_test:
- install_env:
requires:
- checkout_specs
- test:
requires:
- install_test
- install_env
- lint:
requires:
- test

View File

@ -39,6 +39,13 @@ test: $(PY_SPEC_ALL_TARGETS)
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.xml .
install_lint:
cd $(PY_SPEC_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install flake8==3.5.0
lint: $(PY_SPEC_ALL_TARGETS)
cd $(PY_SPEC_DIR); . venv/bin/activate; \
flake8 --max-line-length=120 ./eth2spec;
# "make pyspec" to create the pyspec for all phases.
pyspec: $(PY_SPEC_ALL_TARGETS)

View File

@ -72,7 +72,7 @@ MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
# 2**11 (= 2,048) epochs 9 days
PERSISTENT_COMMITTEE_PERIOD: 2048
# 2**6 (= 64) epochs ~7 hours
MAX_CROSSLINK_EPOCHS: 64
MAX_EPOCHS_PER_CROSSLINK: 64
# 2**2 (= 4) epochs 25.6 minutes
MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4
@ -124,4 +124,4 @@ DOMAIN_RANDAO: 1
DOMAIN_ATTESTATION: 2
DOMAIN_DEPOSIT: 3
DOMAIN_VOLUNTARY_EXIT: 4
DOMAIN_TRANSFER: 5
DOMAIN_TRANSFER: 5

View File

@ -71,7 +71,7 @@ MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
# 2**11 (= 2,048) epochs 9 days
PERSISTENT_COMMITTEE_PERIOD: 2048
# 2**6 (= 64) epochs ~7 hours
MAX_CROSSLINK_EPOCHS: 64
MAX_EPOCHS_PER_CROSSLINK: 64
# 2**2 (= 4) epochs 25.6 minutes
MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4
@ -123,4 +123,4 @@ DOMAIN_RANDAO: 1
DOMAIN_ATTESTATION: 2
DOMAIN_DEPOSIT: 3
DOMAIN_VOLUNTARY_EXIT: 4
DOMAIN_TRANSFER: 5
DOMAIN_TRANSFER: 5

View File

@ -5,6 +5,7 @@ import function_puller
def build_phase0_spec(sourcefile, outfile):
code_lines = []
code_lines.append("""
from typing import (
Any,
Dict,
@ -12,19 +13,22 @@ from typing import (
NewType,
Tuple,
)
from eth2spec.utils.minimal_ssz import *
from eth2spec.utils.bls_stub import *
from eth2spec.utils.minimal_ssz import (
SSZType,
hash_tree_root,
signing_root,
)
from eth2spec.utils.bls_stub import (
bls_aggregate_pubkeys,
bls_verify,
bls_verify_multiple,
)
from eth2spec.utils.hash_function import hash
""")
for i in (1, 2, 3, 4, 8, 32, 48, 96):
code_lines.append("def int_to_bytes%d(x): return x.to_bytes(%d, 'little')" % (i, i))
code_lines.append("""
# stub, will get overwritten by real var
SLOTS_PER_EPOCH = 64
Slot = NewType('Slot', int) # uint64
Epoch = NewType('Epoch', int) # uint64
Shard = NewType('Shard', int) # uint64
@ -68,6 +72,7 @@ def hash(x):
hash_cache[x] = ret
return ret
# Access to overwrite spec constants based on configuration
def apply_constants_preset(preset: Dict[str, Any]):
global_vars = globals()

View File

@ -55,15 +55,19 @@ def get_spec(file_name: str) -> List[str]:
if eligible:
code_lines.append(row[0] + ' = ' + (row[1].replace('**TBD**', '0x1234567890123456789012345678901234567890')))
# Build type-def re-initialization
code_lines.append('')
code_lines.append('\n')
code_lines.append('def init_SSZ_types():')
code_lines.append(' global_vars = globals()')
for ssz_type_name, ssz_type in type_defs:
code_lines.append('')
for type_line in ssz_type:
code_lines.append(' ' + type_line)
if len(type_line) > 0:
code_lines.append(' ' + type_line)
code_lines.append('\n')
code_lines.append('ssz_types = [' + ', '.join([f'\'{ssz_type_name}\'' for (ssz_type_name, _) in type_defs]) + ']')
code_lines.append('ssz_types = [\n')
for (ssz_type_name, _) in type_defs:
code_lines.append(f' {ssz_type_name},\n')
code_lines.append(']')
code_lines.append('\n')
code_lines.append('def get_ssz_type_by_name(name: str) -> SSZType:')
code_lines.append(' return globals()[name]')

View File

@ -1,6 +1,8 @@
# BLS signature verification
**Warning: This document is pending academic review and should not yet be considered secure.**
**Notice**: This document is a placeholder to facilitate the emergence of cross-client testnets. Substantive changes are postponed until [BLS standardisation](https://github.com/pairingwg/bls_standard) is finalized.
**Warning**: The constructions in this document should not be considered secure. In particular, the `hash_to_G2` function is known to be unsecure.
## Table of contents
<!-- TOC -->

View File

@ -75,14 +75,14 @@
- [`compute_committee`](#compute_committee)
- [`get_crosslink_committee`](#get_crosslink_committee)
- [`get_attesting_indices`](#get_attesting_indices)
- [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-)
- [`int_to_bytes`](#int_to_bytes)
- [`bytes_to_int`](#bytes_to_int)
- [`get_total_balance`](#get_total_balance)
- [`get_domain`](#get_domain)
- [`get_bitfield_bit`](#get_bitfield_bit)
- [`verify_bitfield`](#verify_bitfield)
- [`convert_to_indexed`](#convert_to_indexed)
- [`verify_indexed_attestation`](#verify_indexed_attestation)
- [`validate_indexed_attestation`](#validate_indexed_attestation)
- [`is_slashable_attestation_data`](#is_slashable_attestation_data)
- [`integer_squareroot`](#integer_squareroot)
- [`get_delayed_activation_exit_epoch`](#get_delayed_activation_exit_epoch)
@ -98,8 +98,7 @@
- [Genesis state](#genesis-state)
- [Genesis block](#genesis-block)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [State caching](#state-caching)
- [Per-epoch processing](#per-epoch-processing)
- [Epoch processing](#epoch-processing)
- [Helper functions](#helper-functions-1)
- [Justification and finalization](#justification-and-finalization)
- [Crosslinks](#crosslinks)
@ -107,8 +106,7 @@
- [Registry updates](#registry-updates)
- [Slashings](#slashings)
- [Final updates](#final-updates)
- [Per-slot processing](#per-slot-processing)
- [Per-block processing](#per-block-processing)
- [Block processing](#block-processing)
- [Block header](#block-header)
- [RANDAO](#randao)
- [Eth1 data](#eth1-data)
@ -119,7 +117,6 @@
- [Deposits](#deposits)
- [Voluntary exits](#voluntary-exits)
- [Transfers](#transfers)
- [State root verification](#state-root-verification)
<!-- /TOC -->
@ -194,8 +191,8 @@ 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` | `int_to_bytes32(0)` |
| `BLS_WITHDRAWAL_PREFIX_BYTE` | `int_to_bytes1(0)` |
| `ZERO_HASH` | `b'\x00' * 32` |
| `BLS_WITHDRAWAL_PREFIX` | `0` |
### Time parameters
@ -209,10 +206,10 @@ These configurations are updated for releases, but may be out of sync during `de
| `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 |
| `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) | epochs | ~7 hours |
| `MAX_EPOCHS_PER_CROSSLINK` | `2**6` (= 64) | epochs | ~7 hours |
| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes |
* `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH`
* `MAX_EPOCHS_PER_CROSSLINK` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH`
### State list lengths
@ -282,12 +279,15 @@ The types are defined topologically to aid in facilitating an executable version
```python
{
# Epoch number
'epoch': 'uint64',
# Shard number
'shard': 'uint64',
# Crosslinking data from epochs [start....end-1]
'start_epoch': 'uint64',
'end_epoch': 'uint64',
# Root of the previous crosslink
'previous_crosslink_root': 'bytes32',
'parent_root': 'bytes32',
# Root of the crosslinked shard data since the previous crosslink
'crosslink_data_root': 'bytes32',
'data_root': 'bytes32',
}
```
@ -318,9 +318,7 @@ The types are defined topologically to aid in facilitating an executable version
'target_root': 'bytes32',
# Crosslink vote
'shard': 'uint64',
'previous_crosslink_root': 'bytes32',
'crosslink_data_root': 'bytes32',
'crosslink': Crosslink,
}
```
@ -369,12 +367,13 @@ The types are defined topologically to aid in facilitating an executable version
```python
{
'slot': 'uint64',
'previous_block_root': 'bytes32',
'parent_root': 'bytes32',
'state_root': 'bytes32',
'block_body_root': 'bytes32',
'body_root': 'bytes32',
'signature': 'bytes96',
}
```
#### `Validator`
```python
@ -536,7 +535,7 @@ The types are defined topologically to aid in facilitating an executable version
{
# Header
'slot': 'uint64',
'previous_block_root': 'bytes32',
'parent_root': 'bytes32',
'state_root': 'bytes32',
'body': BeaconBlockBody,
'signature': 'bytes96',
@ -579,8 +578,8 @@ The types are defined topologically to aid in facilitating an executable version
'latest_block_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT],
'latest_state_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT],
'latest_active_index_roots': ['bytes32', LATEST_ACTIVE_INDEX_ROOTS_LENGTH],
'latest_slashed_balances': ['uint64', LATEST_SLASHED_EXIT_LENGTH], # Balances slashed at every withdrawal period
'latest_block_header': BeaconBlockHeader, # `latest_block_header.state_root == ZERO_HASH` temporarily
'latest_slashed_balances': ['uint64', LATEST_SLASHED_EXIT_LENGTH],
'latest_block_header': BeaconBlockHeader,
'historical_roots': ['bytes32'],
# Ethereum 1.0 chain data
@ -649,7 +648,7 @@ def get_previous_epoch(state: BeaconState) -> Epoch:
Return the current epoch if it's genesis epoch.
"""
current_epoch = get_current_epoch(state)
return (current_epoch - 1) if current_epoch > GENESIS_EPOCH else current_epoch
return GENESIS_EPOCH if current_epoch == GENESIS_EPOCH else current_epoch - 1
```
### `get_current_epoch`
@ -767,7 +766,7 @@ def get_epoch_start_shard(state: BeaconState, epoch: Epoch) -> Shard:
```python
def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot:
committee_count = get_epoch_committee_count(state, data.target_epoch)
offset = (data.shard + SHARD_COUNT - get_epoch_start_shard(state, data.target_epoch)) % SHARD_COUNT
offset = (data.crosslink.shard + SHARD_COUNT - get_epoch_start_shard(state, data.target_epoch)) % SHARD_COUNT
return get_epoch_start_slot(data.target_epoch) + offset // (committee_count // SLOTS_PER_EPOCH)
```
@ -830,7 +829,7 @@ def generate_seed(state: BeaconState,
return hash(
get_randao_mix(state, epoch + LATEST_RANDAO_MIXES_LENGTH - MIN_SEED_LOOKAHEAD) +
get_active_index_root(state, epoch) +
int_to_bytes32(epoch)
int_to_bytes(epoch, length=32)
)
```
@ -851,7 +850,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
i = 0
while True:
candidate_index = first_committee[(epoch + i) % len(first_committee)]
random_byte = hash(seed + int_to_bytes8(i // 32))[i % 32]
random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32]
effective_balance = state.validator_registry[candidate_index].effective_balance
if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
return candidate_index
@ -888,10 +887,10 @@ def get_shuffled_index(index: ValidatorIndex, index_count: int, seed: Bytes32) -
# Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf)
# See the 'generalized domain' algorithm on page 3
for round in range(SHUFFLE_ROUND_COUNT):
pivot = bytes_to_int(hash(seed + int_to_bytes1(round))[0:8]) % index_count
flip = (pivot - index) % index_count
pivot = bytes_to_int(hash(seed + int_to_bytes(round, length=1))[0:8]) % index_count
flip = (pivot + index_count - index) % index_count
position = max(index, flip)
source = hash(seed + int_to_bytes1(round) + int_to_bytes4(position // 256))
source = hash(seed + int_to_bytes(round, length=1) + int_to_bytes(position // 256, length=4))
byte = source[(position % 256) // 8]
bit = (byte >> (position % 8)) % 2
index = flip if bit else index
@ -929,14 +928,17 @@ def get_attesting_indices(state: BeaconState,
"""
Return the sorted attesting indices corresponding to ``attestation_data`` and ``bitfield``.
"""
committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.shard)
committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.crosslink.shard)
assert verify_bitfield(bitfield, len(committee))
return sorted([index for i, index in enumerate(committee) if get_bitfield_bit(bitfield, i) == 0b1])
```
### `int_to_bytes1`, `int_to_bytes2`, ...
### `int_to_bytes`
`int_to_bytes1(x): return x.to_bytes(1, 'little')`, `int_to_bytes2(x): return x.to_bytes(2, 'little')`, and so on for all integers, particularly 1, 2, 3, 4, 8, 32, 48, 96.
```python
def int_to_bytes(integer: int, length: int) -> bytes:
return integer.to_bytes(length, 'little')
```
### `bytes_to_int`
@ -967,7 +969,7 @@ def get_domain(state: BeaconState,
epoch = get_current_epoch(state) if message_epoch is None else message_epoch
fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version
# fork version is on the big-endian side: when signing using only the type (e.g. deposits), the type can be passed directly.
return bytes_to_int(int_to_bytes4(domain_type) + fork_version)
return bytes_to_int(int_to_bytes(domain_type, length=4) + fork_version)
```
### `get_bitfield_bit`
@ -1017,35 +1019,29 @@ def convert_to_indexed(state: BeaconState, attestation: Attestation) -> IndexedA
)
```
### `verify_indexed_attestation`
### `validate_indexed_attestation`
```python
def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
def validate_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> None:
"""
Verify validity of ``indexed_attestation`` fields.
Verify validity of ``indexed_attestation``.
"""
custody_bit_0_indices = indexed_attestation.custody_bit_0_indices
custody_bit_1_indices = indexed_attestation.custody_bit_1_indices
bit_0_indices = indexed_attestation.custody_bit_0_indices
bit_1_indices = indexed_attestation.custody_bit_1_indices
# Ensure no duplicate indices across custody bits
assert len(set(custody_bit_0_indices).intersection(set(custody_bit_1_indices))) == 0
if len(custody_bit_1_indices) > 0: # [TO BE REMOVED IN PHASE 1]
return False
if not (1 <= len(custody_bit_0_indices) + len(custody_bit_1_indices) <= MAX_INDICES_PER_ATTESTATION):
return False
if custody_bit_0_indices != sorted(custody_bit_0_indices):
return False
if custody_bit_1_indices != sorted(custody_bit_1_indices):
return False
return bls_verify_multiple(
# Verify no index has custody bit equal to 1 [to be removed in phase 1]
assert len(bit_1_indices) == 0
# Verify max number of indices
assert len(bit_0_indices) + len(bit_1_indices) <= MAX_INDICES_PER_ATTESTATION
# Verify index sets are disjoint
assert len(set(bit_0_indices).intersection(bit_1_indices)) == 0
# Verify indices are sorted
assert bit_0_indices == sorted(bit_0_indices) and bit_1_indices == sorted(bit_1_indices)
# Verify aggregate signature
assert bls_verify_multiple(
pubkeys=[
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_0_indices]),
bls_aggregate_pubkeys([state.validator_registry[i].pubkey for i in custody_bit_1_indices]),
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]),
],
message_hashes=[
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b0)),
@ -1131,7 +1127,7 @@ def get_churn_limit(state: BeaconState) -> int:
```python
def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
"""
Initiate the validator of the given ``index``.
Initiate the exit of the validator of the given ``index``.
"""
# Return if validator already initiated exit
validator = state.validator_registry[index]
@ -1153,7 +1149,9 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
#### `slash_validator`
```python
def slash_validator(state: BeaconState, slashed_index: ValidatorIndex, whistleblower_index: ValidatorIndex=None) -> None:
def slash_validator(state: BeaconState,
slashed_index: ValidatorIndex,
whistleblower_index: ValidatorIndex=None) -> None:
"""
Slash the validator with index ``slashed_index``.
"""
@ -1193,7 +1191,11 @@ Let `genesis_state = get_genesis_beacon_state(genesis_deposits, eth2genesis.gene
```python
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)
state = BeaconState(
genesis_time=genesis_time,
latest_eth1_data=genesis_eth1_data,
latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
)
# Process genesis deposits
for deposit in deposits:
@ -1219,50 +1221,61 @@ Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`.
## Beacon chain state transition function
We now define the state transition function. At a high level, the state transition is made up of four parts:
1. State caching, which happens at the start of every slot.
2. The per-epoch transitions, which happens at the start of the first slot of every epoch.
3. The per-slot transitions, which happens at every slot.
4. The per-block transitions, which happens at every block.
Transition section notes:
* The state caching caches the state root of the previous slot and updates block and state roots records.
* The per-epoch transitions focus on the [validator](#dfn-validator) registry, including adjusting balances and activating and exiting [validators](#dfn-validator), as well as processing crosslinks and managing block justification/finalization.
* The per-slot transitions focus on the slot counter.
* The per-block transitions generally focus on verifying aggregate signatures and saving temporary records relating to the per-block activity in the `BeaconState`.
Beacon blocks that trigger unhandled Python exceptions (e.g. out-of-range list accesses) and failed `assert`s during the state transition are considered invalid.
*Note*: If there are skipped slots between a block and its parent block, run the steps in the [state-root](#state-caching), [per-epoch](#per-epoch-processing), and [per-slot](#per-slot-processing) sections once for each skipped slot and then once for the slot containing the new block.
### State caching
At every `slot > GENESIS_SLOT` run the following function:
The post-state corresponding to a pre-state `state` and a block `block` is defined as `state_transition(state, block)`. State transitions that trigger an unhandled excpetion (e.g. a failed `assert` or an out-of-range list access) are considered invalid.
```python
def cache_state(state: BeaconState) -> None:
# Cache latest known state root (for previous slot)
latest_state_root = hash_tree_root(state)
state.latest_state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = latest_state_root
# Store latest known state root (for previous slot) in latest_block_header if it is empty
if state.latest_block_header.state_root == ZERO_HASH:
state.latest_block_header.state_root = latest_state_root
# Cache latest known block root (for previous slot)
latest_block_root = signing_root(state.latest_block_header)
state.latest_block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = latest_block_root
def state_transition(state: BeaconState, block: BeaconBlock, validate_state_root: bool=False) -> BeaconState:
# Process slots (including those with no blocks) since block
process_slots(state, block.slot)
# Process block
process_block(state, block)
# Validate state root (`validate_state_root == True` in production)
if validate_state_root:
assert block.state_root == hash_tree_root(state)
# Return post-state
return state
```
### Per-epoch processing
```python
def process_slots(state: BeaconState, slot: Slot) -> None:
assert state.slot < slot
while state.slot < slot:
process_slot(state)
# Process epoch on the first slot of the next epoch
if (state.slot + 1) % SLOTS_PER_EPOCH == 0:
process_epoch(state)
state.slot += 1
```
The steps below happen when `state.slot > GENESIS_SLOT and (state.slot + 1) % SLOTS_PER_EPOCH == 0`.
```python
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
# Cache latest block header state root
if state.latest_block_header.state_root == ZERO_HASH:
state.latest_block_header.state_root = previous_state_root
# 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
```
### Epoch processing
```python
def process_epoch(state: BeaconState) -> None:
process_justification_and_finalization(state)
process_crosslinks(state)
process_rewards_and_penalties(state)
process_registry_updates(state)
process_slashings(state)
process_final_updates(state)
```
#### Helper functions
We define epoch transition helper functions:
```python
def get_total_active_balance(state: BeaconState) -> Gwei:
return get_total_balance(state, get_active_validator_indices(state, get_current_epoch(state)))
@ -1291,7 +1304,8 @@ def get_matching_head_attestations(state: BeaconState, epoch: Epoch) -> List[Pen
```
```python
def get_unslashed_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]:
def get_unslashed_attesting_indices(state: BeaconState,
attestations: List[PendingAttestation]) -> List[ValidatorIndex]:
output = set()
for a in attestations:
output = output.union(get_attesting_indices(state, a.data, a.aggregation_bitfield))
@ -1304,39 +1318,24 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat
```
```python
def get_crosslink_from_attestation_data(state: BeaconState, data: AttestationData) -> Crosslink:
return Crosslink(
epoch=min(data.target_epoch, state.current_crosslinks[data.shard].epoch + MAX_CROSSLINK_EPOCHS),
previous_crosslink_root=data.previous_crosslink_root,
crosslink_data_root=data.crosslink_data_root,
)
```
```python
def get_winning_crosslink_and_attesting_indices(state: BeaconState, epoch: Epoch, shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]:
shard_attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.shard == shard]
shard_crosslinks = [get_crosslink_from_attestation_data(state, a.data) for a in shard_attestations]
candidate_crosslinks = [
c for c in shard_crosslinks
if hash_tree_root(state.current_crosslinks[shard]) in (c.previous_crosslink_root, hash_tree_root(c))
]
if len(candidate_crosslinks) == 0:
return Crosslink(), []
def get_attestations_for(crosslink: Crosslink) -> List[PendingAttestation]:
return [a for a in shard_attestations if get_crosslink_from_attestation_data(state, a.data) == crosslink]
# Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically)
winning_crosslink = max(candidate_crosslinks, key=lambda crosslink: (
get_attesting_balance(state, get_attestations_for(crosslink)), crosslink.crosslink_data_root
def get_winning_crosslink_and_attesting_indices(state: BeaconState,
epoch: Epoch,
shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]:
attestations = [a for a in get_matching_source_attestations(state, epoch) if a.data.crosslink.shard == shard]
crosslinks = list(filter(
lambda c: hash_tree_root(state.current_crosslinks[shard]) in (c.parent_root, hash_tree_root(c)),
[a.data.crosslink for a in attestations]
))
return winning_crosslink, get_unslashed_attesting_indices(state, get_attestations_for(winning_crosslink))
# Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically)
winning_crosslink = max(crosslinks, key=lambda c: (
get_attesting_balance(state, [a for a in attestations if a.data.crosslink == c]), c.data_root
), default=Crosslink())
winning_attestations = [a for a in attestations if a.data.crosslink == winning_crosslink]
return winning_crosslink, get_unslashed_attesting_indices(state, winning_attestations)
```
#### Justification and finalization
Run the following function:
```python
def process_justification_and_finalization(state: BeaconState) -> None:
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
@ -1351,12 +1350,16 @@ def process_justification_and_finalization(state: BeaconState) -> None:
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
previous_epoch_matching_target_balance = get_attesting_balance(state, get_matching_target_attestations(state, previous_epoch))
previous_epoch_matching_target_balance = get_attesting_balance(
state, get_matching_target_attestations(state, previous_epoch)
)
if previous_epoch_matching_target_balance * 3 >= get_total_active_balance(state) * 2:
state.current_justified_epoch = previous_epoch
state.current_justified_root = get_block_root(state, state.current_justified_epoch)
state.justification_bitfield |= (1 << 1)
current_epoch_matching_target_balance = get_attesting_balance(state, get_matching_target_attestations(state, current_epoch))
current_epoch_matching_target_balance = get_attesting_balance(
state, get_matching_target_attestations(state, current_epoch)
)
if current_epoch_matching_target_balance * 3 >= get_total_active_balance(state) * 2:
state.current_justified_epoch = current_epoch
state.current_justified_root = get_block_root(state, state.current_justified_epoch)
@ -1365,27 +1368,25 @@ def process_justification_and_finalization(state: BeaconState) -> None:
# Process finalizations
bitfield = state.justification_bitfield
# The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source
if (bitfield >> 1) % 8 == 0b111 and old_previous_justified_epoch == current_epoch - 3:
if (bitfield >> 1) % 8 == 0b111 and old_previous_justified_epoch + 3 == current_epoch:
state.finalized_epoch = old_previous_justified_epoch
state.finalized_root = get_block_root(state, state.finalized_epoch)
# The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source
if (bitfield >> 1) % 4 == 0b11 and old_previous_justified_epoch == current_epoch - 2:
if (bitfield >> 1) % 4 == 0b11 and old_previous_justified_epoch + 2 == current_epoch:
state.finalized_epoch = old_previous_justified_epoch
state.finalized_root = get_block_root(state, 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 old_current_justified_epoch == current_epoch - 2:
if (bitfield >> 0) % 8 == 0b111 and old_current_justified_epoch + 2 == current_epoch:
state.finalized_epoch = old_current_justified_epoch
state.finalized_root = get_block_root(state, state.finalized_epoch)
# The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source
if (bitfield >> 0) % 4 == 0b11 and old_current_justified_epoch == current_epoch - 1:
if (bitfield >> 0) % 4 == 0b11 and old_current_justified_epoch + 1 == current_epoch:
state.finalized_epoch = old_current_justified_epoch
state.finalized_root = get_block_root(state, state.finalized_epoch)
```
#### Crosslinks
Run the following function:
```python
def process_crosslinks(state: BeaconState) -> None:
state.previous_crosslinks = [c for c in state.current_crosslinks]
@ -1400,8 +1401,6 @@ def process_crosslinks(state: BeaconState) -> None:
#### Rewards and penalties
First, we define additional helpers:
```python
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
adjusted_quotient = integer_squareroot(get_total_active_balance(state)) // BASE_REWARD_QUOTIENT
@ -1450,7 +1449,9 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
for index in eligible_validator_indices:
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
penalties[index] += (
state.validator_registry[index].effective_balance * finality_delay // INACTIVITY_PENALTY_QUOTIENT
)
return rewards, penalties
```
@ -1475,8 +1476,6 @@ def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
return rewards, penalties
```
Run the following function:
```python
def process_rewards_and_penalties(state: BeaconState) -> None:
if get_current_epoch(state) == GENESIS_EPOCH:
@ -1491,13 +1490,14 @@ def process_rewards_and_penalties(state: BeaconState) -> None:
#### Registry updates
Run the following function:
```python
def process_registry_updates(state: BeaconState) -> None:
# Process activation eligibility and ejections
for index, validator in enumerate(state.validator_registry):
if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and validator.effective_balance >= MAX_EFFECTIVE_BALANCE:
if (
validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and
validator.effective_balance >= MAX_EFFECTIVE_BALANCE
):
validator.activation_eligibility_epoch = get_current_epoch(state)
if is_active_validator(validator, get_current_epoch(state)) and validator.effective_balance <= EJECTION_BALANCE:
@ -1518,8 +1518,6 @@ def process_registry_updates(state: BeaconState) -> None:
#### Slashings
Run the following function:
```python
def process_slashings(state: BeaconState) -> None:
current_epoch = get_current_epoch(state)
@ -1542,8 +1540,6 @@ def process_slashings(state: BeaconState) -> None:
#### Final updates
Run the following function:
```python
def process_final_updates(state: BeaconState) -> None:
current_epoch = get_current_epoch(state)
@ -1582,19 +1578,16 @@ def process_final_updates(state: BeaconState) -> None:
state.current_epoch_attestations = []
```
### Per-slot processing
At every `slot > GENESIS_SLOT` run the following function:
### Block processing
```python
def advance_slot(state: BeaconState) -> None:
state.slot += 1
def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_block_header(state, block)
process_randao(state, block.body)
process_eth1_data(state, block.body)
process_operations(state, block.body)
```
### Per-block processing
For every `block` except the genesis block, run `process_block_header(state, block)`, `process_randao(state, block)` and `process_eth1_data(state, block)`.
#### Block header
```python
@ -1602,12 +1595,12 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
# Verify that the slots match
assert block.slot == state.slot
# Verify that the parent matches
assert block.previous_block_root == signing_root(state.latest_block_header)
assert block.parent_root == signing_root(state.latest_block_header)
# Save current block as the new latest block
state.latest_block_header = BeaconBlockHeader(
slot=block.slot,
previous_block_root=block.previous_block_root,
block_body_root=hash_tree_root(block.body),
parent_root=block.parent_root,
body_root=hash_tree_root(block.body),
)
# Verify proposer is not slashed
proposer = state.validator_registry[get_beacon_proposer_index(state)]
@ -1619,39 +1612,57 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
#### RANDAO
```python
def process_randao(state: BeaconState, block: BeaconBlock) -> None:
def process_randao(state: BeaconState, body: BeaconBlockBody) -> None:
proposer = state.validator_registry[get_beacon_proposer_index(state)]
# Verify that the provided randao value is valid
assert bls_verify(proposer.pubkey, hash_tree_root(get_current_epoch(state)), block.body.randao_reveal, get_domain(state, DOMAIN_RANDAO))
assert bls_verify(
proposer.pubkey,
hash_tree_root(get_current_epoch(state)),
body.randao_reveal,
get_domain(state, DOMAIN_RANDAO),
)
# Mix it in
state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] = (
xor(get_randao_mix(state, get_current_epoch(state)),
hash(block.body.randao_reveal))
hash(body.randao_reveal))
)
```
#### Eth1 data
```python
def process_eth1_data(state: BeaconState, block: BeaconBlock) -> None:
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
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
```
#### Operations
*Note*: All functions in this section mutate `state`.
```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)
# Verify that there are no duplicate transfers
assert len(body.transfers) == len(set(body.transfers))
for operations, max_operations, function in (
(body.proposer_slashings, MAX_PROPOSER_SLASHINGS, process_proposer_slashing),
(body.attester_slashings, MAX_ATTESTER_SLASHINGS, process_attester_slashing),
(body.attestations, MAX_ATTESTATIONS, process_attestation),
(body.deposits, MAX_DEPOSITS, process_deposit),
(body.voluntary_exits, MAX_VOLUNTARY_EXITS, process_voluntary_exit),
(body.transfers, MAX_TRANSFERS, process_transfer),
):
assert len(operations) <= max_operations
for operation in operations:
function(state, operation)
```
##### Proposer slashings
Verify that `len(block.body.proposer_slashings) <= MAX_PROPOSER_SLASHINGS`.
For each `proposer_slashing` in `block.body.proposer_slashings`, run the following function:
```python
def process_proposer_slashing(state: BeaconState,
proposer_slashing: ProposerSlashing) -> None:
def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
"""
Process ``ProposerSlashing`` operation.
"""
@ -1672,21 +1683,16 @@ def process_proposer_slashing(state: BeaconState,
##### Attester slashings
Verify that `len(block.body.attester_slashings) <= MAX_ATTESTER_SLASHINGS`.
For each `attester_slashing` in `block.body.attester_slashings`, run the following function:
```python
def process_attester_slashing(state: BeaconState,
attester_slashing: AttesterSlashing) -> None:
def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None:
"""
Process ``AttesterSlashing`` operation.
"""
attestation_1 = attester_slashing.attestation_1
attestation_2 = attester_slashing.attestation_2
assert is_slashable_attestation_data(attestation_1.data, attestation_2.data)
assert verify_indexed_attestation(state, attestation_1)
assert verify_indexed_attestation(state, attestation_2)
validate_indexed_attestation(state, attestation_1)
validate_indexed_attestation(state, attestation_2)
slashed_any = False
attesting_indices_1 = attestation_1.custody_bit_0_indices + attestation_1.custody_bit_1_indices
@ -1700,10 +1706,6 @@ def process_attester_slashing(state: BeaconState,
##### Attestations
Verify that `len(block.body.attestations) <= MAX_ATTESTATIONS`.
For each `attestation` in `block.body.attestations`, run the following function:
```python
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
"""
@ -1713,37 +1715,34 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
attestation_slot = get_attestation_data_slot(state, data)
assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH
# Check target epoch, source epoch, source root, and source crosslink
assert (data.target_epoch, data.source_epoch, data.source_root, data.previous_crosslink_root) in {
(get_current_epoch(state), state.current_justified_epoch, state.current_justified_root, hash_tree_root(state.current_crosslinks[data.shard])),
(get_previous_epoch(state), state.previous_justified_epoch, state.previous_justified_root, hash_tree_root(state.previous_crosslinks[data.shard])),
}
# Check crosslink data root
assert data.crosslink_data_root == ZERO_HASH # [to be removed in phase 1]
# Check signature and bitfields
assert verify_indexed_attestation(state, convert_to_indexed(state, attestation))
# Cache pending attestation
pending_attestation = PendingAttestation(
data=data,
aggregation_bitfield=attestation.aggregation_bitfield,
inclusion_delay=state.slot - attestation_slot,
proposer_index=get_beacon_proposer_index(state),
)
assert data.target_epoch in (get_previous_epoch(state), get_current_epoch(state))
if data.target_epoch == get_current_epoch(state):
ffg_data = (state.current_justified_epoch, state.current_justified_root, get_current_epoch(state))
parent_crosslink = state.current_crosslinks[data.crosslink.shard]
state.current_epoch_attestations.append(pending_attestation)
else:
ffg_data = (state.previous_justified_epoch, state.previous_justified_root, get_previous_epoch(state))
parent_crosslink = state.previous_crosslinks[data.crosslink.shard]
state.previous_epoch_attestations.append(pending_attestation)
# Check FFG data, crosslink data, and signature
assert ffg_data == (data.source_epoch, data.source_root, data.target_epoch)
assert data.crosslink.start_epoch == parent_crosslink.end_epoch
assert data.crosslink.end_epoch == min(data.target_epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)
assert data.crosslink.parent_root == hash_tree_root(parent_crosslink)
assert data.crosslink.data_root == ZERO_HASH # [to be removed in phase 1]
validate_indexed_attestation(state, convert_to_indexed(state, attestation))
```
##### Deposits
Verify that `len(block.body.deposits) == min(MAX_DEPOSITS, state.latest_eth1_data.deposit_count - state.deposit_index)`.
For each `deposit` in `block.body.deposits`, run the following function:
```python
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
"""
@ -1790,10 +1789,6 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
##### Voluntary exits
Verify that `len(block.body.voluntary_exits) <= MAX_VOLUNTARY_EXITS`.
For each `exit` in `block.body.voluntary_exits`, run the following function:
```python
def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None:
"""
@ -1817,10 +1812,6 @@ def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None:
##### Transfers
Verify that `len(block.body.transfers) <= MAX_TRANSFERS` and that all transfers are distinct.
For each `transfer` in `block.body.transfers`, run the following function:
```python
def process_transfer(state: BeaconState, transfer: Transfer) -> None:
"""
@ -1839,7 +1830,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
# Verify that the pubkey is valid
assert (
state.validator_registry[transfer.sender].withdrawal_credentials ==
BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer.pubkey)[1:]
int_to_bytes(BLS_WITHDRAWAL_PREFIX, length=1) + hash(transfer.pubkey)[1:]
)
# Verify that the signature is valid
assert bls_verify(transfer.pubkey, signing_root(transfer), transfer.signature, get_domain(state, DOMAIN_TRANSFER))
@ -1851,12 +1842,3 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
assert not (0 < state.balances[transfer.sender] < MIN_DEPOSIT_AMOUNT)
assert not (0 < state.balances[transfer.recipient] < MIN_DEPOSIT_AMOUNT)
```
#### State root verification
Verify the block's `state_root` by running the following function:
```python
def verify_block_state_root(state: BeaconState, block: BeaconBlock) -> None:
assert block.state_root == hash_tree_root(state)
```

View File

@ -72,7 +72,7 @@ Every Ethereum 1.0 deposit, of size at least `MIN_DEPOSIT_AMOUNT`, emits a `Depo
### `Eth2Genesis` log
When `CHAIN_START_FULL_DEPOSIT_THRESHOLD` of full deposits have been made, the deposit contract emits the `Eth2Genesis` log. The beacon chain state may then be initialized by calling the `get_genesis_beacon_state` function (defined below) where:
When `CHAIN_START_FULL_DEPOSIT_THRESHOLD` of full deposits have been made, the deposit contract emits the `Eth2Genesis` log. The beacon chain state may then be initialized by calling the `get_genesis_beacon_state` function (defined [here](./0_beacon-chain.md#genesis-state)) where:
* `genesis_time` equals `time` in the `Eth2Genesis` log
* `latest_eth1_data.deposit_root` equals `deposit_root` in the `Eth2Genesis` log

View File

@ -38,7 +38,7 @@ All terminology, constants, functions, and protocol mechanics defined in the [Ph
Processing the beacon chain is similar to processing the Ethereum 1.0 chain. Clients download and process blocks and maintain a view of what is the current "canonical chain", terminating at the current "head". For a beacon block, `block`, to be processed by a node, the following conditions must be met:
* The parent block with root `block.previous_block_root` has been processed and accepted.
* The parent block with root `block.parent_root` has been processed and accepted.
* An Ethereum 1.0 block pointed to by the `state.latest_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`.

View File

@ -35,19 +35,9 @@
- [`empty`](#empty)
- [`get_crosslink_chunk_count`](#get_crosslink_chunk_count)
- [`get_custody_chunk_bit`](#get_custody_chunk_bit)
- [`get_chunk_bits_root`](#get_chunk_bits_root)
- [`get_randao_epoch_for_custody_period`](#get_randao_epoch_for_custody_period)
- [`get_validators_custody_reveal_period`](#get_validators_custody_reveal_period)
- [`get_chunk_bits_root`](#get_chunk_bits_root)
- [`replace_empty_or_append`](#replace_empty_or_append)
- [Per-block processing](#per-block-processing)
- [Operations](#operations)
- [Custody key reveals](#custody-key-reveals)
- [Early derived secret reveals](#early-derived-secret-reveals)
- [Chunk challenges](#chunk-challenges)
- [Bit challenges](#bit-challenges)
- [Custody responses](#custody-responses)
- [Per-epoch processing](#per-epoch-processing)
- [Handling of custody-related deadlines](#handling-of-custody-related-deadlines)
<!-- /TOC -->
@ -104,6 +94,8 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
### Reward and penalty quotients
| Name | Value |
| - | - |
| `EARLY_DERIVED_SECRET_REVEAL_SLOT_REWARD_MULTIPLE` | `2**1` (= 2) |
### Signature domains
@ -146,8 +138,8 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
'challenge_index': 'uint64',
'challenger_index': ValidatorIndex,
'responder_index': ValidatorIndex,
'deadline': Epoch,
'crosslink_data_root': Hash,
'inclusion_epoch': Epoch,
'data_root': Hash,
'depth': 'uint64',
'chunk_index': 'uint64',
}
@ -160,8 +152,8 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
'challenge_index': 'uint64',
'challenger_index': ValidatorIndex,
'responder_index': ValidatorIndex,
'deadline': Epoch,
'crosslink_data_root': Hash,
'inclusion_epoch': Epoch,
'data_root': Hash,
'chunk_count': 'uint64',
'chunk_bits_merkle_root': Hash,
'responder_key': BLSSignature,
@ -262,12 +254,10 @@ The `empty` function accepts and SSZ type as input and returns an object of that
### `get_crosslink_chunk_count`
```python
def get_custody_chunk_count(attestation: Attestation) -> int:
crosslink_start_epoch = attestation.data.latest_crosslink.epoch
crosslink_end_epoch = slot_to_epoch(attestation.data.slot)
crosslink_crosslink_length = min(MAX_CROSSLINK_EPOCHS, end_epoch - start_epoch)
def get_custody_chunk_count(crosslink: Crosslink) -> int:
crosslink_length = min(MAX_EPOCHS_PER_CROSSLINK, crosslink.end_epoch - crosslink.start_epoch)
chunks_per_epoch = 2 * BYTES_PER_SHARD_BLOCK * SLOTS_PER_EPOCH // BYTES_PER_CUSTODY_CHUNK
return crosslink_crosslink_length * chunks_per_epoch
return crosslink_length * chunks_per_epoch
```
### `get_custody_chunk_bit`
@ -426,10 +416,10 @@ def process_early_derived_secret_reveal(state: BeaconState,
# round key
slash_validator(state, reveal.revealed_index, reveal.masker_index)
else:
# Only a small penalty proportional to proposer slot reward for RANDAO reveal
# Only a small penalty proportional to proposer slot reward for RANDAO reveal
# that does not interfere with the custody period
# The penalty is proportional to the max proposer reward
# The penalty is proportional to the max proposer reward
# Calculate penalty
max_proposer_slot_reward = (
get_base_reward(state, reveal.revealed_index) *
@ -448,7 +438,7 @@ def process_early_derived_secret_reveal(state: BeaconState,
increase_balance(state, whistleblower_index, whistleblowing_reward - proposer_reward)
decrease_balance(state, reveal.revealed_index, penalty)
# Mark this derived secret as exposed so validator cannot be punished repeatedly
# Mark this derived secret as exposed so validator cannot be punished repeatedly
state.exposed_derived_secrets[reveal.epoch % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS].append(reveal.revealed_index)
```
@ -474,19 +464,19 @@ def process_chunk_challenge(state: BeaconState,
# Verify the challenge is not a duplicate
for record in state.custody_chunk_challenge_records:
assert (
record.crosslink_data_root != challenge.attestation.data.crosslink_data_root or
record.data_root != challenge.attestation.data.crosslink.data_root or
record.chunk_index != challenge.chunk_index
)
# Verify depth
depth = math.log2(next_power_of_two(get_custody_chunk_count(challenge.attestation)))
depth = math.log2(next_power_of_two(get_custody_chunk_count(challenge.attestation.data.crosslink)))
assert challenge.chunk_index < 2**depth
# Add new chunk challenge record
new_record = CustodyChunkChallengeRecord(
challenge_index=state.custody_challenge_index,
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,
inclusion_epoch=get_current_epoch(state),
data_root=challenge.attestation.data.crosslink.data_root,
depth=depth,
chunk_index=challenge.chunk_index,
)
@ -528,10 +518,9 @@ def process_bit_challenge(state: BeaconState,
attesters = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
assert challenge.responder_index in attesters
# A validator can be the challenger or responder for at most one challenge at a time
# A validator can be the challenger for at most one challenge at a time
for record in state.custody_bit_challenge_records:
assert record.challenger_index != challenge.challenger_index
assert record.responder_index != challenge.responder_index
# Verify the responder is a valid custody key
epoch_to_sign = get_randao_epoch_for_custody_period(
@ -553,7 +542,7 @@ def process_bit_challenge(state: BeaconState,
)
# Verify the chunk count
chunk_count = get_custody_chunk_count(challenge.attestation)
chunk_count = get_custody_chunk_count(challenge.attestation.data.crosslink)
assert verify_bitfield(challenge.chunk_bits, chunk_count)
# Verify the first bit of the hash of the chunk bits does not equal the custody bit
custody_bit = get_bitfield_bit(attestation.custody_bitfield, attesters.index(responder_index))
@ -563,8 +552,8 @@ def process_bit_challenge(state: BeaconState,
challenge_index=state.custody_challenge_index,
challenger_index=challenge.challenger_index,
responder_index=challenge.responder_index,
deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE,
crosslink_data_root=challenge.attestation.data.crosslink_data_root,
inclusion_epoch=get_current_epoch(state),
data_root=challenge.attestation.data.crosslink.data_root,
chunk_count=chunk_count,
chunk_bits_merkle_root=merkle_root(pad_to_power_of_2((challenge.chunk_bits))),
responder_key=challenge.responder_key,
@ -604,13 +593,15 @@ def process_chunk_challenge_response(state: BeaconState,
assert response.chunk_index == challenge.chunk_index
# Verify bit challenge data is null
assert response.chunk_bits_branch == [] and response.chunk_bits_leaf == ZERO_HASH
# Verify minimum delay
assert get_current_epoch(state) >= challenge.inclusion_epoch + ACTIVATION_EXIT_DELAY
# Verify the chunk matches the crosslink data root
assert verify_merkle_branch(
leaf=hash_tree_root(response.chunk),
branch=response.data_branch,
depth=challenge.depth,
index=response.chunk_index,
root=challenge.crosslink_data_root,
root=challenge.data_root,
)
# Clear the challenge
records = state.custody_chunk_challenge_records
@ -626,13 +617,16 @@ def process_bit_challenge_response(state: BeaconState,
challenge: CustodyBitChallengeRecord) -> None:
# Verify chunk index
assert response.chunk_index < challenge.chunk_count
# Verify responder has not been slashed
responder = state.validator_registry[challenge.responder_index]
assert not responder.slashed
# Verify the chunk matches the crosslink data root
assert verify_merkle_branch(
leaf=hash_tree_root(response.chunk),
branch=response.data_branch,
depth=math.log2(next_power_of_two(challenge.chunk_count)),
index=response.chunk_index,
root=challenge.crosslink_data_root,
root=challenge.data_root,
)
# Verify the chunk bit leaf matches the challenge data
assert verify_merkle_branch(
@ -671,13 +665,13 @@ Run `process_challenge_deadlines(state)` immediately after `process_reveal_deadl
```python
def process_challenge_deadlines(state: BeaconState) -> None:
for challenge in state.custody_chunk_challenge_records:
if get_current_epoch(state) > challenge.deadline:
if get_current_epoch(state) > challenge.inclusion_epoch + CUSTODY_RESPONSE_DEADLINE:
slash_validator(state, challenge.responder_index, challenge.challenger_index)
records = state.custody_chunk_challenge_records
records[records.index(challenge)] = CustodyChunkChallengeRecord()
for challenge in state.custody_bit_challenge_records:
if get_current_epoch(state) > challenge.deadline:
if get_current_epoch(state) > challenge.inclusion_epoch + CUSTODY_RESPONSE_DEADLINE:
slash_validator(state, challenge.responder_index, challenge.challenger_index)
records = state.custody_bit_challenge_records
records[records.index(challenge)] = CustodyBitChallengeRecord()
@ -688,6 +682,15 @@ Append this to `process_final_updates(state)`:
```python
# Clean up exposed RANDAO key reveals
state.exposed_derived_secrets[current_epoch % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS] = []
# Reset withdrawable epochs if challenge records are empty
records = state.custody_chunk_challenge_records + state.bit_challenge_records
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):
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
```
In `process_penalties_and_exits`, change the definition of `eligible` to the following (note that it is not a pure function because `state` is declared in the surrounding scope):

View File

@ -78,7 +78,7 @@ This document describes the shard data layer and the shard fork choice rule in P
'slot': Slot,
'shard': Shard,
'beacon_chain_root': Hash,
'previous_block_root': Hash,
'parent_root': Hash,
'data': ShardBlockBody,
'state_root': Hash,
'attestations': [ShardAttestation],
@ -93,7 +93,7 @@ This document describes the shard data layer and the shard fork choice rule in P
'slot': Slot,
'shard': Shard,
'beacon_chain_root': Hash,
'previous_block_root': Hash,
'parent_root': Hash,
'body_root': Hash,
'state_root': Hash,
'attestations': [ShardAttestation],
@ -180,7 +180,7 @@ def get_shard_proposer_index(state: BeaconState,
slot: Slot) -> ValidatorIndex:
# Randomly shift persistent committee
persistent_committee = get_persistent_committee(state, shard, slot)
seed = hash(state.current_shuffling_seed + int_to_bytes8(shard) + int_to_bytes8(slot))
seed = hash(state.current_shuffling_seed + int_to_bytes(shard, length=8) + int_to_bytes(slot, length=8))
random_index = bytes_to_int(seed[0:8]) % len(persistent_committee)
persistent_committee = persistent_committee[random_index:] + persistent_committee[:random_index]
@ -201,7 +201,7 @@ def get_shard_header(block: ShardBlock) -> ShardBlockHeader:
slot: block.slot,
shard: block.shard,
beacon_chain_root: block.beacon_chain_root,
previous_block_root: block.previous_block_root,
parent_root: block.parent_root,
body_root: hash_tree_root(block.body),
state_root: block.state_root,
attestations: block.attestations,
@ -215,7 +215,7 @@ def get_shard_header(block: ShardBlock) -> ShardBlockHeader:
def verify_shard_attestation_signature(state: BeaconState,
attestation: ShardAttestation) -> None:
data = attestation.data
persistent_committee = get_persistent_committee(state, data.shard, data.slot)
persistent_committee = get_persistent_committee(state, data.crosslink.shard, data.slot)
assert verify_bitfield(attestation.aggregation_bitfield, len(persistent_committee))
pubkeys = []
for i, index in enumerate(persistent_committee):
@ -225,7 +225,7 @@ def verify_shard_attestation_signature(state: BeaconState,
pubkeys.append(validator.pubkey)
assert bls_verify(
pubkey=bls_aggregate_pubkeys(pubkeys),
message_hash=data.shard_block_root,
message_hash=data.crosslink.shard_block_root,
signature=attestation.aggregate_signature,
domain=get_domain(state, slot_to_epoch(data.slot), DOMAIN_SHARD_ATTESTER)
)
@ -296,11 +296,11 @@ def is_valid_shard_block(beacon_blocks: List[BeaconBlock],
# Check parent block
if block.slot == PHASE_1_GENESIS_SLOT:
assert candidate.previous_block_root == ZERO_HASH
assert candidate.parent_root == ZERO_HASH
else:
parent_block = next(
block for block in valid_shard_blocks if
signing_root(block) == candidate.previous_block_root
signing_root(block) == candidate.parent_root
, None)
assert parent_block != None
assert parent_block.shard == block.shard
@ -312,7 +312,7 @@ def is_valid_shard_block(beacon_blocks: List[BeaconBlock],
for _, attestation in enumerate(block.attestations):
assert max(GENESIS_SHARD_SLOT, block.slot - SLOTS_PER_EPOCH) <= attestation.data.slot
assert attestation.data.slot <= block.slot - MIN_ATTESTATION_INCLUSION_DELAY
assert attestation.data.shard == block.shard
assert attestation.data.crosslink.shard == block.shard
verify_shard_attestation_signature(beacon_state, attestation)
# Check signature
@ -343,11 +343,11 @@ def is_valid_shard_attestation(valid_shard_blocks: List[ShardBlock],
# Check shard block
shard_block = next(
block for block in valid_shard_blocks if
signing_root(block) == candidate.attestation.data.shard_block_root
signing_root(block) == candidate.attestation.data.crosslink.shard_block_root
, None)
assert shard_block != None
assert shard_block.slot == attestation.data.slot
assert shard_block.shard == attestation.data.shard
assert shard_block.shard == attestation.data.crosslink.shard
# Check signature
verify_shard_attestation_signature(beacon_state, attestation)
@ -378,22 +378,22 @@ def is_valid_beacon_attestation(shard: Shard,
# Check previous attestation
if candidate.data.previous_crosslink.epoch <= PHASE_1_GENESIS_EPOCH:
assert candidate.data.previous_crosslink.crosslink_data_root == ZERO_HASH
assert candidate.data.previous_crosslink.data_root == ZERO_HASH
else:
previous_attestation = next(
attestation for attestation in valid_attestations if
attestation.data.crosslink_data_root == candidate.data.previous_crosslink.crosslink_data_root
attestation.data.crosslink.data_root == candidate.data.previous_crosslink.data_root
, None)
assert previous_attestation != None
assert candidate.data.previous_attestation.epoch < slot_to_epoch(candidate.data.slot)
# Check crosslink data root
start_epoch = state.latest_crosslinks[shard].epoch
end_epoch = min(slot_to_epoch(candidate.data.slot) - CROSSLINK_LOOKBACK, start_epoch + MAX_CROSSLINK_EPOCHS)
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):
blocks.append(shard_blocks[slot])
assert candidate.data.crosslink_data_root == compute_crosslink_data_root(blocks)
assert candidate.data.crosslink.data_root == compute_crosslink_data_root(blocks)
return True
```

View File

@ -146,7 +146,7 @@ def compute_committee(header: BeaconBlockHeader,
]
def get_switchover_epoch(index):
return (
bytes_to_int(hash(validator_memory.earlier_period_data.seed + int_to_bytes3(index))[0:8]) %
bytes_to_int(hash(validator_memory.earlier_period_data.seed + int_to_bytes(index, length=3))[0:8]) %
PERSISTENT_COMMITTEE_PERIOD
)

View File

@ -41,7 +41,7 @@ Remote method calls are wrapped in a "request" structure:
(
id: uint64
method_id: uint16
body: Request
body: (message_body...)
)
```
@ -55,15 +55,7 @@ and their corresponding responses are wrapped in a "response" structure:
)
```
If an error occurs, a variant of the response structure is returned:
```
(
id: uint64
response_code: uint16
result: bytes
)
```
A union type is used to determine the contents of the `body` field in the request structure. Each "body" entry in the RPC calls below corresponds to one subtype in the `body` type union.
The details of the RPC-Over-`libp2p` protocol are similar to [JSON-RPC 2.0](https://www.jsonrpc.org/specification). Specifically:

View File

@ -16,7 +16,7 @@
- [Serialization](#serialization)
- [`"uintN"`](#uintn)
- [`"bool"`](#bool)
- [Containers, vectors, lists](#containers-vectors-lists)
- [Vectors, containers, lists, unions](#vectors-containers-lists-unions)
- [Deserialization](#deserialization)
- [Merkleization](#merkleization)
- [Self-signed containers](#self-signed-containers)
@ -46,8 +46,12 @@
* angle bracket notation `[type, N]`, e.g. `["uint64", N]`
* **list**: ordered variable-length homogeneous collection of values
* angle bracket notation `[type]`, e.g. `["uint64"]`
* **union**: union type containing one of the given subtypes
* round bracket notation `(type_1, type_2, ...)`, e.g. `("null", "uint64")`
We recursively define "variable-size" types to be lists and all types that contains a variable-size type. All other types are said to be "fixed-size".
### Variable-size and fixed-size
We recursively define "variable-size" types to be lists and unions and all types that contain a variable-size type. All other types are said to be "fixed-size".
### Aliases
@ -56,10 +60,15 @@ For convenience we alias:
* `"byte"` to `"uint8"` (this is a basic type)
* `"bytes"` to `["byte"]` (this is *not* a basic type)
* `"bytesN"` to `["byte", N]` (this is *not* a basic type)
* `"null"`: `{}`, i.e. the empty container
### Default values
The default value of a type upon initialization is recursively defined using `0` for `"uintN"`, `False` for `"bool"`, and `[]` for lists.
The default value of a type upon initialization is recursively defined using `0` for `"uintN"`, `False` for `"bool"`, and `[]` for lists. Unions default to the first type in the union (with type index zero), which is `"null"` if present in the union.
### Illegal types
Empty vector types (i.e. `[subtype, 0]` for some `subtype`) are not legal. The `"null"` type is only legal as the first type in a union subtype (i.e., with type index zero).
## Serialization
@ -67,7 +76,6 @@ We recursively define the `serialize` function which consumes an object `value`
> *Note*: In the function definitions below (`serialize`, `hash_tree_root`, `signing_root`, `is_variable_size`, etc.) objects implicitly carry their type.
### `"uintN"`
```python
@ -82,10 +90,16 @@ assert value in (True, False)
return b"\x01" if value is True else b"\x00"
```
### Containers, vectors, lists
### `"null"`
```python
# Reccursively serialize
return b""
```
### Vectors, containers, lists, unions
```python
# Recursively serialize
fixed_parts = [serialize(element) if not is_variable_size(element) else None for element in value]
variable_parts = [serialize(element) if is_variable_size(element) else b"" for element in value]
@ -102,6 +116,16 @@ fixed_parts = [part if part != None else variable_offsets[i] for i, part in enum
return b"".join(fixed_parts + variable_parts)
```
If `value` is a union type:
Define value as an object that has properties `value.value` with the contained value, and `value.type_index` which indexes the type.
```python
serialized_bytes = serialize(value.value)
serialized_type_index = value.type_index.to_bytes(BYTES_PER_LENGTH_OFFSET, "little")
return serialized_type_index + serialized_bytes
```
## Deserialization
Because serialization is an injective function (i.e. two distinct objects of the same type will serialize to different values) any bytestring has at most one object it could deserialize to. Efficient algorithms for computing this object can be found in [the implementations](#implementations).
@ -111,8 +135,9 @@ Because serialization is an injective function (i.e. two distinct objects of the
We first define helper functions:
* `pack`: Given ordered objects of the same basic type, serialize them, pack them into `BYTES_PER_CHUNK`-byte chunks, right-pad the last chunk with zero bytes, and return the chunks.
* `merkleize`: Given ordered `BYTES_PER_CHUNK`-byte chunks, if necessary append zero chunks so that the number of chunks is a power of two, Merkleize the chunks, and return the root.
* `merkleize`: Given ordered `BYTES_PER_CHUNK`-byte chunks, if necessary append zero chunks so that the number of chunks is a power of two, Merkleize the chunks, and return the root. Note that `merkleize` on a single chunk is simply that chunk, i.e. the identity when the number of chunks is one.
* `mix_in_length`: Given a Merkle root `root` and a length `length` (`"uint256"` little-endian serialization) return `hash(root + length)`.
* `mix_in_type`: Given a Merkle root `root` and a type_index `type_index` (`"uint256"` little-endian serialization) return `hash(root + type_index)`.
We now define Merkleization `hash_tree_root(value)` of an object `value` recursively:
@ -120,6 +145,7 @@ We now define Merkleization `hash_tree_root(value)` of an object `value` recursi
* `mix_in_length(merkleize(pack(value)), len(value))` if `value` is a list of basic objects
* `merkleize([hash_tree_root(element) for element in value])` if `value` is a vector of composite objects or a container
* `mix_in_length(merkleize([hash_tree_root(element) for element in value]), len(value))` if `value` is a list of composite objects
* `mix_in_type(merkleize(value.value), value.type_index)` if `value` is of union type
## Self-signed containers

View File

@ -20,6 +20,8 @@
- [Process deposit](#process-deposit)
- [Validator index](#validator-index)
- [Activation](#activation)
- [Validator assignments](#validator-assignments)
- [Lookahead](#lookahead)
- [Beacon chain responsibilities](#beacon-chain-responsibilities)
- [Block proposal](#block-proposal)
- [Block header](#block-header)
@ -37,21 +39,14 @@
- [Voluntary exits](#voluntary-exits)
- [Attestations](#attestations-1)
- [Attestation data](#attestation-data)
- [Slot](#slot-1)
- [Beacon block root](#beacon-block-root)
- [Source epoch](#source-epoch)
- [Source root](#source-root)
- [Target root](#target-root)
- [Shard](#shard)
- [Previous crosslink root](#previous-crosslink-root)
- [Crosslink data root](#crosslink-data-root)
- [LMD GHOST vote](#lmd-ghost-vote)
- [FFG vote](#ffg-vote)
- [Crosslink vote](#crosslink-vote)
- [Construct attestation](#construct-attestation)
- [Data](#data)
- [Aggregation bitfield](#aggregation-bitfield)
- [Custody bitfield](#custody-bitfield)
- [Aggregate signature](#aggregate-signature)
- [Validator assignments](#validator-assignments)
- [Lookahead](#lookahead)
- [How to avoid slashing](#how-to-avoid-slashing)
- [Proposer slashing](#proposer-slashing)
- [Attester slashing](#attester-slashing)
@ -132,13 +127,62 @@ Once a validator is activated, the validator is assigned [responsibilities](#bea
*Note*: There is a maximum validator churn per finalized epoch so the delay until activation is variable depending upon finality, total active validator balance, and the number of validators in the queue to be activated.
## Validator assignments
A validator can get committee assignments for a given epoch using the following helper via `get_committee_assignment(state, epoch, validator_index)` where `epoch <= next_epoch`.
```python
def get_committee_assignment(
state: BeaconState,
epoch: Epoch,
validator_index: ValidatorIndex) -> Tuple[List[ValidatorIndex], Shard, Slot]:
"""
Return the committee assignment in the ``epoch`` for ``validator_index``.
``assignment`` returned is a tuple of the following form:
* ``assignment[0]`` is the list of validators in the committee
* ``assignment[1]`` is the shard to which the committee is assigned
* ``assignment[2]`` is the slot at which the committee is assigned
"""
next_epoch = get_current_epoch(state) + 1
assert epoch <= next_epoch
committees_per_slot = get_epoch_committee_count(state, epoch) // SLOTS_PER_EPOCH
epoch_start_slot = get_epoch_start_slot(epoch)
for slot in range(epoch_start_slot, epoch_start_slot + SLOTS_PER_EPOCH)
offset = committees_per_slot * (slot % SLOTS_PER_EPOCH)
slot_start_shard = (get_epoch_start_shard(state, epoch) + offset) % SHARD_COUNT
for i in range(committees_per_slot):
shard = (slot_start_shard + i) % SHARD_COUNT
committee = get_crosslink_committee(state, epoch, shard)
if validator_index in committee:
return committee, shard, slot
```
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 with a `state` of the slot in question. Proposer selection is only stable within the context of the current epoch.
```python
def is_proposer(state: BeaconState,
validator_index: ValidatorIndex) -> bool:
return get_beacon_proposer_index(state) == validator_index
```
*Note*: To see if a validator is assigned to propose during the slot, the beacon state must be in the epoch in question. At the epoch boundaries, the validator must run an epoch transition into the epoch to successfully check the proposal assignment of the first 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 be checked during the epoch 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 by noting at which future slot they will have to attest and also which shard they should begin syncing (in Phase 1+).
Specifically, a validator should call `get_committee_assignment(state, next_epoch, validator_index)` when checking for next epoch assignments.
## Beacon chain responsibilities
A validator has two primary responsibilities to the beacon chain: [proposing blocks](block-proposal) and [creating attestations](attestations-1). Proposals happen infrequently, whereas attestations should be created once per epoch.
A validator has two primary responsibilities to the beacon chain: [proposing blocks](#block-proposal) and [creating attestations](#attestations-1). Proposals happen infrequently, whereas attestations should be created once per epoch.
### Block proposal
A validator is expected to propose a [`BeaconBlock`](../core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `get_beacon_proposer_index(state)` returns the validator's `validator_index`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator is to create, sign, and broadcast a `block` that is a child of `parent` and that executes a valid [beacon chain state transition](../core/0_beacon-chain.md#beacon-chain-state-transition-function).
A validator is expected to propose a [`BeaconBlock`](../core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `is_proposer(state, validator_index)` returns `True`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator creates, signs, and broadcasts a `block` that is a child of `parent` that satisfies a valid [beacon chain state transition](../core/0_beacon-chain.md#beacon-chain-state-transition-function).
There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (e.g. at 312500 validators = 10 million ETH, that's once per ~3 weeks).
@ -152,7 +196,7 @@ Set `block.slot = slot` where `slot` is the current slot at which the validator
##### Parent root
Set `block.previous_block_root = signing_root(parent)`.
Set `block.parent_root = signing_root(parent)`.
##### State root
@ -234,7 +278,7 @@ Up to `MAX_VOLUNTARY_EXITS` [`VoluntaryExit`](../core/0_beacon-chain.md#voluntar
### Attestations
A validator is expected to create, sign, and broadcast an attestation during each epoch. The slot during which the validator performs this role is any slot at which `get_crosslink_committees_at_slot(state, slot)` contains a committee that contains `validator_index`.
A validator is expected to create, sign, and broadcast an attestation during each epoch. The committee, assigned shard, and assigned slot for which the validator performs this role during an epoch is defined by `get_committee_assignment(state, epoch, validator_index)`.
A validator should create and broadcast the attestation halfway through the `slot` during which the validator is assigned ― that is, `SECONDS_PER_SLOT * 0.5` seconds after the start of `slot`.
@ -243,45 +287,33 @@ A validator should create and broadcast the attestation halfway through the `slo
First the validator should construct `attestation_data`, an [`AttestationData`](../core/0_beacon-chain.md#attestationdata) object based upon the state at the assigned slot.
* Let `head_block` be the result of running the fork choice during the assigned slot.
* Let `head_state` be the state of `head_block` processed through any empty slots up to the assigned slot.
* Let `head_state` be the state of `head_block` processed through any empty slots up to the assigned slot using `process_slots(state, slot)`.
##### Slot
Set `attestation_data.slot = head_state.slot`.
##### Beacon block root
##### LMD GHOST vote
Set `attestation_data.beacon_block_root = signing_root(head_block)`.
##### Source epoch
##### FFG vote
Set `attestation_data.source_epoch = head_state.justified_epoch`.
* Set `attestation_data.source_epoch = head_state.current_justified_epoch`.
* Set `attestation_data.source_root = head_state.current_justified_root`.
* Set `attestation_data.target_epoch = get_current_epoch(head_state)`
* Set `attestation_data.target_root = signing_root(epoch_boundary_block)` where `epoch_boundary_block` is the block at the most recent epoch boundary.
##### Source root
Set `attestation_data.source_root = head_state.current_justified_root`.
##### Target root
Set `attestation_data.target_root = signing_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary.
*Note*: This can be looked up in the state using:
*Note*: `epoch_boundary_block` can be looked up in the state using:
* Let `epoch_start_slot = get_epoch_start_slot(get_current_epoch(head_state))`.
* Set `epoch_boundary = head if epoch_start_slot == head_state.slot else get_block_root(state, epoch_start_slot)`.
* Let `epoch_boundary_block = head if epoch_start_slot == head_state.slot else get_block_root(state, epoch_start_slot)`.
##### Shard
##### Crosslink vote
Set `attestation_data.shard = shard` where `shard` is the shard associated with the validator's committee defined by `get_crosslink_committees_at_slot`.
Construct `attestation_data.crosslink` via the following.
##### Previous crosslink root
Set `attestation_data.previous_crosslink_root = hash_tree_root(head_state.current_crosslinks[shard])`.
##### Crosslink data root
Set `attestation_data.crosslink_data_root = ZERO_HASH`.
*Note*: This is a stub for Phase 0.
* Set `attestation_data.crosslink.shard = shard` where `shard` is the shard associated with the validator's committee.
* Let `parent_crosslink = head_state.current_crosslinks[shard]`.
* Set `attestation_data.crosslink.start_epoch = parent_crosslink.end_epoch`.
* Set `attestation_data.crosslink.end_epoch = min(attestation_data.target_epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)`.
* Set `attestation_data.crosslink.parent_root = hash_tree_root(head_state.current_crosslinks[shard])`.
* Set `attestation_data.crosslink.data_root = ZERO_HASH`. *Note*: This is a stub for Phase 0.
#### Construct attestation
@ -329,67 +361,6 @@ signed_attestation_data = bls_sign(
)
```
## Validator assignments
A validator can get the current, previous, and next epoch committee assignments using the following helper via `get_committee_assignment(state, epoch, validator_index)` where `previous_epoch <= epoch <= next_epoch`.
```python
def get_committee_assignment(
state: BeaconState,
epoch: Epoch,
validator_index: ValidatorIndex) -> Tuple[List[ValidatorIndex], Shard, Slot]:
"""
Return the committee assignment in the ``epoch`` for ``validator_index``.
``assignment`` returned is a tuple of the following form:
* ``assignment[0]`` is the list of validators in the committee
* ``assignment[1]`` is the shard to which the committee is assigned
* ``assignment[2]`` is the slot at which the committee is assigned
"""
previous_epoch = get_previous_epoch(state)
next_epoch = get_current_epoch(state) + 1
assert previous_epoch <= epoch <= next_epoch
epoch_start_slot = get_epoch_start_slot(epoch)
for slot in range(epoch_start_slot, epoch_start_slot + SLOTS_PER_EPOCH):
crosslink_committees = get_crosslink_committees_at_slot(
state,
slot,
)
selected_committees = [
committee # Tuple[List[ValidatorIndex], Shard]
for committee in crosslink_committees
if validator_index in committee[0]
]
if len(selected_committees) > 0:
validators = selected_committees[0][0]
shard = selected_committees[0][1]
assignment = (validators, shard, slot)
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 slot in question. Proposer selection is only stable within the context of the current epoch.
```python
def is_proposer_at_slot(state: BeaconState,
slot: Slot,
validator_index: ValidatorIndex) -> bool:
assert state.slot == slot
return get_beacon_proposer_index(state) == validator_index
```
*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 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+).
Specifically, a validator should call `get_committee_assignment(state, next_epoch, validator_index)` when checking for next epoch assignments.
## How to avoid slashing
"Slashing" is the burning of some amount of validator funds and immediate ejection from the active validator set. In Phase 0, there are two ways in which funds can be slashed -- [proposer slashing](#proposer-slashing) and [attester slashing](#attester-slashing). Although being slashed has serious repercussions, it is simple enough to avoid being slashed all together by remaining _consistent_ with respect to the messages a validator has previously signed.

View File

@ -15,7 +15,7 @@ def shuffling_case(seed: spec.Bytes32, count: int):
@to_tuple
def shuffling_test_cases():
for seed in [spec.hash(spec.int_to_bytes4(seed_init_value)) for seed_init_value in range(30)]:
for seed in [spec.hash(spec.int_to_bytes(seed_init_value, length=4)) for seed_init_value in range(30)]:
for count in [0, 1, 2, 3, 5, 10, 33, 100, 1000]:
yield shuffling_case(seed, count)

View File

@ -16,11 +16,11 @@ def decode(json, typ):
for field, subtype in typ.fields.items():
temp[field] = decode(json[field], subtype)
if field + "_hash_tree_root" in json:
assert(json[field + "_hash_tree_root"][2:] ==
assert(json[field + "_hash_tree_root"][2:] ==
hash_tree_root(temp[field], subtype).hex())
ret = typ(**temp)
if "hash_tree_root" in json:
assert(json["hash_tree_root"][2:] ==
assert(json["hash_tree_root"][2:] ==
hash_tree_root(ret, typ).hex())
return ret
else:

View File

@ -25,4 +25,3 @@ def encode(value, typ, include_hash_tree_roots=False):
else:
print(value, typ)
raise Exception("Type not recognized")

View File

@ -31,7 +31,12 @@ class RandomizationMode(Enum):
return self.value in [0, 4, 5]
def get_random_ssz_object(rng: Random, typ: Any, max_bytes_length: int, max_list_length: int, mode: RandomizationMode, chaos: bool) -> Any:
def get_random_ssz_object(rng: Random,
typ: Any,
max_bytes_length: int,
max_list_length: int,
mode: RandomizationMode,
chaos: bool) -> Any:
"""
Create an object for a given type, filled with random data.
:param rng: The random number generator to use.
@ -77,7 +82,10 @@ def get_random_ssz_object(rng: Random, typ: Any, max_bytes_length: int, max_list
return get_random_basic_value(rng, typ)
# Vector:
elif isinstance(typ, list) and len(typ) == 2:
return [get_random_ssz_object(rng, typ[0], max_bytes_length, max_list_length, mode, chaos) for _ in range(typ[1])]
return [
get_random_ssz_object(rng, typ[0], max_bytes_length, max_list_length, mode, chaos)
for _ in range(typ[1])
]
# List:
elif isinstance(typ, list) and len(typ) == 1:
length = rng.randint(0, max_list_length)
@ -85,10 +93,17 @@ def get_random_ssz_object(rng: Random, typ: Any, max_bytes_length: int, max_list
length = 1
if mode == RandomizationMode.mode_max_count:
length = max_list_length
return [get_random_ssz_object(rng, typ[0], max_bytes_length, max_list_length, mode, chaos) for _ in range(length)]
return [
get_random_ssz_object(rng, typ[0], max_bytes_length, max_list_length, mode, chaos)
for _ in range(length)
]
# Container:
elif hasattr(typ, 'fields'):
return typ(**{field: get_random_ssz_object(rng, subtype, max_bytes_length, max_list_length, mode, chaos) for field, subtype in typ.fields.items()})
return typ(**{
field:
get_random_ssz_object(rng, subtype, max_bytes_length, max_list_length, mode, chaos)
for field, subtype in typ.fields.items()
})
else:
print(typ)
raise Exception("Type not recognized")

View File

@ -1,112 +0,0 @@
from . import spec
from typing import (
Any,
Callable,
List
)
from .spec import (
BeaconState,
BeaconBlock,
Slot,
)
def expected_deposit_count(state: BeaconState) -> int:
return min(
spec.MAX_DEPOSITS,
state.latest_eth1_data.deposit_count - state.deposit_index
)
def process_operation_type(state: BeaconState,
operations: List[Any],
max_operations: int,
tx_fn: Callable[[BeaconState, Any], None]) -> None:
assert len(operations) <= max_operations
for operation in operations:
tx_fn(state, operation)
def process_operations(state: BeaconState, block: BeaconBlock) -> None:
process_operation_type(
state,
block.body.proposer_slashings,
spec.MAX_PROPOSER_SLASHINGS,
spec.process_proposer_slashing,
)
process_operation_type(
state,
block.body.attester_slashings,
spec.MAX_ATTESTER_SLASHINGS,
spec.process_attester_slashing,
)
process_operation_type(
state,
block.body.attestations,
spec.MAX_ATTESTATIONS,
spec.process_attestation,
)
assert len(block.body.deposits) == expected_deposit_count(state)
process_operation_type(
state,
block.body.deposits,
spec.MAX_DEPOSITS,
spec.process_deposit,
)
process_operation_type(
state,
block.body.voluntary_exits,
spec.MAX_VOLUNTARY_EXITS,
spec.process_voluntary_exit,
)
assert len(block.body.transfers) == len(set(block.body.transfers))
process_operation_type(
state,
block.body.transfers,
spec.MAX_TRANSFERS,
spec.process_transfer,
)
def process_block(state: BeaconState,
block: BeaconBlock,
verify_state_root: bool=False) -> None:
spec.process_block_header(state, block)
spec.process_randao(state, block)
spec.process_eth1_data(state, block)
process_operations(state, block)
if verify_state_root:
spec.verify_block_state_root(state, block)
def process_epoch_transition(state: BeaconState) -> None:
spec.process_justification_and_finalization(state)
spec.process_crosslinks(state)
spec.process_rewards_and_penalties(state)
spec.process_registry_updates(state)
spec.process_slashings(state)
spec.process_final_updates(state)
def state_transition_to(state: BeaconState, up_to: Slot) -> BeaconState:
while state.slot < up_to:
spec.cache_state(state)
if (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0:
process_epoch_transition(state)
spec.advance_slot(state)
def state_transition(state: BeaconState,
block: BeaconBlock,
verify_state_root: bool=False) -> BeaconState:
state_transition_to(state, block.slot)
process_block(state, block, verify_state_root)

View File

@ -9,4 +9,4 @@ def bls_verify_multiple(pubkeys, message_hashes, signature, domain):
def bls_aggregate_pubkeys(pubkeys):
return b'\x42' * 96
return b'\x42' * 48

View File

@ -3,13 +3,11 @@ import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.state_transition import (
state_transition,
)
from eth2spec.phase0.spec import (
get_current_epoch,
process_attestation,
slot_to_epoch,
state_transition,
)
from tests.helpers import (
build_empty_block_for_next_slot,
@ -66,6 +64,22 @@ def test_success_prevous_epoch(state):
return pre_state, attestation, post_state
def test_success_since_max_epochs_per_crosslink(state):
for _ in range(spec.MAX_EPOCHS_PER_CROSSLINK + 2):
next_epoch(state)
attestation = get_valid_attestation(state)
data = attestation.data
assert data.crosslink.end_epoch - data.crosslink.start_epoch == spec.MAX_EPOCHS_PER_CROSSLINK
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
pre_state, post_state = run_attestation_processing(state, attestation)
return pre_state, attestation, post_state
def test_before_inclusion_delay(state):
attestation = get_valid_attestation(state)
# do not increment slot to allow for inclusion delay
@ -113,7 +127,7 @@ def test_non_zero_crosslink_data_root(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.data.crosslink_data_root = b'\x42' * 32
attestation.data.crosslink.data_root = b'\x42' * 32
pre_state, post_state = run_attestation_processing(state, attestation, False)
@ -126,7 +140,33 @@ def test_bad_previous_crosslink(state):
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
state.current_crosslinks[attestation.data.shard].epoch += 10
attestation.data.crosslink.parent_root = b'\x27' * 32
pre_state, post_state = run_attestation_processing(state, attestation, False)
return pre_state, attestation, post_state
def test_bad_crosslink_start_epoch(state):
next_epoch(state)
attestation = get_valid_attestation(state)
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
attestation.data.crosslink.start_epoch += 1
pre_state, post_state = run_attestation_processing(state, attestation, False)
return pre_state, attestation, post_state
def test_bad_crosslink_end_epoch(state):
next_epoch(state)
attestation = get_valid_attestation(state)
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
attestation.data.crosslink.end_epoch += 1
pre_state, post_state = run_attestation_processing(state, attestation, False)
@ -150,6 +190,6 @@ def test_empty_aggregation_bitfield(state):
attestation.aggregation_bitfield = b'\x00' * len(attestation.aggregation_bitfield)
pre_state, post_state = run_attestation_processing(state, attestation, False)
pre_state, post_state = run_attestation_processing(state, attestation)
return pre_state, attestation, post_state

View File

@ -4,11 +4,11 @@ import pytest
from eth2spec.phase0.spec import (
get_beacon_proposer_index,
cache_state,
advance_slot,
process_slot,
process_block_header,
)
from tests.helpers import (
advance_slot,
build_empty_block_for_next_slot,
next_slot,
)
@ -18,7 +18,7 @@ pytestmark = pytest.mark.header
def prepare_state_for_header_processing(state):
cache_state(state)
process_slot(state)
advance_slot(state)
@ -53,9 +53,9 @@ def test_invalid_slot(state):
return pre_state, block, None
def test_invalid_previous_block_root(state):
def test_invalid_parent_block_root(state):
block = build_empty_block_for_next_slot(state)
block.previous_block_root = b'\12' * 32 # invalid prev root
block.parent_root = b'\12' * 32 # invalid prev root
pre_state, post_state = run_block_header_processing(state, block, valid=False)
return pre_state, block, None

View File

@ -3,13 +3,11 @@ import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.state_transition import (
state_transition,
)
from eth2spec.phase0.spec import (
cache_state,
process_slot,
get_crosslink_deltas,
process_crosslinks,
state_transition,
)
from tests.helpers import (
add_attestation_to_state,
@ -35,7 +33,7 @@ def run_process_crosslinks(state, valid=True):
state_transition(state, block)
# cache state before epoch transition
cache_state(state)
process_slot(state)
post_state = deepcopy(state)
process_crosslinks(post_state)
@ -64,7 +62,7 @@ def test_single_crosslink_update_from_current_epoch(state):
pre_state, post_state = run_process_crosslinks(state)
shard = attestation.data.shard
shard = attestation.data.crosslink.shard
assert post_state.previous_crosslinks[shard] != post_state.current_crosslinks[shard]
assert pre_state.current_crosslinks[shard] != post_state.current_crosslinks[shard]
@ -84,11 +82,11 @@ def test_single_crosslink_update_from_previous_epoch(state):
pre_state, post_state = run_process_crosslinks(state)
crosslink_deltas = get_crosslink_deltas(state)
shard = attestation.data.shard
shard = attestation.data.crosslink.shard
assert post_state.previous_crosslinks[shard] != post_state.current_crosslinks[shard]
assert pre_state.current_crosslinks[shard] != post_state.current_crosslinks[shard]
# ensure rewarded
for index in get_crosslink_committee(state, attestation.data.target_epoch, attestation.data.shard):
for index in get_crosslink_committee(state, attestation.data.target_epoch, attestation.data.crosslink.shard):
assert crosslink_deltas[0][index] > 0
assert crosslink_deltas[1][index] == 0
@ -108,7 +106,7 @@ def test_double_late_crosslink(state):
for slot in range(spec.SLOTS_PER_EPOCH):
attestation_2 = get_valid_attestation(state)
if attestation_2.data.shard == attestation_1.data.shard:
if attestation_2.data.crosslink.shard == attestation_1.data.crosslink.shard:
break
next_slot(state)
fill_aggregate_attestation(state, attestation_2)
@ -124,12 +122,12 @@ def test_double_late_crosslink(state):
pre_state, post_state = run_process_crosslinks(state)
crosslink_deltas = get_crosslink_deltas(state)
shard = attestation_2.data.shard
shard = attestation_2.data.crosslink.shard
# ensure that the current crosslinks were not updated by the second attestation
assert post_state.previous_crosslinks[shard] == post_state.current_crosslinks[shard]
# ensure no reward, only penalties for the failed crosslink
for index in get_crosslink_committee(state, attestation_2.data.target_epoch, attestation_2.data.shard):
for index in get_crosslink_committee(state, attestation_2.data.target_epoch, attestation_2.data.crosslink.shard):
assert crosslink_deltas[0][index] == 0
assert crosslink_deltas[1][index] > 0

View File

@ -2,14 +2,12 @@ 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 (
# constants
ZERO_HASH,
MAX_EPOCHS_PER_CROSSLINK,
# SSZ
Attestation,
AttestationData,
@ -17,6 +15,7 @@ from eth2spec.phase0.spec import (
AttesterSlashing,
BeaconBlock,
BeaconBlockHeader,
Crosslink,
Deposit,
DepositData,
Eth1Data,
@ -38,6 +37,7 @@ from eth2spec.phase0.spec import (
get_shard_delta,
hash_tree_root,
slot_to_epoch,
state_transition,
verify_merkle_branch,
hash,
)
@ -53,6 +53,10 @@ pubkeys = [bls.privtopub(privkey) for privkey in privkeys]
pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)}
def advance_slot(state) -> None:
state.slot += 1
def get_balance(state, index):
return state.balances[index]
@ -126,7 +130,7 @@ def build_empty_block_for_next_slot(state):
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()
empty_block.previous_block_root = signing_root(previous_block_header)
empty_block.parent_root = signing_root(previous_block_header)
return empty_block
@ -153,7 +157,7 @@ def build_attestation_data(state, slot, shard):
assert state.slot >= slot
if slot == state.slot:
block_root = build_empty_block_for_next_slot(state).previous_block_root
block_root = build_empty_block_for_next_slot(state).parent_root
else:
block_root = get_block_root_at_slot(state, slot)
@ -173,15 +177,20 @@ def build_attestation_data(state, slot, shard):
justified_block_root = state.current_justified_root
crosslinks = state.current_crosslinks if slot_to_epoch(slot) == get_current_epoch(state) else state.previous_crosslinks
parent_crosslink = crosslinks[shard]
return AttestationData(
shard=shard,
beacon_block_root=block_root,
source_epoch=justified_epoch,
source_root=justified_block_root,
target_epoch=slot_to_epoch(slot),
target_root=epoch_boundary_root,
crosslink_data_root=spec.ZERO_HASH,
previous_crosslink_root=hash_tree_root(crosslinks[shard]),
crosslink=Crosslink(
shard=shard,
start_epoch=parent_crosslink.end_epoch,
end_epoch=min(slot_to_epoch(slot), parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK),
data_root=spec.ZERO_HASH,
parent_root=hash_tree_root(parent_crosslink),
),
)
@ -235,12 +244,12 @@ def get_valid_proposer_slashing(state):
header_1 = BeaconBlockHeader(
slot=slot,
previous_block_root=ZERO_HASH,
parent_root=ZERO_HASH,
state_root=ZERO_HASH,
block_body_root=ZERO_HASH,
body_root=ZERO_HASH,
)
header_2 = deepcopy(header_1)
header_2.previous_block_root = b'\x02' * 32
header_2.parent_root = b'\x02' * 32
header_2.slot = slot + 1
domain = get_domain(
@ -288,7 +297,7 @@ def get_valid_attestation(state, slot=None):
attestation_data = build_attestation_data(state, slot, shard)
crosslink_committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.shard)
crosslink_committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.crosslink.shard)
committee_size = len(crosslink_committee)
bitfield_length = (committee_size + 7) // 8
@ -381,7 +390,7 @@ def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0)
def fill_aggregate_attestation(state, attestation):
crosslink_committee = get_crosslink_committee(state, attestation.data.target_epoch, attestation.data.shard)
crosslink_committee = get_crosslink_committee(state, attestation.data.target_epoch, attestation.data.crosslink.shard)
for i in range(len(crosslink_committee)):
attestation.aggregation_bitfield = set_bitfield_bit(attestation.aggregation_bitfield, i)

View File

@ -4,9 +4,6 @@ import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.state_transition import (
state_transition,
)
from .helpers import (
build_empty_block_for_next_slot,
fill_aggregate_attestation,
@ -67,7 +64,7 @@ def next_epoch_with_attestations(state,
fill_aggregate_attestation(post_state, prev_attestation)
block.body.attestations.append(prev_attestation)
state_transition(post_state, block)
spec.state_transition(post_state, block)
blocks.append(block)
return state, blocks, post_state

View File

@ -20,13 +20,10 @@ from eth2spec.phase0.spec import (
get_block_root_at_slot,
get_current_epoch,
get_domain,
advance_slot,
cache_state,
process_slot,
verify_merkle_branch,
hash,
)
from eth2spec.phase0.state_transition import (
state_transition,
hash,
)
from eth2spec.utils.merkle_minimal import (
calc_merkle_tree_from_leaves,
@ -34,6 +31,7 @@ from eth2spec.utils.merkle_minimal import (
get_merkle_root,
)
from .helpers import (
advance_slot,
get_balance,
build_deposit_data,
build_empty_block_for_next_slot,
@ -54,7 +52,7 @@ pytestmark = pytest.mark.sanity
def test_slot_transition(state):
test_state = deepcopy(state)
cache_state(test_state)
process_slot(test_state)
advance_slot(test_state)
assert test_state.slot == state.slot + 1
assert get_state_root(test_state, state.slot) == state.hash_tree_root()
@ -68,7 +66,7 @@ def test_empty_block_transition(state):
state_transition(test_state, block)
assert len(test_state.eth1_data_votes) == len(state.eth1_data_votes) + 1
assert get_block_root_at_slot(test_state, state.slot) == block.previous_block_root
assert get_block_root_at_slot(test_state, state.slot) == block.parent_root
return state, [block], test_state
@ -82,7 +80,7 @@ def test_skipped_slots(state):
assert test_state.slot == block.slot
for slot in range(state.slot, test_state.slot):
assert get_block_root_at_slot(test_state, slot) == block.previous_block_root
assert get_block_root_at_slot(test_state, slot) == block.parent_root
return state, [block], test_state
@ -96,7 +94,7 @@ def test_empty_epoch_transition(state):
assert test_state.slot == block.slot
for slot in range(state.slot, test_state.slot):
assert get_block_root_at_slot(test_state, slot) == block.previous_block_root
assert get_block_root_at_slot(test_state, slot) == block.parent_root
return state, [block], test_state