mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-02-20 14:28:22 +00:00
Merge branch 'dev' into phase1rebase
This commit is contained in:
commit
c9f52d0099
@ -48,13 +48,13 @@ commands:
|
||||
description: "Restore the cache with deposit_contract keys"
|
||||
steps:
|
||||
- restore_cached_venv:
|
||||
venv_name: v6-deposit-contract
|
||||
venv_name: v7-deposit-contract
|
||||
reqs_checksum: cache-{{ checksum "test_libs/pyspec/requirements.txt" }}-{{ checksum "deposit_contract/requirements-testing.txt" }}
|
||||
save_deposit_contract_cached_venv:
|
||||
description: Save a venv into a cache with deposit_contract keys"
|
||||
steps:
|
||||
- save_cached_venv:
|
||||
venv_name: v6-deposit-contract
|
||||
venv_name: v7-deposit-contract
|
||||
reqs_checksum: cache-{{ checksum "test_libs/pyspec/requirements.txt" }}-{{ checksum "deposit_contract/requirements-testing.txt" }}
|
||||
venv_path: ./deposit_contract/venv
|
||||
jobs:
|
||||
@ -103,6 +103,24 @@ 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
|
||||
codespell:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
working_directory: ~/specs-repo
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Check codespell
|
||||
command: pip install codespell --user && make codespell
|
||||
lint:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
@ -148,6 +166,8 @@ workflows:
|
||||
- test:
|
||||
requires:
|
||||
- install_pyspec_test
|
||||
- table_of_contents
|
||||
- codespell
|
||||
- lint:
|
||||
requires:
|
||||
- test
|
||||
|
21
Makefile
21
Makefile
@ -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,17 @@ 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
|
||||
|
||||
codespell:
|
||||
codespell . --skip ./.git -I .codespell-whitelist
|
||||
|
||||
lint: $(PY_SPEC_ALL_TARGETS)
|
||||
cd $(PY_SPEC_DIR); . venv/bin/activate; \
|
||||
flake8 --ignore=E252,W504,W503 --max-line-length=120 ./eth2spec \
|
||||
|
@ -142,6 +142,7 @@ DOMAIN_BEACON_ATTESTER: 0x01000000
|
||||
DOMAIN_RANDAO: 0x02000000
|
||||
DOMAIN_DEPOSIT: 0x03000000
|
||||
DOMAIN_VOLUNTARY_EXIT: 0x04000000
|
||||
DOMAIN_CUSTODY_BIT_CHALLENGE: 0x06000000
|
||||
DOMAIN_SHARD_PROPOSER: 0x80000000
|
||||
DOMAIN_SHARD_ATTESTER: 0x81000000
|
||||
DOMAIN_SHARD_PROPOSAL: 0x80000000
|
||||
DOMAIN_SHARD_COMMITTEE: 0x81000000
|
||||
DOMAIN_LIGHT_CLIENT: 0x82000000
|
||||
DOMAIN_CUSTODY_BIT_SLASHING: 0x83000000
|
||||
|
@ -145,9 +145,10 @@ DOMAIN_BEACON_ATTESTER: 0x01000000
|
||||
DOMAIN_RANDAO: 0x02000000
|
||||
DOMAIN_DEPOSIT: 0x03000000
|
||||
DOMAIN_VOLUNTARY_EXIT: 0x04000000
|
||||
DOMAIN_CUSTODY_BIT_CHALLENGE: 0x06000000
|
||||
DOMAIN_SHARD_PROPOSER: 0x80000000
|
||||
DOMAIN_SHARD_ATTESTER: 0x81000000
|
||||
DOMAIN_SHARD_PROPOSAL: 0x80000000
|
||||
DOMAIN_SHARD_COMMITTEE: 0x81000000
|
||||
DOMAIN_LIGHT_CLIENT: 0x82000000
|
||||
DOMAIN_CUSTODY_BIT_SLASHING: 0x83000000
|
||||
|
||||
|
||||
# Phase 1
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
||||
# Vyper target 0.1.0b13
|
||||
# Vyper target 0.1.0b13.hotfix1761
|
||||
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
|
||||
|
@ -1,5 +1,5 @@
|
||||
eth-tester[py-evm]==0.1.0b39
|
||||
vyper==0.1.0b13
|
||||
git+https://github.com/vyperlang/vyper@1761-HOTFIX-v0.1.0-beta.13
|
||||
web3==5.0.0b2
|
||||
pytest==3.6.1
|
||||
../test_libs/pyspec
|
||||
|
@ -85,7 +85,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]
|
||||
@ -129,8 +129,6 @@ def objects_to_spec(functions: Dict[str, str],
|
||||
del functions[k]
|
||||
functions_spec = '\n\n'.join(functions.values())
|
||||
for k in list(constants.keys()):
|
||||
if k.startswith('DOMAIN_'):
|
||||
constants[k] = f"DomainType(({constants[k]}).to_bytes(length=4, byteorder='little'))"
|
||||
if k == "BLS12_381_Q":
|
||||
constants[k] += " # noqa: E501"
|
||||
constants_spec = '\n'.join(map(lambda x: '%s = %s' % (x, constants[x]), constants))
|
||||
|
@ -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
|
||||
|
@ -4,119 +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)
|
||||
- [`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_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
|
||||
@ -178,7 +182,7 @@ The following values are (non-configurable) constants used throughout the specif
|
||||
| `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `2**14` (= 16,384) |
|
||||
| `MIN_GENESIS_TIME` | `1578009600` (Jan 3, 2020) |
|
||||
|
||||
- For the safety of committees, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
|
||||
- For the safety of committees, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](http://web.archive.org/web/20190504131341/https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
|
||||
|
||||
### Gwei values
|
||||
|
||||
@ -195,7 +199,7 @@ The following values are (non-configurable) constants used throughout the specif
|
||||
| - | - |
|
||||
| `GENESIS_SLOT` | `Slot(0)` |
|
||||
| `GENESIS_EPOCH` | `Epoch(0)` |
|
||||
| `BLS_WITHDRAWAL_PREFIX` | `Bytes1(b'\x00')` |
|
||||
| `BLS_WITHDRAWAL_PREFIX` | `Bytes1('0x00')` |
|
||||
|
||||
### Time parameters
|
||||
|
||||
@ -245,15 +249,13 @@ The following values are (non-configurable) constants used throughout the specif
|
||||
|
||||
### Domain types
|
||||
|
||||
The following types are defined, mapping into `DomainType` (little endian):
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `DOMAIN_BEACON_PROPOSER` | `0` |
|
||||
| `DOMAIN_BEACON_ATTESTER` | `1` |
|
||||
| `DOMAIN_RANDAO` | `2` |
|
||||
| `DOMAIN_DEPOSIT` | `3` |
|
||||
| `DOMAIN_VOLUNTARY_EXIT` | `4` |
|
||||
| `DOMAIN_BEACON_PROPOSER` | `DomainType('0x00000000')` |
|
||||
| `DOMAIN_BEACON_ATTESTER` | `DomainType('0x01000000')` |
|
||||
| `DOMAIN_RANDAO` | `DomainType('0x02000000')` |
|
||||
| `DOMAIN_DEPOSIT` | `DomainType('0x03000000')` |
|
||||
| `DOMAIN_VOLUNTARY_EXIT` | `DomainType('0x04000000')` |
|
||||
|
||||
## Containers
|
||||
|
||||
@ -484,7 +486,7 @@ class BeaconState(Container):
|
||||
|
||||
### Signed envelopes
|
||||
|
||||
Some messages in the protocol are wrapped in an envelop to better facilitate adding/pruning the signature and to `hash_tree_root` the `message` separate from the signature.
|
||||
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`
|
||||
|
||||
@ -556,7 +558,7 @@ def int_to_bytes(n: uint64, length: uint64) -> bytes:
|
||||
```python
|
||||
def bytes_to_int(data: bytes) -> uint64:
|
||||
"""
|
||||
Return the integer deserialization of ``data`` intepreted as ``ENDIANNESS``-endian.
|
||||
Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian.
|
||||
"""
|
||||
return int.from_bytes(data, ENDIANNESS)
|
||||
```
|
||||
@ -591,6 +593,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
|
||||
@ -628,8 +658,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(
|
||||
@ -1296,26 +1326,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
|
||||
@ -1475,6 +1501,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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
@ -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 tree 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
|
||||
|
||||
@ -209,7 +280,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
||||
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[hash_tree_root(block)] = block
|
||||
# Check block is a descendant of the finalized block
|
||||
@ -226,7 +297,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
||||
|
||||
# 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
|
||||
|
||||
@ -252,12 +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]
|
||||
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
|
||||
@ -272,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)
|
||||
|
@ -1,3 +1,68 @@
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
||||
|
||||
- [Ethereum 2.0 Phase 1 -- The Beacon Chain for Shards](#ethereum-20-phase-1----the-beacon-chain-for-shards)
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Introduction](#introduction)
|
||||
- [Custom types](#custom-types)
|
||||
- [Configuration](#configuration)
|
||||
- [Misc](#misc)
|
||||
- [Updated containers](#updated-containers)
|
||||
- [Extended `AttestationData`](#extended-attestationdata)
|
||||
- [Extended `Attestation`](#extended-attestation)
|
||||
- [Extended `PendingAttestation`](#extended-pendingattestation)
|
||||
- [Extended `Validator`](#extended-validator)
|
||||
- [Extended `BeaconBlockBody`](#extended-beaconblockbody)
|
||||
- [Extended `BeaconBlock`](#extended-beaconblock)
|
||||
- [Extended `SignedBeaconBlock`](#extended-signedbeaconblock)
|
||||
- [Extended `BeaconState`](#extended-beaconstate)
|
||||
- [New containers](#new-containers)
|
||||
- [`ShardBlockWrapper`](#shardblockwrapper)
|
||||
- [`ShardSignableHeader`](#shardsignableheader)
|
||||
- [`ShardState`](#shardstate)
|
||||
- [`ShardTransition`](#shardtransition)
|
||||
- [`AttestationAndCommittee`](#attestationandcommittee)
|
||||
- [`CompactCommittee`](#compactcommittee)
|
||||
- [`AttestationCustodyBitWrapper`](#attestationcustodybitwrapper)
|
||||
- [Helper functions](#helper-functions)
|
||||
- [Crypto](#crypto)
|
||||
- [`bls_verify_multiple`](#bls_verify_multiple)
|
||||
- [Misc](#misc-1)
|
||||
- [`pack_compact_validator`](#pack_compact_validator)
|
||||
- [`committee_to_compact_committee`](#committee_to_compact_committee)
|
||||
- [`chunks_to_body_root`](#chunks_to_body_root)
|
||||
- [Beacon state accessors](#beacon-state-accessors)
|
||||
- [`get_previous_slot`](#get_previous_slot)
|
||||
- [`get_online_validator_indices`](#get_online_validator_indices)
|
||||
- [`get_shard_committee`](#get_shard_committee)
|
||||
- [`get_shard_proposer_index`](#get_shard_proposer_index)
|
||||
- [`get_light_client_committee`](#get_light_client_committee)
|
||||
- [`get_indexed_attestation`](#get_indexed_attestation)
|
||||
- [`get_updated_gasprice`](#get_updated_gasprice)
|
||||
- [`get_start_shard`](#get_start_shard)
|
||||
- [`get_shard`](#get_shard)
|
||||
- [`get_next_slot_for_shard`](#get_next_slot_for_shard)
|
||||
- [`get_offset_slots`](#get_offset_slots)
|
||||
- [Predicates](#predicates)
|
||||
- [Updated `is_valid_indexed_attestation`](#updated-is_valid_indexed_attestation)
|
||||
- [Block processing](#block-processing)
|
||||
- [Operations](#operations)
|
||||
- [New Attestation processing](#new-attestation-processing)
|
||||
- [`validate_attestation`](#validate_attestation)
|
||||
- [`apply_shard_transition`](#apply_shard_transition)
|
||||
- [`process_crosslink_for_shard`](#process_crosslink_for_shard)
|
||||
- [`process_crosslinks`](#process_crosslinks)
|
||||
- [`process_attestations`](#process_attestations)
|
||||
- [Shard transition false positives](#shard-transition-false-positives)
|
||||
- [Light client processing](#light-client-processing)
|
||||
- [Epoch transition](#epoch-transition)
|
||||
- [Custody game updates](#custody-game-updates)
|
||||
- [Online-tracking](#online-tracking)
|
||||
- [Light client committee updates](#light-client-committee-updates)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
# Ethereum 2.0 Phase 1 -- The Beacon Chain for Shards
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
@ -47,9 +112,9 @@ Configuration is not namespaced. Instead it is strictly an extension;
|
||||
| `MAX_GASPRICE` | `Gwei(2**14)` (= 16,384) | Gwei | |
|
||||
| `MIN_GASPRICE` | `Gwei(2**5)` (= 32) | Gwei | |
|
||||
| `GASPRICE_ADJUSTMENT_COEFFICIENT` | `2**3` (= 8) | |
|
||||
| `DOMAIN_LIGHT_CLIENT` | `192` | |
|
||||
| `DOMAIN_SHARD_COMMITTEE` | `192` | |
|
||||
| `DOMAIN_SHARD_PROPOSAL` | `193` | |
|
||||
| `DOMAIN_SHARD_PROPOSAL` | `DomainType('0x80000000')` | |
|
||||
| `DOMAIN_SHARD_COMMITTEE` | `DomainType('0x81000000')` | |
|
||||
| `DOMAIN_LIGHT_CLIENT` | `DomainType('0x82000000')` | |
|
||||
|
||||
## Updated containers
|
||||
|
||||
|
@ -4,39 +4,39 @@
|
||||
|
||||
## 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)
|
||||
- [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)
|
||||
- [Data structures](#data-structures)
|
||||
- [New beacon operations](#new-beacon-operations)
|
||||
- [`CustodySlashing`](#custody-slashing)
|
||||
- [`CustodyKeyReveal`](#custodykeyreveal)
|
||||
- [`EarlyDerivedSecretReveal`](#earlyderivedsecretreveal)
|
||||
- [Helpers](#helpers)
|
||||
- [`legendre_bit`](#legendre_bit)
|
||||
- [`get_custody_atoms`](#get_custody_atoms)
|
||||
- [`compute_custody_bit`](#compute_custody_bit)
|
||||
- [`get_randao_epoch_for_custody_period`](#get_randao_epoch_for_custody_period)
|
||||
- [`get_custody_period_for_validator`](#get_custody_period_for_validator)
|
||||
- [Per-block processing](#per-block-processing)
|
||||
- [Custody Game Operations](#custody-game-operations)
|
||||
- [Custody key reveals](#custody-key-reveals)
|
||||
- [Early derived secret reveals](#early-derived-secret-reveals)
|
||||
- [Custody Slashings](#custody-slashings)
|
||||
- [Per-epoch processing](#per-epoch-processing)
|
||||
- [Handling of reveal deadlines](#handling-of-reveal-deadlines)
|
||||
- [Final updates](#final-updates)
|
||||
|
||||
<!-- /TOC -->
|
||||
- [Introduction](#introduction)
|
||||
- [Constants](#constants)
|
||||
- [Misc](#misc)
|
||||
- [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)
|
||||
- [Data structures](#data-structures)
|
||||
- [New Beacon Chain operations](#new-beacon-chain-operations)
|
||||
- [`CustodySlashing`](#custodyslashing)
|
||||
- [`SignedCustodySlashing`](#signedcustodyslashing)
|
||||
- [`CustodyKeyReveal`](#custodykeyreveal)
|
||||
- [`EarlyDerivedSecretReveal`](#earlyderivedsecretreveal)
|
||||
- [Helpers](#helpers)
|
||||
- [`legendre_bit`](#legendre_bit)
|
||||
- [`custody_atoms`](#custody_atoms)
|
||||
- [`compute_custody_bit`](#compute_custody_bit)
|
||||
- [`get_randao_epoch_for_custody_period`](#get_randao_epoch_for_custody_period)
|
||||
- [`get_custody_period_for_validator`](#get_custody_period_for_validator)
|
||||
- [Per-block processing](#per-block-processing)
|
||||
- [Custody Game Operations](#custody-game-operations)
|
||||
- [Custody key reveals](#custody-key-reveals)
|
||||
- [Early derived secret reveals](#early-derived-secret-reveals)
|
||||
- [Custody Slashings](#custody-slashings)
|
||||
- [Per-epoch processing](#per-epoch-processing)
|
||||
- [Handling of reveal deadlines](#handling-of-reveal-deadlines)
|
||||
- [Final updates](#final-updates)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
## Introduction
|
||||
|
||||
@ -81,8 +81,7 @@ The following types are defined, mapping into `DomainType` (little endian):
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `DOMAIN_CUSTODY_BIT_SLASHING` | `6` |
|
||||
|
||||
| `DOMAIN_CUSTODY_BIT_SLASHING` | `DomainType('0x83000000')` |
|
||||
|
||||
## Data structures
|
||||
|
||||
|
@ -1,3 +1,16 @@
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
||||
|
||||
- [Ethereum 2.0 Phase 1 -- Shard Transition and Fraud Proofs](#ethereum-20-phase-1----shard-transition-and-fraud-proofs)
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Introduction](#introduction)
|
||||
- [Fraud proofs](#fraud-proofs)
|
||||
- [Shard state transition function](#shard-state-transition-function)
|
||||
- [Honest committee member behavior](#honest-committee-member-behavior)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
# Ethereum 2.0 Phase 1 -- Shard Transition and Fraud Proofs
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
|
@ -1,3 +1,17 @@
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
||||
|
||||
- [Ethereum 2.0 Phase 1 -- From Phase 0 to Phase 1](#ethereum-20-phase-1----from-phase-0-to-phase-1)
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Introduction](#introduction)
|
||||
- [Configuration](#configuration)
|
||||
- [Fork to Phase 1](#fork-to-phase-1)
|
||||
- [Fork trigger.](#fork-trigger)
|
||||
- [Upgrading the state](#upgrading-the-state)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
# Ethereum 2.0 Phase 1 -- From Phase 0 to Phase 1
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -18,23 +18,89 @@ 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)
|
||||
- [Attestation subnet bitfield](#attestation-subnet-bitfield)
|
||||
- [Interop](#interop-5)
|
||||
- [Mainnet](#mainnet-5)
|
||||
- [Topic advertisement](#topic-advertisement)
|
||||
- [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 we overriding the default libp2p pubsub `message-id`?](#why-are-we-overriding-the-default-libp2p-pubsub-message-id)
|
||||
- [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 -->
|
||||
@ -147,6 +213,13 @@ Topics are plain UTF-8 strings and are encoded on the wire as determined by prot
|
||||
|
||||
Each gossipsub [message](https://github.com/libp2p/go-libp2p-pubsub/blob/master/pb/rpc.proto#L17-L24) has a maximum size of `GOSSIP_MAX_SIZE`. Clients MUST reject (fail validation) messages that are over this size limit. Likewise, clients MUST NOT emit or propagate messages larger than this limit.
|
||||
|
||||
The `message-id` of a gossipsub message MUST be:
|
||||
|
||||
```python
|
||||
message-id: base64(SHA256(message.data))
|
||||
```
|
||||
where `base64` is the [URL-safe base64 alphabet](https://tools.ietf.org/html/rfc4648#section-3.2) with padding characters omitted.
|
||||
|
||||
The payload is carried in the `data` field of a gossipsub message, and varies depending on the topic:
|
||||
|
||||
| Topic | Message Type |
|
||||
@ -356,7 +429,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.
|
||||
@ -372,6 +445,8 @@ Clients SHOULD immediately disconnect from one another following the handshake a
|
||||
|
||||
Once the handshake completes, the client with the lower `finalized_epoch` or `head_slot` (if the clients have equal `finalized_epoch`s) SHOULD request beacon blocks from its counterparty via the `BeaconBlocksByRange` request.
|
||||
|
||||
*Note*: Under abnormal network condition or after some rounds of `BeaconBlocksByRange` requests, the client might need to send `Status` request again to learn if the peer has a higher head. Implementers are free to implement such behavior in their own way.
|
||||
|
||||
#### Goodbye
|
||||
|
||||
**Protocol ID:** ``/eth2/beacon_chain/req/goodbye/1/``
|
||||
@ -492,6 +567,14 @@ The Ethereum Node Record (ENR) for an Ethereum 2.0 client MUST contain the follo
|
||||
|
||||
Specifications of these parameters can be found in the [ENR Specification](http://eips.ethereum.org/EIPS/eip-778).
|
||||
|
||||
#### Attestation subnet bitfield
|
||||
|
||||
The ENR MAY contain an entry (`attnets`) signifying the attestation subnet bitfield with the following form to more easily discover peers participating in particular attestation gossip subnets.
|
||||
|
||||
| Key | Value |
|
||||
|:-------------|:-------------------------------------------------|
|
||||
| `attnets` | SSZ `Bitvector[ATTESTATION_SUBNET_COUNT]` |
|
||||
|
||||
#### Interop
|
||||
|
||||
In the interoperability testnet, all peers will support all capabilities defined in this document (gossip, full Req/Resp suite, discovery protocol), therefore the ENR record does not need to carry Eth2 capability information, as it would be superfluous.
|
||||
@ -504,13 +587,11 @@ On mainnet, ENRs MUST include a structure enumerating the capabilities offered b
|
||||
|
||||
### Topic advertisement
|
||||
|
||||
#### Interop
|
||||
|
||||
This feature will not be used in the interoperability testnet.
|
||||
|
||||
#### Mainnet
|
||||
|
||||
In mainnet, we plan to use discv5’s topic advertisement feature as a rendezvous facility for peers on shards (thus subscribing to the relevant gossipsub topics).
|
||||
discv5's topic advertisement feature is not expected to be ready for mainnet launch of Phase 0.
|
||||
|
||||
Once this feature is built out and stable, we expect to use topic advertisement as a rendezvous facility for peers on shards. Until then, the ENR [attestation subnet bitfield](#attestation-subnet-bitfield) will be used for discovery of peers on particular subnets.
|
||||
|
||||
# Design decision rationale
|
||||
|
||||
@ -567,7 +648,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?
|
||||
|
||||
@ -673,6 +754,16 @@ No security or privacy guarantees are lost as a result of choosing plaintext top
|
||||
|
||||
Furthermore, the Eth2 topic names are shorter than their digest equivalents (assuming SHA-256 hash), so hashing topics would bloat messages unnecessarily.
|
||||
|
||||
## Why are we overriding the default libp2p pubsub `message-id`?
|
||||
|
||||
For our current purposes, there is no need to address messages based on source peer, and it seems likely we might even override the message `from` to obfuscate the peer. By overriding the default `message-id` to use content-addressing we can filter unnecessary duplicates before hitting the application layer.
|
||||
|
||||
Some examples of where messages could be duplicated:
|
||||
|
||||
* A validator client connected to multiple beacon nodes publishing duplicate gossip messages
|
||||
* Attestation aggregation strategies where clients partially aggregate attestations and propagate them. Partial aggregates could be duplicated
|
||||
* Clients re-publishing seen messages
|
||||
|
||||
### Why are there `ATTESTATION_SUBNET_COUNT` attestation subnets?
|
||||
|
||||
Depending on the number of validators, it may be more efficient to group shard subnets and might provide better stability for the gossipsub channel. The exact grouping will be dependent on more involved network tests. This constant allows for more flexibility in setting up the network topology for attestation aggregation (as aggregation should happen on each subnet). The value is currently set to to be equal `MAX_COMMITTEES_PER_SLOT` until network tests indicate otherwise.
|
||||
@ -699,9 +790,9 @@ The prohibition of unverified-block-gossiping extends to nodes that cannot verif
|
||||
|
||||
### How are we going to discover peers in a gossipsub topic?
|
||||
|
||||
Via discv5 topics. ENRs should not be used for this purpose, as they store identity, location, and capability information, not volatile [advertisements](#topic-advertisement).
|
||||
In Phase 0, peers for attestation subnets will be found using the `attnets` entry in the ENR.
|
||||
|
||||
In the interoperability testnet, all peers will be subscribed to all global beacon chain topics, so discovering peers in specific shard topics will be unnecessary.
|
||||
Although this method will be sufficient for early phases of Eth2, we aim to use the more appropriate discv5 topics for this and other similar tasks in the future. ENRs should ultimately not be used for this purpose. They are best suited to store identity, location, and capability information, rather than more volatile advertisements.
|
||||
|
||||
## Req/Resp
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
@ -99,8 +101,8 @@ This document describes the shard transition function (data layer only) and the
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `DOMAIN_SHARD_PROPOSER` | `128` |
|
||||
| `DOMAIN_SHARD_ATTESTER` | `129` |
|
||||
| `DOMAIN_SHARD_PROPOSER` | `DomainType('0x80000000')` |
|
||||
| `DOMAIN_SHARD_ATTESTER` | `DomainType('0x81000000')` |
|
||||
|
||||
## Containers
|
||||
|
||||
|
@ -4,30 +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)
|
||||
- [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
|
||||
|
@ -37,7 +37,7 @@ 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
|
||||
@ -61,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`
|
||||
|
||||
@ -151,7 +150,7 @@ Template:
|
||||
|
||||
Data:
|
||||
|
||||
{container name}: Any of the container names listed below (exluding the `(Container)` python super type)
|
||||
{container name}: Any of the container names listed below (excluding the `(Container)` python super type)
|
||||
```
|
||||
|
||||
```python
|
||||
|
@ -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
|
||||
@ -194,8 +197,8 @@ The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahe
|
||||
Specifically a validator should:
|
||||
* Call `get_committee_assignment(state, next_epoch, validator_index)` when checking for next epoch assignments.
|
||||
* Join the pubsub topic -- `committee_index{committee_index % ATTESTATION_SUBNET_COUNT}_beacon_attestation`.
|
||||
* If any current peers are subscribed to the topic, the validator simply sends `subscribe` messages for the new topic.
|
||||
* If no current peers are subscribed to the topic, the validator must discover new peers on this topic. If "topic discovery" is available, use topic discovery to find peers that advertise subscription to the topic. If not, "guess and check" by connecting with a number of random new peers, persisting connections with peers subscribed to the topic and (potentially) dropping the new peers otherwise.
|
||||
* For any current peer subscribed to the topic, the validator simply sends a `subscribe` message for the new topic.
|
||||
* If an _insufficient_ number of current peers are subscribed to the topic, the validator must discover new peers on this topic. Via the discovery protocol, find peers with an ENR containing the `attnets` entry such that `ENR["attnets"][committee_index % ATTESTATION_SUBNET_COUNT] == True`.
|
||||
|
||||
## Beacon chain responsibilities
|
||||
|
||||
@ -440,7 +443,11 @@ Where
|
||||
|
||||
## Phase 0 attestation subnet stability
|
||||
|
||||
Because Phase 0 does not have shards and thus does not have Shard Committees, there is no stable backbone to the attestation subnets (`committee_index{subnet_id}_beacon_attestation`). To provide this stability, each validator must randomly select and remain subscribed to `RANDOM_SUBNETS_PER_VALIDATOR` attestation subnets. The lifetime of each random subscription should be a random number of epochs between `EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION` and `2 * EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION]`.
|
||||
Because Phase 0 does not have shards and thus does not have Shard Committees, there is no stable backbone to the attestation subnets (`committee_index{subnet_id}_beacon_attestation`). To provide this stability, each validator must:
|
||||
|
||||
* Randomly select and remain subscribed to `RANDOM_SUBNETS_PER_VALIDATOR` attestation subnets
|
||||
* Maintain advertisement of the randomly selected subnets in their node's ENR `attnets` entry by setting the randomly selected `subnet_id` bits to `True` (e.g. `ENR["attnets"][subnet_id] = True`) for all persistent attestation subnets
|
||||
* Set the lifetime of each random subscription to a random number of epochs between `EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION` and `2 * EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION]`. At the end of life for a subscription, select a new random subnet, update subnet subscriptions, and publish an updated ENR
|
||||
|
||||
## How to avoid slashing
|
||||
|
||||
|
@ -1,7 +1,11 @@
|
||||
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, signed_block):
|
||||
@ -112,3 +116,79 @@ def test_shorter_chain_but_heavier_weight(spec, state):
|
||||
add_attestation_to_store(spec, store, short_attestation)
|
||||
|
||||
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
|
||||
|
@ -84,6 +84,29 @@ def test_on_attestation_past_epoch(spec, state):
|
||||
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):
|
||||
|
@ -168,7 +168,7 @@ def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state):
|
||||
|
||||
@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
|
||||
@ -187,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)
|
||||
signed_block = 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, signed_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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
||||
# The signature will be valid for a single occurrence. 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)
|
||||
|
||||
# The signature will be valid for a single occurrence. 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):
|
||||
|
@ -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))
|
||||
)
|
||||
|
@ -155,7 +155,7 @@ def test_duplicate_attestation(spec, state):
|
||||
next_epoch(spec, single_state)
|
||||
next_epoch(spec, dup_state)
|
||||
|
||||
# Run non-duplicate inclusion rewards for comparision. Do not yield test vectors
|
||||
# Run non-duplicate inclusion rewards for comparison. Do not yield test vectors
|
||||
for _ in run_process_rewards_and_penalties(spec, single_state):
|
||||
pass
|
||||
|
||||
|
@ -3,7 +3,7 @@ from copy import deepcopy
|
||||
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.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
|
||||
@ -83,7 +83,7 @@ def test_invalid_state_root(spec, state):
|
||||
|
||||
expect_assertion_error(lambda: spec.state_transition(state, signed_block))
|
||||
|
||||
yield 'blocks', [block]
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@ -91,6 +91,8 @@ def test_invalid_state_root(spec, state):
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_zero_block_sig(spec, state):
|
||||
yield 'pre', 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))
|
||||
@ -103,6 +105,8 @@ def test_zero_block_sig(spec, state):
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_invalid_block_sig(spec, state):
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
invalid_signed_block = spec.SignedBeaconBlock(
|
||||
message=block,
|
||||
@ -253,6 +257,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):
|
||||
|
@ -451,10 +451,15 @@ class BaseBytes(bytes, Elements, metaclass=BytesType):
|
||||
@classmethod
|
||||
def extract_args(cls, *args):
|
||||
x = args
|
||||
if len(x) == 1 and isinstance(x[0], (GeneratorType, bytes)):
|
||||
if len(x) == 1 and isinstance(x[0], (GeneratorType, bytes, str)):
|
||||
x = x[0]
|
||||
if isinstance(x, bytes): # Includes BytesLike
|
||||
return x
|
||||
if isinstance(x, str):
|
||||
if x[:2] == '0x':
|
||||
return bytes.fromhex(x[2:])
|
||||
else:
|
||||
return bytes.fromhex(x)
|
||||
else:
|
||||
return bytes(x) # E.g. GeneratorType put into bytes.
|
||||
|
||||
|
@ -2,6 +2,6 @@
|
||||
pytest>=4.4
|
||||
../config_helpers
|
||||
flake8==3.7.7
|
||||
mypy==0.701
|
||||
mypy==0.750
|
||||
pytest-cov
|
||||
pytest-xdist
|
||||
|
@ -1,6 +1,6 @@
|
||||
eth-utils>=1.3.0,<2
|
||||
eth-typing>=2.1.0,<3.0.0
|
||||
pycryptodome==3.7.3
|
||||
pycryptodome==3.9.4
|
||||
py_ecc==1.7.1
|
||||
dataclasses==0.6
|
||||
ssz==0.1.3
|
||||
|
@ -7,7 +7,7 @@ setup(
|
||||
install_requires=[
|
||||
"eth-utils>=1.3.0,<2",
|
||||
"eth-typing>=2.1.0,<3.0.0",
|
||||
"pycryptodome==3.7.3",
|
||||
"pycryptodome==3.9.4",
|
||||
"py_ecc==1.7.1",
|
||||
"ssz==0.1.3",
|
||||
"dataclasses==0.6",
|
||||
|
Loading…
x
Reference in New Issue
Block a user