Merge pull request #1507 from ethereum/v09x

Release v0.9.3 to master
This commit is contained in:
Danny Ryan
2019-12-12 12:38:12 -07:00
committed by GitHub
44 changed files with 1526 additions and 885 deletions

View File

@@ -103,6 +103,15 @@ jobs:
command: make citest
- store_test_results:
path: test_libs/pyspec/test-reports
table_of_contents:
docker:
- image: circleci/node:10.16.3
working_directory: ~/specs-repo
steps:
- checkout
- run:
name: Check table of contents
command: sudo npm install -g doctoc && make check_toc
lint:
docker:
- image: circleci/python:3.6
@@ -148,6 +157,7 @@ workflows:
- test:
requires:
- install_pyspec_test
- table_of_contents
- lint:
requires:
- test

View File

@@ -17,18 +17,22 @@ GENERATOR_VENVS = $(patsubst $(GENERATOR_DIR)/%, $(GENERATOR_DIR)/%venv, $(GENER
#$(info $$GENERATOR_TARGETS is [${GENERATOR_TARGETS}])
PY_SPEC_PHASE_0_TARGETS = $(PY_SPEC_DIR)/eth2spec/phase0/spec.py
PY_SPEC_PHASE_0_DEPS = $(SPEC_DIR)/core/0_*.md
PY_SPEC_PHASE_0_DEPS = $(wildcard $(SPEC_DIR)/core/0_*.md)
PY_SPEC_PHASE_1_TARGETS = $(PY_SPEC_DIR)/eth2spec/phase1/spec.py
PY_SPEC_PHASE_1_DEPS = $(SPEC_DIR)/core/1_*.md
PY_SPEC_PHASE_1_DEPS = $(wildcard $(SPEC_DIR)/core/1_*.md)
PY_SPEC_ALL_DEPS = $(PY_SPEC_PHASE_0_DEPS) $(PY_SPEC_PHASE_1_DEPS)
PY_SPEC_ALL_TARGETS = $(PY_SPEC_PHASE_0_TARGETS) $(PY_SPEC_PHASE_1_TARGETS)
MARKDOWN_FILES = $(PY_SPEC_ALL_DEPS) $(wildcard $(SPEC_DIR)/*.md) $(wildcard $(SPEC_DIR)/light_client/*.md) $(wildcard $(SPEC_DIR)/networking/*.md) $(wildcard $(SPEC_DIR)/validator/*.md)
COV_HTML_OUT=.htmlcov
COV_INDEX_FILE=$(PY_SPEC_DIR)/$(COV_HTML_OUT)/index.html
.PHONY: clean partial_clean all test citest lint generate_tests pyspec phase0 phase1 install_test open_cov \
install_deposit_contract_test test_deposit_contract compile_deposit_contract
install_deposit_contract_test test_deposit_contract compile_deposit_contract check_toc
all: $(PY_SPEC_ALL_TARGETS)
@@ -65,6 +69,14 @@ citest: $(PY_SPEC_ALL_TARGETS)
open_cov:
((open "$(COV_INDEX_FILE)" || xdg-open "$(COV_INDEX_FILE)") &> /dev/null) &
check_toc: $(MARKDOWN_FILES:=.toc)
%.toc:
cp $* $*.tmp && \
doctoc $* && \
diff -q $* $*.tmp && \
rm $*.tmp
lint: $(PY_SPEC_ALL_TARGETS)
cd $(PY_SPEC_DIR); . venv/bin/activate; \
flake8 --ignore=E252,W504,W503 --max-line-length=120 ./eth2spec \

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
# Vyper target 0.1.0b12
# Vyper target 0.1.0b13
MIN_DEPOSIT_AMOUNT: constant(uint256) = 1000000000 # Gwei
DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32
MAX_DEPOSIT_COUNT: constant(uint256) = 4294967295 # 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1
@@ -75,7 +75,7 @@ def deposit(pubkey: bytes[PUBKEY_LENGTH],
deposit_amount: uint256 = msg.value / as_wei_value(1, "gwei")
assert deposit_amount >= MIN_DEPOSIT_AMOUNT
# Length checks to facilitate formal verification (see https://github.com/ethereum/eth2.0-specs/pull/1362/files#r320361859)
# Length checks for safety
assert len(pubkey) == PUBKEY_LENGTH
assert len(withdrawal_credentials) == WITHDRAWAL_CREDENTIALS_LENGTH
assert len(signature) == SIGNATURE_LENGTH

View File

@@ -1,5 +1,5 @@
eth-tester[py-evm]==0.1.0b39
vyper==0.1.0b12
vyper==0.1.0b13
web3==5.0.0b2
pytest==3.6.1
../test_libs/pyspec

View File

@@ -19,10 +19,7 @@ from dataclasses import (
field,
)
from eth2spec.utils.ssz.ssz_impl import (
hash_tree_root,
signing_root,
)
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
from eth2spec.utils.ssz.ssz_typing import (
boolean, Container, List, Vector, uint64,
Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector,
@@ -50,7 +47,6 @@ from dataclasses import (
from eth2spec.utils.ssz.ssz_impl import (
hash_tree_root,
signing_root,
is_zero,
)
from eth2spec.utils.ssz.ssz_typing import (
@@ -86,7 +82,7 @@ def get_eth1_data(distance: uint64) -> Bytes32:
return hash(distance)
def hash(x: bytes) -> Bytes32:
def hash(x: bytes) -> Bytes32: # type: ignore
if x not in hash_cache:
hash_cache[x] = Bytes32(_hash(x))
return hash_cache[x]

View File

@@ -6,23 +6,25 @@
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [BLS signature verification](#bls-signature-verification)
- [Table of contents](#table-of-contents)
- [Curve parameters](#curve-parameters)
- [Point representations](#point-representations)
- [G1 points](#g1-points)
- [G2 points](#g2-points)
- [Helpers](#helpers)
- [`hash_to_G2`](#hash_to_g2)
- [`modular_squareroot`](#modular_squareroot)
- [Aggregation operations](#aggregation-operations)
- [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys)
- [`bls_aggregate_signatures`](#bls_aggregate_signatures)
- [Signature verification](#signature-verification)
- [`bls_verify`](#bls_verify)
- [`bls_verify_multiple`](#bls_verify_multiple)
- [Curve parameters](#curve-parameters)
- [Point representations](#point-representations)
- [G1 points](#g1-points)
- [G2 points](#g2-points)
- [Helpers](#helpers)
- [`hash_to_G2`](#hash_to_g2)
- [`modular_squareroot`](#modular_squareroot)
- [Aggregation operations](#aggregation-operations)
- [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys)
- [`bls_aggregate_signatures`](#bls_aggregate_signatures)
- [Signature verification](#signature-verification)
- [`bls_verify`](#bls_verify)
- [`bls_verify_multiple`](#bls_verify_multiple)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Curve parameters

View File

@@ -4,115 +4,123 @@
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Ethereum 2.0 Phase 0 -- The Beacon Chain](#ethereum-20-phase-0----the-beacon-chain)
- [Table of contents](#table-of-contents)
- [Introduction](#introduction)
- [Notation](#notation)
- [Custom types](#custom-types)
- [Constants](#constants)
- [Configuration](#configuration)
- [Misc](#misc)
- [Gwei values](#gwei-values)
- [Initial values](#initial-values)
- [Time parameters](#time-parameters)
- [State list lengths](#state-list-lengths)
- [Rewards and penalties](#rewards-and-penalties)
- [Max operations per block](#max-operations-per-block)
- [Domain types](#domain-types)
- [Containers](#containers)
- [Misc dependencies](#misc-dependencies)
- [`Fork`](#fork)
- [`Checkpoint`](#checkpoint)
- [`Validator`](#validator)
- [`AttestationData`](#attestationdata)
- [`IndexedAttestation`](#indexedattestation)
- [`PendingAttestation`](#pendingattestation)
- [`Eth1Data`](#eth1data)
- [`HistoricalBatch`](#historicalbatch)
- [`DepositData`](#depositdata)
- [`BeaconBlockHeader`](#beaconblockheader)
- [Beacon operations](#beacon-operations)
- [`ProposerSlashing`](#proposerslashing)
- [`AttesterSlashing`](#attesterslashing)
- [`Attestation`](#attestation)
- [`Deposit`](#deposit)
- [`VoluntaryExit`](#voluntaryexit)
- [Beacon blocks](#beacon-blocks)
- [`BeaconBlockBody`](#beaconblockbody)
- [`BeaconBlock`](#beaconblock)
- [Beacon state](#beacon-state)
- [`BeaconState`](#beaconstate)
- [Helper functions](#helper-functions)
- [Math](#math)
- [`integer_squareroot`](#integer_squareroot)
- [`xor`](#xor)
- [`int_to_bytes`](#int_to_bytes)
- [`bytes_to_int`](#bytes_to_int)
- [Crypto](#crypto)
- [`hash`](#hash)
- [`hash_tree_root`](#hash_tree_root)
- [`signing_root`](#signing_root)
- [`bls_verify`](#bls_verify)
- [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys)
- [Predicates](#predicates)
- [`is_active_validator`](#is_active_validator)
- [`is_slashable_validator`](#is_slashable_validator)
- [`is_slashable_attestation_data`](#is_slashable_attestation_data)
- [`is_valid_indexed_attestation`](#is_valid_indexed_attestation)
- [`is_valid_merkle_branch`](#is_valid_merkle_branch)
- [Misc](#misc-1)
- [`compute_shuffled_index`](#compute_shuffled_index)
- [`compute_proposer_index`](#compute_proposer_index)
- [`compute_committee`](#compute_committee)
- [`compute_epoch_at_slot`](#compute_epoch_at_slot)
- [`compute_start_slot_at_epoch`](#compute_start_slot_at_epoch)
- [`compute_activation_exit_epoch`](#compute_activation_exit_epoch)
- [`compute_domain`](#compute_domain)
- [Beacon state accessors](#beacon-state-accessors)
- [`get_current_epoch`](#get_current_epoch)
- [`get_previous_epoch`](#get_previous_epoch)
- [`get_block_root`](#get_block_root)
- [`get_block_root_at_slot`](#get_block_root_at_slot)
- [`get_randao_mix`](#get_randao_mix)
- [`get_active_validator_indices`](#get_active_validator_indices)
- [`get_validator_churn_limit`](#get_validator_churn_limit)
- [`get_seed`](#get_seed)
- [`get_committee_count_at_slot`](#get_committee_count_at_slot)
- [`get_beacon_committee`](#get_beacon_committee)
- [`get_beacon_proposer_index`](#get_beacon_proposer_index)
- [`get_total_balance`](#get_total_balance)
- [`get_total_active_balance`](#get_total_active_balance)
- [`get_domain`](#get_domain)
- [`get_indexed_attestation`](#get_indexed_attestation)
- [`get_attesting_indices`](#get_attesting_indices)
- [Beacon state mutators](#beacon-state-mutators)
- [`increase_balance`](#increase_balance)
- [`decrease_balance`](#decrease_balance)
- [`initiate_validator_exit`](#initiate_validator_exit)
- [`slash_validator`](#slash_validator)
- [Genesis](#genesis)
- [Genesis state](#genesis-state)
- [Genesis block](#genesis-block)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Epoch processing](#epoch-processing)
- [Helper functions](#helper-functions-1)
- [Justification and finalization](#justification-and-finalization)
- [Rewards and penalties](#rewards-and-penalties-1)
- [Registry updates](#registry-updates)
- [Slashings](#slashings)
- [Final updates](#final-updates)
- [Block processing](#block-processing)
- [Block header](#block-header)
- [RANDAO](#randao)
- [Eth1 data](#eth1-data)
- [Operations](#operations)
- [Proposer slashings](#proposer-slashings)
- [Attester slashings](#attester-slashings)
- [Attestations](#attestations)
- [Deposits](#deposits)
- [Voluntary exits](#voluntary-exits)
- [Introduction](#introduction)
- [Notation](#notation)
- [Custom types](#custom-types)
- [Constants](#constants)
- [Configuration](#configuration)
- [Misc](#misc)
- [Gwei values](#gwei-values)
- [Initial values](#initial-values)
- [Time parameters](#time-parameters)
- [State list lengths](#state-list-lengths)
- [Rewards and penalties](#rewards-and-penalties)
- [Max operations per block](#max-operations-per-block)
- [Domain types](#domain-types)
- [Containers](#containers)
- [Misc dependencies](#misc-dependencies)
- [`Fork`](#fork)
- [`Checkpoint`](#checkpoint)
- [`Validator`](#validator)
- [`AttestationData`](#attestationdata)
- [`IndexedAttestation`](#indexedattestation)
- [`PendingAttestation`](#pendingattestation)
- [`Eth1Data`](#eth1data)
- [`HistoricalBatch`](#historicalbatch)
- [`DepositMessage`](#depositmessage)
- [`DepositData`](#depositdata)
- [`BeaconBlockHeader`](#beaconblockheader)
- [Beacon operations](#beacon-operations)
- [`ProposerSlashing`](#proposerslashing)
- [`AttesterSlashing`](#attesterslashing)
- [`Attestation`](#attestation)
- [`Deposit`](#deposit)
- [`VoluntaryExit`](#voluntaryexit)
- [Beacon blocks](#beacon-blocks)
- [`BeaconBlockBody`](#beaconblockbody)
- [`BeaconBlock`](#beaconblock)
- [Beacon state](#beacon-state)
- [`BeaconState`](#beaconstate)
- [Signed envelopes](#signed-envelopes)
- [`SignedVoluntaryExit`](#signedvoluntaryexit)
- [`SignedBeaconBlock`](#signedbeaconblock)
- [`SignedBeaconBlockHeader`](#signedbeaconblockheader)
- [Helper functions](#helper-functions)
- [Math](#math)
- [`integer_squareroot`](#integer_squareroot)
- [`xor`](#xor)
- [`int_to_bytes`](#int_to_bytes)
- [`bytes_to_int`](#bytes_to_int)
- [Crypto](#crypto)
- [`hash`](#hash)
- [`hash_tree_root`](#hash_tree_root)
- [`bls_verify`](#bls_verify)
- [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys)
- [Predicates](#predicates)
- [`is_active_validator`](#is_active_validator)
- [`is_eligible_for_activation_queue`](#is_eligible_for_activation_queue)
- [`is_eligible_for_activation`](#is_eligible_for_activation)
- [`is_slashable_validator`](#is_slashable_validator)
- [`is_slashable_attestation_data`](#is_slashable_attestation_data)
- [`is_valid_indexed_attestation`](#is_valid_indexed_attestation)
- [`is_valid_merkle_branch`](#is_valid_merkle_branch)
- [Misc](#misc-1)
- [`compute_shuffled_index`](#compute_shuffled_index)
- [`compute_proposer_index`](#compute_proposer_index)
- [`compute_committee`](#compute_committee)
- [`compute_epoch_at_slot`](#compute_epoch_at_slot)
- [`compute_start_slot_at_epoch`](#compute_start_slot_at_epoch)
- [`compute_activation_exit_epoch`](#compute_activation_exit_epoch)
- [`compute_domain`](#compute_domain)
- [Beacon state accessors](#beacon-state-accessors)
- [`get_current_epoch`](#get_current_epoch)
- [`get_previous_epoch`](#get_previous_epoch)
- [`get_block_root`](#get_block_root)
- [`get_block_root_at_slot`](#get_block_root_at_slot)
- [`get_randao_mix`](#get_randao_mix)
- [`get_active_validator_indices`](#get_active_validator_indices)
- [`get_validator_churn_limit`](#get_validator_churn_limit)
- [`get_seed`](#get_seed)
- [`get_committee_count_at_slot`](#get_committee_count_at_slot)
- [`get_beacon_committee`](#get_beacon_committee)
- [`get_beacon_proposer_index`](#get_beacon_proposer_index)
- [`get_total_balance`](#get_total_balance)
- [`get_total_active_balance`](#get_total_active_balance)
- [`get_domain`](#get_domain)
- [`get_indexed_attestation`](#get_indexed_attestation)
- [`get_attesting_indices`](#get_attesting_indices)
- [Beacon state mutators](#beacon-state-mutators)
- [`increase_balance`](#increase_balance)
- [`decrease_balance`](#decrease_balance)
- [`initiate_validator_exit`](#initiate_validator_exit)
- [`slash_validator`](#slash_validator)
- [Genesis](#genesis)
- [Genesis state](#genesis-state)
- [Genesis block](#genesis-block)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Epoch processing](#epoch-processing)
- [Helper functions](#helper-functions-1)
- [Justification and finalization](#justification-and-finalization)
- [Rewards and penalties](#rewards-and-penalties-1)
- [Registry updates](#registry-updates)
- [Slashings](#slashings)
- [Final updates](#final-updates)
- [Block processing](#block-processing)
- [Block header](#block-header)
- [RANDAO](#randao)
- [Eth1 data](#eth1-data)
- [Operations](#operations)
- [Proposer slashings](#proposer-slashings)
- [Attester slashings](#attester-slashings)
- [Attestations](#attestations)
- [Deposits](#deposits)
- [Voluntary exits](#voluntary-exits)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
@@ -342,6 +350,15 @@ class HistoricalBatch(Container):
state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
```
#### `DepositMessage`
```python
class DepositMessage(Container):
pubkey: BLSPubkey
withdrawal_credentials: Bytes32
amount: Gwei
```
#### `DepositData`
```python
@@ -349,7 +366,7 @@ class DepositData(Container):
pubkey: BLSPubkey
withdrawal_credentials: Bytes32
amount: Gwei
signature: BLSSignature
signature: BLSSignature # signing over DepositMessage
```
#### `BeaconBlockHeader`
@@ -360,7 +377,6 @@ class BeaconBlockHeader(Container):
parent_root: Root
state_root: Root
body_root: Root
signature: BLSSignature
```
### Beacon operations
@@ -370,8 +386,8 @@ class BeaconBlockHeader(Container):
```python
class ProposerSlashing(Container):
proposer_index: ValidatorIndex
header_1: BeaconBlockHeader
header_2: BeaconBlockHeader
signed_header_1: SignedBeaconBlockHeader
signed_header_2: SignedBeaconBlockHeader
```
#### `AttesterSlashing`
@@ -405,7 +421,6 @@ class Deposit(Container):
class VoluntaryExit(Container):
epoch: Epoch # Earliest epoch when voluntary exit can be processed
validator_index: ValidatorIndex
signature: BLSSignature
```
### Beacon blocks
@@ -422,7 +437,7 @@ class BeaconBlockBody(Container):
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
attestations: List[Attestation, MAX_ATTESTATIONS]
deposits: List[Deposit, MAX_DEPOSITS]
voluntary_exits: List[VoluntaryExit, MAX_VOLUNTARY_EXITS]
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
```
#### `BeaconBlock`
@@ -433,7 +448,6 @@ class BeaconBlock(Container):
parent_root: Root
state_root: Root
body: BeaconBlockBody
signature: BLSSignature
```
### Beacon state
@@ -472,6 +486,34 @@ class BeaconState(Container):
finalized_checkpoint: Checkpoint
```
### Signed envelopes
Some messages in the protocol are wrapped in an envelope to better facilitate adding/pruning the signature and to `hash_tree_root` the `message` separate from the signature.
#### `SignedVoluntaryExit`
```python
class SignedVoluntaryExit(Container):
message: VoluntaryExit
signature: BLSSignature
```
#### `SignedBeaconBlock`
```python
class SignedBeaconBlock(Container):
message: BeaconBlock
signature: BLSSignature
```
#### `SignedBeaconBlockHeader`
```python
class SignedBeaconBlockHeader(Container):
message: BeaconBlockHeader
signature: BLSSignature
```
## Helper functions
*Note*: The definitions below are for specification purposes and are not necessarily optimal implementations.
@@ -533,10 +575,6 @@ def bytes_to_int(data: bytes) -> uint64:
`def hash_tree_root(object: SSZSerializable) -> Root` is a function for hashing objects into a single root by utilizing a hash tree structure, as defined in the [SSZ spec](../simple-serialize.md#merkleization).
#### `signing_root`
`def signing_root(object: Container) -> Root` is a function for computing signing messages, as defined in the [SSZ spec](../simple-serialize.md#self-signed-containers).
#### `bls_verify`
`bls_verify` is a function for verifying a BLS signature, as defined in the [BLS Signature spec](../bls_signature.md#bls_verify).
@@ -557,6 +595,34 @@ def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
return validator.activation_epoch <= epoch < validator.exit_epoch
```
#### `is_eligible_for_activation_queue`
```python
def is_eligible_for_activation_queue(validator: Validator) -> bool:
"""
Check if ``validator`` is eligible to be placed into the activation queue.
"""
return (
validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
and validator.effective_balance == MAX_EFFECTIVE_BALANCE
)
```
#### `is_eligible_for_activation`
```python
def is_eligible_for_activation(state: BeaconState, validator: Validator) -> bool:
"""
Check if ``validator`` is eligible for activation.
"""
return (
# Placement in queue is finalized
validator.activation_eligibility_epoch <= state.finalized_checkpoint.epoch
# Has not yet been activated
and validator.activation_epoch == FAR_FUTURE_EPOCH
)
```
#### `is_slashable_validator`
```python
@@ -594,8 +660,8 @@ def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: Indexe
# Verify max number of indices
if not len(indices) <= MAX_VALIDATORS_PER_COMMITTEE:
return False
# Verify indices are sorted
if not indices == sorted(indices):
# Verify indices are sorted and unique
if not indices == sorted(set(indices)):
return False
# Verify aggregate signature
if not bls_verify(
@@ -1048,18 +1114,27 @@ Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`.
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 exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid.
```python
def state_transition(state: BeaconState, block: BeaconBlock, validate_state_root: bool=False) -> BeaconState:
def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> BeaconState:
# Process slots (including those with no blocks) since block
process_slots(state, block.slot)
process_slots(state, signed_block.message.slot)
# Verify signature
if validate_result:
assert verify_block_signature(state, signed_block)
# 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)
process_block(state, signed_block.message)
if validate_result:
assert signed_block.message.state_root == hash_tree_root(state) # Validate state root
# Return post-state
return state
```
```python
def verify_block_signature(state: BeaconState, signed_block: SignedBeaconBlock) -> bool:
proposer = state.validators[get_beacon_proposer_index(state)]
domain = get_domain(state, DOMAIN_BEACON_PROPOSER)
return bls_verify(proposer.pubkey, hash_tree_root(signed_block.message), signed_block.signature, domain)
```
```python
def process_slots(state: BeaconState, slot: Slot) -> None:
assert state.slot <= slot
@@ -1080,7 +1155,7 @@ def process_slot(state: BeaconState) -> None:
if state.latest_block_header.state_root == Bytes32():
state.latest_block_header.state_root = previous_state_root
# Cache block root
previous_block_root = signing_root(state.latest_block_header)
previous_block_root = hash_tree_root(state.latest_block_header)
state.block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root
```
@@ -1259,26 +1334,22 @@ def process_rewards_and_penalties(state: BeaconState) -> None:
def process_registry_updates(state: BeaconState) -> None:
# Process activation eligibility and ejections
for index, validator in enumerate(state.validators):
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_eligible_for_activation_queue(validator):
validator.activation_eligibility_epoch = get_current_epoch(state) + 1
if is_active_validator(validator, get_current_epoch(state)) and validator.effective_balance <= EJECTION_BALANCE:
initiate_validator_exit(state, ValidatorIndex(index))
# Queue validators eligible for activation and not dequeued for activation prior to finalized epoch
# Queue validators eligible for activation and not yet dequeued for activation
activation_queue = sorted([
index for index, validator in enumerate(state.validators)
if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH
and validator.activation_epoch >= compute_activation_exit_epoch(state.finalized_checkpoint.epoch)
], key=lambda index: state.validators[index].activation_eligibility_epoch)
# Dequeued validators for activation up to churn limit (without resetting activation epoch)
if is_eligible_for_activation(state, validator)
# Order by the sequence of activation_eligibility_epoch setting and then index
], key=lambda index: (state.validators[index].activation_eligibility_epoch, index))
# Dequeued validators for activation up to churn limit
for index in activation_queue[:get_validator_churn_limit(state)]:
validator = state.validators[index]
if validator.activation_epoch == FAR_FUTURE_EPOCH:
validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state))
validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state))
```
#### Slashings
@@ -1340,20 +1411,17 @@ 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.parent_root == signing_root(state.latest_block_header)
assert block.parent_root == hash_tree_root(state.latest_block_header)
# Save current block as the new latest block
state.latest_block_header = BeaconBlockHeader(
slot=block.slot,
parent_root=block.parent_root,
# `state_root` is zeroed and overwritten in the next `process_slot` call
body_root=hash_tree_root(block.body),
# `signature` is zeroed
)
# Verify proposer is not slashed
proposer = state.validators[get_beacon_proposer_index(state)]
assert not proposer.slashed
# Verify proposer signature
assert bls_verify(proposer.pubkey, signing_root(block), block.signature, get_domain(state, DOMAIN_BEACON_PROPOSER))
```
#### RANDAO
@@ -1403,15 +1471,15 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
proposer = state.validators[proposer_slashing.proposer_index]
# Verify slots match
assert proposer_slashing.header_1.slot == proposer_slashing.header_2.slot
assert proposer_slashing.signed_header_1.message.slot == proposer_slashing.signed_header_2.message.slot
# But the headers are different
assert proposer_slashing.header_1 != proposer_slashing.header_2
assert proposer_slashing.signed_header_1.message != proposer_slashing.signed_header_2.message
# Check proposer is slashable
assert is_slashable_validator(proposer, get_current_epoch(state))
# Signatures are valid
for header in (proposer_slashing.header_1, proposer_slashing.header_2):
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(header.slot))
assert bls_verify(proposer.pubkey, signing_root(header), header.signature, domain)
for signed_header in (proposer_slashing.signed_header_1, proposer_slashing.signed_header_2):
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(signed_header.message.slot))
assert bls_verify(proposer.pubkey, hash_tree_root(signed_header.message), signed_header.signature, domain)
slash_validator(state, proposer_slashing.proposer_index)
```
@@ -1442,6 +1510,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
data = attestation.data
assert data.index < get_committee_count_at_slot(state, data.slot)
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
assert data.target.epoch == compute_epoch_at_slot(data.slot)
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
committee = get_beacon_committee(state, data.slot, data.index)
@@ -1489,7 +1558,11 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
# Note: The deposit contract does not check signatures.
# Note: Deposits are valid across forks, thus the deposit domain is retrieved directly from `compute_domain`.
domain = compute_domain(DOMAIN_DEPOSIT)
if not bls_verify(pubkey, signing_root(deposit.data), deposit.data.signature, domain):
deposit_message = DepositMessage(
pubkey=deposit.data.pubkey,
withdrawal_credentials=deposit.data.withdrawal_credentials,
amount=deposit.data.amount)
if not bls_verify(pubkey, hash_tree_root(deposit_message), deposit.data.signature, domain):
return
# Add validator and balance entries
@@ -1512,19 +1585,20 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
##### Voluntary exits
```python
def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None:
validator = state.validators[exit.validator_index]
def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
voluntary_exit = signed_voluntary_exit.message
validator = state.validators[voluntary_exit.validator_index]
# Verify the validator is active
assert is_active_validator(validator, get_current_epoch(state))
# Verify the validator has not yet exited
assert validator.exit_epoch == FAR_FUTURE_EPOCH
# Exits must specify an epoch when they become valid; they are not valid before then
assert get_current_epoch(state) >= exit.epoch
assert get_current_epoch(state) >= voluntary_exit.epoch
# Verify the validator has been active long enough
assert get_current_epoch(state) >= validator.activation_epoch + PERSISTENT_COMMITTEE_PERIOD
# Verify signature
domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, exit.epoch)
assert bls_verify(validator.pubkey, signing_root(exit), exit.signature, domain)
domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
assert bls_verify(validator.pubkey, hash_tree_root(voluntary_exit), signed_voluntary_exit.signature, domain)
# Initiate exit
initiate_validator_exit(state, exit.validator_index)
initiate_validator_exit(state, voluntary_exit.validator_index)
```

View File

@@ -4,19 +4,21 @@
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Ethereum 2.0 Phase 0 -- Deposit Contract](#ethereum-20-phase-0----deposit-contract)
- [Table of contents](#table-of-contents)
- [Introduction](#introduction)
- [Constants](#constants)
- [Contract](#contract)
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
- [`deposit` function](#deposit-function)
- [Deposit amount](#deposit-amount)
- [Withdrawal credentials](#withdrawal-credentials)
- [`DepositEvent` log](#depositevent-log)
- [Vyper code](#vyper-code)
- [Introduction](#introduction)
- [Constants](#constants)
- [Contract](#contract)
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
- [`deposit` function](#deposit-function)
- [Deposit amount](#deposit-amount)
- [Withdrawal credentials](#withdrawal-credentials)
- [`DepositEvent` log](#depositevent-log)
- [Vyper code](#vyper-code)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction

View File

@@ -4,23 +4,32 @@
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Ethereum 2.0 Phase 0 -- Beacon Chain Fork Choice](#ethereum-20-phase-0----beacon-chain-fork-choice)
- [Table of contents](#table-of-contents)
- [Introduction](#introduction)
- [Fork choice](#fork-choice)
- [Helpers](#helpers)
- [`LatestMessage`](#latestmessage)
- [`Store`](#store)
- [`get_genesis_store`](#get_genesis_store)
- [`get_ancestor`](#get_ancestor)
- [`get_latest_attesting_balance`](#get_latest_attesting_balance)
- [`get_head`](#get_head)
- [Handlers](#handlers)
- [`on_tick`](#on_tick)
- [`on_block`](#on_block)
- [`on_attestation`](#on_attestation)
- [Introduction](#introduction)
- [Fork choice](#fork-choice)
- [Configuration](#configuration)
- [Helpers](#helpers)
- [`LatestMessage`](#latestmessage)
- [`Store`](#store)
- [`get_genesis_store`](#get_genesis_store)
- [`get_slots_since_genesis`](#get_slots_since_genesis)
- [`get_current_slot`](#get_current_slot)
- [`compute_slots_since_epoch_start`](#compute_slots_since_epoch_start)
- [`get_ancestor`](#get_ancestor)
- [`get_latest_attesting_balance`](#get_latest_attesting_balance)
- [`filter_block_tree`](#filter_block_tree)
- [`get_filtered_block_tree`](#get_filtered_block_tree)
- [`get_head`](#get_head)
- [`should_update_justified_checkpoint`](#should_update_justified_checkpoint)
- [Handlers](#handlers)
- [`on_tick`](#on_tick)
- [`on_block`](#on_block)
- [`on_attestation`](#on_attestation)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
@@ -32,7 +41,7 @@ This document is the beacon chain fork choice spec, part of Ethereum 2.0 Phase 0
The head block root associated with a `store` is defined as `get_head(store)`. At genesis, let `store = get_genesis_store(genesis_state)` and update `store` by running:
- `on_tick(time)` whenever `time > store.time` where `time` is the current Unix time
- `on_block(block)` whenever a block `block` is received
- `on_block(block)` whenever a block `block: SignedBeaconBlock` is received
- `on_attestation(attestation)` whenever an attestation `attestation` is received
*Notes*:
@@ -81,7 +90,7 @@ class Store(object):
```python
def get_genesis_store(genesis_state: BeaconState) -> Store:
genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))
root = signing_root(genesis_block)
root = hash_tree_root(genesis_block)
justified_checkpoint = Checkpoint(epoch=GENESIS_EPOCH, root=root)
finalized_checkpoint = Checkpoint(epoch=GENESIS_EPOCH, root=root)
return Store(
@@ -96,11 +105,18 @@ def get_genesis_store(genesis_state: BeaconState) -> Store:
)
```
#### `get_slots_since_genesis`
```python
def get_slots_since_genesis(store: Store) -> int:
return (store.time - store.genesis_time) // SECONDS_PER_SLOT
```
#### `get_current_slot`
```python
def get_current_slot(store: Store) -> Slot:
return Slot((store.time - store.genesis_time) // SECONDS_PER_SLOT)
return Slot(GENESIS_SLOT + get_slots_since_genesis(store))
```
#### `compute_slots_since_epoch_start`
@@ -136,17 +152,72 @@ def get_latest_attesting_balance(store: Store, root: Root) -> Gwei:
))
```
#### `filter_block_tree`
```python
def filter_block_tree(store: Store, block_root: Root, blocks: Dict[Root, BeaconBlock]) -> bool:
block = store.blocks[block_root]
children = [
root for root in store.blocks.keys()
if store.blocks[root].parent_root == block_root
]
# If any children branches contain expected finalized/justified checkpoints,
# add to filtered block-tree and signal viability to parent.
if any(children):
filter_block_tree_result = [filter_block_tree(store, child, blocks) for child in children]
if any(filter_block_tree_result):
blocks[block_root] = block
return True
return False
# If leaf block, check finalized/justified checkpoints as matching latest.
head_state = store.block_states[block_root]
correct_justified = (
store.justified_checkpoint.epoch == GENESIS_EPOCH
or head_state.current_justified_checkpoint == store.justified_checkpoint
)
correct_finalized = (
store.finalized_checkpoint.epoch == GENESIS_EPOCH
or head_state.finalized_checkpoint == store.finalized_checkpoint
)
# If expected finalized/justified, add to viable block-tree and signal viability to parent.
if correct_justified and correct_finalized:
blocks[block_root] = block
return True
# Otherwise, branch not viable
return False
```
#### `get_filtered_block_tree`
```python
def get_filtered_block_tree(store: Store) -> Dict[Root, BeaconBlock]:
"""
Retrieve a filtered block true from ``store``, only returning branches
whose leaf state's justified/finalized info agrees with that in ``store``.
"""
base = store.justified_checkpoint.root
blocks: Dict[Root, BeaconBlock] = {}
filter_block_tree(store, base, blocks)
return blocks
```
#### `get_head`
```python
def get_head(store: Store) -> Root:
# Get filtered block tree that only includes viable branches
blocks = get_filtered_block_tree(store)
# Execute the LMD-GHOST fork choice
head = store.justified_checkpoint.root
justified_slot = compute_start_slot_at_epoch(store.justified_checkpoint.epoch)
while True:
children = [
root for root in store.blocks.keys()
if store.blocks[root].parent_root == head and store.blocks[root].slot > justified_slot
root for root in blocks.keys()
if blocks[root].parent_root == head and blocks[root].slot > justified_slot
]
if len(children) == 0:
return head
@@ -172,8 +243,8 @@ def should_update_justified_checkpoint(store: Store, new_justified_checkpoint: C
if new_justified_block.slot <= compute_start_slot_at_epoch(store.justified_checkpoint.epoch):
return False
if not (
get_ancestor(store, new_justified_checkpoint.root, store.blocks[store.justified_checkpoint.root].slot) ==
store.justified_checkpoint.root
get_ancestor(store, new_justified_checkpoint.root, store.blocks[store.justified_checkpoint.root].slot)
== store.justified_checkpoint.root
):
return False
@@ -203,29 +274,31 @@ def on_tick(store: Store, time: uint64) -> None:
#### `on_block`
```python
def on_block(store: Store, block: BeaconBlock) -> None:
def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
block = signed_block.message
# Make a copy of the state to avoid mutability issues
assert block.parent_root in store.block_states
pre_state = store.block_states[block.parent_root].copy()
# Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past.
assert store.time >= pre_state.genesis_time + block.slot * SECONDS_PER_SLOT
assert get_current_slot(store) >= block.slot
# Add new block to the store
store.blocks[signing_root(block)] = block
store.blocks[hash_tree_root(block)] = block
# Check block is a descendant of the finalized block
assert (
get_ancestor(store, signing_root(block), store.blocks[store.finalized_checkpoint.root].slot) ==
get_ancestor(store, hash_tree_root(block), store.blocks[store.finalized_checkpoint.root].slot) ==
store.finalized_checkpoint.root
)
# Check that block is later than the finalized epoch slot
assert block.slot > compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
# Check the block is valid and compute the post-state
state = state_transition(pre_state, block, True)
state = state_transition(pre_state, signed_block, True)
# Add new state for this block to the store
store.block_states[signing_root(block)] = state
store.block_states[hash_tree_root(block)] = state
# Update justified checkpoint
if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
store.best_justified_checkpoint = state.current_justified_checkpoint
if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch:
store.best_justified_checkpoint = state.current_justified_checkpoint
if should_update_justified_checkpoint(store, state.current_justified_checkpoint):
store.justified_checkpoint = state.current_justified_checkpoint
@@ -251,14 +324,13 @@ def on_attestation(store: Store, attestation: Attestation) -> None:
# Use GENESIS_EPOCH for previous when genesis to avoid underflow
previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else GENESIS_EPOCH
assert target.epoch in [current_epoch, previous_epoch]
# Cannot calculate the current shuffling if have not seen the target
assert target.root in store.blocks
assert target.epoch == compute_epoch_at_slot(attestation.data.slot)
# Attestations target be for a known block. If target block is unknown, delay consideration until the block is found
assert target.root in store.blocks
# Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrives
base_state = store.block_states[target.root].copy()
assert store.time >= base_state.genesis_time + compute_start_slot_at_epoch(target.epoch) * SECONDS_PER_SLOT
assert get_current_slot(store) >= compute_start_slot_at_epoch(target.epoch)
# Attestations must be for a known block. If block is unknown, delay consideration until the block is found
assert attestation.data.beacon_block_root in store.blocks
@@ -273,7 +345,7 @@ def on_attestation(store: Store, attestation: Attestation) -> None:
# Attestations can only affect the fork choice of subsequent slots.
# Delay consideration in the fork choice until their slot is in the past.
assert store.time >= (attestation.data.slot + 1) * SECONDS_PER_SLOT
assert get_current_slot(store) >= attestation.data.slot + 1
# Get state at the `target` to validate attestation and calculate the committees
indexed_attestation = get_indexed_attestation(target_state, attestation)

View File

@@ -3,29 +3,31 @@
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Phase 1 miscellaneous beacon chain changes](#phase-1-miscellaneous-beacon-chain-changes)
- [Table of contents](#table-of-contents)
- [Configuration](#configuration)
- [Containers](#containers)
- [`CompactCommittee`](#compactcommittee)
- [`ShardReceiptDelta`](#shardreceiptdelta)
- [`ShardReceiptProof`](#shardreceiptproof)
- [Helper functions](#helper-functions)
- [`pack_compact_validator`](#pack_compact_validator)
- [`unpack_compact_validator`](#unpack_compact_validator)
- [`committee_to_compact_committee`](#committee_to_compact_committee)
- [`verify_merkle_proof`](#verify_merkle_proof)
- [`compute_historical_state_generalized_index`](#compute_historical_state_generalized_index)
- [`get_generalized_index_of_crosslink_header`](#get_generalized_index_of_crosslink_header)
- [`process_shard_receipt_proof`](#process_shard_receipt_proof)
- [Changes](#changes)
- [Phase 0 container updates](#phase-0-container-updates)
- [`BeaconState`](#beaconstate)
- [`BeaconBlockBody`](#beaconblockbody)
- [Persistent committees](#persistent-committees)
- [Shard receipt processing](#shard-receipt-processing)
- [Configuration](#configuration)
- [Containers](#containers)
- [`CompactCommittee`](#compactcommittee)
- [`ShardReceiptDelta`](#shardreceiptdelta)
- [`ShardReceiptProof`](#shardreceiptproof)
- [Helper functions](#helper-functions)
- [`pack_compact_validator`](#pack_compact_validator)
- [`unpack_compact_validator`](#unpack_compact_validator)
- [`committee_to_compact_committee`](#committee_to_compact_committee)
- [`verify_merkle_proof`](#verify_merkle_proof)
- [`compute_historical_state_generalized_index`](#compute_historical_state_generalized_index)
- [`get_generalized_index_of_crosslink_header`](#get_generalized_index_of_crosslink_header)
- [`process_shard_receipt_proof`](#process_shard_receipt_proof)
- [Changes](#changes)
- [Phase 0 container updates](#phase-0-container-updates)
- [`BeaconState`](#beaconstate)
- [`BeaconBlockBody`](#beaconblockbody)
- [Persistent committees](#persistent-committees)
- [Shard receipt processing](#shard-receipt-processing)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Configuration

View File

@@ -5,54 +5,56 @@
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Ethereum 2.0 Phase 1 -- Custody Game](#ethereum-20-phase-1----custody-game)
- [Table of contents](#table-of-contents)
- [Introduction](#introduction)
- [Terminology](#terminology)
- [Constants](#constants)
- [Misc](#misc)
- [Custody game parameters](#custody-game-parameters)
- [Time parameters](#time-parameters)
- [Max operations per block](#max-operations-per-block)
- [Reward and penalty quotients](#reward-and-penalty-quotients)
- [Signature domain types](#signature-domain-types)
- [TODO PLACEHOLDER](#todo-placeholder)
- [Data structures](#data-structures)
- [Custody objects](#custody-objects)
- [`CustodyChunkChallenge`](#custodychunkchallenge)
- [`CustodyBitChallenge`](#custodybitchallenge)
- [`CustodyChunkChallengeRecord`](#custodychunkchallengerecord)
- [`CustodyBitChallengeRecord`](#custodybitchallengerecord)
- [`CustodyResponse`](#custodyresponse)
- [New beacon operations](#new-beacon-operations)
- [`CustodyKeyReveal`](#custodykeyreveal)
- [`EarlyDerivedSecretReveal`](#earlyderivedsecretreveal)
- [Phase 0 container updates](#phase-0-container-updates)
- [`Validator`](#validator)
- [`BeaconState`](#beaconstate)
- [`BeaconBlockBody`](#beaconblockbody)
- [Helpers](#helpers)
- [`ceillog2`](#ceillog2)
- [`is_valid_merkle_branch_with_mixin`](#is_valid_merkle_branch_with_mixin)
- [`get_crosslink_chunk_count`](#get_crosslink_chunk_count)
- [`legendre_bit`](#legendre_bit)
- [`custody_subchunkify`](#custody_subchunkify)
- [`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_custody_period_for_validator`](#get_custody_period_for_validator)
- [`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)
- [Introduction](#introduction)
- [Terminology](#terminology)
- [Constants](#constants)
- [Misc](#misc)
- [Custody game parameters](#custody-game-parameters)
- [Time parameters](#time-parameters)
- [Max operations per block](#max-operations-per-block)
- [Reward and penalty quotients](#reward-and-penalty-quotients)
- [Signature domain types](#signature-domain-types)
- [TODO PLACEHOLDER](#todo-placeholder)
- [Data structures](#data-structures)
- [Custody objects](#custody-objects)
- [`CustodyChunkChallenge`](#custodychunkchallenge)
- [`CustodyBitChallenge`](#custodybitchallenge)
- [`CustodyChunkChallengeRecord`](#custodychunkchallengerecord)
- [`CustodyBitChallengeRecord`](#custodybitchallengerecord)
- [`CustodyResponse`](#custodyresponse)
- [New beacon operations](#new-beacon-operations)
- [`CustodyKeyReveal`](#custodykeyreveal)
- [`EarlyDerivedSecretReveal`](#earlyderivedsecretreveal)
- [Phase 0 container updates](#phase-0-container-updates)
- [`Validator`](#validator)
- [`BeaconState`](#beaconstate)
- [`BeaconBlockBody`](#beaconblockbody)
- [Helpers](#helpers)
- [`ceillog2`](#ceillog2)
- [`is_valid_merkle_branch_with_mixin`](#is_valid_merkle_branch_with_mixin)
- [`get_crosslink_chunk_count`](#get_crosslink_chunk_count)
- [`legendre_bit`](#legendre_bit)
- [`custody_subchunkify`](#custody_subchunkify)
- [`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_custody_period_for_validator`](#get_custody_period_for_validator)
- [`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)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
@@ -595,7 +597,8 @@ def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) ->
# Verify challenge signature
challenger = state.validators[challenge.challenger_index]
domain = get_domain(state, DOMAIN_CUSTODY_BIT_CHALLENGE, get_current_epoch(state))
assert bls_verify(challenger.pubkey, signing_root(challenge), challenge.signature, domain)
# TODO incorrect hash-tree-root, but this changes with phase 1 PR #1483
assert bls_verify(challenger.pubkey, hash_tree_root(challenge), challenge.signature, domain)
# Verify challenger is slashable
assert is_slashable_validator(challenger, get_current_epoch(state))
# Verify attestation

View File

@@ -5,45 +5,47 @@
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Ethereum 2.0 Phase 1 -- Shard Data Chains](#ethereum-20-phase-1----shard-data-chains)
- [Table of contents](#table-of-contents)
- [Introduction](#introduction)
- [Custom types](#custom-types)
- [Configuration](#configuration)
- [Misc](#misc)
- [Initial values](#initial-values)
- [Time parameters](#time-parameters)
- [State list lengths](#state-list-lengths)
- [Rewards and penalties](#rewards-and-penalties)
- [Signature domain types](#signature-domain-types)
- [Containers](#containers)
- [`Crosslink`](#crosslink)
- [`ShardBlock`](#shardblock)
- [`ShardBlockHeader`](#shardblockheader)
- [`ShardState`](#shardstate)
- [`ShardAttestationData`](#shardattestationdata)
- [Helper functions](#helper-functions)
- [Misc](#misc-1)
- [`compute_epoch_of_shard_slot`](#compute_epoch_of_shard_slot)
- [`compute_shard_period_start_epoch`](#compute_shard_period_start_epoch)
- [Beacon state accessors](#beacon-state-accessors)
- [`get_period_committee`](#get_period_committee)
- [`get_shard_committee`](#get_shard_committee)
- [`get_shard_proposer_index`](#get_shard_proposer_index)
- [Shard state mutators](#shard-state-mutators)
- [`process_delta`](#process_delta)
- [Genesis](#genesis)
- [`get_genesis_shard_state`](#get_genesis_shard_state)
- [`get_genesis_shard_block`](#get_genesis_shard_block)
- [Shard state transition function](#shard-state-transition-function)
- [Period processing](#period-processing)
- [Block processing](#block-processing)
- [Block header](#block-header)
- [Attestations](#attestations)
- [Block body](#block-body)
- [Shard fork choice rule](#shard-fork-choice-rule)
- [Introduction](#introduction)
- [Custom types](#custom-types)
- [Configuration](#configuration)
- [Misc](#misc)
- [Initial values](#initial-values)
- [Time parameters](#time-parameters)
- [State list lengths](#state-list-lengths)
- [Rewards and penalties](#rewards-and-penalties)
- [Signature domain types](#signature-domain-types)
- [Containers](#containers)
- [`Crosslink`](#crosslink)
- [`ShardBlock`](#shardblock)
- [`ShardBlockHeader`](#shardblockheader)
- [`ShardState`](#shardstate)
- [`ShardAttestationData`](#shardattestationdata)
- [Helper functions](#helper-functions)
- [Misc](#misc-1)
- [`compute_epoch_of_shard_slot`](#compute_epoch_of_shard_slot)
- [`compute_shard_period_start_epoch`](#compute_shard_period_start_epoch)
- [Beacon state accessors](#beacon-state-accessors)
- [`get_period_committee`](#get_period_committee)
- [`get_shard_committee`](#get_shard_committee)
- [`get_shard_proposer_index`](#get_shard_proposer_index)
- [Shard state mutators](#shard-state-mutators)
- [`process_delta`](#process_delta)
- [Genesis](#genesis)
- [`get_genesis_shard_state`](#get_genesis_shard_state)
- [`get_genesis_shard_block`](#get_genesis_shard_block)
- [Shard state transition function](#shard-state-transition-function)
- [Period processing](#period-processing)
- [Block processing](#block-processing)
- [Block header](#block-header)
- [Attestations](#attestations)
- [Block body](#block-body)
- [Shard fork choice rule](#shard-fork-choice-rule)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
@@ -359,9 +361,9 @@ def process_shard_block_header(beacon_state: BeaconState, shard_state: ShardStat
)
if beacon_block_header.state_root == Bytes32():
beacon_block_header.state_root = hash_tree_root(beacon_state)
assert block.beacon_block_root == signing_root(beacon_block_header)
assert block.beacon_block_root == hash_tree_root(beacon_block_header)
# Verify the parent root
assert block.parent_root == signing_root(shard_state.latest_block_header)
assert block.parent_root == hash_tree_root(shard_state.latest_block_header)
# Save current block as the new latest block
shard_state.latest_block_header = ShardBlockHeader(
shard=block.shard,
@@ -384,7 +386,7 @@ def process_shard_block_header(beacon_state: BeaconState, shard_state: ShardStat
assert not proposer.slashed
# Verify proposer signature
domain = get_domain(beacon_state, DOMAIN_SHARD_PROPOSER, compute_epoch_of_shard_slot(block.slot))
assert bls_verify(proposer.pubkey, signing_root(block), block.signature, domain)
assert bls_verify(proposer.pubkey, hash_tree_root(block), block.signature, domain)
```
#### Attestations

View File

@@ -4,21 +4,23 @@
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Merkle proof formats](#merkle-proof-formats)
- [Table of contents](#table-of-contents)
- [Helper functions](#helper-functions)
- [Generalized Merkle tree index](#generalized-merkle-tree-index)
- [SSZ object to index](#ssz-object-to-index)
- [Helpers for generalized indices](#helpers-for-generalized-indices)
- [`concat_generalized_indices`](#concat_generalized_indices)
- [`get_generalized_index_length`](#get_generalized_index_length)
- [`get_generalized_index_bit`](#get_generalized_index_bit)
- [`generalized_index_sibling`](#generalized_index_sibling)
- [`generalized_index_child`](#generalized_index_child)
- [`generalized_index_parent`](#generalized_index_parent)
- [Merkle multiproofs](#merkle-multiproofs)
- [Helper functions](#helper-functions)
- [Generalized Merkle tree index](#generalized-merkle-tree-index)
- [SSZ object to index](#ssz-object-to-index)
- [Helpers for generalized indices](#helpers-for-generalized-indices)
- [`concat_generalized_indices`](#concat_generalized_indices)
- [`get_generalized_index_length`](#get_generalized_index_length)
- [`get_generalized_index_bit`](#get_generalized_index_bit)
- [`generalized_index_sibling`](#generalized_index_sibling)
- [`generalized_index_child`](#generalized_index_child)
- [`generalized_index_parent`](#generalized_index_parent)
- [Merkle multiproofs](#merkle-multiproofs)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Helper functions

View File

@@ -5,20 +5,22 @@
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Minimal Light Client Design](#minimal-light-client-design)
- [Table of contents](#table-of-contents)
- [Introduction](#introduction)
- [Custom types](#custom-types)
- [Constants](#constants)
- [Containers](#containers)
- [`LightClientUpdate`](#lightclientupdate)
- [Helpers](#helpers)
- [`LightClientMemory`](#lightclientmemory)
- [`get_persistent_committee_pubkeys_and_balances`](#get_persistent_committee_pubkeys_and_balances)
- [Light client state updates](#light-client-state-updates)
- [Data overhead](#data-overhead)
- [Introduction](#introduction)
- [Custom types](#custom-types)
- [Constants](#constants)
- [Containers](#containers)
- [`LightClientUpdate`](#lightclientupdate)
- [Helpers](#helpers)
- [`LightClientMemory`](#lightclientmemory)
- [`get_persistent_committee_pubkeys_and_balances`](#get_persistent_committee_pubkeys_and_balances)
- [Light client state updates](#light-client-state-updates)
- [Data overhead](#data-overhead)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction

View File

@@ -18,23 +18,88 @@ It consists of four main sections:
- [Network fundamentals](#network-fundamentals)
- [Transport](#transport)
- [Interop](#interop)
- [Mainnet](#mainnet)
- [Encryption and identification](#encryption-and-identification)
- [Protocol negotiation](#protocol-negotiation)
- [Interop](#interop-1)
- [Mainnet](#mainnet-1)
- [Protocol Negotiation](#protocol-negotiation)
- [Interop](#interop-2)
- [Mainnet](#mainnet-2)
- [Multiplexing](#multiplexing)
- [Eth2 network interaction domains](#eth2-network-interaction-domains)
- [Configuration](#configuration)
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
- [Topics and messages](#topics-and-messages)
- [Global topics](#global-topics)
- [Attestation subnets](#attestation-subnets)
- [Interop](#interop-3)
- [Mainnet](#mainnet-3)
- [Encodings](#encodings)
- [Interop](#interop-4)
- [Mainnet](#mainnet-4)
- [The Req/Resp domain](#the-reqresp-domain)
- [Protocol identification](#protocol-identification)
- [Req/Resp interaction](#reqresp-interaction)
- [Requesting side](#requesting-side)
- [Responding side](#responding-side)
- [Encoding strategies](#encoding-strategies)
- [SSZ-encoding strategy (with or without Snappy)](#ssz-encoding-strategy-with-or-without-snappy)
- [Messages](#messages)
- [Status](#status)
- [Goodbye](#goodbye)
- [BeaconBlocksByRange](#beaconblocksbyrange)
- [BeaconBlocksByRoot](#beaconblocksbyroot)
- [The discovery domain: discv5](#the-discovery-domain-discv5)
- [Integration into libp2p stacks](#integration-into-libp2p-stacks)
- [ENR structure](#enr-structure)
- [Interop](#interop-5)
- [Mainnet](#mainnet-5)
- [Topic advertisement](#topic-advertisement)
- [Interop](#interop-6)
- [Mainnet](#mainnet-6)
- [Design decision rationale](#design-decision-rationale)
- [Transport](#transport-1)
- [Why are we defining specific transports?](#why-are-we-defining-specific-transports)
- [Can clients support other transports/handshakes than the ones mandated by the spec?](#can-clients-support-other-transportshandshakes-than-the-ones-mandated-by-the-spec)
- [What are the advantages of using TCP/QUIC/Websockets?](#what-are-the-advantages-of-using-tcpquicwebsockets)
- [Why do we not just support a single transport?](#why-do-we-not-just-support-a-single-transport)
- [Why are we not using QUIC for mainnet from the start?](#why-are-we-not-using-quic-for-mainnet-from-the-start)
- [Multiplexing](#multiplexing-1)
- [Protocol negotiation](#protocol-negotiation-1)
- [Why are we using mplex/yamux?](#why-are-we-using-mplexyamux)
- [Protocol Negotiation](#protocol-negotiation-1)
- [When is multiselect 2.0 due and why are we using it for mainnet?](#when-is-multiselect-20-due-and-why-are-we-using-it-for-mainnet)
- [What is the difference between connection-level and stream-level protocol negotiation?](#what-is-the-difference-between-connection-level-and-stream-level-protocol-negotiation)
- [Encryption](#encryption)
- [Why are we using SecIO for interop? Why not for mainnet?](#why-are-we-using-secio-for-interop-why-not-for-mainnet)
- [Why are we using Noise/TLS 1.3 for mainnet?](#why-are-we-using-noisetls-13-for-mainnet)
- [Why are we using encryption at all?](#why-are-we-using-encryption-at-all)
- [Will mainnnet networking be untested when it launches?](#will-mainnnet-networking-be-untested-when-it-launches)
- [Gossipsub](#gossipsub)
- [Why are we using a pub/sub algorithm for block and attestation propagation?](#why-are-we-using-a-pubsub-algorithm-for-block-and-attestation-propagation)
- [Why are we using topics to segregate encodings, yet only support one encoding?](#why-are-we-using-topics-to-segregate-encodings-yet-only-support-one-encoding)
- [How do we upgrade gossip channels (e.g. changes in encoding, compression)?](#how-do-we-upgrade-gossip-channels-eg-changes-in-encoding-compression)
- [Why must all clients use the same gossip topic instead of one negotiated between each peer pair?](#why-must-all-clients-use-the-same-gossip-topic-instead-of-one-negotiated-between-each-peer-pair)
- [Why are the topics strings and not hashes?](#why-are-the-topics-strings-and-not-hashes)
- [Why are there `ATTESTATION_SUBNET_COUNT` attestation subnets?](#why-are-there-attestation_subnet_count-attestation-subnets)
- [Why are attestations limited to be broadcast on gossip channels within `SLOTS_PER_EPOCH` slots?](#why-are-attestations-limited-to-be-broadcast-on-gossip-channels-within-slots_per_epoch-slots)
- [Why are aggregate attestations broadcast to the global topic as `AggregateAndProof`s rather than just as `Attestation`s?](#why-are-aggregate-attestations-broadcast-to-the-global-topic-as-aggregateandproofs-rather-than-just-as-attestations)
- [Why are we sending entire objects in the pubsub and not just hashes?](#why-are-we-sending-entire-objects-in-the-pubsub-and-not-just-hashes)
- [Should clients gossip blocks if they *cannot* validate the proposer signature due to not yet being synced, not knowing the head block, etc?](#should-clients-gossip-blocks-if-they-cannot-validate-the-proposer-signature-due-to-not-yet-being-synced-not-knowing-the-head-block-etc)
- [How are we going to discover peers in a gossipsub topic?](#how-are-we-going-to-discover-peers-in-a-gossipsub-topic)
- [Req/Resp](#reqresp)
- [Why segregate requests into dedicated protocol IDs?](#why-segregate-requests-into-dedicated-protocol-ids)
- [Why are messages length-prefixed with a protobuf varint in the SSZ-encoding?](#why-are-messages-length-prefixed-with-a-protobuf-varint-in-the-ssz-encoding)
- [Why do we version protocol strings with ordinals instead of semver?](#why-do-we-version-protocol-strings-with-ordinals-instead-of-semver)
- [Why is it called Req/Resp and not RPC?](#why-is-it-called-reqresp-and-not-rpc)
- [Discovery](#discovery)
- [Why are we using discv5 and not libp2p Kademlia DHT?](#why-are-we-using-discv5-and-not-libp2p-kademlia-dht)
- [What is the difference between an ENR and a multiaddr, and why are we using ENRs?](#what-is-the-difference-between-an-enr-and-a-multiaddr-and-why-are-we-using-enrs)
- [Compression/Encoding](#compressionencoding)
- [Why are we using SSZ for encoding?](#why-are-we-using-ssz-for-encoding)
- [Why are we compressing, and at which layers?](#why-are-we-compressing-and-at-which-layers)
- [Why are using Snappy for compression?](#why-are-using-snappy-for-compression)
- [Can I get access to unencrypted bytes on the wire for debugging purposes?](#can-i-get-access-to-unencrypted-bytes-on-the-wire-for-debugging-purposes)
- [libp2p implementations matrix](#libp2p-implementations-matrix)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -151,7 +216,7 @@ The payload is carried in the `data` field of a gossipsub message, and varies de
| Topic | Message Type |
|----------------------------------------|-------------------|
| beacon_block | BeaconBlock |
| beacon_block | SignedBeaconBlock |
| beacon_aggregate_and_proof | AggregateAndProof |
| beacon_attestation\* | Attestation |
| committee_index{subnet_id}\_beacon_attestation | Attestation |
@@ -175,7 +240,7 @@ There are two primary global topics used to propagate beacon blocks and aggregat
- The block being voted for (`aggregate_and_proof.aggregate.data.beacon_block_root`) passes validation.
- `aggregate_and_proof.aggregate.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (`aggregate_and_proof.aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= aggregate_and_proof.aggregate.data.slot`).
- The validator index is within the aggregate's committee -- i.e. `aggregate_and_proof.aggregator_index in get_attesting_indices(state, aggregate_and_proof.aggregate.data, aggregate_and_proof.aggregate.aggregation_bits)`.
- `aggregate_and_proof.selection_proof` selects the validator as an aggregator for the slot -- i.e. `is_aggregator(state, aggregate_and_proof.aggregate.data.index, aggregate_and_proof.selection_proof)` returns `True`.
- `aggregate_and_proof.selection_proof` selects the validator as an aggregator for the slot -- i.e. `is_aggregator(state, aggregate_and_proof.aggregate.data.slot, aggregate_and_proof.aggregate.data.index, aggregate_and_proof.selection_proof)` returns `True`.
- The `aggregate_and_proof.selection_proof` is a valid signature of the `aggregate_and_proof.aggregate.data.slot` by the validator with index `aggregate_and_proof.aggregator_index`.
- The signature of `aggregate_and_proof.aggregate` is valid.
@@ -214,7 +279,7 @@ Topics are post-fixed with an encoding. Encodings define how the payload of a go
#### Interop
- `ssz` - All objects are [SSZ-encoded](#ssz-encoding). Example: The beacon block topic string is `/eth2/beacon_block/ssz`, and the data field of a gossipsub message is an ssz-encoded `BeaconBlock`.
- `ssz` - All objects are [SSZ-encoded](#ssz-encoding). Example: The beacon block topic string is `/eth2/beacon_block/ssz`, and the data field of a gossipsub message is an ssz-encoded `SignedBeaconBlock`.
#### Mainnet
@@ -331,9 +396,9 @@ The [SimpleSerialize (SSZ) specification](../simple-serialize.md) outlines how o
All messages that contain only a single field MUST be encoded directly as the type of that field and MUST NOT be encoded as an SSZ container.
Responses that are SSZ-lists (for example `[]BeaconBlocks`) send their
Responses that are SSZ-lists (for example `[]SignedBeaconBlock`) send their
constituents individually as `response_chunk`s. For example, the
`[]BeaconBlocks` response type sends one or more `response_chunk`s. Each _successful_ `response_chunk` contains a single `BeaconBlock` payload.
`[]SignedBeaconBlock` response type sends one or more `response_chunk`s. Each _successful_ `response_chunk` contains a single `SignedBeaconBlock` payload.
### Messages
@@ -356,7 +421,7 @@ The fields are, as seen by the client at the time of sending the message:
- `head_fork_version`: The beacon_state `Fork` version.
- `finalized_root`: `state.finalized_checkpoint.root` for the state corresponding to the head block.
- `finalized_epoch`: `state.finalized_checkpoint.epoch` for the state corresponding to the head block.
- `head_root`: The signing root of the current head block.
- `head_root`: The hash_tree_root root of the current head block.
- `head_slot`: The slot of the block corresponding to the `head_root`.
The dialing client MUST send a `Status` request upon connection.
@@ -413,15 +478,15 @@ Request Content:
Response Content:
```
(
[]BeaconBlock
[]SignedBeaconBlock
)
```
Requests count beacon blocks from the peer starting from `start_slot` on the chain defined by `head_block_root` (= `signing_root(BeaconBlock)`). The response MUST contain no more than count blocks. `step` defines the slot increment between blocks. For example, requesting blocks starting at `start_slot` 2 with a step value of 2 would return the blocks at [2, 4, 6, …]. In cases where a slot is empty for a given slot number, no block is returned. For example, if slot 4 were empty in the previous example, the returned array would contain [2, 6, …]. A step value of 1 returns all blocks on the range `[start_slot, start_slot + count)`.
Requests count beacon blocks from the peer starting from `start_slot` on the chain defined by `head_block_root` (= `hash_tree_root(SignedBeaconBlock.message)`). The response MUST contain no more than count blocks. `step` defines the slot increment between blocks. For example, requesting blocks starting at `start_slot` 2 with a step value of 2 would return the blocks at [2, 4, 6, …]. In cases where a slot is empty for a given slot number, no block is returned. For example, if slot 4 were empty in the previous example, the returned array would contain [2, 6, …]. A step value of 1 returns all blocks on the range `[start_slot, start_slot + count)`.
The request MUST be encoded as an SSZ-container.
The response MUST consist of at least one `response_chunk` and MAY consist of many. Each _successful_ `response_chunk` MUST contain a single `BeaconBlock` payload.
The response MUST consist of at least one `response_chunk` and MAY consist of many. Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlock` payload.
`BeaconBlocksByRange` is primarily used to sync historical blocks.
@@ -449,17 +514,17 @@ Response Content:
```
(
[]BeaconBlock
[]SignedBeaconBlock
)
```
Requests blocks by block root (= `signing_root(BeaconBlock)`). The response is a list of `BeaconBlock` whose length is less than or equal to the number of requested blocks. It may be less in the case that the responding peer is missing blocks.
Requests blocks by block root (= `hash_tree_root(SignedBeaconBlock.message)`). The response is a list of `SignedBeaconBlock` whose length is less than or equal to the number of requested blocks. It may be less in the case that the responding peer is missing blocks.
`BeaconBlocksByRoot` is primarily used to recover recent blocks (e.g. when receiving a block or attestation whose parent is unknown).
The request MUST be encoded as an SSZ-field.
The response MUST consist of at least one `response_chunk` and MAY consist of many. Each _successful_ `response_chunk` MUST contain a single `BeaconBlock` payload.
The response MUST consist of at least one `response_chunk` and MAY consist of many. Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlock` payload.
Clients MUST support requesting blocks since the latest finalized epoch.
@@ -567,7 +632,7 @@ Conscious of that, the libp2p community conceptualized [mplex](https://github.co
Overlay multiplexers are not necessary with QUIC since the protocol provides native multiplexing, but they need to be layered atop TCP, WebSockets, and other transports that lack such support.
## Protocol negotiation
## Protocol Negotiation
### When is multiselect 2.0 due and why are we using it for mainnet?

View File

@@ -4,31 +4,32 @@
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [SimpleSerialize (SSZ)](#simpleserialize-ssz)
- [Table of contents](#table-of-contents)
- [Constants](#constants)
- [Typing](#typing)
- [Basic types](#basic-types)
- [Composite types](#composite-types)
- [Variable-size and fixed-size](#variable-size-and-fixed-size)
- [Aliases](#aliases)
- [Default values](#default-values)
- [`is_zero`](#is_zero)
- [Illegal types](#illegal-types)
- [Serialization](#serialization)
- [`uintN`](#uintn)
- [`boolean`](#boolean)
- [`null`](#null)
- [`Bitvector[N]`](#bitvectorn)
- [`Bitlist[N]`](#bitlistn)
- [Vectors, containers, lists, unions](#vectors-containers-lists-unions)
- [Deserialization](#deserialization)
- [Merkleization](#merkleization)
- [Self-signed containers](#self-signed-containers)
- [Summaries and expansions](#summaries-and-expansions)
- [Implementations](#implementations)
- [Constants](#constants)
- [Typing](#typing)
- [Basic types](#basic-types)
- [Composite types](#composite-types)
- [Variable-size and fixed-size](#variable-size-and-fixed-size)
- [Aliases](#aliases)
- [Default values](#default-values)
- [`is_zero`](#is_zero)
- [Illegal types](#illegal-types)
- [Serialization](#serialization)
- [`uintN`](#uintn)
- [`boolean`](#boolean)
- [`null`](#null)
- [`Bitvector[N]`](#bitvectorn)
- [`Bitlist[N]`](#bitlistn)
- [Vectors, containers, lists, unions](#vectors-containers-lists-unions)
- [Deserialization](#deserialization)
- [Merkleization](#merkleization)
- [Summaries and expansions](#summaries-and-expansions)
- [Implementations](#implementations)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Constants
@@ -106,7 +107,7 @@ An SSZ object is called zeroed (and thus, `is_zero(object)` returns true) if it
We recursively define the `serialize` function which consumes an object `value` (of the type specified) and returns a bytestring of type `bytes`.
*Note*: In the function definitions below (`serialize`, `hash_tree_root`, `signing_root`, `is_variable_size`, etc.) objects implicitly carry their type.
*Note*: In the function definitions below (`serialize`, `hash_tree_root`, `is_variable_size`, etc.) objects implicitly carry their type.
### `uintN`
@@ -234,10 +235,6 @@ We now define Merkleization `hash_tree_root(value)` of an object `value` recursi
* `mix_in_length(merkleize([hash_tree_root(element) for element in value], limit=chunk_count(type)), 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
Let `value` be a self-signed container object. The convention is that the signature (e.g. a `"bytes96"` BLS12-381 signature) be the last field of `value`. Further, the signed message for `value` is `signing_root(value) = hash_tree_root(truncate_last(value))` where `truncate_last` truncates the last element of `value`.
## Summaries and expansions
Let `A` be an object derived from another object `B` by replacing some of the (possibly nested) values of `B` by their `hash_tree_root`. We say `A` is a "summary" of `B`, and that `B` is an "expansion" of `A`. Notice `hash_tree_root(A) == hash_tree_root(B)`.

View File

@@ -37,12 +37,11 @@ Valid has 3 parts: `meta.yaml`, `serialized.ssz`, `value.yaml`
### `meta.yaml`
Valid ssz objects can have a hash-tree-root, and for some types also a signing-root.
Valid ssz objects can have a hash-tree-root.
The expected roots are encoded into the metadata yaml:
```yaml
root: Bytes32 -- Hash-tree-root of the object
signing_root: Bytes32 -- Signing-root of the object
```
The `Bytes32` is encoded as a string, hexadecimal encoding, prefixed with `0x`.
@@ -62,7 +61,6 @@ The conditions are the same for each type:
- Encoding: After encoding the given `value` object, the output should match `serialized`.
- Decoding: After decoding the given `serialized` bytes, it should match the `value` object.
- Hash-tree-root: the root should match the root declared in the metadata.
- Signing-root: if present in metadata, the signing root of the object should match the container.
## `invalid`

View File

@@ -24,8 +24,6 @@ The output parts are: `roots.yaml`, `serialized.ssz`, `value.yaml`
```yaml
root: bytes32 -- string, hash-tree-root of the value, hex encoded, with prefix 0x
signing_root: bytes32 -- string, signing-root of the value, hex encoded, with prefix 0x.
*Optional*, present if type is a container and ends with a ``signature`` field.
```
### `serialized.ssz`
@@ -47,11 +45,9 @@ A test-runner can implement the following assertions:
- Serialization in 2 steps: deserialize `serialized`, then serialize the result,
and verify if the bytes match the original `serialized`.
- Hash-tree-root: After parsing the `value` (or deserializing `serialized`), Hash-tree-root it: the output should match `root`
- Optionally also check `signing_root`, if present.
## References
**`serialized`**—[SSZ serialization](../../simple-serialize.md#serialization)
**`root`**—[hash_tree_root](../../simple-serialize.md#merkleization) function
**`signing_root`**—[signing_root](../../simple-serialize.md#self-signed-containers) function

View File

@@ -5,61 +5,64 @@
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Ethereum 2.0 Phase 0 -- Honest Validator](#ethereum-20-phase-0----honest-validator)
- [Table of contents](#table-of-contents)
- [Introduction](#introduction)
- [Prerequisites](#prerequisites)
- [Constants](#constants)
- [Misc](#misc)
- [Becoming a validator](#becoming-a-validator)
- [Initialization](#initialization)
- [BLS public key](#bls-public-key)
- [BLS withdrawal key](#bls-withdrawal-key)
- [Submit deposit](#submit-deposit)
- [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)
- [Slot](#slot)
- [Parent root](#parent-root)
- [State root](#state-root)
- [Randao reveal](#randao-reveal)
- [Eth1 Data](#eth1-data)
- [Signature](#signature)
- [Block body](#block-body)
- [Proposer slashings](#proposer-slashings)
- [Attester slashings](#attester-slashings)
- [Attestations](#attestations)
- [Deposits](#deposits)
- [Voluntary exits](#voluntary-exits)
- [Attesting](#attesting)
- [Attestation data](#attestation-data)
- [General](#general)
- [LMD GHOST vote](#lmd-ghost-vote)
- [FFG vote](#ffg-vote)
- [Construct attestation](#construct-attestation)
- [Data](#data)
- [Aggregation bits](#aggregation-bits)
- [Aggregate signature](#aggregate-signature)
- [Broadcast attestation](#broadcast-attestation)
- [Attestation aggregation](#attestation-aggregation)
- [Aggregation selection](#aggregation-selection)
- [Construct aggregate](#construct-aggregate)
- [Data](#data-1)
- [Aggregation bits](#aggregation-bits-1)
- [Aggregate signature](#aggregate-signature-1)
- [Broadcast aggregate](#broadcast-aggregate)
- [`AggregateAndProof`](#aggregateandproof)
- [Phase 0 attestation subnet stability](#phase-0-attestation-subnet-stability)
- [How to avoid slashing](#how-to-avoid-slashing)
- [Proposer slashing](#proposer-slashing)
- [Attester slashing](#attester-slashing)
- [Introduction](#introduction)
- [Prerequisites](#prerequisites)
- [Constants](#constants)
- [Misc](#misc)
- [Becoming a validator](#becoming-a-validator)
- [Initialization](#initialization)
- [BLS public key](#bls-public-key)
- [BLS withdrawal key](#bls-withdrawal-key)
- [Submit deposit](#submit-deposit)
- [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)
- [Preparing for a `BeaconBlock`](#preparing-for-a-beaconblock)
- [Slot](#slot)
- [Parent root](#parent-root)
- [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody)
- [Randao reveal](#randao-reveal)
- [Eth1 Data](#eth1-data)
- [Proposer slashings](#proposer-slashings)
- [Attester slashings](#attester-slashings)
- [Attestations](#attestations)
- [Deposits](#deposits)
- [Voluntary exits](#voluntary-exits)
- [Packaging into a `SignedBeaconBlock`](#packaging-into-a-signedbeaconblock)
- [State root](#state-root)
- [Signature](#signature)
- [Attesting](#attesting)
- [Attestation data](#attestation-data)
- [General](#general)
- [LMD GHOST vote](#lmd-ghost-vote)
- [FFG vote](#ffg-vote)
- [Construct attestation](#construct-attestation)
- [Data](#data)
- [Aggregation bits](#aggregation-bits)
- [Aggregate signature](#aggregate-signature)
- [Broadcast attestation](#broadcast-attestation)
- [Attestation aggregation](#attestation-aggregation)
- [Aggregation selection](#aggregation-selection)
- [Construct aggregate](#construct-aggregate)
- [Data](#data-1)
- [Aggregation bits](#aggregation-bits-1)
- [Aggregate signature](#aggregate-signature-1)
- [Broadcast aggregate](#broadcast-aggregate)
- [`AggregateAndProof`](#aggregateandproof)
- [Phase 0 attestation subnet stability](#phase-0-attestation-subnet-stability)
- [How to avoid slashing](#how-to-avoid-slashing)
- [Proposer slashing](#proposer-slashing)
- [Attester slashing](#attester-slashing)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
@@ -110,8 +113,11 @@ To submit a deposit:
- Pack the validator's [initialization parameters](#initialization) into `deposit_data`, a [`DepositData`](../core/0_beacon-chain.md#depositdata) SSZ object.
- Let `amount` be the amount in Gwei to be deposited by the validator where `amount >= MIN_DEPOSIT_AMOUNT`.
- Set `deposit_data.amount = amount`.
- Let `signature` be the result of `bls_sign` of the `signing_root(deposit_data)` with `domain=compute_domain(DOMAIN_DEPOSIT)`. (Deposits are valid regardless of fork version, `compute_domain` will default to zeroes there).
- Set `deposit_data.pubkey` to validator's `pubkey`.
- Set `deposit_data.withdrawal_credentials` to `withdrawal_credentials`.
- Set `deposit_data.amount` to `amount`.
- Let `deposit_message` be a `DepositMessage` with all the `DepositData` contents except the `signature`.
- Let `signature` be the result of `bls_sign` of the `hash_tree_root(deposit_message)` with `domain=compute_domain(DOMAIN_DEPOSIT)`. (Deposits are valid regardless of fork version, `compute_domain` will default to zeroes there).
- Let `deposit_data_root` be `hash_tree_root(deposit_data)`.
- Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `def deposit(pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96], deposit_data_root: bytes32)` along with a deposit of `amount` Gwei.
@@ -200,11 +206,13 @@ A validator has two primary responsibilities to the beacon chain: [proposing blo
### Block proposal
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).
A validator is expected to propose a [`SignedBeaconBlock`](../core/0_beacon-chain.md#signedbeaconblock) 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 312,500 validators = 10 million ETH, that's once per ~6 weeks).
#### Block header
#### Preparing for a `BeaconBlock`
To construct a `BeaconBlockBody`, a `block` (`BeaconBlock`) is defined with the necessary context for a block proposal:
##### Slot
@@ -214,17 +222,14 @@ Set `block.slot = slot` where `slot` is the current slot at which the validator
##### Parent root
Set `block.parent_root = signing_root(parent)`.
Set `block.parent_root = hash_tree_root(parent)`.
##### State root
Set `block.state_root = hash_tree_root(state)` of the resulting `state` of the `parent -> block` state transition.
*Note*: To calculate `state_root`, the validator should first run the state transition function on an unsigned `block` containing a stub for the `state_root`. It is useful to be able to run a state transition function that does _not_ validate signatures or state root for this purpose.
#### Constructing the `BeaconBlockBody`
##### Randao reveal
Set `block.randao_reveal = epoch_signature` where `epoch_signature` is obtained from:
Set `block.body.randao_reveal = epoch_signature` where `epoch_signature` is obtained from:
```python
def get_epoch_signature(state: BeaconState, block: BeaconBlock, privkey: int) -> BLSSignature:
@@ -234,11 +239,11 @@ def get_epoch_signature(state: BeaconState, block: BeaconBlock, privkey: int) ->
##### Eth1 Data
The `block.eth1_data` field is for block proposers to vote on recent Eth1 data. This recent data contains an Eth1 block hash as well as the associated deposit root (as calculated by the `get_deposit_root()` method of the deposit contract) and deposit count after execution of the corresponding Eth1 block. If over half of the block proposers in the current Eth1 voting period vote for the same `eth1_data` then `state.eth1_data` updates at the end of the voting period. Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`.
The `block.body.eth1_data` field is for block proposers to vote on recent Eth1 data. This recent data contains an Eth1 block hash as well as the associated deposit root (as calculated by the `get_deposit_root()` method of the deposit contract) and deposit count after execution of the corresponding Eth1 block. If over half of the block proposers in the current Eth1 voting period vote for the same `eth1_data` then `state.eth1_data` updates at the end of the voting period. Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`.
Let `get_eth1_data(distance: uint64) -> Eth1Data` be the (subjective) function that returns the Eth1 data at distance `distance` relative to the Eth1 head at the start of the current Eth1 voting period. Let `previous_eth1_distance` be the distance relative to the Eth1 block corresponding to `eth1_data.block_hash` found in the state at the _start_ of the current Eth1 voting period. Note that `eth1_data` can be updated in the middle of a voting period and thus the starting `eth1_data.block_hash` must be stored separately.
An honest block proposer sets `block.eth1_data = get_eth1_vote(state, previous_eth1_distance)` where:
An honest block proposer sets `block.body.eth1_data = get_eth1_vote(state, previous_eth1_distance)` where:
```python
def get_eth1_vote(state: BeaconState, previous_eth1_distance: uint64) -> Eth1Data:
@@ -260,18 +265,6 @@ def get_eth1_vote(state: BeaconState, previous_eth1_distance: uint64) -> Eth1Dat
)
```
##### Signature
Set `header.signature = block_signature` where `block_signature` is obtained from:
```python
def get_block_signature(state: BeaconState, header: BeaconBlockHeader, privkey: int) -> BLSSignature:
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(header.slot))
return bls_sign(privkey, signing_root(header), domain)
```
#### Block body
##### Proposer slashings
Up to `MAX_PROPOSER_SLASHINGS`, [`ProposerSlashing`](../core/0_beacon-chain.md#proposerslashing) objects can be included in the `block`. The proposer slashings must satisfy the verification conditions found in [proposer slashings processing](../core/0_beacon-chain.md#proposer-slashings). The validator receives a small "whistleblower" reward for each proposer slashing found and included.
@@ -294,6 +287,33 @@ The `proof` for each deposit must be constructed against the deposit root contai
Up to `MAX_VOLUNTARY_EXITS`, [`VoluntaryExit`](../core/0_beacon-chain.md#voluntaryexit) objects can be included in the `block`. The exits must satisfy the verification conditions found in [exits processing](../core/0_beacon-chain.md#voluntary-exits).
#### Packaging into a `SignedBeaconBlock`
##### State root
Set `block.state_root = hash_tree_root(state)` of the resulting `state` of the `parent -> block` state transition.
*Note*: To calculate `state_root`, the validator should first run the state transition function on an unsigned `block` containing a stub for the `state_root`.
It is useful to be able to run a state transition function (working on a copy of the state) that does _not_ validate signatures or state root for this purpose:
```python
def compute_new_state_root(state: BeaconState, block: BeaconBlock) -> Root:
process_slots(state, block.slot)
process_block(state, block)
return hash_tree_root(state)
```
##### Signature
`signed_block = SignedBeaconBlock(message=block, signature=block_signature)`, where `block_signature` is obtained from:
```python
def get_block_signature(state: BeaconState, header: BeaconBlockHeader, privkey: int) -> BLSSignature:
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(header.slot))
return bls_sign(privkey, hash_tree_root(header), domain)
```
### Attesting
A validator is expected to create, sign, and broadcast an attestation during each epoch. The `committee`, assigned `index`, and assigned `slot` for which the validator performs this role during an epoch are defined by `get_committee_assignment(state, epoch, validator_index)`.
@@ -316,7 +336,7 @@ First, the validator should construct `attestation_data`, an [`AttestationData`]
##### LMD GHOST vote
Set `attestation_data.beacon_block_root = signing_root(head_block)`.
Set `attestation_data.beacon_block_root = hash_tree_root(head_block)`.
##### FFG vote
@@ -326,7 +346,7 @@ Set `attestation_data.beacon_block_root = signing_root(head_block)`.
*Note*: `epoch_boundary_block_root` can be looked up in the state using:
- Let `start_slot = compute_start_slot_at_epoch(get_current_epoch(head_state))`.
- Let `epoch_boundary_block_root = signing_root(head_block) if start_slot == head_state.slot else get_block_root(state, start_slot)`.
- Let `epoch_boundary_block_root = hash_tree_root(head_block) if start_slot == head_state.slot else get_block_root(state, start_slot)`.
#### Construct attestation

View File

@@ -1,4 +1,4 @@
from eth2spec.utils.ssz.ssz_impl import serialize, hash_tree_root, signing_root
from eth2spec.utils.ssz.ssz_impl import serialize, hash_tree_root
from eth2spec.debug.encode import encode
from eth2spec.utils.ssz.ssz_typing import SSZValue, Container
from typing import Callable
@@ -10,8 +10,6 @@ def valid_test_case(value_fn: Callable[[], SSZValue]):
yield "value", "data", encode(value)
yield "serialized", "ssz", serialize(value)
yield "root", "meta", '0x' + hash_tree_root(value).hex()
if isinstance(value, Container):
yield "signing_root", "meta", '0x' + signing_root(value).hex()
return case_fn

View File

@@ -7,7 +7,6 @@ from eth2spec.phase0 import spec
from eth2spec.utils.ssz.ssz_typing import Container
from eth2spec.utils.ssz.ssz_impl import (
hash_tree_root,
signing_root,
serialize,
)
from gen_base import gen_runner, gen_typing
@@ -24,8 +23,6 @@ def create_test_case(rng: Random, typ, mode: random_value.RandomizationMode, cha
roots_data = {
"root": '0x' + hash_tree_root(value).hex()
}
if isinstance(value, Container) and hasattr(value, "signature"):
roots_data["signing_root"] = '0x' + signing_root(value).hex()
yield "roots", "data", roots_data

View File

@@ -1,22 +1,26 @@
from eth2spec.test.context import with_all_phases, spec_state_test
from eth2spec.test.helpers.attestations import get_valid_attestation
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
from eth2spec.test.helpers.state import state_transition_and_sign_block
from eth2spec.test.helpers.state import (
next_epoch,
next_epoch_with_attestations,
state_transition_and_sign_block,
)
def add_block_to_store(spec, store, block):
pre_state = store.block_states[block.parent_root]
block_time = pre_state.genesis_time + block.slot * spec.SECONDS_PER_SLOT
def add_block_to_store(spec, store, signed_block):
pre_state = store.block_states[signed_block.message.parent_root]
block_time = pre_state.genesis_time + signed_block.message.slot * spec.SECONDS_PER_SLOT
if store.time < block_time:
spec.on_tick(store, block_time)
spec.on_block(store, block)
spec.on_block(store, signed_block)
def add_attestation_to_store(spec, store, attestation):
parent_block = store.blocks[attestation.data.beacon_block_root]
pre_state = store.block_states[spec.signing_root(parent_block)]
pre_state = store.block_states[spec.hash_tree_root(parent_block)]
block_time = pre_state.genesis_time + parent_block.slot * spec.SECONDS_PER_SLOT
next_epoch_time = block_time + spec.SLOTS_PER_EPOCH * spec.SECONDS_PER_SLOT
@@ -32,7 +36,7 @@ def test_genesis(spec, state):
# Initialization
store = spec.get_genesis_store(state)
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
assert spec.get_head(store) == spec.signing_root(genesis_block)
assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
@with_all_phases
@@ -41,19 +45,19 @@ def test_chain_no_attestations(spec, state):
# Initialization
store = spec.get_genesis_store(state)
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
assert spec.get_head(store) == spec.signing_root(genesis_block)
assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
# On receiving a block of `GENESIS_SLOT + 1` slot
block_1 = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block_1)
add_block_to_store(spec, store, block_1)
signed_block_1 = state_transition_and_sign_block(spec, state, block_1)
add_block_to_store(spec, store, signed_block_1)
# On receiving a block of next epoch
block_2 = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block_2)
add_block_to_store(spec, store, block_2)
signed_block_2 = state_transition_and_sign_block(spec, state, block_2)
add_block_to_store(spec, store, signed_block_2)
assert spec.get_head(store) == spec.signing_root(block_2)
assert spec.get_head(store) == spec.hash_tree_root(block_2)
@with_all_phases
@@ -64,22 +68,22 @@ def test_split_tie_breaker_no_attestations(spec, state):
# Initialization
store = spec.get_genesis_store(state)
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
assert spec.get_head(store) == spec.signing_root(genesis_block)
assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
# block at slot 1
block_1_state = genesis_state.copy()
block_1 = build_empty_block_for_next_slot(spec, block_1_state)
state_transition_and_sign_block(spec, block_1_state, block_1)
add_block_to_store(spec, store, block_1)
signed_block_1 = state_transition_and_sign_block(spec, block_1_state, block_1)
add_block_to_store(spec, store, signed_block_1)
# additional block at slot 1
block_2_state = genesis_state.copy()
block_2 = build_empty_block_for_next_slot(spec, block_2_state)
block_2.body.graffiti = b'\x42' * 32
state_transition_and_sign_block(spec, block_2_state, block_2)
add_block_to_store(spec, store, block_2)
signed_block_2 = state_transition_and_sign_block(spec, block_2_state, block_2)
add_block_to_store(spec, store, signed_block_2)
highest_root = max(spec.signing_root(block_1), spec.signing_root(block_2))
highest_root = max(spec.hash_tree_root(block_1), spec.hash_tree_root(block_2))
assert spec.get_head(store) == highest_root
@@ -92,23 +96,99 @@ def test_shorter_chain_but_heavier_weight(spec, state):
# Initialization
store = spec.get_genesis_store(state)
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
assert spec.get_head(store) == spec.signing_root(genesis_block)
assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
# build longer tree
long_state = genesis_state.copy()
for i in range(3):
long_block = build_empty_block_for_next_slot(spec, long_state)
state_transition_and_sign_block(spec, long_state, long_block)
add_block_to_store(spec, store, long_block)
signed_long_block = state_transition_and_sign_block(spec, long_state, long_block)
add_block_to_store(spec, store, signed_long_block)
# build short tree
short_state = genesis_state.copy()
short_block = build_empty_block_for_next_slot(spec, short_state)
short_block.body.graffiti = b'\x42' * 32
state_transition_and_sign_block(spec, short_state, short_block)
add_block_to_store(spec, store, short_block)
signed_short_block = state_transition_and_sign_block(spec, short_state, short_block)
add_block_to_store(spec, store, signed_short_block)
short_attestation = get_valid_attestation(spec, short_state, short_block.slot, signed=True)
add_attestation_to_store(spec, store, short_attestation)
assert spec.get_head(store) == spec.signing_root(short_block)
assert spec.get_head(store) == spec.hash_tree_root(short_block)
@with_all_phases
@spec_state_test
def test_filtered_block_tree(spec, state):
# Initialization
genesis_state_root = state.hash_tree_root()
store = spec.get_genesis_store(state)
genesis_block = spec.BeaconBlock(state_root=genesis_state_root)
# transition state past initial couple of epochs
next_epoch(spec, state)
next_epoch(spec, state)
assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
# fill in attestations for entire epoch, justifying the recent epoch
prev_state, signed_blocks, state = next_epoch_with_attestations(spec, state, True, False)
attestations = [
attestation for signed_block in signed_blocks
for attestation in signed_block.message.body.attestations
]
assert state.current_justified_checkpoint.epoch > prev_state.current_justified_checkpoint.epoch
# tick time forward and add blocks and attestations to store
current_time = state.slot * spec.SECONDS_PER_SLOT + store.genesis_time
spec.on_tick(store, current_time)
for signed_block in signed_blocks:
spec.on_block(store, signed_block)
for attestation in attestations:
spec.on_attestation(store, attestation)
assert store.justified_checkpoint == state.current_justified_checkpoint
# the last block in the branch should be the head
expected_head_root = spec.hash_tree_root(signed_blocks[-1].message)
assert spec.get_head(store) == expected_head_root
#
# create branch containing the justified block but not containing enough on
# chain votes to justify that block
#
# build a chain without attestations off of previous justified block
non_viable_state = store.block_states[store.justified_checkpoint.root].copy()
# ensure that next wave of votes are for future epoch
next_epoch(spec, non_viable_state)
next_epoch(spec, non_viable_state)
next_epoch(spec, non_viable_state)
assert spec.get_current_epoch(non_viable_state) > store.justified_checkpoint.epoch
# create rogue block that will be attested to in this non-viable branch
rogue_block = build_empty_block_for_next_slot(spec, non_viable_state)
signed_rogue_block = state_transition_and_sign_block(spec, non_viable_state, rogue_block)
# create an epoch's worth of attestations for the rogue block
next_epoch(spec, non_viable_state)
attestations = []
for i in range(spec.SLOTS_PER_EPOCH):
slot = rogue_block.slot + i
for index in range(spec.get_committee_count_at_slot(non_viable_state, slot)):
attestation = get_valid_attestation(spec, non_viable_state, rogue_block.slot + i, index)
attestations.append(attestation)
# tick time forward to be able to include up to the latest attestation
current_time = (attestations[-1].data.slot + 1) * spec.SECONDS_PER_SLOT + store.genesis_time
spec.on_tick(store, current_time)
# include rogue block and associated attestations in the store
spec.on_block(store, signed_rogue_block)
for attestation in attestations:
spec.on_attestation(store, attestation)
# ensure that get_head still returns the head from the previous branch
assert spec.get_head(store) == expected_head_root

View File

@@ -31,12 +31,12 @@ def test_on_attestation_current_epoch(spec, state):
spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * 2)
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
# store block in store
spec.on_block(store, block)
spec.on_block(store, signed_block)
attestation = get_valid_attestation(spec, state, slot=block.slot)
attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
assert attestation.data.target.epoch == spec.GENESIS_EPOCH
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH
@@ -50,12 +50,12 @@ def test_on_attestation_previous_epoch(spec, state):
spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH)
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
# store block in store
spec.on_block(store, block)
spec.on_block(store, signed_block)
attestation = get_valid_attestation(spec, state, slot=block.slot)
attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
assert attestation.data.target.epoch == spec.GENESIS_EPOCH
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + 1
@@ -73,17 +73,40 @@ def test_on_attestation_past_epoch(spec, state):
# create and store block from 3 epochs ago
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
spec.on_block(store, block)
signed_block = state_transition_and_sign_block(spec, state, block)
spec.on_block(store, signed_block)
# create attestation for past block
attestation = get_valid_attestation(spec, state, slot=state.slot)
attestation = get_valid_attestation(spec, state, slot=state.slot, signed=True)
assert attestation.data.target.epoch == spec.GENESIS_EPOCH
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + 2
run_on_attestation(spec, state, store, attestation, False)
@with_all_phases
@spec_state_test
def test_on_attestation_mismatched_target_and_slot(spec, state):
store = spec.get_genesis_store(state)
spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH)
block = build_empty_block_for_next_slot(spec, state)
signed_block = state_transition_and_sign_block(spec, state, block)
# store block in store
spec.on_block(store, signed_block)
attestation = get_valid_attestation(spec, state, slot=block.slot)
attestation.data.target.epoch += 1
sign_attestation(spec, state, attestation)
assert attestation.data.target.epoch == spec.GENESIS_EPOCH + 1
assert spec.compute_epoch_at_slot(attestation.data.slot) == spec.GENESIS_EPOCH
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + 1
run_on_attestation(spec, state, store, attestation, False)
@with_all_phases
@spec_state_test
def test_on_attestation_target_not_in_store(spec, state):
@@ -99,8 +122,8 @@ def test_on_attestation_target_not_in_store(spec, state):
# do not add target block to store
attestation = get_valid_attestation(spec, state, slot=target_block.slot)
assert attestation.data.target.root == target_block.signing_root()
attestation = get_valid_attestation(spec, state, slot=target_block.slot, signed=True)
assert attestation.data.target.root == target_block.hash_tree_root()
run_on_attestation(spec, state, store, attestation, False)
@@ -116,19 +139,19 @@ def test_on_attestation_beacon_block_not_in_store(spec, state):
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH - 1)
target_block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, target_block)
signed_target_block = state_transition_and_sign_block(spec, state, target_block)
# store target in store
spec.on_block(store, target_block)
spec.on_block(store, signed_target_block)
head_block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, head_block)
# do not add head block to store
attestation = get_valid_attestation(spec, state, slot=head_block.slot)
assert attestation.data.target.root == target_block.signing_root()
assert attestation.data.beacon_block_root == head_block.signing_root()
attestation = get_valid_attestation(spec, state, slot=head_block.slot, signed=True)
assert attestation.data.target.root == target_block.hash_tree_root()
assert attestation.data.beacon_block_root == head_block.hash_tree_root()
run_on_attestation(spec, state, store, attestation, False)
@@ -141,15 +164,15 @@ def test_on_attestation_future_epoch(spec, state):
spec.on_tick(store, time)
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
# store block in store
spec.on_block(store, block)
spec.on_block(store, signed_block)
# move state forward but not store
state.slot = block.slot + spec.SLOTS_PER_EPOCH
attestation = get_valid_attestation(spec, state, slot=state.slot)
attestation = get_valid_attestation(spec, state, slot=state.slot, signed=True)
run_on_attestation(spec, state, store, attestation, False)
@@ -161,13 +184,13 @@ def test_on_attestation_future_block(spec, state):
spec.on_tick(store, time)
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
spec.on_block(store, block)
spec.on_block(store, signed_block)
# attestation for slot immediately prior to the block being attested to
attestation = get_valid_attestation(spec, state, slot=block.slot - 1, signed=False)
attestation.data.beacon_block_root = block.signing_root()
attestation.data.beacon_block_root = block.hash_tree_root()
sign_attestation(spec, state, attestation)
run_on_attestation(spec, state, store, attestation, False)
@@ -181,11 +204,11 @@ def test_on_attestation_same_slot(spec, state):
spec.on_tick(store, time)
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
spec.on_block(store, block)
spec.on_block(store, signed_block)
attestation = get_valid_attestation(spec, state, slot=block.slot)
attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
run_on_attestation(spec, state, store, attestation, False)
@@ -197,11 +220,11 @@ def test_on_attestation_invalid_attestation(spec, state):
spec.on_tick(store, time)
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
spec.on_block(store, block)
spec.on_block(store, signed_block)
attestation = get_valid_attestation(spec, state, slot=block.slot)
attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
# make invalid by using an invalid committee index
attestation.data.index = spec.MAX_COMMITTEES_PER_SLOT * spec.SLOTS_PER_EPOCH

View File

@@ -1,33 +1,35 @@
from copy import deepcopy
from eth2spec.utils.ssz.ssz_impl import signing_root
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
from eth2spec.test.context import with_all_phases, spec_state_test
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block, transition_unsigned_block, \
build_empty_block
from eth2spec.test.helpers.state import next_epoch, next_epoch_with_attestations, state_transition_and_sign_block
def run_on_block(spec, store, block, valid=True):
def run_on_block(spec, store, signed_block, valid=True):
if not valid:
try:
spec.on_block(store, block)
spec.on_block(store, signed_block)
except AssertionError:
return
else:
assert False
spec.on_block(store, block)
assert store.blocks[signing_root(block)] == block
spec.on_block(store, signed_block)
assert store.blocks[hash_tree_root(signed_block.message)] == signed_block.message
def apply_next_epoch_with_attestations(spec, state, store):
_, new_blocks, post_state = next_epoch_with_attestations(spec, state, True, False)
for block in new_blocks:
block_root = signing_root(block)
_, new_signed_blocks, post_state = next_epoch_with_attestations(spec, state, True, False)
for signed_block in new_signed_blocks:
block = signed_block.message
block_root = hash_tree_root(block)
store.blocks[block_root] = block
store.block_states[block_root] = post_state
last_block = block
last_signed_block = signed_block
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
return post_state, store, last_block
return post_state, store, last_signed_block
@with_all_phases
@@ -41,16 +43,15 @@ def test_basic(spec, state):
# On receiving a block of `GENESIS_SLOT + 1` slot
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
run_on_block(spec, store, block)
signed_block = state_transition_and_sign_block(spec, state, block)
run_on_block(spec, store, signed_block)
# On receiving a block of next epoch
store.time = time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
block = build_empty_block_for_next_slot(spec, state)
block.slot += spec.SLOTS_PER_EPOCH
state_transition_and_sign_block(spec, state, block)
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
signed_block = state_transition_and_sign_block(spec, state, block)
run_on_block(spec, store, block)
run_on_block(spec, store, signed_block)
# TODO: add tests for justified_root and finalized_root
@@ -65,10 +66,10 @@ def test_on_block_checkpoints(spec, state):
next_epoch(spec, state)
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
state, store, last_block = apply_next_epoch_with_attestations(spec, state, store)
state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store)
next_epoch(spec, state)
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
last_block_root = signing_root(last_block)
last_block_root = hash_tree_root(last_signed_block.message)
# Mock the finalized_checkpoint
fin_state = store.block_states[last_block_root]
@@ -77,8 +78,8 @@ def test_on_block_checkpoints(spec, state):
)
block = build_empty_block_for_next_slot(spec, fin_state)
state_transition_and_sign_block(spec, deepcopy(fin_state), block)
run_on_block(spec, store, block)
signed_block = state_transition_and_sign_block(spec, deepcopy(fin_state), block)
run_on_block(spec, store, signed_block)
@with_all_phases
@@ -91,8 +92,8 @@ def test_on_block_future_block(spec, state):
# Fail receiving block of `GENESIS_SLOT + 1` slot
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
run_on_block(spec, store, block, False)
signed_block = state_transition_and_sign_block(spec, state, block)
run_on_block(spec, store, signed_block, False)
@with_all_phases
@@ -105,14 +106,14 @@ def test_on_block_bad_parent_root(spec, state):
# Fail receiving block of `GENESIS_SLOT + 1` slot
block = build_empty_block_for_next_slot(spec, state)
spec.state_transition(state, block)
transition_unsigned_block(spec, state, block)
block.state_root = state.hash_tree_root()
block.parent_root = b'\x45' * 32
sign_block(spec, state, block)
signed_block = sign_block(spec, state, block)
run_on_block(spec, store, block, False)
run_on_block(spec, store, signed_block, False)
@with_all_phases
@@ -130,8 +131,8 @@ def test_on_block_before_finalized(spec, state):
# Fail receiving block of `GENESIS_SLOT + 1` slot
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
run_on_block(spec, store, block, False)
signed_block = state_transition_and_sign_block(spec, state, block)
run_on_block(spec, store, signed_block, False)
@with_all_phases
@@ -144,10 +145,10 @@ def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state):
next_epoch(spec, state)
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
state, store, last_block = apply_next_epoch_with_attestations(spec, state, store)
state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store)
next_epoch(spec, state)
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
last_block_root = signing_root(last_block)
last_block_root = hash_tree_root(last_signed_block.message)
# Mock the justified checkpoint
just_state = store.block_states[last_block_root]
@@ -158,16 +159,16 @@ def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state):
just_state.current_justified_checkpoint = new_justified
block = build_empty_block_for_next_slot(spec, just_state)
state_transition_and_sign_block(spec, deepcopy(just_state), block)
signed_block = state_transition_and_sign_block(spec, deepcopy(just_state), block)
assert spec.get_current_slot(store) % spec.SLOTS_PER_EPOCH < spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED
run_on_block(spec, store, block)
run_on_block(spec, store, signed_block)
assert store.justified_checkpoint == new_justified
@with_all_phases
@spec_state_test
def test_on_block_outside_safe_slots_and_old_block(spec, state):
def test_on_block_outside_safe_slots_and_multiple_better_justified(spec, state):
# Initialization
store = spec.get_genesis_store(state)
time = 100
@@ -175,10 +176,10 @@ def test_on_block_outside_safe_slots_and_old_block(spec, state):
next_epoch(spec, state)
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
state, store, last_block = apply_next_epoch_with_attestations(spec, state, store)
state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store)
next_epoch(spec, state)
spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
last_block_root = signing_root(last_block)
last_block_root = hash_tree_root(last_signed_block.message)
# Mock justified block in store
just_block = build_empty_block_for_next_slot(spec, state)
@@ -186,20 +187,30 @@ def test_on_block_outside_safe_slots_and_old_block(spec, state):
just_block.slot = spec.compute_start_slot_at_epoch(store.justified_checkpoint.epoch)
store.blocks[just_block.hash_tree_root()] = just_block
# Mock the justified checkpoint
just_state = store.block_states[last_block_root]
new_justified = spec.Checkpoint(
epoch=just_state.current_justified_checkpoint.epoch + 1,
root=just_block.hash_tree_root(),
)
just_state.current_justified_checkpoint = new_justified
block = build_empty_block_for_next_slot(spec, just_state)
state_transition_and_sign_block(spec, deepcopy(just_state), block)
# Step time past safe slots
spec.on_tick(store, store.time + spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED * spec.SECONDS_PER_SLOT)
assert spec.get_current_slot(store) % spec.SLOTS_PER_EPOCH >= spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED
run_on_block(spec, store, block)
assert store.justified_checkpoint != new_justified
assert store.best_justified_checkpoint == new_justified
previously_justified = store.justified_checkpoint
# Add a series of new blocks with "better" justifications
best_justified_checkpoint = spec.Checkpoint(epoch=0)
for i in range(3, 0, -1):
just_state = store.block_states[last_block_root]
new_justified = spec.Checkpoint(
epoch=previously_justified.epoch + i,
root=just_block.hash_tree_root(),
)
if new_justified.epoch > best_justified_checkpoint.epoch:
best_justified_checkpoint = new_justified
just_state.current_justified_checkpoint = new_justified
block = build_empty_block_for_next_slot(spec, just_state)
signed_block = state_transition_and_sign_block(spec, deepcopy(just_state), block)
run_on_block(spec, store, signed_block)
assert store.justified_checkpoint == previously_justified
# ensure the best from the series was stored
assert store.best_justified_checkpoint == best_justified_checkpoint

View File

@@ -1,6 +1,7 @@
from typing import List
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, transition_unsigned_block, \
build_empty_block
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils.bls import bls_sign, bls_aggregate_signatures
from eth2spec.utils.ssz.ssz_typing import Bitlist
@@ -122,10 +123,8 @@ def fill_aggregate_attestation(spec, state, attestation, signed=False):
def add_attestations_to_state(spec, state, attestations, slot):
block = build_empty_block_for_next_slot(spec, state)
block.slot = slot
block = build_empty_block(spec, state, slot)
for attestation in attestations:
block.body.attestations.append(attestation)
spec.process_slots(state, block.slot)
sign_block(spec, state, block)
spec.state_transition(state, block)
transition_unsigned_block(spec, state, block)

View File

@@ -2,26 +2,30 @@ from copy import deepcopy
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils.bls import bls_sign, only_with_bls
from eth2spec.utils.ssz.ssz_impl import signing_root, hash_tree_root
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
# Fully ignore the function if BLS is off, beacon-proposer index calculation is slow.
@only_with_bls()
def sign_block(spec, state, block, proposer_index=None):
assert state.slot <= block.slot
def get_proposer_index_maybe(spec, state, slot, proposer_index=None):
if proposer_index is None:
if block.slot == state.slot:
assert state.slot <= slot
if slot == state.slot:
proposer_index = spec.get_beacon_proposer_index(state)
else:
if spec.compute_epoch_at_slot(state.slot) + 1 > spec.compute_epoch_at_slot(block.slot):
if spec.compute_epoch_at_slot(state.slot) + 1 > spec.compute_epoch_at_slot(slot):
print("warning: block slot far away, and no proposer index manually given."
" Signing block is slow due to transition for proposer index calculation.")
# use stub state to get proposer index of future slot
stub_state = deepcopy(state)
spec.process_slots(stub_state, block.slot)
spec.process_slots(stub_state, slot)
proposer_index = spec.get_beacon_proposer_index(stub_state)
return proposer_index
@only_with_bls()
def apply_randao_reveal(spec, state, block, proposer_index=None):
assert state.slot <= block.slot
proposer_index = get_proposer_index_maybe(spec, state, block.slot, proposer_index)
privkey = privkeys[proposer_index]
block.body.randao_reveal = bls_sign(
@@ -33,8 +37,18 @@ def sign_block(spec, state, block, proposer_index=None):
domain_type=spec.DOMAIN_RANDAO,
)
)
block.signature = bls_sign(
message_hash=signing_root(block),
# Fully ignore the function if BLS is off, beacon-proposer index calculation is slow.
@only_with_bls()
def apply_sig(spec, state, signed_block, proposer_index=None):
block = signed_block.message
proposer_index = get_proposer_index_maybe(spec, state, block.slot, proposer_index)
privkey = privkeys[proposer_index]
signed_block.signature = bls_sign(
message_hash=hash_tree_root(block),
privkey=privkey,
domain=spec.get_domain(
state,
@@ -42,17 +56,26 @@ def sign_block(spec, state, block, proposer_index=None):
spec.compute_epoch_at_slot(block.slot)))
def sign_block(spec, state, block, proposer_index=None):
signed_block = spec.SignedBeaconBlock(message=block)
apply_sig(spec, state, signed_block, proposer_index)
return signed_block
def transition_unsigned_block(spec, state, block):
spec.process_slots(state, block.slot)
spec.process_block(state, block)
def apply_empty_block(spec, state):
"""
Transition via an empty block (on current slot, assuming no block has been applied yet).
:return: the empty block that triggered the transition.
"""
block = build_empty_block(spec, state, signed=True)
spec.state_transition(state, block)
return block
block = build_empty_block(spec, state)
transition_unsigned_block(spec, state, block)
def build_empty_block(spec, state, slot=None, signed=False):
def build_empty_block(spec, state, slot=None):
if slot is None:
slot = state.slot
empty_block = spec.BeaconBlock()
@@ -61,13 +84,10 @@ def build_empty_block(spec, state, slot=None, signed=False):
previous_block_header = deepcopy(state.latest_block_header)
if previous_block_header.state_root == spec.Root():
previous_block_header.state_root = state.hash_tree_root()
empty_block.parent_root = signing_root(previous_block_header)
if signed:
sign_block(spec, state, empty_block)
empty_block.parent_root = hash_tree_root(previous_block_header)
apply_randao_reveal(spec, state, empty_block)
return empty_block
def build_empty_block_for_next_slot(spec, state, signed=False):
return build_empty_block(spec, state, state.slot + 1, signed=signed)
def build_empty_block_for_next_slot(spec, state):
return build_empty_block(spec, state, state.slot + 1)

View File

@@ -1,5 +1,5 @@
from eth2spec.utils.bls import bls_sign
from eth2spec.utils.ssz.ssz_impl import signing_root
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
def sign_block_header(spec, state, header, privkey):
@@ -7,8 +7,8 @@ def sign_block_header(spec, state, header, privkey):
state=state,
domain_type=spec.DOMAIN_BEACON_PROPOSER,
)
header.signature = bls_sign(
message_hash=signing_root(header),
return spec.SignedBeaconBlockHeader(message=header, signature=bls_sign(
message_hash=hash_tree_root(header),
privkey=privkey,
domain=domain,
)
))

View File

@@ -1,7 +1,7 @@
from eth2spec.test.helpers.keys import pubkeys, privkeys
from eth2spec.utils.bls import bls_sign
from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merkle_proof
from eth2spec.utils.ssz.ssz_impl import signing_root, hash_tree_root
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
from eth2spec.utils.ssz.ssz_typing import List
@@ -26,8 +26,12 @@ def sign_deposit_data(spec, deposit_data, privkey, state=None):
spec.DOMAIN_DEPOSIT,
)
deposit_message = spec.DepositMessage(
pubkey=deposit_data.pubkey,
withdrawal_credentials=deposit_data.withdrawal_credentials,
amount=deposit_data.amount)
signature = bls_sign(
message_hash=signing_root(deposit_data),
message_hash=hash_tree_root(deposit_message),
privkey=privkey,
domain=domain,
)

View File

@@ -6,7 +6,7 @@ from eth2spec.utils.bls import (
only_with_bls,
)
from eth2spec.utils.ssz.ssz_impl import (
signing_root,
hash_tree_root,
)
from .attestations import (
@@ -22,7 +22,7 @@ def sign_shard_block(spec, beacon_state, shard_state, block, proposer_index=None
privkey = privkeys[proposer_index]
block.signature = bls_sign(
message_hash=signing_root(block),
message_hash=hash_tree_root(block),
privkey=privkey,
domain=spec.get_domain(
beacon_state,
@@ -44,12 +44,12 @@ def build_empty_shard_block(spec,
previous_beacon_header = deepcopy(beacon_state.latest_block_header)
if previous_beacon_header.state_root == spec.Bytes32():
previous_beacon_header.state_root = beacon_state.hash_tree_root()
beacon_block_root = spec.signing_root(previous_beacon_header)
beacon_block_root = hash_tree_root(previous_beacon_header)
previous_block_header = deepcopy(shard_state.latest_block_header)
if previous_block_header.state_root == spec.Bytes32():
previous_block_header.state_root = shard_state.hash_tree_root()
parent_root = signing_root(previous_block_header)
parent_root = hash_tree_root(previous_block_header)
block = spec.ShardBlock(
shard=shard_state.shard,

View File

@@ -20,12 +20,16 @@ def get_valid_proposer_slashing(spec, state, signed_1=False, signed_2=False):
header_2.parent_root = b'\x99' * 32
if signed_1:
sign_block_header(spec, state, header_1, privkey)
signed_header_1 = sign_block_header(spec, state, header_1, privkey)
else:
signed_header_1 = spec.SignedBeaconBlockHeader(message=header_1)
if signed_2:
sign_block_header(spec, state, header_2, privkey)
signed_header_2 = sign_block_header(spec, state, header_2, privkey)
else:
signed_header_2 = spec.SignedBeaconBlockHeader(message=header_2)
return spec.ProposerSlashing(
proposer_index=validator_index,
header_1=header_1,
header_2=header_2,
signed_header_1=signed_header_1,
signed_header_2=signed_header_2,
)

View File

@@ -1,6 +1,8 @@
from copy import deepcopy
from eth2spec.test.context import expect_assertion_error
from eth2spec.test.helpers.attestations import get_valid_attestation
from eth2spec.test.helpers.block import sign_block, build_empty_block_for_next_slot
from eth2spec.test.helpers.block import sign_block, build_empty_block_for_next_slot, transition_unsigned_block
def get_balance(state, index):
@@ -40,14 +42,17 @@ def get_state_root(spec, state, slot) -> bytes:
return state.state_roots[slot % spec.SLOTS_PER_HISTORICAL_ROOT]
def state_transition_and_sign_block(spec, state, block):
def state_transition_and_sign_block(spec, state, block, expect_fail=False):
"""
State transition via the provided ``block``
then package the block with the state root and signature.
then package the block with the correct state root and signature.
"""
spec.state_transition(state, block)
if expect_fail:
expect_assertion_error(lambda: transition_unsigned_block(spec, state, block))
else:
transition_unsigned_block(spec, state, block)
block.state_root = state.hash_tree_root()
sign_block(spec, state, block)
return sign_block(spec, state, block)
def next_epoch_with_attestations(spec,
@@ -57,7 +62,7 @@ def next_epoch_with_attestations(spec,
assert state.slot % spec.SLOTS_PER_EPOCH == 0
post_state = deepcopy(state)
blocks = []
signed_blocks = []
for _ in range(spec.SLOTS_PER_EPOCH):
block = build_empty_block_for_next_slot(spec, post_state)
if fill_cur_epoch and post_state.slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY:
@@ -65,17 +70,17 @@ def next_epoch_with_attestations(spec,
committees_per_slot = spec.get_committee_count_at_slot(state, slot_to_attest)
if slot_to_attest >= spec.compute_start_slot_at_epoch(spec.get_current_epoch(post_state)):
for index in range(committees_per_slot):
cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index)
cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index, signed=True)
block.body.attestations.append(cur_attestation)
if fill_prev_epoch:
slot_to_attest = post_state.slot - spec.SLOTS_PER_EPOCH + 1
committees_per_slot = spec.get_committee_count_at_slot(state, slot_to_attest)
for index in range(committees_per_slot):
prev_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index)
prev_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index, signed=True)
block.body.attestations.append(prev_attestation)
state_transition_and_sign_block(spec, post_state, block)
blocks.append(block)
signed_block = state_transition_and_sign_block(spec, post_state, block)
signed_blocks.append(signed_block)
return state, blocks, post_state
return state, signed_blocks, post_state

View File

@@ -1,24 +1,17 @@
from eth2spec.utils.bls import bls_sign
from eth2spec.utils.ssz.ssz_impl import signing_root
def build_voluntary_exit(spec, state, epoch, validator_index, privkey, signed=False):
voluntary_exit = spec.VoluntaryExit(
epoch=epoch,
validator_index=validator_index,
)
if signed:
sign_voluntary_exit(spec, state, voluntary_exit, privkey)
return voluntary_exit
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
def sign_voluntary_exit(spec, state, voluntary_exit, privkey):
voluntary_exit.signature = bls_sign(
message_hash=signing_root(voluntary_exit),
privkey=privkey,
domain=spec.get_domain(
state=state,
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
message_epoch=voluntary_exit.epoch,
return spec.SignedVoluntaryExit(
message=voluntary_exit,
signature=bls_sign(
message_hash=hash_tree_root(voluntary_exit),
privkey=privkey,
domain=spec.get_domain(
state=state,
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
message_epoch=voluntary_exit.epoch,
)
)
)

View File

@@ -177,6 +177,20 @@ def test_invalid_index(spec, state):
yield from run_attestation_processing(spec, state, attestation, False)
@with_all_phases
@spec_state_test
def test_mismatched_target_and_slot(spec, state):
next_epoch(spec, state)
next_epoch(spec, state)
attestation = get_valid_attestation(spec, state)
attestation.data.slot = attestation.data.slot - spec.SLOTS_PER_EPOCH
sign_attestation(spec, state, attestation)
yield from run_attestation_processing(spec, state, attestation, False)
@with_all_phases
@spec_state_test
def test_old_target_epoch(spec, state):

View File

@@ -252,6 +252,76 @@ def test_att2_bad_replaced_index(spec, state):
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
@with_all_phases
@spec_state_test
@always_bls
def test_att1_duplicate_index_normal_signed(spec, state):
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
indices = attester_slashing.attestation_1.attesting_indices
indices.pop(1) # remove an index, make room for the additional duplicate index.
attester_slashing.attestation_1.attesting_indices = sorted(indices)
# sign it, the signature will be valid for a single occurence. If the transition accidentally ignores the duplicate.
sign_indexed_attestation(spec, state, attester_slashing.attestation_1)
indices.append(indices[0]) # add one of the indices a second time
attester_slashing.attestation_1.attesting_indices = sorted(indices)
# it will just appear normal, unless the double index is spotted
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
@with_all_phases
@spec_state_test
@always_bls
def test_att2_duplicate_index_normal_signed(spec, state):
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False)
indices = attester_slashing.attestation_2.attesting_indices
indices.pop(2) # remove an index, make room for the additional duplicate index.
attester_slashing.attestation_2.attesting_indices = sorted(indices)
# sign it, the signature will be valid for a single occurence. If the transition accidentally ignores the duplicate.
sign_indexed_attestation(spec, state, attester_slashing.attestation_2)
indices.append(indices[1]) # add one of the indices a second time
attester_slashing.attestation_2.attesting_indices = sorted(indices)
# it will just appear normal, unless the double index is spotted
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
@with_all_phases
@spec_state_test
@always_bls
def test_att1_duplicate_index_double_signed(spec, state):
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
indices = attester_slashing.attestation_1.attesting_indices
indices.pop(1) # remove an index, make room for the additional duplicate index.
indices.append(indices[2]) # add one of the indices a second time
attester_slashing.attestation_1.attesting_indices = sorted(indices)
sign_indexed_attestation(spec, state, attester_slashing.attestation_1) # will have one attester signing it double
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
@with_all_phases
@spec_state_test
@always_bls
def test_att2_duplicate_index_double_signed(spec, state):
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False)
indices = attester_slashing.attestation_2.attesting_indices
indices.pop(1) # remove an index, make room for the additional duplicate index.
indices.append(indices[2]) # add one of the indices a second time
attester_slashing.attestation_2.attesting_indices = sorted(indices)
sign_indexed_attestation(spec, state, attester_slashing.attestation_2) # will have one attester signing it double
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
@with_all_phases
@spec_state_test
def test_unsorted_att_1(spec, state):

View File

@@ -1,10 +1,7 @@
from copy import deepcopy
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot,
sign_block
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_all_phases
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
from eth2spec.test.helpers.state import next_slot
@@ -37,16 +34,8 @@ def run_block_header_processing(spec, state, block, valid=True):
@with_all_phases
@spec_state_test
def test_success_block_header(spec, state):
block = build_empty_block_for_next_slot(spec, state, signed=True)
yield from run_block_header_processing(spec, state, block)
@with_all_phases
@spec_state_test
@always_bls
def test_invalid_sig_block_header(spec, state):
block = build_empty_block_for_next_slot(spec, state)
yield from run_block_header_processing(spec, state, block, valid=False)
yield from run_block_header_processing(spec, state, block)
@with_all_phases
@@ -54,7 +43,6 @@ def test_invalid_sig_block_header(spec, state):
def test_invalid_slot_block_header(spec, state):
block = build_empty_block_for_next_slot(spec, state)
block.slot = state.slot + 2 # invalid slot
sign_block(spec, state, block)
yield from run_block_header_processing(spec, state, block, valid=False)
@@ -64,7 +52,6 @@ def test_invalid_slot_block_header(spec, state):
def test_invalid_parent_root(spec, state):
block = build_empty_block_for_next_slot(spec, state)
block.parent_root = b'\12' * 32 # invalid prev root
sign_block(spec, state, block)
yield from run_block_header_processing(spec, state, block, valid=False)
@@ -80,6 +67,6 @@ def test_proposer_slashed(spec, state):
# set proposer to slashed
state.validators[proposer_index].slashed = True
block = build_empty_block_for_next_slot(spec, state, signed=True)
block = build_empty_block_for_next_slot(spec, state)
yield from run_block_header_processing(spec, state, block, valid=False)

View File

@@ -88,8 +88,10 @@ def test_epochs_are_different(spec, state):
proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=False)
# set slots to be in different epochs
proposer_slashing.header_2.slot += spec.SLOTS_PER_EPOCH
sign_block_header(spec, state, proposer_slashing.header_2, privkeys[proposer_slashing.proposer_index])
header_2 = proposer_slashing.signed_header_2.message
header_2.slot += spec.SLOTS_PER_EPOCH
proposer_slashing.signed_header_2 = sign_block_header(
spec, state, header_2, privkeys[proposer_slashing.proposer_index])
yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
@@ -100,7 +102,7 @@ def test_headers_are_same(spec, state):
proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=False)
# set headers to be the same
proposer_slashing.header_2 = proposer_slashing.header_1
proposer_slashing.signed_header_2 = proposer_slashing.signed_header_1
yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)

View File

@@ -1,9 +1,9 @@
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.voluntary_exits import build_voluntary_exit, sign_voluntary_exit
from eth2spec.test.helpers.voluntary_exits import sign_voluntary_exit
def run_voluntary_exit_processing(spec, state, voluntary_exit, valid=True):
def run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=True):
"""
Run ``process_voluntary_exit``, yielding:
- pre-state ('pre')
@@ -11,19 +11,19 @@ def run_voluntary_exit_processing(spec, state, voluntary_exit, valid=True):
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
validator_index = voluntary_exit.validator_index
validator_index = signed_voluntary_exit.message.validator_index
yield 'pre', state
yield 'voluntary_exit', voluntary_exit
yield 'voluntary_exit', signed_voluntary_exit
if not valid:
expect_assertion_error(lambda: spec.process_voluntary_exit(state, voluntary_exit))
expect_assertion_error(lambda: spec.process_voluntary_exit(state, signed_voluntary_exit))
yield 'post', None
return
pre_exit_epoch = state.validators[validator_index].exit_epoch
spec.process_voluntary_exit(state, voluntary_exit)
spec.process_voluntary_exit(state, signed_voluntary_exit)
yield 'post', state
@@ -41,9 +41,10 @@ def test_success(spec, state):
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(spec, state, current_epoch, validator_index, privkey, signed=True)
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
yield from run_voluntary_exit_processing(spec, state, voluntary_exit)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
@with_all_phases
@@ -55,11 +56,14 @@ def test_invalid_signature(spec, state):
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(spec, state, current_epoch, validator_index, privkey)
voluntary_exit = spec.VoluntaryExit(
epoch=current_epoch,
validator_index=validator_index,
)
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, 12345)
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
@with_all_phases
@@ -77,14 +81,11 @@ def test_success_exit_queue(spec, state):
exit_queue = []
for index in initial_indices:
privkey = pubkey_to_privkey[state.validators[index].pubkey]
exit_queue.append(build_voluntary_exit(
spec,
state,
current_epoch,
index,
privkey,
signed=True,
))
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=index), privkey)
exit_queue.append(signed_voluntary_exit)
# Now run all the exits
for voluntary_exit in exit_queue:
@@ -95,18 +96,13 @@ def test_success_exit_queue(spec, state):
# exit an additional validator
validator_index = spec.get_active_validator_indices(state, current_epoch)[-1]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
spec,
state,
current_epoch,
validator_index,
privkey,
signed=True,
)
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
# This is the interesting part of the test: on a pre-state with a full exit queue,
# when processing an additional exit, it results in an exit in a later epoch
yield from run_voluntary_exit_processing(spec, state, voluntary_exit)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
assert (
state.validators[validator_index].exit_epoch ==
@@ -124,18 +120,13 @@ def test_validator_exit_in_future(spec, state):
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
spec,
state,
current_epoch,
validator_index,
privkey,
signed=False,
voluntary_exit = spec.VoluntaryExit(
epoch=current_epoch + 1,
validator_index=validator_index,
)
voluntary_exit.epoch += 1
sign_voluntary_exit(spec, state, voluntary_exit, privkey)
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, privkey)
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
@with_all_phases
@@ -148,18 +139,13 @@ def test_validator_invalid_validator_index(spec, state):
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
spec,
state,
current_epoch,
validator_index,
privkey,
signed=False,
voluntary_exit = spec.VoluntaryExit(
epoch=current_epoch,
validator_index=len(state.validators),
)
voluntary_exit.validator_index = len(state.validators)
sign_voluntary_exit(spec, state, voluntary_exit, privkey)
signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, privkey)
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
@with_all_phases
@@ -171,17 +157,10 @@ def test_validator_not_active(spec, state):
state.validators[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
# build and test voluntary exit
voluntary_exit = build_voluntary_exit(
spec,
state,
current_epoch,
validator_index,
privkey,
signed=True,
)
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
@with_all_phases
@@ -197,16 +176,10 @@ def test_validator_already_exited(spec, state):
# but validator already has exited
state.validators[validator_index].exit_epoch = current_epoch + 2
voluntary_exit = build_voluntary_exit(
spec,
state,
current_epoch,
validator_index,
privkey,
signed=True,
)
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
@with_all_phases
@@ -216,18 +189,12 @@ def test_validator_not_active_long_enough(spec, state):
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
spec,
state,
current_epoch,
validator_index,
privkey,
signed=True,
)
signed_voluntary_exit = sign_voluntary_exit(
spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
assert (
current_epoch - state.validators[validator_index].activation_epoch <
spec.PERSISTENT_COMMITTEE_PERIOD
)
yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)

View File

@@ -17,24 +17,80 @@ def mock_deposit(spec, state, index):
@with_all_phases
@spec_state_test
def test_activation(spec, state):
def test_add_to_activation_queue(spec, state):
# move past first two irregular epochs wrt finality
next_epoch(spec, state)
next_epoch(spec, state)
index = 0
mock_deposit(spec, state, index)
for _ in range(spec.MAX_SEED_LOOKAHEAD + 1):
next_epoch(spec, state)
yield from run_process_registry_updates(spec, state)
# validator moved into queue
assert state.validators[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
assert state.validators[index].activation_epoch == spec.FAR_FUTURE_EPOCH
assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
@with_all_phases
@spec_state_test
def test_activation_queue_to_activated_if_finalized(spec, state):
# move past first two irregular epochs wrt finality
next_epoch(spec, state)
next_epoch(spec, state)
index = 0
mock_deposit(spec, state, index)
# mock validator as having been in queue since latest finalized
state.finalized_checkpoint.epoch = spec.get_current_epoch(state) - 1
state.validators[index].activation_eligibility_epoch = state.finalized_checkpoint.epoch
assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
yield from run_process_registry_updates(spec, state)
# validator activated for future epoch
assert state.validators[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
assert state.validators[index].activation_epoch != spec.FAR_FUTURE_EPOCH
assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
assert spec.is_active_validator(
state.validators[index],
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
)
@with_all_phases
@spec_state_test
def test_activation_queue_no_activation_no_finality(spec, state):
# move past first two irregular epochs wrt finality
next_epoch(spec, state)
next_epoch(spec, state)
index = 0
mock_deposit(spec, state, index)
# mock validator as having been in queue only after latest finalized
state.finalized_checkpoint.epoch = spec.get_current_epoch(state) - 1
state.validators[index].activation_eligibility_epoch = state.finalized_checkpoint.epoch + 1
assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
yield from run_process_registry_updates(spec, state)
# validator not activated
assert state.validators[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
assert state.validators[index].activation_epoch == spec.FAR_FUTURE_EPOCH
@with_all_phases
@spec_state_test
def test_activation_queue_sorting(spec, state):
mock_activations = 10
churn_limit = spec.get_validator_churn_limit(state)
# try to activate more than the per-epoch churn linmit
mock_activations = churn_limit * 2
epoch = spec.get_current_epoch(state)
for i in range(mock_activations):
@@ -44,9 +100,9 @@ def test_activation_queue_sorting(spec, state):
# give the last priority over the others
state.validators[mock_activations - 1].activation_eligibility_epoch = epoch
# make sure we are hitting the churn
churn_limit = spec.get_validator_churn_limit(state)
assert mock_activations > churn_limit
# move state forward and finalize to allow for activations
state.slot += spec.SLOTS_PER_EPOCH * 3
state.finalized_checkpoint.epoch = epoch + 1
yield from run_process_registry_updates(spec, state)
@@ -63,6 +119,38 @@ def test_activation_queue_sorting(spec, state):
assert state.validators[churn_limit - 2].activation_epoch != spec.FAR_FUTURE_EPOCH
@with_all_phases
@spec_state_test
def test_activation_queue_efficiency(spec, state):
churn_limit = spec.get_validator_churn_limit(state)
mock_activations = churn_limit * 2
epoch = spec.get_current_epoch(state)
for i in range(mock_activations):
mock_deposit(spec, state, i)
state.validators[i].activation_eligibility_epoch = epoch + 1
# move state forward and finalize to allow for activations
state.slot += spec.SLOTS_PER_EPOCH * 3
state.finalized_checkpoint.epoch = epoch + 1
# Run first registry update. Do not yield test vectors
for _ in run_process_registry_updates(spec, state):
pass
# Half should churn in first run of registry update
for i in range(mock_activations):
if i < mock_activations // 2:
assert state.validators[i].activation_epoch < spec.FAR_FUTURE_EPOCH
else:
assert state.validators[i].activation_epoch == spec.FAR_FUTURE_EPOCH
# Second half should churn in second run of registry update
yield from run_process_registry_updates(spec, state)
for i in range(mock_activations):
assert state.validators[i].activation_epoch < spec.FAR_FUTURE_EPOCH
@with_all_phases
@spec_state_test
def test_ejection(spec, state):
@@ -73,13 +161,87 @@ def test_ejection(spec, state):
# Mock an ejection
state.validators[index].effective_balance = spec.EJECTION_BALANCE
for _ in range(spec.MAX_SEED_LOOKAHEAD + 1):
next_epoch(spec, state)
yield from run_process_registry_updates(spec, state)
assert state.validators[index].exit_epoch != spec.FAR_FUTURE_EPOCH
assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
assert not spec.is_active_validator(
state.validators[index],
spec.get_current_epoch(state),
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
)
@with_all_phases
@spec_state_test
def test_ejection_past_churn_limit(spec, state):
churn_limit = spec.get_validator_churn_limit(state)
# try to eject more than per-epoch churn limit
mock_ejections = churn_limit * 3
for i in range(mock_ejections):
state.validators[i].effective_balance = spec.EJECTION_BALANCE
expected_ejection_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
yield from run_process_registry_updates(spec, state)
for i in range(mock_ejections):
# first third ejected in normal speed
if i < mock_ejections // 3:
assert state.validators[i].exit_epoch == expected_ejection_epoch
# second thirdgets delayed by 1 epoch
elif mock_ejections // 3 <= i < mock_ejections * 2 // 3:
assert state.validators[i].exit_epoch == expected_ejection_epoch + 1
# second thirdgets delayed by 2 epochs
else:
assert state.validators[i].exit_epoch == expected_ejection_epoch + 2
@with_all_phases
@spec_state_test
def test_activation_queue_activation_and_ejection(spec, state):
# move past first two irregular epochs wrt finality
next_epoch(spec, state)
next_epoch(spec, state)
# ready for entrance into activation queue
activation_queue_index = 0
mock_deposit(spec, state, activation_queue_index)
# ready for activation
activation_index = 1
mock_deposit(spec, state, activation_index)
state.finalized_checkpoint.epoch = spec.get_current_epoch(state) - 1
state.validators[activation_index].activation_eligibility_epoch = state.finalized_checkpoint.epoch
# ready for ejection
ejection_index = 2
state.validators[ejection_index].effective_balance = spec.EJECTION_BALANCE
yield from run_process_registry_updates(spec, state)
# validator moved into activation queue
validator = state.validators[activation_queue_index]
assert validator.activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
assert validator.activation_epoch == spec.FAR_FUTURE_EPOCH
assert not spec.is_active_validator(validator, spec.get_current_epoch(state))
# validator activated for future epoch
validator = state.validators[activation_index]
assert validator.activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
assert validator.activation_epoch != spec.FAR_FUTURE_EPOCH
assert not spec.is_active_validator(validator, spec.get_current_epoch(state))
assert spec.is_active_validator(
validator,
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
)
# validator ejected for future epoch
validator = state.validators[ejection_index]
assert validator.exit_epoch != spec.FAR_FUTURE_EPOCH
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
assert not spec.is_active_validator(
validator,
spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
)

View File

@@ -140,7 +140,7 @@ def test_skipped_slots(spec, state):
assert shard_state.slot == block.slot
latest_block_header = deepcopy(shard_state.latest_block_header)
latest_block_header.state_root = shard_state.hash_tree_root()
assert latest_block_header.signing_root() == block.signing_root()
assert latest_block_header.hash_tree_root() == block.hash_tree_root()
@with_all_phases_except(['phase0'])

View File

@@ -1,17 +1,18 @@
from copy import deepcopy
from eth2spec.utils.ssz.ssz_impl import signing_root
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
from eth2spec.utils.bls import bls_sign
from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block
from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block, next_slot
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block, \
transition_unsigned_block
from eth2spec.test.helpers.keys import privkeys, pubkeys
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing
from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing
from eth2spec.test.helpers.attestations import get_valid_attestation
from eth2spec.test.helpers.deposits import prepare_state_and_deposit
from eth2spec.test.context import spec_state_test, with_all_phases, expect_assertion_error
from eth2spec.test.context import spec_state_test, with_all_phases, expect_assertion_error, always_bls
@with_all_phases
@@ -20,13 +21,18 @@ def test_prev_slot_block_transition(spec, state):
# Go to clean slot
spec.process_slots(state, state.slot + 1)
# Make a block for it
block = build_empty_block(spec, state, slot=state.slot, signed=True)
block = build_empty_block(spec, state, slot=state.slot)
proposer_index = spec.get_beacon_proposer_index(state)
# Transition to next slot, above block will not be invalid on top of new state.
spec.process_slots(state, state.slot + 1)
yield 'pre', state
expect_assertion_error(lambda: state_transition_and_sign_block(spec, state, block))
yield 'blocks', [block]
# State is beyond block slot, but the block can still be realistic when invalid.
# Try the transition, and update the state root to where it is halted. Then sign with the supposed proposer.
expect_assertion_error(lambda: transition_unsigned_block(spec, state, block))
block.state_root = state.hash_tree_root()
signed_block = sign_block(spec, state, block, proposer_index=proposer_index)
yield 'blocks', [signed_block]
yield 'post', None
@@ -36,13 +42,13 @@ def test_same_slot_block_transition(spec, state):
# Same slot on top of pre-state, but move out of slot 0 first.
spec.process_slots(state, state.slot + 1)
block = build_empty_block(spec, state, slot=state.slot, signed=True)
block = build_empty_block(spec, state, slot=state.slot)
yield 'pre', state
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', state
@@ -54,15 +60,15 @@ def test_empty_block_transition(spec, state):
yield 'pre', state
block = build_empty_block_for_next_slot(spec, state, signed=True)
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', state
assert len(state.eth1_data_votes) == pre_eth1_votes + 1
assert spec.get_block_root_at_slot(state, pre_slot) == block.parent_root
assert spec.get_block_root_at_slot(state, pre_slot) == signed_block.message.parent_root
assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.Bytes32()
@@ -73,28 +79,58 @@ def test_invalid_state_root(spec, state):
block = build_empty_block_for_next_slot(spec, state)
block.state_root = b"\xaa" * 32
sign_block(spec, state, block)
signed_block = sign_block(spec, state, block)
expect_assertion_error(
lambda: spec.state_transition(state, block, validate_state_root=True))
expect_assertion_error(lambda: spec.state_transition(state, signed_block))
yield 'blocks', [block]
yield 'post', None
@with_all_phases
@spec_state_test
@always_bls
def test_zero_block_sig(spec, state):
block = build_empty_block_for_next_slot(spec, state)
invalid_signed_block = spec.SignedBeaconBlock(message=block)
expect_assertion_error(lambda: spec.state_transition(state, invalid_signed_block))
yield 'blocks', [invalid_signed_block]
yield 'post', None
@with_all_phases
@spec_state_test
@always_bls
def test_invalid_block_sig(spec, state):
block = build_empty_block_for_next_slot(spec, state)
invalid_signed_block = spec.SignedBeaconBlock(
message=block,
signature=bls_sign(
message_hash=hash_tree_root(block),
privkey=123456,
domain=spec.get_domain(
state,
spec.DOMAIN_BEACON_PROPOSER,
spec.compute_epoch_at_slot(block.slot)))
)
expect_assertion_error(lambda: spec.state_transition(state, invalid_signed_block))
yield 'blocks', [invalid_signed_block]
yield 'post', None
@with_all_phases
@spec_state_test
def test_skipped_slots(spec, state):
pre_slot = state.slot
yield 'pre', state
block = build_empty_block_for_next_slot(spec, state)
block.slot += 3
sign_block(spec, state, block)
block = build_empty_block(spec, state, state.slot + 4)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', state
assert state.slot == block.slot
@@ -109,13 +145,11 @@ def test_empty_epoch_transition(spec, state):
pre_slot = state.slot
yield 'pre', state
block = build_empty_block_for_next_slot(spec, state)
block.slot += spec.SLOTS_PER_EPOCH
sign_block(spec, state, block)
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', state
assert state.slot == block.slot
@@ -136,10 +170,10 @@ def test_empty_epoch_transition_not_finalizing(spec, state):
yield 'pre', state
spec.process_slots(state, state.slot + (spec.SLOTS_PER_EPOCH * 5))
block = build_empty_block_for_next_slot(spec, state, signed=True)
state_transition_and_sign_block(spec, state, block)
block = build_empty_block_for_next_slot(spec, state)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', state
assert state.slot == block.slot
@@ -165,11 +199,10 @@ def test_proposer_slashing(spec, state):
#
block = build_empty_block_for_next_slot(spec, state)
block.body.proposer_slashings.append(proposer_slashing)
sign_block(spec, state, block)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', state
# check if slashed
@@ -199,11 +232,10 @@ def test_attester_slashing(spec, state):
#
block = build_empty_block_for_next_slot(spec, state)
block.body.attester_slashings.append(attester_slashing)
sign_block(spec, state, block)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', state
slashed_validator = state.validators[validator_index]
@@ -221,6 +253,58 @@ def test_attester_slashing(spec, state):
)
@with_all_phases
@spec_state_test
def test_proposer_after_inactive_index(spec, state):
# disable some low validator index to check after for
inactive_index = 10
state.validators[inactive_index].exit_epoch = spec.get_current_epoch(state)
# skip forward, get brand new proposers
state.slot = spec.SLOTS_PER_EPOCH * 2
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
while True:
next_slot(spec, state)
proposer_index = spec.get_beacon_proposer_index(state)
if proposer_index > inactive_index:
# found a proposer that has a higher index than a disabled validator
yield 'pre', state
# test if the proposer can be recognized correctly after the inactive validator
signed_block = state_transition_and_sign_block(spec, state, build_empty_block(spec, state))
yield 'blocks', [signed_block]
yield 'post', state
break
@with_all_phases
@spec_state_test
def test_high_proposer_index(spec, state):
# disable a good amount of validators to make the active count lower, for a faster test
current_epoch = spec.get_current_epoch(state)
for i in range(len(state.validators) // 3):
state.validators[i].exit_epoch = current_epoch
# skip forward, get brand new proposers
state.slot = spec.SLOTS_PER_EPOCH * 2
block = build_empty_block_for_next_slot(spec, state)
state_transition_and_sign_block(spec, state, block)
active_count = len(spec.get_active_validator_indices(state, current_epoch))
while True:
next_slot(spec, state)
proposer_index = spec.get_beacon_proposer_index(state)
if proposer_index >= active_count:
# found a proposer that has a higher index than the active validator count
yield 'pre', state
# test if the proposer can be recognized correctly, even while it has a high index.
signed_block = state_transition_and_sign_block(spec, state, build_empty_block(spec, state))
yield 'blocks', [signed_block]
yield 'post', state
break
@with_all_phases
@spec_state_test
def test_expected_deposit_in_block(spec, state):
@@ -229,17 +313,9 @@ def test_expected_deposit_in_block(spec, state):
yield 'pre', state
block = build_empty_block_for_next_slot(spec, state)
sign_block(spec, state, block)
bad = False
try:
state_transition_and_sign_block(spec, state, block)
bad = True
except AssertionError:
pass
if bad:
raise AssertionError("expected deposit was not enforced")
signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', None
@@ -257,11 +333,9 @@ def test_deposit_in_block(spec, state):
block = build_empty_block_for_next_slot(spec, state)
block.body.deposits.append(deposit)
sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
state_transition_and_sign_block(spec, state, block)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', state
assert len(state.validators) == initial_registry_len + 1
@@ -285,11 +359,10 @@ def test_deposit_top_up(spec, state):
block = build_empty_block_for_next_slot(spec, state)
block.body.deposits.append(deposit)
sign_block(spec, state, block)
state_transition_and_sign_block(spec, state, block)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', state
assert len(state.validators) == initial_registry_len
@@ -308,23 +381,19 @@ def test_attestation(spec, state):
# Add to state via block transition
pre_current_attestations_len = len(state.current_epoch_attestations)
attestation_block = build_empty_block_for_next_slot(spec, state)
attestation_block.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation_block = build_empty_block(spec, state, state.slot + 1 + spec.MIN_ATTESTATION_INCLUSION_DELAY)
attestation_block.body.attestations.append(attestation)
sign_block(spec, state, attestation_block)
state_transition_and_sign_block(spec, state, attestation_block)
signed_attestation_block = state_transition_and_sign_block(spec, state, attestation_block)
assert len(state.current_epoch_attestations) == pre_current_attestations_len + 1
# Epoch transition should move to previous_epoch_attestations
pre_current_attestations_root = spec.hash_tree_root(state.current_epoch_attestations)
epoch_block = build_empty_block_for_next_slot(spec, state)
epoch_block.slot += spec.SLOTS_PER_EPOCH
sign_block(spec, state, epoch_block)
state_transition_and_sign_block(spec, state, epoch_block)
epoch_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
signed_epoch_block = state_transition_and_sign_block(spec, state, epoch_block)
yield 'blocks', [attestation_block, epoch_block]
yield 'blocks', [signed_attestation_block, signed_epoch_block]
yield 'post', state
assert len(state.current_epoch_attestations) == 0
@@ -348,30 +417,30 @@ def test_voluntary_exit(spec, state):
epoch=spec.get_current_epoch(state),
validator_index=validator_index,
)
voluntary_exit.signature = bls_sign(
message_hash=signing_root(voluntary_exit),
privkey=privkeys[validator_index],
domain=spec.get_domain(
state=state,
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
signed_voluntary_exit = spec.SignedVoluntaryExit(
message=voluntary_exit,
signature=bls_sign(
message_hash=hash_tree_root(voluntary_exit),
privkey=privkeys[validator_index],
domain=spec.get_domain(
state=state,
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
)
)
)
# Add to state via block transition
initiate_exit_block = build_empty_block_for_next_slot(spec, state)
initiate_exit_block.body.voluntary_exits.append(voluntary_exit)
sign_block(spec, state, initiate_exit_block)
state_transition_and_sign_block(spec, state, initiate_exit_block)
initiate_exit_block.body.voluntary_exits.append(signed_voluntary_exit)
signed_initiate_exit_block = state_transition_and_sign_block(spec, state, initiate_exit_block)
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
# Process within epoch transition
exit_block = build_empty_block_for_next_slot(spec, state)
exit_block.slot += spec.SLOTS_PER_EPOCH
sign_block(spec, state, exit_block)
state_transition_and_sign_block(spec, state, exit_block)
exit_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
signed_exit_block = state_transition_and_sign_block(spec, state, exit_block)
yield 'blocks', [initiate_exit_block, exit_block]
yield 'blocks', [signed_initiate_exit_block, signed_exit_block]
yield 'post', state
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
@@ -391,12 +460,10 @@ def test_balance_driven_status_transitions(spec, state):
yield 'pre', state
# trigger epoch transition
block = build_empty_block_for_next_slot(spec, state)
block.slot += spec.SLOTS_PER_EPOCH
sign_block(spec, state, block)
state_transition_and_sign_block(spec, state, block)
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', state
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
@@ -410,11 +477,10 @@ def test_historical_batch(spec, state):
yield 'pre', state
block = build_empty_block_for_next_slot(spec, state, signed=True)
sign_block(spec, state, block)
state_transition_and_sign_block(spec, state, block)
block = build_empty_block_for_next_slot(spec, state)
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [block]
yield 'blocks', [signed_block]
yield 'post', state
assert state.slot == block.slot
@@ -430,7 +496,6 @@ def test_eth1_data_votes_consensus(spec, state):
return
offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1)
sign_block(spec, state, offset_block)
state_transition_and_sign_block(spec, state, offset_block)
yield 'pre', state
@@ -444,9 +509,8 @@ def test_eth1_data_votes_consensus(spec, state):
block = build_empty_block_for_next_slot(spec, state)
# wait for over 50% for A, then start voting B
block.body.eth1_data.block_hash = b if i * 2 > spec.SLOTS_PER_ETH1_VOTING_PERIOD else a
sign_block(spec, state, block)
state_transition_and_sign_block(spec, state, block)
blocks.append(block)
signed_block = state_transition_and_sign_block(spec, state, block)
blocks.append(signed_block)
assert len(state.eth1_data_votes) == spec.SLOTS_PER_ETH1_VOTING_PERIOD
assert state.eth1_data.block_hash == a
@@ -454,9 +518,8 @@ def test_eth1_data_votes_consensus(spec, state):
# transition to next eth1 voting period
block = build_empty_block_for_next_slot(spec, state)
block.body.eth1_data.block_hash = c
sign_block(spec, state, block)
state_transition_and_sign_block(spec, state, block)
blocks.append(block)
signed_block = state_transition_and_sign_block(spec, state, block)
blocks.append(signed_block)
yield 'blocks', blocks
yield 'post', state
@@ -477,7 +540,6 @@ def test_eth1_data_votes_no_consensus(spec, state):
pre_eth1_hash = state.eth1_data.block_hash
offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1)
sign_block(spec, state, offset_block)
state_transition_and_sign_block(spec, state, offset_block)
yield 'pre', state
@@ -490,9 +552,8 @@ def test_eth1_data_votes_no_consensus(spec, state):
block = build_empty_block_for_next_slot(spec, state)
# wait for precisely 50% for A, then start voting B for other 50%
block.body.eth1_data.block_hash = b if i * 2 >= spec.SLOTS_PER_ETH1_VOTING_PERIOD else a
sign_block(spec, state, block)
state_transition_and_sign_block(spec, state, block)
blocks.append(block)
signed_block = state_transition_and_sign_block(spec, state, block)
blocks.append(signed_block)
assert len(state.eth1_data_votes) == spec.SLOTS_PER_ETH1_VOTING_PERIOD
assert state.eth1_data.block_hash == pre_eth1_hash

View File

@@ -155,10 +155,3 @@ def hash_tree_root(obj: SSZValue):
return mix_in_length(merkleize_chunks(leaves, limit=chunk_count(obj.type())), len(obj))
else:
return merkleize_chunks(leaves)
def signing_root(obj: Container):
# ignore last field
fields = [field for field in obj][:-1]
leaves = [hash_tree_root(f) for f in fields]
return merkleize_chunks(chunkify(b''.join(leaves)))

View File

@@ -156,10 +156,6 @@ class Container(Series, metaclass=SSZType):
from .ssz_impl import hash_tree_root
return hash_tree_root(self)
def signing_root(self):
from .ssz_impl import signing_root
return signing_root(self)
def __setattr__(self, name, value):
if name not in self.__class__.__annotations__:
raise AttributeError("Cannot change non-existing SSZ-container attribute")

View File

@@ -2,6 +2,6 @@
pytest>=4.4
../config_helpers
flake8==3.7.7
mypy==0.701
mypy==0.750
pytest-cov
pytest-xdist