commit
7748c70c15
|
@ -44,32 +44,19 @@ commands:
|
|||
venv_name: v22-pyspec
|
||||
reqs_checksum: cache-{{ checksum "setup.py" }}
|
||||
venv_path: ./venv
|
||||
restore_deposit_contract_compiler_cached_venv:
|
||||
description: "Restore the venv from cache for the deposit contract compiler"
|
||||
steps:
|
||||
- restore_cached_venv:
|
||||
venv_name: v23-deposit-contract-compiler
|
||||
reqs_checksum: cache-{{ checksum "deposit_contract/compiler/requirements.txt" }}
|
||||
save_deposit_contract_compiler_cached_venv:
|
||||
description: "Save the venv to cache for later use of the deposit contract compiler"
|
||||
steps:
|
||||
- save_cached_venv:
|
||||
venv_name: v23-deposit-contract-compiler
|
||||
reqs_checksum: cache-{{ checksum "deposit_contract/compiler/requirements.txt" }}
|
||||
venv_path: ./deposit_contract/compiler/venv
|
||||
restore_deposit_contract_tester_cached_venv:
|
||||
description: "Restore the venv from cache for the deposit contract tester"
|
||||
steps:
|
||||
- restore_cached_venv:
|
||||
venv_name: v22-deposit-contract-tester
|
||||
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "deposit_contract/tester/requirements.txt" }}
|
||||
venv_name: v23-deposit-contract-tester
|
||||
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "solidity_deposit_contract/web3_tester/requirements.txt" }}
|
||||
save_deposit_contract_tester_cached_venv:
|
||||
description: "Save the venv to cache for later use of the deposit contract tester"
|
||||
steps:
|
||||
- save_cached_venv:
|
||||
venv_name: v22-deposit-contract-tester
|
||||
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "deposit_contract/tester/requirements.txt" }}
|
||||
venv_path: ./deposit_contract/tester/venv
|
||||
venv_name: v23-deposit-contract-tester
|
||||
reqs_checksum: cache-{{ checksum "setup.py" }}-{{ checksum "solidity_deposit_contract/web3_tester/requirements.txt" }}
|
||||
venv_path: ./solidity_deposit_contract/web3_tester/venv
|
||||
jobs:
|
||||
checkout_specs:
|
||||
docker:
|
||||
|
@ -143,22 +130,54 @@ jobs:
|
|||
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
|
||||
- restore_pyspec_cached_venv
|
||||
- run:
|
||||
name: Run linter
|
||||
name: Run linter for pyspec
|
||||
command: make lint
|
||||
install_deposit_contract_compiler:
|
||||
docker:
|
||||
# The deposit contract compiler is pinned to python 3.7 because of the vyper version pin.
|
||||
- image: circleci/python:3.7
|
||||
working_directory: ~/specs-repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
|
||||
- restore_deposit_contract_compiler_cached_venv
|
||||
- run:
|
||||
name: Install deposit contract compiler requirements
|
||||
command: make install_deposit_contract_compiler
|
||||
- save_deposit_contract_compiler_cached_venv
|
||||
install_deposit_contract_tester:
|
||||
name: Run linter for test generators
|
||||
command: make lint_generators
|
||||
build_deposit_contract:
|
||||
docker:
|
||||
- image: ethereum/solc:0.6.11-alpine
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install build essentials
|
||||
command: |
|
||||
apk update
|
||||
apk add git make
|
||||
- run:
|
||||
name: Compile the contract
|
||||
command: |
|
||||
make compile_deposit_contract
|
||||
git diff --color --exit-code
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ./solidity_deposit_contract/deposit_contract.json
|
||||
- ./build/combined.json
|
||||
- ./solidity_deposit_contract/lib
|
||||
test_deposit_contract:
|
||||
docker:
|
||||
- image: nixorg/nix:circleci
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: nix-store-test-v2
|
||||
- attach_workspace:
|
||||
at: /tmp/
|
||||
- run:
|
||||
name: Test the contract
|
||||
command: |
|
||||
mkdir build
|
||||
cp -r /tmp/build/* build
|
||||
cp -r /tmp/solidity_deposit_contract/lib/* ./solidity_deposit_contract/lib
|
||||
cp -r /tmp/solidity_deposit_contract/deposit_contract.json ./solidity_deposit_contract/deposit_contract.json
|
||||
nix-shell --command 'make test_deposit_contract' ./solidity_deposit_contract/shell.nix
|
||||
- save_cache:
|
||||
key: nix-store-test-v2
|
||||
paths:
|
||||
- /nix
|
||||
install_deposit_contract_web3_tester:
|
||||
docker:
|
||||
- image: circleci/python:3.8
|
||||
working_directory: ~/specs-repo
|
||||
|
@ -168,20 +187,9 @@ jobs:
|
|||
- restore_deposit_contract_tester_cached_venv
|
||||
- run:
|
||||
name: Install deposit contract tester requirements
|
||||
command: make install_deposit_contract_tester
|
||||
command: make install_deposit_contract_web3_tester
|
||||
- save_deposit_contract_tester_cached_venv
|
||||
test_compile_deposit_contract:
|
||||
docker:
|
||||
- image: circleci/python:3.7
|
||||
working_directory: ~/specs-repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
|
||||
- restore_deposit_contract_compiler_cached_venv
|
||||
- run:
|
||||
name: Run deposit contract compile test
|
||||
command: make test_compile_deposit_contract
|
||||
test_deposit_contract:
|
||||
test_deposit_contract_web3_tests:
|
||||
docker:
|
||||
- image: circleci/python:3.8
|
||||
working_directory: ~/specs-repo
|
||||
|
@ -190,9 +198,8 @@ jobs:
|
|||
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
|
||||
- restore_deposit_contract_tester_cached_venv
|
||||
- run:
|
||||
name: Run deposit contract test
|
||||
command: make test_deposit_contract
|
||||
|
||||
name: Run deposit contract test with web3.py
|
||||
command: make test_deposit_contract_web3_tests
|
||||
workflows:
|
||||
version: 2.1
|
||||
test_spec:
|
||||
|
@ -209,15 +216,15 @@ workflows:
|
|||
- lint:
|
||||
requires:
|
||||
- test
|
||||
- install_deposit_contract_compiler:
|
||||
- install_deposit_contract_web3_tester:
|
||||
requires:
|
||||
- checkout_specs
|
||||
- test_compile_deposit_contract:
|
||||
- test_deposit_contract_web3_tests:
|
||||
requires:
|
||||
- install_deposit_contract_compiler
|
||||
- install_deposit_contract_tester:
|
||||
requires:
|
||||
- checkout_specs
|
||||
- install_deposit_contract_web3_tester
|
||||
build_and_test_deposit_contract:
|
||||
jobs:
|
||||
- build_deposit_contract
|
||||
- test_deposit_contract:
|
||||
requires:
|
||||
- install_deposit_contract_tester
|
||||
- build_deposit_contract
|
||||
|
|
|
@ -1 +1 @@
|
|||
*.vy linguist-language=Python
|
||||
*.sol linguist-language=Solidity
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "lib/ds-test"]
|
||||
path = solidity_deposit_contract/lib/ds-test
|
||||
url = https://github.com/dapphub/ds-test
|
51
Makefile
51
Makefile
|
@ -1,11 +1,14 @@
|
|||
SPEC_DIR = ./specs
|
||||
SSZ_DIR = ./ssz
|
||||
TEST_LIBS_DIR = ./tests/core
|
||||
TEST_GENERATORS_DIR = ./tests/generators
|
||||
PY_SPEC_DIR = $(TEST_LIBS_DIR)/pyspec
|
||||
TEST_VECTOR_DIR = ../eth2.0-spec-tests/tests
|
||||
GENERATOR_DIR = ./tests/generators
|
||||
DEPOSIT_CONTRACT_COMPILER_DIR = ./deposit_contract/compiler
|
||||
DEPOSIT_CONTRACT_TESTER_DIR = ./deposit_contract/tester
|
||||
SOLIDITY_DEPOSIT_CONTRACT_DIR = ./solidity_deposit_contract
|
||||
SOLIDITY_DEPOSIT_CONTRACT_SOURCE = ${SOLIDITY_DEPOSIT_CONTRACT_DIR}/deposit_contract.sol
|
||||
SOLIDITY_FILE_NAME = deposit_contract.json
|
||||
DEPOSIT_CONTRACT_TESTER_DIR = ${SOLIDITY_DEPOSIT_CONTRACT_DIR}/web3_tester
|
||||
CONFIGS_DIR = ./configs
|
||||
|
||||
# Collect a list of generator names
|
||||
|
@ -25,6 +28,11 @@ COV_INDEX_FILE=$(PY_SPEC_DIR)/$(COV_HTML_OUT)/index.html
|
|||
CURRENT_DIR = ${CURDIR}
|
||||
LINTER_CONFIG_FILE = $(CURRENT_DIR)/linter.ini
|
||||
|
||||
export DAPP_SKIP_BUILD:=1
|
||||
export DAPP_SRC:=$(SOLIDITY_DEPOSIT_CONTRACT_DIR)
|
||||
export DAPP_LIB:=$(SOLIDITY_DEPOSIT_CONTRACT_DIR)/lib
|
||||
export DAPP_JSON:=build/combined.json
|
||||
|
||||
.PHONY: clean partial_clean all test citest lint generate_tests pyspec install_test open_cov \
|
||||
install_deposit_contract_tester test_deposit_contract install_deposit_contract_compiler \
|
||||
compile_deposit_contract test_compile_deposit_contract check_toc
|
||||
|
@ -38,7 +46,6 @@ partial_clean:
|
|||
rm -rf .pytest_cache
|
||||
rm -f .coverage
|
||||
rm -rf $(PY_SPEC_DIR)/.pytest_cache
|
||||
rm -rf $(DEPOSIT_CONTRACT_COMPILER_DIR)/.pytest_cache
|
||||
rm -rf $(DEPOSIT_CONTRACT_TESTER_DIR)/.pytest_cache
|
||||
rm -rf $(PY_SPEC_DIR)/phase0
|
||||
rm -rf $(PY_SPEC_DIR)/phase1
|
||||
|
@ -46,7 +53,7 @@ partial_clean:
|
|||
rm -rf $(PY_SPEC_DIR)/.coverage
|
||||
rm -rf $(PY_SPEC_DIR)/test-reports
|
||||
rm -rf eth2spec.egg-info dist build
|
||||
|
||||
rm -rf build
|
||||
|
||||
clean: partial_clean
|
||||
rm -rf venv
|
||||
|
@ -86,7 +93,7 @@ find_test: pyspec
|
|||
|
||||
citest: pyspec
|
||||
mkdir -p tests/core/pyspec/test-reports/eth2spec; . venv/bin/activate; cd $(PY_SPEC_DIR); \
|
||||
python -m pytest -n 4 --disable-bls --junitxml=eth2spec/test_results.xml eth2spec
|
||||
python -m pytest -n 4 --bls-type=milagro --junitxml=eth2spec/test_results.xml eth2spec
|
||||
|
||||
open_cov:
|
||||
((open "$(COV_INDEX_FILE)" || xdg-open "$(COV_INDEX_FILE)") &> /dev/null) &
|
||||
|
@ -107,23 +114,29 @@ lint: pyspec
|
|||
flake8 --config $(LINTER_CONFIG_FILE) ./eth2spec \
|
||||
&& mypy --config-file $(LINTER_CONFIG_FILE) -p eth2spec.phase0 -p eth2spec.phase1
|
||||
|
||||
install_deposit_contract_tester:
|
||||
cd $(DEPOSIT_CONTRACT_TESTER_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt
|
||||
|
||||
test_deposit_contract:
|
||||
cd $(DEPOSIT_CONTRACT_TESTER_DIR); . venv/bin/activate; \
|
||||
python -m pytest .
|
||||
|
||||
install_deposit_contract_compiler:
|
||||
cd $(DEPOSIT_CONTRACT_COMPILER_DIR); python3.7 -m venv venv; . venv/bin/activate; pip3.7 install -r requirements.txt
|
||||
lint_generators: pyspec
|
||||
. venv/bin/activate; cd $(TEST_GENERATORS_DIR); \
|
||||
flake8 --config $(LINTER_CONFIG_FILE)
|
||||
|
||||
compile_deposit_contract:
|
||||
cd $(DEPOSIT_CONTRACT_COMPILER_DIR); . venv/bin/activate; \
|
||||
python3.7 deposit_contract/compile.py ../contracts/validator_registration.vy
|
||||
@cd $(SOLIDITY_DEPOSIT_CONTRACT_DIR)
|
||||
@git submodule update --recursive --init
|
||||
@solc --metadata-literal --optimize --optimize-runs 5000000 --bin --abi --combined-json=abi,bin,bin-runtime,srcmap,srcmap-runtime,ast,metadata,storage-layout --overwrite -o build $(SOLIDITY_DEPOSIT_CONTRACT_SOURCE) $(SOLIDITY_DEPOSIT_CONTRACT_DIR)/tests/deposit_contract.t.sol
|
||||
@/bin/echo -n '{"abi": ' > $(SOLIDITY_FILE_NAME)
|
||||
@cat build/DepositContract.abi >> $(SOLIDITY_FILE_NAME)
|
||||
@/bin/echo -n ', "bytecode": "0x' >> $(SOLIDITY_FILE_NAME)
|
||||
@cat build/DepositContract.bin >> $(SOLIDITY_FILE_NAME)
|
||||
@/bin/echo -n '"}' >> $(SOLIDITY_FILE_NAME)
|
||||
|
||||
test_compile_deposit_contract:
|
||||
cd $(DEPOSIT_CONTRACT_COMPILER_DIR); . venv/bin/activate; \
|
||||
python3.7 -m pytest .
|
||||
test_deposit_contract:
|
||||
dapp test -v --fuzz-runs 5
|
||||
|
||||
install_deposit_contract_web3_tester:
|
||||
cd $(DEPOSIT_CONTRACT_TESTER_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt
|
||||
|
||||
test_deposit_contract_web3_tests:
|
||||
cd $(DEPOSIT_CONTRACT_TESTER_DIR); . venv/bin/activate; \
|
||||
python -m pytest .
|
||||
|
||||
# Runs a generator, identified by param 1
|
||||
define run_generator
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Ethereum 2.0 Specifications
|
||||
|
||||
[![Join the chat at https://discord.gg/hpFs23p](https://img.shields.io/badge/chat-on%20discord-blue.svg)](https://discord.gg/hpFs23p) [![Join the chat at https://gitter.im/ethereum/sharding](https://badges.gitter.im/ethereum/sharding.svg)](https://gitter.im/ethereum/sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[![Join the chat at https://discord.gg/qGpsxSA](https://img.shields.io/badge/chat-on%20discord-blue.svg)](https://discord.gg/qGpsxSA) [![Join the chat at https://gitter.im/ethereum/sharding](https://badges.gitter.im/ethereum/sharding.svg)](https://gitter.im/ethereum/sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
To learn more about sharding and Ethereum 2.0 (Serenity), see the [sharding FAQ](https://eth.wiki/sharding/Sharding-FAQs) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm).
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ Over time, the need to sync an older state may be deprecated.
|
|||
In this case, the prefix on the new constant may be removed, and the old constant will keep a special name before completely being removed.
|
||||
|
||||
A previous iteration of forking made use of "timelines", but this collides with the definitions used in the spec (constants for special forking slots, etc.), and was not integrated sufficiently in any of the spec tools or implementations.
|
||||
Instead, the config essentially doubles as fork definition now, e.g. changing the value for `PHASE_1_GENESIS_SLOT` changes the fork.
|
||||
Instead, the config essentially doubles as fork definition now, e.g. changing the value for `PHASE_1_FORK_SLOT` changes the fork.
|
||||
|
||||
Another reason to prefer forking through constants is the ability to program a forking moment based on context, instead of being limited to a static slot number.
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# Note: the intention of this file (for now) is to illustrate what a mainnet configuration could look like.
|
||||
# Some of these constants may still change before the launch of Phase 0.
|
||||
|
||||
CONFIG_NAME: "mainnet"
|
||||
|
||||
# Misc
|
||||
# ---------------------------------------------------------------
|
||||
|
@ -27,6 +28,8 @@ HYSTERESIS_QUOTIENT: 4
|
|||
HYSTERESIS_DOWNWARD_MULTIPLIER: 1
|
||||
# 5 (plus 1.25)
|
||||
HYSTERESIS_UPWARD_MULTIPLIER: 5
|
||||
# 3
|
||||
PROPORTIONAL_SLASHING_MULTIPLIER: 3
|
||||
|
||||
|
||||
# Fork Choice
|
||||
|
@ -99,8 +102,6 @@ SLOTS_PER_HISTORICAL_ROOT: 8192
|
|||
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
|
||||
# 2**8 (= 256) epochs ~27 hours
|
||||
SHARD_COMMITTEE_PERIOD: 256
|
||||
# 2**6 (= 64) epochs ~7 hours
|
||||
MAX_EPOCHS_PER_CROSSLINK: 64
|
||||
# 2**2 (= 4) epochs 25.6 minutes
|
||||
MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
# Mainnet preset - phase 1
|
||||
|
||||
CONFIG_NAME: "mainnet"
|
||||
|
||||
# phase1-fork
|
||||
# ---------------------------------------------------------------
|
||||
PHASE_1_FORK_VERSION: 0x01000000
|
||||
# [STUB]
|
||||
PHASE_1_GENESIS_SLOT: 32
|
||||
PHASE_1_FORK_SLOT: 0
|
||||
INITIAL_ACTIVE_SHARDS: 64
|
||||
|
||||
|
||||
|
@ -69,8 +70,6 @@ EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 32768
|
|||
EPOCHS_PER_CUSTODY_PERIOD: 16384
|
||||
# 2**11 (= 2,048) epochs, ~9 days
|
||||
CUSTODY_PERIOD_TO_RANDAO_PADDING: 2048
|
||||
# 2**14 (= 16,384) epochs
|
||||
CUSTODY_RESPONSE_DEADLINE: 16384
|
||||
# 2**15 (= 32,768) epochs, ~146 days
|
||||
MAX_CHUNK_CHALLENGE_DELAY: 32768
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Minimal preset
|
||||
|
||||
CONFIG_NAME: "minimal"
|
||||
|
||||
# Misc
|
||||
# ---------------------------------------------------------------
|
||||
|
@ -26,7 +27,8 @@ HYSTERESIS_QUOTIENT: 4
|
|||
HYSTERESIS_DOWNWARD_MULTIPLIER: 1
|
||||
# 5 (plus 1.25)
|
||||
HYSTERESIS_UPWARD_MULTIPLIER: 5
|
||||
|
||||
# 3
|
||||
PROPORTIONAL_SLASHING_MULTIPLIER: 3
|
||||
|
||||
|
||||
# Fork Choice
|
||||
|
@ -99,8 +101,6 @@ SLOTS_PER_HISTORICAL_ROOT: 64
|
|||
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
|
||||
# [customized] higher frequency of committee turnover and faster time to acceptable voluntary exit
|
||||
SHARD_COMMITTEE_PERIOD: 64
|
||||
# [customized] fast catchup crosslinks
|
||||
MAX_EPOCHS_PER_CROSSLINK: 4
|
||||
# 2**2 (= 4) epochs
|
||||
MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
# Minimal preset - phase 1
|
||||
|
||||
CONFIG_NAME: "minimal"
|
||||
|
||||
# phase1-fork
|
||||
# ---------------------------------------------------------------
|
||||
# [customized] for testnet distinction
|
||||
PHASE_1_FORK_VERSION: 0x01000001
|
||||
# [customized] for testing
|
||||
PHASE_1_GENESIS_SLOT: 8
|
||||
# [STUB]
|
||||
PHASE_1_FORK_SLOT: 0
|
||||
# [customized] reduced for testing
|
||||
INITIAL_ACTIVE_SHARDS: 2
|
||||
|
||||
|
@ -66,15 +67,13 @@ DOMAIN_LIGHT_AGGREGATE_AND_PROOF: 0x85000000
|
|||
# 2**1 (= 2) epochs
|
||||
RANDAO_PENALTY_EPOCHS: 2
|
||||
# [customized] quicker for testing
|
||||
EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 128
|
||||
EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 64
|
||||
# [customized] quicker for testing
|
||||
EPOCHS_PER_CUSTODY_PERIOD: 64
|
||||
EPOCHS_PER_CUSTODY_PERIOD: 32
|
||||
# [customized] quicker for testing
|
||||
CUSTODY_PERIOD_TO_RANDAO_PADDING: 8
|
||||
# [customized] quicker for testing
|
||||
CUSTODY_RESPONSE_DEADLINE: 128
|
||||
# [customize for faster testing]
|
||||
MAX_CHUNK_CHALLENGE_DELAY: 128
|
||||
MAX_CHUNK_CHALLENGE_DELAY: 64
|
||||
|
||||
|
||||
# Misc parameters
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
# Deposit contract
|
||||
|
||||
## How to set up the testing environment?
|
||||
|
||||
Under the `eth2.0-specs` directory, execute:
|
||||
|
||||
```sh
|
||||
make install_deposit_contract_tester
|
||||
```
|
||||
|
||||
## How to compile the contract?
|
||||
|
||||
```sh
|
||||
make compile_deposit_contract
|
||||
```
|
||||
|
||||
The compiler dependencies can be installed with:
|
||||
|
||||
```sh
|
||||
make install_deposit_contract_compiler
|
||||
```
|
||||
|
||||
Note that this requires python 3.7 to be installed. The pinned vyper version will not work on 3.8.
|
||||
|
||||
The ABI and bytecode will be updated at [`contracts/validator_registration.json`](./contracts/validator_registration.json).
|
||||
|
||||
|
||||
## How to run tests?
|
||||
|
||||
For running the contract tests:
|
||||
```sh
|
||||
make test_deposit_contract
|
||||
```
|
||||
|
||||
For testing the compiler output against the expected formally-verified bytecode:
|
||||
```sh
|
||||
make test_compile_deposit_contract
|
||||
```
|
|
@ -1,31 +0,0 @@
|
|||
import argparse
|
||||
import json
|
||||
import os
|
||||
|
||||
from vyper import compiler
|
||||
|
||||
DIR = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def generate_compiled_json(file_path: str):
|
||||
deposit_contract_code = open(file_path).read()
|
||||
abi = compiler.mk_full_signature(deposit_contract_code)
|
||||
bytecode = compiler.compile_code(deposit_contract_code)['bytecode']
|
||||
contract_json = {
|
||||
'abi': abi,
|
||||
'bytecode': bytecode,
|
||||
}
|
||||
# write json
|
||||
basename = os.path.basename(file_path)
|
||||
dirname = os.path.dirname(file_path)
|
||||
contract_name = basename.split('.')[0]
|
||||
with open(dirname + "/{}.json".format(contract_name), 'w') as f_write:
|
||||
json.dump(contract_json, f_write)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("path", type=str, help="the path of the contract")
|
||||
args = parser.parse_args()
|
||||
path = args.path
|
||||
generate_compiled_json(path)
|
|
@ -1,29 +0,0 @@
|
|||
from vyper import compiler
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
DIR = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def get_deposit_contract_code():
|
||||
file_path = os.path.join(DIR, '../../contracts/validator_registration.vy')
|
||||
deposit_contract_code = open(file_path).read()
|
||||
return deposit_contract_code
|
||||
|
||||
|
||||
def get_deposit_contract_json():
|
||||
file_path = os.path.join(DIR, '../../contracts/validator_registration.json')
|
||||
deposit_contract_json = open(file_path).read()
|
||||
return json.loads(deposit_contract_json)
|
||||
|
||||
|
||||
def test_compile_deposit_contract():
|
||||
compiled_deposit_contract_json = get_deposit_contract_json()
|
||||
|
||||
deposit_contract_code = get_deposit_contract_code()
|
||||
abi = compiler.mk_full_signature(deposit_contract_code)
|
||||
bytecode = compiler.compile_code(deposit_contract_code)['bytecode']
|
||||
|
||||
assert abi == compiled_deposit_contract_json["abi"]
|
||||
assert bytecode == compiled_deposit_contract_json["bytecode"]
|
|
@ -1,7 +0,0 @@
|
|||
# Vyper beta version used to generate the bytecode that was then formally verified.
|
||||
# On top of this beta version, a later change was backported, and included in the formal verification:
|
||||
# https://github.com/vyperlang/vyper/issues/1761
|
||||
# The resulting vyper version is pinned and maintained as protected branch.
|
||||
git+https://github.com/vyperlang/vyper@1761-HOTFIX-v0.1.0-beta.13
|
||||
|
||||
pytest==3.6.1
|
|
@ -1,10 +0,0 @@
|
|||
from distutils.core import setup
|
||||
|
||||
setup(
|
||||
name='deposit_contract_compiler',
|
||||
packages=['deposit_contract'],
|
||||
package_dir={"": "."},
|
||||
python_requires="3.7", # pinned vyper compiler stops working after 3.7. See vyper issue 1835.
|
||||
tests_requires=["pytest==3.6.1"],
|
||||
install_requires=[], # see requirements.txt file
|
||||
)
|
File diff suppressed because one or more lines are too long
|
@ -1,110 +0,0 @@
|
|||
# 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
|
||||
PUBKEY_LENGTH: constant(uint256) = 48 # bytes
|
||||
WITHDRAWAL_CREDENTIALS_LENGTH: constant(uint256) = 32 # bytes
|
||||
SIGNATURE_LENGTH: constant(uint256) = 96 # bytes
|
||||
AMOUNT_LENGTH: constant(uint256) = 8 # bytes
|
||||
|
||||
DepositEvent: event({
|
||||
pubkey: bytes[48],
|
||||
withdrawal_credentials: bytes[32],
|
||||
amount: bytes[8],
|
||||
signature: bytes[96],
|
||||
index: bytes[8],
|
||||
})
|
||||
|
||||
branch: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
||||
deposit_count: uint256
|
||||
|
||||
# Compute hashes in empty sparse Merkle tree
|
||||
zero_hashes: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
||||
@public
|
||||
def __init__():
|
||||
for i in range(DEPOSIT_CONTRACT_TREE_DEPTH - 1):
|
||||
self.zero_hashes[i + 1] = sha256(concat(self.zero_hashes[i], self.zero_hashes[i]))
|
||||
|
||||
|
||||
@private
|
||||
@constant
|
||||
def to_little_endian_64(value: uint256) -> bytes[8]:
|
||||
# Reversing bytes using bitwise uint256 manipulations
|
||||
# Note: array accesses of bytes[] are not currently supported in Vyper
|
||||
# Note: this function is only called when `value < 2**64`
|
||||
y: uint256 = 0
|
||||
x: uint256 = value
|
||||
for _ in range(8):
|
||||
y = shift(y, 8)
|
||||
y = y + bitwise_and(x, 255)
|
||||
x = shift(x, -8)
|
||||
return slice(convert(y, bytes32), start=24, len=8)
|
||||
|
||||
|
||||
@public
|
||||
@constant
|
||||
def get_deposit_root() -> bytes32:
|
||||
zero_bytes32: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
node: bytes32 = zero_bytes32
|
||||
size: uint256 = self.deposit_count
|
||||
for height in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
||||
if bitwise_and(size, 1) == 1: # More gas efficient than `size % 2 == 1`
|
||||
node = sha256(concat(self.branch[height], node))
|
||||
else:
|
||||
node = sha256(concat(node, self.zero_hashes[height]))
|
||||
size /= 2
|
||||
return sha256(concat(node, self.to_little_endian_64(self.deposit_count), slice(zero_bytes32, start=0, len=24)))
|
||||
|
||||
|
||||
@public
|
||||
@constant
|
||||
def get_deposit_count() -> bytes[8]:
|
||||
return self.to_little_endian_64(self.deposit_count)
|
||||
|
||||
|
||||
@payable
|
||||
@public
|
||||
def deposit(pubkey: bytes[PUBKEY_LENGTH],
|
||||
withdrawal_credentials: bytes[WITHDRAWAL_CREDENTIALS_LENGTH],
|
||||
signature: bytes[SIGNATURE_LENGTH],
|
||||
deposit_data_root: bytes32):
|
||||
# Avoid overflowing the Merkle tree (and prevent edge case in computing `self.branch`)
|
||||
assert self.deposit_count < MAX_DEPOSIT_COUNT
|
||||
|
||||
# Check deposit amount
|
||||
deposit_amount: uint256 = msg.value / as_wei_value(1, "gwei")
|
||||
assert deposit_amount >= MIN_DEPOSIT_AMOUNT
|
||||
|
||||
# Length checks for safety
|
||||
assert len(pubkey) == PUBKEY_LENGTH
|
||||
assert len(withdrawal_credentials) == WITHDRAWAL_CREDENTIALS_LENGTH
|
||||
assert len(signature) == SIGNATURE_LENGTH
|
||||
|
||||
# Emit `DepositEvent` log
|
||||
amount: bytes[8] = self.to_little_endian_64(deposit_amount)
|
||||
log.DepositEvent(pubkey, withdrawal_credentials, amount, signature, self.to_little_endian_64(self.deposit_count))
|
||||
|
||||
# Compute deposit data root (`DepositData` hash tree root)
|
||||
zero_bytes32: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
pubkey_root: bytes32 = sha256(concat(pubkey, slice(zero_bytes32, start=0, len=64 - PUBKEY_LENGTH)))
|
||||
signature_root: bytes32 = sha256(concat(
|
||||
sha256(slice(signature, start=0, len=64)),
|
||||
sha256(concat(slice(signature, start=64, len=SIGNATURE_LENGTH - 64), zero_bytes32)),
|
||||
))
|
||||
node: bytes32 = sha256(concat(
|
||||
sha256(concat(pubkey_root, withdrawal_credentials)),
|
||||
sha256(concat(amount, slice(zero_bytes32, start=0, len=32 - AMOUNT_LENGTH), signature_root)),
|
||||
))
|
||||
# Verify computed and expected deposit data roots match
|
||||
assert node == deposit_data_root
|
||||
|
||||
# Add deposit data root to Merkle tree (update a single `branch` node)
|
||||
self.deposit_count += 1
|
||||
size: uint256 = self.deposit_count
|
||||
for height in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
||||
if bitwise_and(size, 1) == 1: # More gas efficient than `size % 2 == 1`
|
||||
self.branch[height] = node
|
||||
break
|
||||
node = sha256(concat(self.branch[height], node))
|
||||
size /= 2
|
||||
|
73
setup.py
73
setup.py
|
@ -1,3 +1,4 @@
|
|||
from enum import Enum, auto
|
||||
from setuptools import setup, find_packages, Command
|
||||
from setuptools.command.build_py import build_py
|
||||
from distutils import dir_util
|
||||
|
@ -14,6 +15,13 @@ class SpecObject(NamedTuple):
|
|||
custom_types: Dict[str, str]
|
||||
constants: Dict[str, str]
|
||||
ssz_objects: Dict[str, str]
|
||||
dataclasses: Dict[str, str]
|
||||
|
||||
|
||||
class CodeBlockType(Enum):
|
||||
SSZ = auto()
|
||||
DATACLASS = auto()
|
||||
FUNCTION = auto()
|
||||
|
||||
|
||||
def get_spec(file_name: str) -> SpecObject:
|
||||
|
@ -28,8 +36,9 @@ def get_spec(file_name: str) -> SpecObject:
|
|||
functions: Dict[str, str] = {}
|
||||
constants: Dict[str, str] = {}
|
||||
ssz_objects: Dict[str, str] = {}
|
||||
dataclasses: Dict[str, str] = {}
|
||||
function_matcher = re.compile(FUNCTION_REGEX)
|
||||
is_ssz = False
|
||||
block_type = CodeBlockType.FUNCTION
|
||||
custom_types: Dict[str, str] = {}
|
||||
for linenum, line in enumerate(open(file_name).readlines()):
|
||||
line = line.rstrip()
|
||||
|
@ -43,20 +52,26 @@ def get_spec(file_name: str) -> SpecObject:
|
|||
else:
|
||||
# Handle function definitions & ssz_objects
|
||||
if pulling_from is not None:
|
||||
# SSZ Object
|
||||
if len(line) > 18 and line[:6] == 'class ' and line[-12:] == '(Container):':
|
||||
name = line[6:-12]
|
||||
# Check consistency with markdown header
|
||||
assert name == current_name
|
||||
is_ssz = True
|
||||
# function definition
|
||||
block_type = CodeBlockType.SSZ
|
||||
elif line[:10] == '@dataclass':
|
||||
block_type = CodeBlockType.DATACLASS
|
||||
elif function_matcher.match(line) is not None:
|
||||
current_name = function_matcher.match(line).group(0)
|
||||
is_ssz = False
|
||||
if is_ssz:
|
||||
block_type = CodeBlockType.FUNCTION
|
||||
|
||||
if block_type == CodeBlockType.SSZ:
|
||||
ssz_objects[current_name] = ssz_objects.get(current_name, '') + line + '\n'
|
||||
else:
|
||||
elif block_type == CodeBlockType.DATACLASS:
|
||||
dataclasses[current_name] = dataclasses.get(current_name, '') + line + '\n'
|
||||
elif block_type == CodeBlockType.FUNCTION:
|
||||
functions[current_name] = functions.get(current_name, '') + line + '\n'
|
||||
else:
|
||||
pass
|
||||
|
||||
# Handle constant and custom types table entries
|
||||
elif pulling_from is None and len(line) > 0 and line[0] == '|':
|
||||
row = line[1:].split('|')
|
||||
|
@ -75,7 +90,7 @@ def get_spec(file_name: str) -> SpecObject:
|
|||
constants[row[0]] = row[1].replace('**TBD**', '2**32')
|
||||
elif row[1].startswith('uint') or row[1].startswith('Bytes'):
|
||||
custom_types[row[0]] = row[1]
|
||||
return SpecObject(functions, custom_types, constants, ssz_objects)
|
||||
return SpecObject(functions, custom_types, constants, ssz_objects, dataclasses)
|
||||
|
||||
|
||||
CONFIG_LOADER = '''
|
||||
|
@ -104,12 +119,15 @@ from eth2spec.utils import bls
|
|||
from eth2spec.utils.hash_function import hash
|
||||
|
||||
SSZObject = TypeVar('SSZObject', bound=View)
|
||||
|
||||
CONFIG_NAME = 'mainnet'
|
||||
'''
|
||||
PHASE1_IMPORTS = '''from eth2spec.phase0 import spec as phase0
|
||||
from eth2spec.config.config_util import apply_constants_config
|
||||
from typing import (
|
||||
Any, Dict, Set, Sequence, NewType, Tuple, TypeVar, Callable, Optional
|
||||
)
|
||||
from typing import List as PyList
|
||||
|
||||
from dataclasses import (
|
||||
dataclass,
|
||||
|
@ -135,10 +153,14 @@ reload(phase0)
|
|||
SSZVariableName = str
|
||||
GeneralizedIndex = NewType('GeneralizedIndex', int)
|
||||
SSZObject = TypeVar('SSZObject', bound=View)
|
||||
|
||||
CONFIG_NAME = 'mainnet'
|
||||
'''
|
||||
SUNDRY_CONSTANTS_FUNCTIONS = '''
|
||||
def ceillog2(x: uint64) -> int:
|
||||
return (x - 1).bit_length()
|
||||
def ceillog2(x: int) -> uint64:
|
||||
if x < 1:
|
||||
raise ValueError(f"ceillog2 accepts only positive values, x={x}")
|
||||
return uint64((x - 1).bit_length())
|
||||
'''
|
||||
PHASE0_SUNDRY_FUNCTIONS = '''
|
||||
def get_eth1_data(block: Eth1Block) -> Eth1Data:
|
||||
|
@ -214,19 +236,13 @@ get_attesting_indices = cache_this(
|
|||
|
||||
PHASE1_SUNDRY_FUNCTIONS = '''
|
||||
|
||||
def get_block_data_merkle_root(data: ByteList) -> Root:
|
||||
# To get the Merkle root of the block data, we need the Merkle root without the length mix-in
|
||||
# The below implements this in the Remerkleable framework
|
||||
return Root(data.get_backing().get_left().merkle_root())
|
||||
|
||||
|
||||
_get_start_shard = get_start_shard
|
||||
get_start_shard = cache_this(
|
||||
lambda state, slot: (state.validators.hash_tree_root(), slot),
|
||||
_get_start_shard, lru_size=SLOTS_PER_EPOCH * 3)'''
|
||||
|
||||
|
||||
def objects_to_spec(spec_object: SpecObject, imports: str, fork: str) -> str:
|
||||
def objects_to_spec(spec_object: SpecObject, imports: str, fork: str, ordered_class_objects: Dict[str, str]) -> str:
|
||||
"""
|
||||
Given all the objects that constitute a spec, combine them into a single pyfile.
|
||||
"""
|
||||
|
@ -246,7 +262,7 @@ def objects_to_spec(spec_object: SpecObject, imports: str, fork: str) -> str:
|
|||
if k == "BLS12_381_Q":
|
||||
spec_object.constants[k] += " # noqa: E501"
|
||||
constants_spec = '\n'.join(map(lambda x: '%s = %s' % (x, spec_object.constants[x]), spec_object.constants))
|
||||
ssz_objects_instantiation_spec = '\n\n'.join(spec_object.ssz_objects.values())
|
||||
ordered_class_objects_spec = '\n\n'.join(ordered_class_objects.values())
|
||||
spec = (
|
||||
imports
|
||||
+ '\n\n' + f"fork = \'{fork}\'\n"
|
||||
|
@ -254,7 +270,7 @@ def objects_to_spec(spec_object: SpecObject, imports: str, fork: str) -> str:
|
|||
+ '\n' + SUNDRY_CONSTANTS_FUNCTIONS
|
||||
+ '\n\n' + constants_spec
|
||||
+ '\n\n' + CONFIG_LOADER
|
||||
+ '\n\n' + ssz_objects_instantiation_spec
|
||||
+ '\n\n' + ordered_class_objects_spec
|
||||
+ '\n\n' + functions_spec
|
||||
+ '\n' + PHASE0_SUNDRY_FUNCTIONS
|
||||
)
|
||||
|
@ -280,11 +296,12 @@ ignored_dependencies = [
|
|||
'bit', 'boolean', 'Vector', 'List', 'Container', 'BLSPubkey', 'BLSSignature',
|
||||
'Bytes1', 'Bytes4', 'Bytes32', 'Bytes48', 'Bytes96', 'Bitlist', 'Bitvector',
|
||||
'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256',
|
||||
'bytes', 'byte', 'ByteList', 'ByteVector'
|
||||
'bytes', 'byte', 'ByteList', 'ByteVector',
|
||||
'Dict', 'dict', 'field',
|
||||
]
|
||||
|
||||
|
||||
def dependency_order_ssz_objects(objects: Dict[str, str], custom_types: Dict[str, str]) -> None:
|
||||
def dependency_order_class_objects(objects: Dict[str, str], custom_types: Dict[str, str]) -> None:
|
||||
"""
|
||||
Determines which SSZ Object is dependent on which other and orders them appropriately
|
||||
"""
|
||||
|
@ -321,13 +338,14 @@ def combine_spec_objects(spec0: SpecObject, spec1: SpecObject) -> SpecObject:
|
|||
"""
|
||||
Takes in two spec variants (as tuples of their objects) and combines them using the appropriate combiner function.
|
||||
"""
|
||||
functions0, custom_types0, constants0, ssz_objects0 = spec0
|
||||
functions1, custom_types1, constants1, ssz_objects1 = spec1
|
||||
functions0, custom_types0, constants0, ssz_objects0, dataclasses0 = spec0
|
||||
functions1, custom_types1, constants1, ssz_objects1, dataclasses1 = spec1
|
||||
functions = combine_functions(functions0, functions1)
|
||||
custom_types = combine_constants(custom_types0, custom_types1)
|
||||
constants = combine_constants(constants0, constants1)
|
||||
ssz_objects = combine_ssz_objects(ssz_objects0, ssz_objects1, custom_types)
|
||||
return SpecObject(functions, custom_types, constants, ssz_objects)
|
||||
dataclasses = combine_functions(dataclasses0, dataclasses1)
|
||||
return SpecObject(functions, custom_types, constants, ssz_objects, dataclasses)
|
||||
|
||||
|
||||
fork_imports = {
|
||||
|
@ -343,9 +361,10 @@ def build_spec(fork: str, source_files: List[str]) -> str:
|
|||
for value in all_specs[1:]:
|
||||
spec_object = combine_spec_objects(spec_object, value)
|
||||
|
||||
dependency_order_ssz_objects(spec_object.ssz_objects, spec_object.custom_types)
|
||||
class_objects = {**spec_object.ssz_objects, **spec_object.dataclasses}
|
||||
dependency_order_class_objects(class_objects, spec_object.custom_types)
|
||||
|
||||
return objects_to_spec(spec_object, fork_imports[fork], fork)
|
||||
return objects_to_spec(spec_object, fork_imports[fork], fork, class_objects)
|
||||
|
||||
|
||||
class PySpecCommand(Command):
|
||||
|
@ -382,12 +401,14 @@ class PySpecCommand(Command):
|
|||
specs/phase0/beacon-chain.md
|
||||
specs/phase0/fork-choice.md
|
||||
specs/phase0/validator.md
|
||||
specs/phase0/weak-subjectivity.md
|
||||
"""
|
||||
elif self.spec_fork == "phase1":
|
||||
self.md_doc_paths = """
|
||||
specs/phase0/beacon-chain.md
|
||||
specs/phase0/fork-choice.md
|
||||
specs/phase0/validator.md
|
||||
specs/phase0/weak-subjectivity.md
|
||||
specs/phase1/custody-game.md
|
||||
specs/phase1/beacon-chain.md
|
||||
specs/phase1/shard-transition.md
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
The contract logic here is based on the deposit contract written in Vyper.
|
||||
|
||||
The original repository (https://github.com/ethereum/deposit_contract) had the following authors:
|
||||
|
||||
@NIC619
|
||||
@carver
|
||||
@hwwhww
|
||||
@davesque
|
||||
@ralexstokes
|
||||
@ChihChengLiang
|
||||
@nisdas
|
||||
@djrtwo
|
||||
@JustinDrake
|
||||
@vbuterin
|
||||
@njgheorghita
|
||||
@CarlBeek
|
||||
|
||||
In eth2.0-specs repository (https://github.com/ethereum/eth2.0-specs), its current location, it had the following additional authors:
|
||||
|
||||
@daejunpark
|
||||
|
||||
And this repository has the following authors:
|
||||
|
||||
@axic
|
||||
@MrChico
|
||||
@chriseth
|
||||
|
||||
(All of the above are GitHub usernames in order of they appeared first in the commit history.)
|
|
@ -0,0 +1,43 @@
|
|||
# Deposit Contract
|
||||
|
||||
## History
|
||||
|
||||
This is a rewrite of the [Vyper Eth 2.0 deposit contract](https://github.com/ethereum/eth2.0-specs/blob/v0.12.2/deposit_contract/contracts/validator_registration.vy) to Solidity.
|
||||
|
||||
The original motivation was to run the SMTChecker and the new Yul IR generator option (`--ir`) in the compiler.
|
||||
|
||||
As of June 2020, version `r1` of the Solidity deposit contract has been verified and is considered for adoption.
|
||||
See this [blog post](https://blog.ethereum.org/2020/06/23/eth2-quick-update-no-12/) for more information.
|
||||
|
||||
In August 2020, version `r2` was released with metadata modifications and relicensed to CC0-1.0. Afterward, this contract has been ported back to from [`axic/eth2-deposit-contract`](https://github.com/axic/eth2-deposit-contract) to this repository and replaced the Vyper deposit contract.
|
||||
|
||||
## Compiling solidity deposit contract
|
||||
|
||||
In the `eth2.0-specs` directory run:
|
||||
```sh
|
||||
make compile_deposit_contract
|
||||
```
|
||||
|
||||
The following parameters were used to generate the bytecode for the `DepositContract` available in this repository:
|
||||
|
||||
* Contract Name: `DepositContract`
|
||||
* Compiler Version: Solidity `v0.6.11+commit.5ef660b1`
|
||||
* Optimization Enabled: `Yes` with `5000000` runs
|
||||
* Metadata Options: `--metadata-literal` (to verify metadata hash)
|
||||
|
||||
```sh
|
||||
solc --optimize --optimize-runs 5000000 --metadata-literal --bin deposit_contract.sol
|
||||
```
|
||||
|
||||
## Running web3 tests
|
||||
|
||||
1. In the `eth2.0-specs` directory run `make install_deposit_contract_web3_tester` to install the tools needed (make sure to have Python 3.7 and pip installed).
|
||||
2. In the `eth2.0-specs` directory run `make test_deposit_contract_web3_tests` to execute the tests.
|
||||
|
||||
## Running randomized `dapp` tests:
|
||||
|
||||
Install the latest version of `dapp` by following the instructions at [dapp.tools](https://dapp.tools/). Then in the `eth2.0-specs` directory run:
|
||||
|
||||
```sh
|
||||
make test_deposit_contract
|
||||
```
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,178 @@
|
|||
// ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
|
||||
// ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
|
||||
// ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
|
||||
// ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
|
||||
// ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
|
||||
// ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
pragma solidity 0.6.11;
|
||||
|
||||
// This interface is designed to be compatible with the Vyper version.
|
||||
/// @notice This is the Ethereum 2.0 deposit contract interface.
|
||||
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
|
||||
interface IDepositContract {
|
||||
/// @notice A processed deposit event.
|
||||
event DepositEvent(
|
||||
bytes pubkey,
|
||||
bytes withdrawal_credentials,
|
||||
bytes amount,
|
||||
bytes signature,
|
||||
bytes index
|
||||
);
|
||||
|
||||
/// @notice Submit a Phase 0 DepositData object.
|
||||
/// @param pubkey A BLS12-381 public key.
|
||||
/// @param withdrawal_credentials Commitment to a public key for withdrawals.
|
||||
/// @param signature A BLS12-381 signature.
|
||||
/// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
|
||||
/// Used as a protection against malformed input.
|
||||
function deposit(
|
||||
bytes calldata pubkey,
|
||||
bytes calldata withdrawal_credentials,
|
||||
bytes calldata signature,
|
||||
bytes32 deposit_data_root
|
||||
) external payable;
|
||||
|
||||
/// @notice Query the current deposit root hash.
|
||||
/// @return The deposit root hash.
|
||||
function get_deposit_root() external view returns (bytes32);
|
||||
|
||||
/// @notice Query the current deposit count.
|
||||
/// @return The deposit count encoded as a little endian 64-bit number.
|
||||
function get_deposit_count() external view returns (bytes memory);
|
||||
}
|
||||
|
||||
// Based on official specification in https://eips.ethereum.org/EIPS/eip-165
|
||||
interface ERC165 {
|
||||
/// @notice Query if a contract implements an interface
|
||||
/// @param interfaceId The interface identifier, as specified in ERC-165
|
||||
/// @dev Interface identification is specified in ERC-165. This function
|
||||
/// uses less than 30,000 gas.
|
||||
/// @return `true` if the contract implements `interfaceId` and
|
||||
/// `interfaceId` is not 0xffffffff, `false` otherwise
|
||||
function supportsInterface(bytes4 interfaceId) external pure returns (bool);
|
||||
}
|
||||
|
||||
// This is a rewrite of the Vyper Eth2.0 deposit contract in Solidity.
|
||||
// It tries to stay as close as possible to the original source code.
|
||||
/// @notice This is the Ethereum 2.0 deposit contract interface.
|
||||
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
|
||||
contract DepositContract is IDepositContract, ERC165 {
|
||||
uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32;
|
||||
// NOTE: this also ensures `deposit_count` will fit into 64-bits
|
||||
uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1;
|
||||
|
||||
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] branch;
|
||||
uint256 deposit_count;
|
||||
|
||||
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes;
|
||||
|
||||
constructor() public {
|
||||
// Compute hashes in empty sparse Merkle tree
|
||||
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++)
|
||||
zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height]));
|
||||
}
|
||||
|
||||
function get_deposit_root() override external view returns (bytes32) {
|
||||
bytes32 node;
|
||||
uint size = deposit_count;
|
||||
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
|
||||
if ((size & 1) == 1)
|
||||
node = sha256(abi.encodePacked(branch[height], node));
|
||||
else
|
||||
node = sha256(abi.encodePacked(node, zero_hashes[height]));
|
||||
size /= 2;
|
||||
}
|
||||
return sha256(abi.encodePacked(
|
||||
node,
|
||||
to_little_endian_64(uint64(deposit_count)),
|
||||
bytes24(0)
|
||||
));
|
||||
}
|
||||
|
||||
function get_deposit_count() override external view returns (bytes memory) {
|
||||
return to_little_endian_64(uint64(deposit_count));
|
||||
}
|
||||
|
||||
function deposit(
|
||||
bytes calldata pubkey,
|
||||
bytes calldata withdrawal_credentials,
|
||||
bytes calldata signature,
|
||||
bytes32 deposit_data_root
|
||||
) override external payable {
|
||||
// Extended ABI length checks since dynamic types are used.
|
||||
require(pubkey.length == 48, "DepositContract: invalid pubkey length");
|
||||
require(withdrawal_credentials.length == 32, "DepositContract: invalid withdrawal_credentials length");
|
||||
require(signature.length == 96, "DepositContract: invalid signature length");
|
||||
|
||||
// Check deposit amount
|
||||
require(msg.value >= 1 ether, "DepositContract: deposit value too low");
|
||||
require(msg.value % 1 gwei == 0, "DepositContract: deposit value not multiple of gwei");
|
||||
uint deposit_amount = msg.value / 1 gwei;
|
||||
require(deposit_amount <= type(uint64).max, "DepositContract: deposit value too high");
|
||||
|
||||
// Emit `DepositEvent` log
|
||||
bytes memory amount = to_little_endian_64(uint64(deposit_amount));
|
||||
emit DepositEvent(
|
||||
pubkey,
|
||||
withdrawal_credentials,
|
||||
amount,
|
||||
signature,
|
||||
to_little_endian_64(uint64(deposit_count))
|
||||
);
|
||||
|
||||
// Compute deposit data root (`DepositData` hash tree root)
|
||||
bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));
|
||||
bytes32 signature_root = sha256(abi.encodePacked(
|
||||
sha256(abi.encodePacked(signature[:64])),
|
||||
sha256(abi.encodePacked(signature[64:], bytes32(0)))
|
||||
));
|
||||
bytes32 node = sha256(abi.encodePacked(
|
||||
sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
|
||||
sha256(abi.encodePacked(amount, bytes24(0), signature_root))
|
||||
));
|
||||
|
||||
// Verify computed and expected deposit data roots match
|
||||
require(node == deposit_data_root, "DepositContract: reconstructed DepositData does not match supplied deposit_data_root");
|
||||
|
||||
// Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`)
|
||||
require(deposit_count < MAX_DEPOSIT_COUNT, "DepositContract: merkle tree full");
|
||||
|
||||
// Add deposit data root to Merkle tree (update a single `branch` node)
|
||||
deposit_count += 1;
|
||||
uint size = deposit_count;
|
||||
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
|
||||
if ((size & 1) == 1) {
|
||||
branch[height] = node;
|
||||
return;
|
||||
}
|
||||
node = sha256(abi.encodePacked(branch[height], node));
|
||||
size /= 2;
|
||||
}
|
||||
// As the loop should always end prematurely with the `return` statement,
|
||||
// this code should be unreachable. We assert `false` just to be safe.
|
||||
assert(false);
|
||||
}
|
||||
|
||||
function supportsInterface(bytes4 interfaceId) override external pure returns (bool) {
|
||||
return interfaceId == type(ERC165).interfaceId || interfaceId == type(IDepositContract).interfaceId;
|
||||
}
|
||||
|
||||
function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
|
||||
ret = new bytes(8);
|
||||
bytes8 bytesValue = bytes8(value);
|
||||
// Byteswapping during copying to bytes.
|
||||
ret[0] = bytesValue[7];
|
||||
ret[1] = bytesValue[6];
|
||||
ret[2] = bytesValue[5];
|
||||
ret[3] = bytesValue[4];
|
||||
ret[4] = bytesValue[3];
|
||||
ret[5] = bytesValue[2];
|
||||
ret[6] = bytesValue[1];
|
||||
ret[7] = bytesValue[0];
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Subproject commit eb7148d43c1ca6f9890361e2e2378364af2430ba
|
|
@ -0,0 +1,18 @@
|
|||
let dapptools = builtins.fetchGit {
|
||||
url = "https://github.com/dapphub/dapptools.git";
|
||||
rev = "11dcefe1f03b0acafe76b4d7d54821ef6bd63131";
|
||||
};
|
||||
nixpkgs = builtins.fetchGit {
|
||||
url = "https://github.com/nixos/nixpkgs";
|
||||
ref = "release-19.03";
|
||||
rev = "f1707d8875276cfa110139435a7e8998b4c2a4fd";
|
||||
};
|
||||
pkgs-for-dapp = import nixpkgs {
|
||||
overlays = [
|
||||
(import (dapptools + /overlay.nix))
|
||||
];
|
||||
};
|
||||
in
|
||||
pkgs-for-dapp.mkShell {
|
||||
buildInputs = [ pkgs-for-dapp.dapp pkgs-for-dapp.solc pkgs-for-dapp.hevm ];
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
pragma solidity ^0.6.2;
|
||||
|
||||
import "../lib/ds-test/src/test.sol";
|
||||
|
||||
import "./vyper_setup.sol";
|
||||
import "../deposit_contract.sol";
|
||||
|
||||
contract DepositContractTest is DSTest {
|
||||
DepositContract depositContract_sol;
|
||||
DepositContract depositContract_vyp;
|
||||
uint64 constant GWEI = 1000000000;
|
||||
|
||||
function setUp() public {
|
||||
VyperSetup vyperSetup = new VyperSetup();
|
||||
depositContract_vyp = DepositContract(vyperSetup.deployDeposit());
|
||||
depositContract_sol = new DepositContract();
|
||||
}
|
||||
|
||||
// --- SUCCESS TESTS ---
|
||||
|
||||
// Tests initialized storage values, comparing vyper and solidity
|
||||
function test_empty_root() public {
|
||||
bytes32 zHash = 0x0000000000000000000000000000000000000000000000000000000000000000;
|
||||
bytes32 zHashN = zHash;
|
||||
for (uint i = 0; i <= 31; i++) {
|
||||
zHashN = sha256(abi.encodePacked(zHashN, zHashN));
|
||||
}
|
||||
assertEq(sha256(abi.encodePacked(zHashN, zHash)), depositContract_vyp.get_deposit_root());
|
||||
assertEq(depositContract_sol.get_deposit_root(), depositContract_vyp.get_deposit_root());
|
||||
}
|
||||
|
||||
// Generates 16 random deposits, insert them in both vyper and solidity version and compare get_deposit_root after each insertion
|
||||
function test_16_deposits(bytes32[16] memory pubkey_one, bytes16[16] memory pubkey_two, bytes32[16] memory _withdrawal_credentials,
|
||||
bytes32[16] memory sig_one, bytes32[16] memory sig_two, bytes32[16] memory sig_three, uint32[16] memory amount) public {
|
||||
uint j;
|
||||
for (uint i = 0; i < 16; i++) {
|
||||
// as of dcaa774, the solidity version is more restrictive than vyper and requires deposits to be divisible by GWEI
|
||||
uint64 gweiamount = amount[i] * GWEI;
|
||||
if (1 ether <= gweiamount) {
|
||||
j++;
|
||||
deposit_in(depositContract_sol, pubkey_one[i], pubkey_two[i], _withdrawal_credentials[i], sig_one[i], sig_two[i], sig_three[i], gweiamount);
|
||||
deposit_in(depositContract_vyp, pubkey_one[i], pubkey_two[i], _withdrawal_credentials[i], sig_one[i], sig_two[i], sig_three[i], gweiamount);
|
||||
|
||||
assertEq(depositContract_sol.get_deposit_root(), depositContract_vyp.get_deposit_root());
|
||||
assertEq(keccak256(abi.encodePacked(depositContract_sol.get_deposit_count())), keccak256(abi.encodePacked(depositContract_vyp.get_deposit_count())));
|
||||
assertEq(keccak256(abi.encodePacked(depositContract_sol.get_deposit_count())), keccak256(to_little_endian_64(uint64(j))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The solidity contract fails when given a deposit which is not divisible by GWEI
|
||||
|
||||
function testFail_deposit_not_divisible_by_gwei(bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials,
|
||||
bytes32 sig_one, bytes32 sig_two, bytes32 sig_three) public {
|
||||
deposit_in(depositContract_sol, pubkey_one, pubkey_two, _withdrawal_credentials, sig_one, sig_two, sig_three, 1 ether + 1);
|
||||
}
|
||||
|
||||
// --- FAILURE TESTS ---
|
||||
|
||||
// if the node is given randomly instead of as the ssz root, the chances of success are so unlikely that we can assert it to be false
|
||||
function testFail_malformed_node_vyp(bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials, bytes32 sig_one,
|
||||
bytes32 sig_two, bytes32 sig_three, uint64 amount, bytes32 node) public {
|
||||
bytes memory pubkey = abi.encodePacked(pubkey_one, pubkey_two);
|
||||
bytes memory withdrawal_credentials = abi.encodePacked(_withdrawal_credentials); //I wish just recasting to `bytes` would work..
|
||||
bytes memory signature = abi.encodePacked(sig_one, sig_two, sig_three);
|
||||
depositContract_vyp.deposit{value: amount}(pubkey, withdrawal_credentials, signature, node);
|
||||
}
|
||||
|
||||
// If the node is taken randomly instead of as the ssz root, the chances of success are so unlikely that we can assert it to be false
|
||||
function testFail_malformed_node_sol(bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials, bytes32 sig_one,
|
||||
bytes32 sig_two, bytes32 sig_three, uint64 amount, bytes32 node) public {
|
||||
bytes memory pubkey = abi.encodePacked(pubkey_one, pubkey_two);
|
||||
bytes memory withdrawal_credentials = abi.encodePacked(_withdrawal_credentials);
|
||||
bytes memory signature = abi.encodePacked(sig_one, sig_two, sig_three);
|
||||
depositContract_sol.deposit{value: amount}(pubkey, withdrawal_credentials, signature, node);
|
||||
}
|
||||
|
||||
// if bytes lengths are wrong, the call will fail
|
||||
function testFail_malformed_calldata_vyp(bytes memory pubkey, bytes memory withdrawal_credentials, bytes memory signature, uint64 amount) public {
|
||||
if (amount >= 1000000000000000000) {
|
||||
if (!(pubkey.length == 48 && withdrawal_credentials.length == 32 && signature.length == 96)) {
|
||||
depositContract_vyp.deposit{value: amount}(pubkey, withdrawal_credentials, signature,
|
||||
encode_node(pubkey, withdrawal_credentials, signature, to_little_endian_64(amount / GWEI))
|
||||
);
|
||||
} else { revert(); }
|
||||
} else { revert(); }
|
||||
}
|
||||
|
||||
function testFail_malformed_calldata_sol(bytes memory pubkey, bytes memory withdrawal_credentials, bytes memory signature, uint64 amount) public {
|
||||
if (amount >= 1000000000000000000) {
|
||||
if (!(pubkey.length == 48 && withdrawal_credentials.length == 32 && signature.length == 96)) {
|
||||
depositContract_sol.deposit{value: amount}(pubkey, withdrawal_credentials, signature,
|
||||
encode_node(pubkey, withdrawal_credentials, signature, to_little_endian_64(amount / GWEI))
|
||||
);
|
||||
} else { revert(); }
|
||||
} else { revert(); }
|
||||
}
|
||||
|
||||
// --- HELPER FUNCTIONS ---
|
||||
|
||||
function deposit_in(DepositContract depositContract, bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials, bytes32 sig_one, bytes32 sig_two, bytes32 sig_three, uint64 amount) public {
|
||||
bytes memory pubkey = abi.encodePacked(pubkey_one, pubkey_two);
|
||||
bytes memory withdrawal_credentials = abi.encodePacked(_withdrawal_credentials);
|
||||
bytes memory signature = abi.encodePacked(sig_one, sig_two, sig_three);
|
||||
bytes32 node = encode_node(pubkey, withdrawal_credentials, signature, to_little_endian_64(amount / GWEI));
|
||||
depositContract.deposit{value: amount}(pubkey, withdrawal_credentials, signature, node);
|
||||
}
|
||||
|
||||
function slice(bytes memory a, uint32 offset, uint32 size) pure internal returns (bytes memory result) {
|
||||
result = new bytes(size);
|
||||
for (uint i = 0; i < size; i++) {
|
||||
result[i] = a[offset + i];
|
||||
}
|
||||
}
|
||||
|
||||
function encode_node(bytes memory pubkey, bytes memory withdrawal_credentials, bytes memory signature, bytes memory amount) public pure returns (bytes32) {
|
||||
bytes16 zero_bytes16;
|
||||
bytes24 zero_bytes24;
|
||||
bytes32 zero_bytes32;
|
||||
bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, zero_bytes16));
|
||||
bytes32 signature_root = sha256(abi.encodePacked(
|
||||
sha256(abi.encodePacked(slice(signature, 0, 64))),
|
||||
sha256(abi.encodePacked(slice(signature, 64, 32), zero_bytes32))
|
||||
));
|
||||
return sha256(abi.encodePacked(
|
||||
sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
|
||||
sha256(abi.encodePacked(amount, zero_bytes24, signature_root))
|
||||
));
|
||||
}
|
||||
|
||||
function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
|
||||
ret = new bytes(8);
|
||||
ret[0] = bytes1(uint8(value & 0xff));
|
||||
ret[1] = bytes1(uint8((value >> 8) & 0xff));
|
||||
ret[2] = bytes1(uint8((value >> 16) & 0xff));
|
||||
ret[3] = bytes1(uint8((value >> 24) & 0xff));
|
||||
ret[4] = bytes1(uint8((value >> 32) & 0xff));
|
||||
ret[5] = bytes1(uint8((value >> 40) & 0xff));
|
||||
ret[6] = bytes1(uint8((value >> 48) & 0xff));
|
||||
ret[7] = bytes1(uint8((value >> 56) & 0xff));
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -15,7 +15,7 @@ DIR = os.path.dirname(__file__)
|
|||
|
||||
|
||||
def get_deposit_contract_json():
|
||||
file_path = os.path.join(DIR, '../../contracts/validator_registration.json')
|
||||
file_path = os.path.join(DIR, '../../deposit_contract.json')
|
||||
deposit_contract_json = open(file_path).read()
|
||||
return json.loads(deposit_contract_json)
|
||||
|
|
@ -6,7 +6,7 @@ from eth2spec.phase0.spec import DepositData
|
|||
from eth2spec.utils.ssz.ssz_typing import List
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
|
||||
from deposit_contract.conftest import (
|
||||
from tests.conftest import (
|
||||
FULL_DEPOSIT_AMOUNT,
|
||||
MIN_DEPOSIT_AMOUNT,
|
||||
)
|
|
@ -195,6 +195,7 @@ The following values are (non-configurable) constants used throughout the specif
|
|||
| `HYSTERESIS_QUOTIENT` | `uint64(4)` |
|
||||
| `HYSTERESIS_DOWNWARD_MULTIPLIER` | `uint64(1)` |
|
||||
| `HYSTERESIS_UPWARD_MULTIPLIER` | `uint64(5)` |
|
||||
| `PROPORTIONAL_SLASHING_MULTIPLIER` | `uint64(3)` |
|
||||
|
||||
- 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.)
|
||||
|
||||
|
@ -602,7 +603,7 @@ def bytes_to_uint64(data: bytes) -> uint64:
|
|||
|
||||
#### BLS Signatures
|
||||
|
||||
Eth2 makes use of BLS signatures as specified in the [IETF draft BLS specification draft-irtf-cfrg-bls-signature-02](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-02) but uses [Hashing to Elliptic Curves - draft-irtf-cfrg-hash-to-curve-07](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-07) instead of draft-irtf-cfrg-hash-to-curve-06. Specifically, eth2 uses the `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_` ciphersuite which implements the following interfaces:
|
||||
Eth2 makes use of BLS signatures as specified in the [IETF draft BLS specification draft-irtf-cfrg-bls-signature-03](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03). Specifically, eth2 uses the `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_` ciphersuite which implements the following interfaces:
|
||||
|
||||
- `def Sign(SK: int, message: Bytes) -> BLSSignature`
|
||||
- `def Verify(PK: BLSPubkey, message: Bytes, signature: BLSSignature) -> bool`
|
||||
|
@ -612,8 +613,6 @@ Eth2 makes use of BLS signatures as specified in the [IETF draft BLS specificati
|
|||
|
||||
Within these specifications, BLS signatures are treated as a module for notational clarity, thus to verify a signature `bls.Verify(...)` is used.
|
||||
|
||||
*Note*: The non-standard configuration of the BLS and hash to curve specs is temporary and will be resolved once IETF releases BLS spec draft 3.
|
||||
|
||||
### Predicates
|
||||
|
||||
#### `is_active_validator`
|
||||
|
@ -773,7 +772,7 @@ def compute_committee(indices: Sequence[ValidatorIndex],
|
|||
Return the committee corresponding to ``indices``, ``seed``, ``index``, and committee ``count``.
|
||||
"""
|
||||
start = (len(indices) * index) // count
|
||||
end = (len(indices) * (index + 1)) // count
|
||||
end = (len(indices) * uint64(index + 1)) // count
|
||||
return [indices[compute_shuffled_index(uint64(i), uint64(len(indices)), seed)] for i in range(start, end)]
|
||||
```
|
||||
|
||||
|
@ -1305,6 +1304,8 @@ def get_attesting_balance(state: BeaconState, attestations: Sequence[PendingAtte
|
|||
|
||||
```python
|
||||
def process_justification_and_finalization(state: BeaconState) -> None:
|
||||
# Initial FFG checkpoint values have a `0x00` stub for `root`.
|
||||
# Skip FFG updates in the first two epochs to avoid corner cases that might result in modifying this stub.
|
||||
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
|
||||
return
|
||||
|
||||
|
@ -1452,7 +1453,7 @@ def get_inclusion_delay_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequ
|
|||
if index in get_attesting_indices(state, a.data, a.aggregation_bits)
|
||||
], key=lambda a: a.inclusion_delay)
|
||||
rewards[attestation.proposer_index] += get_proposer_reward(state, index)
|
||||
max_attester_reward = get_base_reward(state, index) - get_proposer_reward(state, index)
|
||||
max_attester_reward = Gwei(get_base_reward(state, index) - get_proposer_reward(state, index))
|
||||
rewards[index] += Gwei(max_attester_reward // attestation.inclusion_delay)
|
||||
|
||||
# No penalties associated with inclusion delay
|
||||
|
@ -1512,6 +1513,7 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence
|
|||
|
||||
```python
|
||||
def process_rewards_and_penalties(state: BeaconState) -> None:
|
||||
# No rewards are applied at the end of `GENESIS_EPOCH` because rewards are for work done in the previous epoch
|
||||
if get_current_epoch(state) == GENESIS_EPOCH:
|
||||
return
|
||||
|
||||
|
@ -1551,10 +1553,11 @@ def process_registry_updates(state: BeaconState) -> None:
|
|||
def process_slashings(state: BeaconState) -> None:
|
||||
epoch = get_current_epoch(state)
|
||||
total_balance = get_total_active_balance(state)
|
||||
adjusted_total_slashing_balance = min(sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER, total_balance)
|
||||
for index, validator in enumerate(state.validators):
|
||||
if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
|
||||
increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from penalty numerator to avoid uint64 overflow
|
||||
penalty_numerator = validator.effective_balance // increment * min(sum(state.slashings) * 3, total_balance)
|
||||
penalty_numerator = validator.effective_balance // increment * adjusted_total_slashing_balance
|
||||
penalty = penalty_numerator // total_balance * increment
|
||||
decrease_balance(state, ValidatorIndex(index), penalty)
|
||||
```
|
||||
|
@ -1571,7 +1574,7 @@ def process_final_updates(state: BeaconState) -> None:
|
|||
# Update effective balances with hysteresis
|
||||
for index, validator in enumerate(state.validators):
|
||||
balance = state.balances[index]
|
||||
HYSTERESIS_INCREMENT = EFFECTIVE_BALANCE_INCREMENT // HYSTERESIS_QUOTIENT
|
||||
HYSTERESIS_INCREMENT = uint64(EFFECTIVE_BALANCE_INCREMENT // HYSTERESIS_QUOTIENT)
|
||||
DOWNWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_DOWNWARD_MULTIPLIER
|
||||
UPWARD_THRESHOLD = HYSTERESIS_INCREMENT * HYSTERESIS_UPWARD_MULTIPLIER
|
||||
if (
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
- [Deposit amount](#deposit-amount)
|
||||
- [Withdrawal credentials](#withdrawal-credentials)
|
||||
- [`DepositEvent` log](#depositevent-log)
|
||||
- [Vyper code](#vyper-code)
|
||||
- [Solidity code](#solidity-code)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
|
@ -53,7 +53,7 @@ _Note_: See [here](https://chainid.network/) for a comprehensive list of public
|
|||
|
||||
### `deposit` function
|
||||
|
||||
The deposit contract has a public `deposit` function to make deposits. It takes as arguments `pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96], deposit_data_root: bytes32`. The first three arguments populate a [`DepositData`](./beacon-chain.md#depositdata) object, and `deposit_data_root` is the expected `DepositData` root as a protection against malformatted calldata.
|
||||
The deposit contract has a public `deposit` function to make deposits. It takes as arguments `bytes calldata pubkey, bytes calldata withdrawal_credentials, bytes calldata signature, bytes32 deposit_data_root`. The first three arguments populate a [`DepositData`](./beacon-chain.md#depositdata) object, and `deposit_data_root` is the expected `DepositData` root as a protection against malformatted calldata.
|
||||
|
||||
#### Deposit amount
|
||||
|
||||
|
@ -72,8 +72,8 @@ The private key corresponding to `withdrawal_pubkey` will be required to initiat
|
|||
|
||||
Every Ethereum 1.0 deposit emits a `DepositEvent` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12-381 signature) is not verified by the deposit contract.
|
||||
|
||||
## Vyper code
|
||||
## Solidity code
|
||||
|
||||
The deposit contract source code, written in Vyper, is available [here](../../deposit_contract/contracts/validator_registration.vy).
|
||||
The deposit contract source code, written in Solidity, is available [here](../../solidity_deposit_contract/deposit_contract.sol).
|
||||
|
||||
*Note*: To save on gas, the deposit contract uses a progressive Merkle root calculation algorithm that requires only O(log(n)) storage. See [here](https://github.com/ethereum/research/blob/master/beacon_chain_impl/progressive_merkle_tree.py) for a Python implementation, and [here](https://github.com/runtimeverification/verified-smart-contracts/blob/master/deposit/formal-incremental-merkle-tree-algorithm.pdf) for a formal correctness proof.
|
||||
|
|
|
@ -98,14 +98,10 @@ This should be the genesis state for a full client.
|
|||
|
||||
*Note* With regards to fork choice, block headers are interchangeable with blocks. The spec is likely to move to headers for reduced overhead in test vectors and better encapsulation. Full implementations store blocks as part of their database and will often use full blocks when dealing with production fork choice.
|
||||
|
||||
_The block for `anchor_root` is incorrectly initialized to the block header, rather than the full block. This does not affect functionality but will be cleaned up in subsequent releases._
|
||||
|
||||
```python
|
||||
def get_forkchoice_store(anchor_state: BeaconState) -> Store:
|
||||
anchor_block_header = copy(anchor_state.latest_block_header)
|
||||
if anchor_block_header.state_root == Bytes32():
|
||||
anchor_block_header.state_root = hash_tree_root(anchor_state)
|
||||
anchor_root = hash_tree_root(anchor_block_header)
|
||||
def get_forkchoice_store(anchor_state: BeaconState, anchor_block: BeaconBlock) -> Store:
|
||||
assert anchor_block.state_root == hash_tree_root(anchor_state)
|
||||
anchor_root = hash_tree_root(anchor_block)
|
||||
anchor_epoch = get_current_epoch(anchor_state)
|
||||
justified_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
|
||||
finalized_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
|
||||
|
@ -115,7 +111,7 @@ def get_forkchoice_store(anchor_state: BeaconState) -> Store:
|
|||
justified_checkpoint=justified_checkpoint,
|
||||
finalized_checkpoint=finalized_checkpoint,
|
||||
best_justified_checkpoint=justified_checkpoint,
|
||||
blocks={anchor_root: anchor_block_header},
|
||||
blocks={anchor_root: copy(anchor_block)},
|
||||
block_states={anchor_root: copy(anchor_state)},
|
||||
checkpoint_states={justified_checkpoint: copy(anchor_state)},
|
||||
)
|
||||
|
|
|
@ -243,12 +243,11 @@ Each gossipsub [message](https://github.com/libp2p/go-libp2p-pubsub/blob/master/
|
|||
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:
|
||||
The `message-id` of a gossipsub message MUST be the first 8 bytes of the SHA-256 hash of the message data, i.e.:
|
||||
|
||||
```python
|
||||
message-id: base64(SHA256(message.data))
|
||||
message-id: SHA256(message.data)[0:8]
|
||||
```
|
||||
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:
|
||||
|
||||
|
@ -383,6 +382,7 @@ The `beacon_attestation_{subnet_id}` topics are used to propagate unaggregated a
|
|||
to the subnet `subnet_id` (typically beacon and persistent committees) to be aggregated before being gossiped to `beacon_aggregate_and_proof`.
|
||||
|
||||
The following validations MUST pass before forwarding the `attestation` on the subnet.
|
||||
- _[REJECT]_ The committee index is within the expected range -- i.e. `data.index < get_committee_count_per_slot(state, data.target.epoch)`.
|
||||
- _[REJECT]_ The attestation is for the correct subnet --
|
||||
i.e. `compute_subnet_for_attestation(committees_per_slot, attestation.data.slot, attestation.data.index) == subnet_id`,
|
||||
where `committees_per_slot = get_committee_count_per_slot(state, attestation.data.target.epoch)`,
|
||||
|
@ -391,8 +391,12 @@ The following validations MUST pass before forwarding the `attestation` on the s
|
|||
(within a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) --
|
||||
i.e. `attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= attestation.data.slot`
|
||||
(a client MAY queue future attestations for processing at the appropriate slot).
|
||||
- _[REJECT]_ The attestation's epoch matches its target -- i.e. `attestation.data.target.epoch ==
|
||||
compute_epoch_at_slot(attestation.data.slot)`
|
||||
- _[REJECT]_ The attestation is unaggregated --
|
||||
that is, it has exactly one participating validator (`len([bit in bit attestation.aggregation_bits if bit]) == 1`, i.e. exactly 1 bit is set).
|
||||
that is, it has exactly one participating validator (`len([bit for bit in attestation.aggregation_bits if bit]) == 1`, i.e. exactly 1 bit is set).
|
||||
- _[REJECT]_ The number of aggregation bits matches the committee size -- i.e.
|
||||
`len(attestation.aggregation_bits) == len(get_beacon_committee(state, data.slot, data.index))`.
|
||||
- _[IGNORE]_ There has been no other valid attestation seen on an attestation subnet
|
||||
that has an identical `attestation.data.target.epoch` and participating validator index.
|
||||
- _[REJECT]_ The signature of `attestation` is valid.
|
||||
|
@ -400,6 +404,8 @@ The following validations MUST pass before forwarding the `attestation` on the s
|
|||
(via both gossip and non-gossip sources)
|
||||
(a client MAY queue aggregates for processing once block is retrieved).
|
||||
- _[REJECT]_ The block being voted for (`attestation.data.beacon_block_root`) passes validation.
|
||||
- _[REJECT]_ The attestation's target block is an ancestor of the block named in the LMD vote -- i.e.
|
||||
`get_ancestor(store, attestation.data.beacon_block_root, compute_start_slot_at_epoch(attestation.data.target.epoch)) == attestation.data.target.root`
|
||||
- _[REJECT]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `attestation.data.beacon_block_root` -- i.e.
|
||||
`get_ancestor(store, attestation.data.beacon_block_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch))
|
||||
== store.finalized_checkpoint.root`
|
||||
|
@ -723,7 +729,7 @@ The request MUST be encoded as an SSZ-container.
|
|||
The response MUST consist of zero or more `response_chunk`.
|
||||
Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlock` payload.
|
||||
|
||||
Clients MUST keep a record of signed blocks seen since the since the start of the weak subjectivity period
|
||||
Clients MUST keep a record of signed blocks seen since the start of the weak subjectivity period
|
||||
and MUST support serving requests of blocks up to their own `head_block_root`.
|
||||
|
||||
Clients MUST respond with at least the first block that exists in the range, if they have it, and no more than `MAX_REQUEST_BLOCKS` blocks.
|
||||
|
|
|
@ -168,7 +168,7 @@ def get_committee_assignment(state: BeaconState,
|
|||
* ``assignment[2]`` is the slot at which the committee is assigned
|
||||
Return None if no assignment.
|
||||
"""
|
||||
next_epoch = get_current_epoch(state) + 1
|
||||
next_epoch = Epoch(get_current_epoch(state) + 1)
|
||||
assert epoch <= next_epoch
|
||||
|
||||
start_slot = compute_start_slot_at_epoch(epoch)
|
||||
|
@ -194,9 +194,14 @@ def is_proposer(state: BeaconState, validator_index: ValidatorIndex) -> bool:
|
|||
|
||||
### Lookahead
|
||||
|
||||
The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing, which must be checked during the epoch in question.
|
||||
The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead
|
||||
on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot.
|
||||
Note that this lookahead does not apply to proposing, which must be checked during the epoch in question.
|
||||
|
||||
`get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments by noting at which future slot they will have to attest and joining the committee index attestation subnet related to their committee assignment.
|
||||
`get_committee_assignment` should be called at the start of each epoch
|
||||
to get the assignment for the next epoch (`current_epoch + 1`).
|
||||
A validator should plan for future assignments by noting their assigned attestation
|
||||
slot and joining the committee index attestation subnet related to their committee assignment.
|
||||
|
||||
Specifically a validator should:
|
||||
* Call `get_committee_assignment(state, next_epoch, validator_index)` when checking for next epoch assignments.
|
||||
|
@ -214,9 +219,18 @@ A validator has two primary responsibilities to the beacon chain: [proposing blo
|
|||
|
||||
### Block proposal
|
||||
|
||||
A validator is expected to propose a [`SignedBeaconBlock`](./beacon-chain.md#signedbeaconblock) at the beginning of any slot during which `is_proposer(state, validator_index)` returns `True`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator creates, signs, and broadcasts a `block` that is a child of `parent` that satisfies a valid [beacon chain state transition](./beacon-chain.md#beacon-chain-state-transition-function).
|
||||
A validator is expected to propose a [`SignedBeaconBlock`](./beacon-chain.md#signedbeaconblock) at
|
||||
the beginning of any slot during which `is_proposer(state, validator_index)` returns `True`.
|
||||
To propose, the validator selects the `BeaconBlock`, `parent`,
|
||||
that in their view of the fork choice is the head of the chain during `slot - 1`.
|
||||
The validator creates, signs, and broadcasts a `block` that is a child of `parent`
|
||||
that satisfies a valid [beacon chain state transition](./beacon-chain.md#beacon-chain-state-transition-function).
|
||||
|
||||
There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (e.g. at 312,500 validators = 10 million ETH, that's once per ~6 weeks).
|
||||
There is one proposer per slot, so if there are N active validators any individual validator
|
||||
will on average be assigned to propose once per N slots (e.g. at 312,500 validators = 10 million ETH, that's once per ~6 weeks).
|
||||
|
||||
*Note*: In this section, `state` is the state of the slot for the block proposal _without_ the block yet applied.
|
||||
That is, `state` is the `previous_state` processed through any empty slots up to the assigned slot using `process_slots(previous_state, slot)`.
|
||||
|
||||
#### Preparing for a `BeaconBlock`
|
||||
|
||||
|
@ -251,7 +265,13 @@ def get_epoch_signature(state: BeaconState, block: BeaconBlock, privkey: int) ->
|
|||
|
||||
##### Eth1 Data
|
||||
|
||||
The `block.body.eth1_data` field is for block proposers to vote on recent Eth1 data. This recent data contains an Eth1 block hash as well as the associated deposit root (as calculated by the `get_deposit_root()` method of the deposit contract) and deposit count after execution of the corresponding Eth1 block. If over half of the block proposers in the current Eth1 voting period vote for the same `eth1_data` then `state.eth1_data` updates immediately allowing new deposits to be processed. Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`.
|
||||
The `block.body.eth1_data` field is for block proposers to vote on recent Eth1 data.
|
||||
This recent data contains an Eth1 block hash as well as the associated deposit root
|
||||
(as calculated by the `get_deposit_root()` method of the deposit contract) and
|
||||
deposit count after execution of the corresponding Eth1 block.
|
||||
If over half of the block proposers in the current Eth1 voting period vote for the same
|
||||
`eth1_data` then `state.eth1_data` updates immediately allowing new deposits to be processed.
|
||||
Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`.
|
||||
|
||||
###### `Eth1Block`
|
||||
|
||||
|
@ -440,7 +460,7 @@ def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, comm
|
|||
Compute the correct subnet for an attestation for Phase 0.
|
||||
Note, this mimics expected Phase 1 behavior where attestations will be mapped to their shard subnet.
|
||||
"""
|
||||
slots_since_epoch_start = slot % SLOTS_PER_EPOCH
|
||||
slots_since_epoch_start = uint64(slot % SLOTS_PER_EPOCH)
|
||||
committees_since_epoch_start = committees_per_slot * slots_since_epoch_start
|
||||
|
||||
return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
# Ethereum 2.0 Phase 0 -- Weak Subjectivity Guide
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
|
||||
## 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 -->
|
||||
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Constants](#constants)
|
||||
- [Weak Subjectivity Checkpoint](#weak-subjectivity-checkpoint)
|
||||
- [Weak Subjectivity Period](#weak-subjectivity-period)
|
||||
- [Calculating the Weak Subjectivity Period](#calculating-the-weak-subjectivity-period)
|
||||
- [Weak Subjectivity Sync](#weak-subjectivity-sync)
|
||||
- [Weak Subjectivity Sync Procedure](#weak-subjectivity-sync-procedure)
|
||||
- [Checking for Stale Weak Subjectivity Checkpoint](#checking-for-stale-weak-subjectivity-checkpoint)
|
||||
- [Distributing Weak Subjectivity Checkpoints](#distributing-weak-subjectivity-checkpoints)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
|
||||
## Introduction
|
||||
|
||||
This document is a guide for implementing the Weak Subjectivity protections in Phase 0 of Ethereum 2.0.
|
||||
This document is still a work-in-progress, and is subject to large changes.
|
||||
For more information about weak subjectivity and why it is required, please refer to:
|
||||
|
||||
- [Weak Subjectivity in Eth2.0](https://notes.ethereum.org/@adiasg/weak-subjectvity-eth2)
|
||||
- [Proof of Stake: How I Learned to Love Weak Subjectivity](https://blog.ethereum.org/2014/11/25/proof-stake-learned-love-weak-subjectivity/)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This document uses data structures, constants, functions, and terminology from
|
||||
[Phase 0 -- The Beacon Chain](./beacon-chain.md) and [Phase 0 -- Beacon Chain Fork Choice](./fork-choice.md).
|
||||
|
||||
## Constants
|
||||
|
||||
| Name | Value |
|
||||
|----------------|--------------|
|
||||
| `SAFETY_DECAY` | `uint64(10)` |
|
||||
|
||||
## Weak Subjectivity Checkpoint
|
||||
|
||||
Any `Checkpoint` can used be a Weak Subjectivity Checkpoint.
|
||||
These Weak Subjectivity Checkpoints are distributed by providers,
|
||||
downloaded by users and/or distributed as a part of clients, and used as input while syncing a client.
|
||||
|
||||
## Weak Subjectivity Period
|
||||
|
||||
The Weak Subjectivity Period is the number of recent epochs within which there
|
||||
must be a Weak Subjectivity Checkpoint to ensure that an attacker who takes control
|
||||
of the validator set at the beginning of the period is slashed at least a minimum threshold
|
||||
in the event that a conflicting `Checkpoint` is finalized.
|
||||
|
||||
`SAFETY_DECAY` is defined as the maximum percentage tolerable loss in the one-third
|
||||
safety margin of FFG finality. Thus, any attack exploiting the Weak Subjectivity Period has
|
||||
a safety margin of at least `1/3 - SAFETY_DECAY/100`.
|
||||
|
||||
### Calculating the Weak Subjectivity Period
|
||||
|
||||
*Note*: `compute_weak_subjectivity_period()` is planned to be updated when a more accurate calculation is made.
|
||||
|
||||
```python
|
||||
def compute_weak_subjectivity_period(state: BeaconState) -> uint64:
|
||||
weak_subjectivity_period = MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
validator_count = len(get_active_validator_indices(state, get_current_epoch(state)))
|
||||
if validator_count >= MIN_PER_EPOCH_CHURN_LIMIT * CHURN_LIMIT_QUOTIENT:
|
||||
weak_subjectivity_period += SAFETY_DECAY * CHURN_LIMIT_QUOTIENT / (2 * 100)
|
||||
else:
|
||||
weak_subjectivity_period += SAFETY_DECAY * validator_count / (2 * 100 * MIN_PER_EPOCH_CHURN_LIMIT)
|
||||
return weak_subjectivity_period
|
||||
```
|
||||
|
||||
*Details about the calculation*:
|
||||
- `100` appears in the denominator to get the actual percentage ratio from `SAFETY_DECAY`
|
||||
- For more information about other terms in this equation, refer to
|
||||
[Weak Subjectivity in Eth2.0](https://notes.ethereum.org/@adiasg/weak-subjectvity-eth2)
|
||||
|
||||
A brief reference for what these values look like in practice:
|
||||
|
||||
| `validator_count` | `weak_subjectivity_period` |
|
||||
| ---- | ---- |
|
||||
| 1024 | 268 |
|
||||
| 2048 | 281 |
|
||||
| 4096 | 307 |
|
||||
| 8192 | 358 |
|
||||
| 16384 | 460 |
|
||||
| 32768 | 665 |
|
||||
| 65536 | 1075 |
|
||||
| 131072 | 1894 |
|
||||
| 262144 | 3532 |
|
||||
| 524288 | 3532 |
|
||||
|
||||
## Weak Subjectivity Sync
|
||||
|
||||
Clients should allow users to input a Weak Subjectivity Checkpoint at startup, and guarantee that any successful sync leads to the given Weak Subjectivity Checkpoint along the canonical chain. If such a sync is not possible, the client should treat this as a critical and irrecoverable failure.
|
||||
|
||||
### Weak Subjectivity Sync Procedure
|
||||
|
||||
1. Input a Weak Subjectivity Checkpoint as a CLI parameter in `block_root:epoch_number` format,
|
||||
where `block_root` (an "0x" prefixed 32-byte hex string) and `epoch_number` (an integer) represent a valid `Checkpoint`.
|
||||
Example of the format:
|
||||
```
|
||||
0x8584188b86a9296932785cc2827b925f9deebacce6d72ad8d53171fa046b43d9:9544
|
||||
```
|
||||
2. - *IF* `epoch_number > store.finalized_checkpoint.epoch`,
|
||||
then *ASSERT* during block sync that block with root `block_root` is in the sync path at epoch `epoch_number`.
|
||||
Emit descriptive critical error if this assert fails, then exit client process.
|
||||
- *IF* `epoch_number <= store.finalized_checkpoint.epoch`,
|
||||
then *ASSERT* that the block in the canonical chain at epoch `epoch_number` has root `block_root`.
|
||||
Emit descriptive critical error if this assert fails, then exit client process.
|
||||
|
||||
### Checking for Stale Weak Subjectivity Checkpoint
|
||||
Clients may choose to validate that the input Weak Subjectivity Checkpoint is not stale at the time of startup.
|
||||
To support this mechanism, the client needs to take the state at the Weak Subjectivity Checkpoint as
|
||||
a CLI parameter input (or fetch the state associated with the input Weak Subjectivity Checkpoint from some source).
|
||||
The check can be implemented in the following way:
|
||||
|
||||
```python
|
||||
def is_within_weak_subjectivity_period(store: Store, ws_state: BeaconState, ws_checkpoint: Checkpoint) -> bool:
|
||||
# Clients may choose to validate the input state against the input Weak Subjectivity Checkpoint
|
||||
assert ws_state.latest_block_header.state_root == ws_checkpoint.root
|
||||
assert compute_epoch_at_slot(ws_state.slot) == ws_checkpoint.epoch
|
||||
|
||||
ws_period = compute_weak_subjectivity_period(ws_state)
|
||||
ws_state_epoch = compute_epoch_at_slot(ws_state.slot)
|
||||
current_epoch = compute_epoch_at_slot(get_current_slot(store))
|
||||
return current_epoch <= ws_state_epoch + ws_period
|
||||
```
|
||||
|
||||
## Distributing Weak Subjectivity Checkpoints
|
||||
This section will be updated soon.
|
|
@ -35,7 +35,6 @@
|
|||
- [`ShardState`](#shardstate)
|
||||
- [`ShardTransition`](#shardtransition)
|
||||
- [`CompactCommittee`](#compactcommittee)
|
||||
- [`AttestationCustodyBitWrapper`](#attestationcustodybitwrapper)
|
||||
- [Helper functions](#helper-functions)
|
||||
- [Misc](#misc-1)
|
||||
- [`compute_previous_slot`](#compute_previous_slot)
|
||||
|
@ -47,6 +46,7 @@
|
|||
- [`compute_updated_gasprice`](#compute_updated_gasprice)
|
||||
- [`compute_committee_source_epoch`](#compute_committee_source_epoch)
|
||||
- [Beacon state accessors](#beacon-state-accessors)
|
||||
- [Updated `get_committee_count_per_slot`](#updated-get_committee_count_per_slot)
|
||||
- [`get_active_shard_count`](#get_active_shard_count)
|
||||
- [`get_online_validator_indices`](#get_online_validator_indices)
|
||||
- [`get_shard_committee`](#get_shard_committee)
|
||||
|
@ -105,19 +105,20 @@ Configuration is not namespaced. Instead it is strictly an extension;
|
|||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `MAX_SHARDS` | `2**10` (= 1024) |
|
||||
| `LIGHT_CLIENT_COMMITTEE_SIZE` | `2**7` (= 128) |
|
||||
| `GASPRICE_ADJUSTMENT_COEFFICIENT` | `2**3` (= 8) |
|
||||
| `MAX_SHARDS` | `uint64(2**10)` (= 1024) |
|
||||
| `INITIAL_ACTIVE_SHARDS` | `uint64(2**6)` (= 64) |
|
||||
| `LIGHT_CLIENT_COMMITTEE_SIZE` | `uint64(2**7)` (= 128) |
|
||||
| `GASPRICE_ADJUSTMENT_COEFFICIENT` | `uint64(2**3)` (= 8) |
|
||||
|
||||
### Shard block configs
|
||||
|
||||
| Name | Value | Unit |
|
||||
| - | - | - |
|
||||
| `MAX_SHARD_BLOCK_SIZE` | `2**20` (= 1,048,576) | bytes |
|
||||
| `TARGET_SHARD_BLOCK_SIZE` | `2**18` (= 262,144) | bytes |
|
||||
| `SHARD_BLOCK_OFFSETS` | `[1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]` | - |
|
||||
| `MAX_SHARD_BLOCK_SIZE` | `uint64(2**20)` (= 1,048,576) | bytes |
|
||||
| `TARGET_SHARD_BLOCK_SIZE` | `uint64(2**18)` (= 262,144) | bytes |
|
||||
| `SHARD_BLOCK_OFFSETS` | `List[uint64, 12]([1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233])` | - |
|
||||
| `MAX_SHARD_BLOCKS_PER_ATTESTATION` | `len(SHARD_BLOCK_OFFSETS)` | - |
|
||||
| `BYTES_PER_CUSTODY_CHUNK` | `2**12` (= 4,096) | bytes |
|
||||
| `BYTES_PER_CUSTODY_CHUNK` | `uint64(2**12)` (= 4,096) | bytes |
|
||||
| `CUSTODY_RESPONSE_DEPTH` | `ceillog2(MAX_SHARD_BLOCK_SIZE // BYTES_PER_CUSTODY_CHUNK)` | - |
|
||||
|
||||
### Gwei values
|
||||
|
@ -408,15 +409,6 @@ class CompactCommittee(Container):
|
|||
compact_validators: List[uint64, MAX_VALIDATORS_PER_COMMITTEE]
|
||||
```
|
||||
|
||||
### `AttestationCustodyBitWrapper`
|
||||
|
||||
```python
|
||||
class AttestationCustodyBitWrapper(Container):
|
||||
attestation_data_root: Root
|
||||
block_index: uint64
|
||||
bit: boolean
|
||||
```
|
||||
|
||||
## Helper functions
|
||||
|
||||
### Misc
|
||||
|
@ -512,7 +504,7 @@ def compute_committee_source_epoch(epoch: Epoch, period: uint64) -> Epoch:
|
|||
"""
|
||||
Return the source epoch for computing the committee.
|
||||
"""
|
||||
source_epoch = epoch - epoch % period
|
||||
source_epoch = Epoch(epoch - epoch % period)
|
||||
if source_epoch >= period:
|
||||
source_epoch -= period # `period` epochs lookahead
|
||||
return source_epoch
|
||||
|
@ -520,11 +512,28 @@ def compute_committee_source_epoch(epoch: Epoch, period: uint64) -> Epoch:
|
|||
|
||||
### Beacon state accessors
|
||||
|
||||
#### Updated `get_committee_count_per_slot`
|
||||
|
||||
```python
|
||||
def get_committee_count_per_slot(state: BeaconState, epoch: Epoch) -> uint64:
|
||||
"""
|
||||
Return the number of committees in each slot for the given ``epoch``.
|
||||
"""
|
||||
return max(uint64(1), min(
|
||||
get_active_shard_count(state),
|
||||
uint64(len(get_active_validator_indices(state, epoch))) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
||||
))
|
||||
```
|
||||
|
||||
#### `get_active_shard_count`
|
||||
|
||||
```python
|
||||
def get_active_shard_count(state: BeaconState) -> uint64:
|
||||
return len(state.shard_states) # May adapt in the future, or change over time.
|
||||
"""
|
||||
Return the number of active shards.
|
||||
Note that this puts an upper bound on the number of committees per slot.
|
||||
"""
|
||||
return INITIAL_ACTIVE_SHARDS
|
||||
```
|
||||
|
||||
#### `get_online_validator_indices`
|
||||
|
@ -545,12 +554,11 @@ def get_shard_committee(beacon_state: BeaconState, epoch: Epoch, shard: Shard) -
|
|||
source_epoch = compute_committee_source_epoch(epoch, SHARD_COMMITTEE_PERIOD)
|
||||
active_validator_indices = get_active_validator_indices(beacon_state, source_epoch)
|
||||
seed = get_seed(beacon_state, source_epoch, DOMAIN_SHARD_COMMITTEE)
|
||||
active_shard_count = get_active_shard_count(beacon_state)
|
||||
return compute_committee(
|
||||
indices=active_validator_indices,
|
||||
seed=seed,
|
||||
index=shard,
|
||||
count=active_shard_count,
|
||||
count=get_active_shard_count(beacon_state),
|
||||
)
|
||||
```
|
||||
|
||||
|
@ -567,7 +575,7 @@ def get_light_client_committee(beacon_state: BeaconState, epoch: Epoch) -> Seque
|
|||
return compute_committee(
|
||||
indices=active_validator_indices,
|
||||
seed=seed,
|
||||
index=0,
|
||||
index=uint64(0),
|
||||
count=get_active_shard_count(beacon_state),
|
||||
)[:LIGHT_CLIENT_COMMITTEE_SIZE]
|
||||
```
|
||||
|
@ -593,10 +601,10 @@ def get_committee_count_delta(state: BeaconState, start_slot: Slot, stop_slot: S
|
|||
"""
|
||||
Return the sum of committee counts in range ``[start_slot, stop_slot)``.
|
||||
"""
|
||||
return sum(
|
||||
return uint64(sum(
|
||||
get_committee_count_per_slot(state, compute_epoch_at_slot(Slot(slot)))
|
||||
for slot in range(start_slot, stop_slot)
|
||||
)
|
||||
))
|
||||
```
|
||||
|
||||
#### `get_start_shard`
|
||||
|
@ -617,10 +625,11 @@ def get_start_shard(state: BeaconState, slot: Slot) -> Shard:
|
|||
else:
|
||||
# Previous epoch
|
||||
shard_delta = get_committee_count_delta(state, start_slot=slot, stop_slot=current_epoch_start_slot)
|
||||
max_committees_per_epoch = MAX_COMMITTEES_PER_SLOT * SLOTS_PER_EPOCH
|
||||
max_committees_per_slot = active_shard_count
|
||||
max_committees_in_span = max_committees_per_slot * (current_epoch_start_slot - slot)
|
||||
return Shard(
|
||||
# Ensure positive
|
||||
(state.current_epoch_start_shard + max_committees_per_epoch * active_shard_count - shard_delta)
|
||||
(state.current_epoch_start_shard + max_committees_in_span - shard_delta)
|
||||
% active_shard_count
|
||||
)
|
||||
```
|
||||
|
@ -752,7 +761,6 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
|||
def validate_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||
data = attestation.data
|
||||
assert data.index < get_committee_count_per_slot(state, data.target.epoch)
|
||||
assert data.index < get_active_shard_count(state)
|
||||
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
|
||||
|
@ -760,20 +768,24 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|||
committee = get_beacon_committee(state, data.slot, data.index)
|
||||
assert len(attestation.aggregation_bits) == len(committee)
|
||||
|
||||
if attestation.data.target.epoch == get_current_epoch(state):
|
||||
assert attestation.data.source == state.current_justified_checkpoint
|
||||
if data.target.epoch == get_current_epoch(state):
|
||||
assert data.source == state.current_justified_checkpoint
|
||||
else:
|
||||
assert attestation.data.source == state.previous_justified_checkpoint
|
||||
assert data.source == state.previous_justified_checkpoint
|
||||
|
||||
# Type 1: on-time attestations
|
||||
if is_on_time_attestation(state, attestation.data):
|
||||
if is_on_time_attestation(state, data):
|
||||
# Correct parent block root
|
||||
assert data.beacon_block_root == get_block_root_at_slot(state, compute_previous_slot(state.slot))
|
||||
# Correct shard number
|
||||
shard = compute_shard_from_committee_index(state, attestation.data.index, attestation.data.slot)
|
||||
assert attestation.data.shard == shard
|
||||
# On-time attestations should have a non-empty shard transition root
|
||||
assert attestation.data.shard_transition_root != hash_tree_root(ShardTransition())
|
||||
shard = compute_shard_from_committee_index(state, data.index, data.slot)
|
||||
assert data.shard == shard
|
||||
# NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors.
|
||||
if data.slot > GENESIS_SLOT:
|
||||
# On-time attestations should have a non-empty shard transition root
|
||||
assert data.shard_transition_root != hash_tree_root(ShardTransition())
|
||||
else:
|
||||
assert data.shard_transition_root == hash_tree_root(ShardTransition())
|
||||
# Type 2: no shard transition
|
||||
else:
|
||||
# Ensure delayed attestation
|
||||
|
@ -811,7 +823,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|||
```python
|
||||
def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTransition) -> None:
|
||||
# TODO: only need to check it once when phase 1 starts
|
||||
assert state.slot > PHASE_1_GENESIS_SLOT
|
||||
assert state.slot > PHASE_1_FORK_SLOT
|
||||
|
||||
# Correct data root count
|
||||
offset_slots = get_offset_slots(state, shard)
|
||||
|
@ -863,9 +875,10 @@ def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTr
|
|||
# Verify combined proposer signature
|
||||
assert optional_aggregate_verify(pubkeys, signing_roots, transition.proposer_signature_aggregate)
|
||||
|
||||
# Save updated state
|
||||
state.shard_states[shard] = transition.shard_states[len(transition.shard_states) - 1]
|
||||
state.shard_states[shard].slot = compute_previous_slot(state.slot)
|
||||
# Copy and save updated shard state
|
||||
shard_state = copy(transition.shard_states[len(transition.shard_states) - 1])
|
||||
shard_state.slot = compute_previous_slot(state.slot)
|
||||
state.shard_states[shard] = shard_state
|
||||
```
|
||||
|
||||
###### `process_crosslink_for_shard`
|
||||
|
@ -976,8 +989,11 @@ def verify_empty_shard_transition(state: BeaconState, shard_transitions: Sequenc
|
|||
def process_shard_transitions(state: BeaconState,
|
||||
shard_transitions: Sequence[ShardTransition],
|
||||
attestations: Sequence[Attestation]) -> None:
|
||||
# Process crosslinks
|
||||
process_crosslinks(state, shard_transitions, attestations)
|
||||
# NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors.
|
||||
if compute_previous_slot(state.slot) > GENESIS_SLOT:
|
||||
# Process crosslinks
|
||||
process_crosslinks(state, shard_transitions, attestations)
|
||||
|
||||
# Verify the empty proposal shard states
|
||||
assert verify_empty_shard_transition(state, shard_transitions)
|
||||
```
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
- [`CustodyKeyReveal`](#custodykeyreveal)
|
||||
- [`EarlyDerivedSecretReveal`](#earlyderivedsecretreveal)
|
||||
- [Helpers](#helpers)
|
||||
- [`get_block_data_merkle_root`](#get_block_data_merkle_root)
|
||||
- [`replace_empty_or_append`](#replace_empty_or_append)
|
||||
- [`legendre_bit`](#legendre_bit)
|
||||
- [`get_custody_atoms`](#get_custody_atoms)
|
||||
|
@ -57,10 +56,10 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
|
|||
|
||||
| Name | Value | Unit |
|
||||
| - | - | - |
|
||||
| `CUSTODY_PRIME` | `2 ** 256 - 189` | - |
|
||||
| `CUSTODY_SECRETS` | `3` | - |
|
||||
| `BYTES_PER_CUSTODY_ATOM` | `32` | bytes |
|
||||
| `CUSTODY_PROBABILITY_EXPONENT` | `10` | - |
|
||||
| `CUSTODY_PRIME` | `int(2 ** 256 - 189)` | - |
|
||||
| `CUSTODY_SECRETS` | `uint64(3)` | - |
|
||||
| `BYTES_PER_CUSTODY_ATOM` | `uint64(32)` | bytes |
|
||||
| `CUSTODY_PROBABILITY_EXPONENT` | `uint64(10)` | - |
|
||||
|
||||
## Configuration
|
||||
|
||||
|
@ -68,30 +67,29 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
|
|||
|
||||
| Name | Value | Unit | Duration |
|
||||
| - | - | :-: | :-: |
|
||||
| `RANDAO_PENALTY_EPOCHS` | `2**1` (= 2) | epochs | 12.8 minutes |
|
||||
| `EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS` | `2**15` (= 32,768) | epochs | ~146 days |
|
||||
| `EPOCHS_PER_CUSTODY_PERIOD` | `2**14` (= 16,384) | epochs | ~73 days |
|
||||
| `CUSTODY_PERIOD_TO_RANDAO_PADDING` | `2**11` (= 2,048) | epochs | ~9 days |
|
||||
| `MAX_CHUNK_CHALLENGE_DELAY` | `2**15` (= 32,768) | epochs | ~146 days |
|
||||
| `CHUNK_RESPONSE_DEADLINE` | `2**14` (= 16,384) | epochs | ~73 days |
|
||||
| `RANDAO_PENALTY_EPOCHS` | `uint64(2**1)` (= 2) | epochs | 12.8 minutes |
|
||||
| `EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS` | `uint64(2**15)` (= 32,768) | epochs | ~146 days |
|
||||
| `EPOCHS_PER_CUSTODY_PERIOD` | `uint64(2**14)` (= 16,384) | epochs | ~73 days |
|
||||
| `CUSTODY_PERIOD_TO_RANDAO_PADDING` | `uint64(2**11)` (= 2,048) | epochs | ~9 days |
|
||||
| `MAX_CHUNK_CHALLENGE_DELAY` | `uint64(2**15)` (= 32,768) | epochs | ~146 days |
|
||||
|
||||
### Max operations per block
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `MAX_CUSTODY_CHUNK_CHALLENGE_RECORDS` | `2**20` (= 1,048,576) |
|
||||
| `MAX_CUSTODY_KEY_REVEALS` | `2**8` (= 256) |
|
||||
| `MAX_EARLY_DERIVED_SECRET_REVEALS` | `2**0` (= 1) |
|
||||
| `MAX_CUSTODY_CHUNK_CHALLENGES` | `2**2` (= 4) |
|
||||
| `MAX_CUSTODY_CHUNK_CHALLENGE_RESPONSES` | `2**4` (= 16) |
|
||||
| `MAX_CUSTODY_SLASHINGS` | `2**0` (= 1) |
|
||||
| `MAX_CUSTODY_CHUNK_CHALLENGE_RECORDS` | `uint64(2**20)` (= 1,048,576) |
|
||||
| `MAX_CUSTODY_KEY_REVEALS` | `uint64(2**8)` (= 256) |
|
||||
| `MAX_EARLY_DERIVED_SECRET_REVEALS` | `uint64(2**0)` (= 1) |
|
||||
| `MAX_CUSTODY_CHUNK_CHALLENGES` | `uint64(2**2)` (= 4) |
|
||||
| `MAX_CUSTODY_CHUNK_CHALLENGE_RESPONSES` | `uint64(2**4)` (= 16) |
|
||||
| `MAX_CUSTODY_SLASHINGS` | `uint64(2**0)` (= 1) |
|
||||
|
||||
### Reward and penalty quotients
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `EARLY_DERIVED_SECRET_REVEAL_SLOT_REWARD_MULTIPLE` | `2**1` (= 2) |
|
||||
| `MINOR_REWARD_QUOTIENT` | `2**8` (= 256) |
|
||||
| `EARLY_DERIVED_SECRET_REVEAL_SLOT_REWARD_MULTIPLE` | `uint64(2**1)` (= 2) |
|
||||
| `MINOR_REWARD_QUOTIENT` | `uint64(2**8)` (= 256) |
|
||||
|
||||
## Data structures
|
||||
|
||||
|
@ -127,7 +125,7 @@ class CustodyChunkResponse(Container):
|
|||
challenge_index: uint64
|
||||
chunk_index: uint64
|
||||
chunk: ByteVector[BYTES_PER_CUSTODY_CHUNK]
|
||||
branch: Vector[Root, CUSTODY_RESPONSE_DEPTH]
|
||||
branch: Vector[Root, CUSTODY_RESPONSE_DEPTH + 1]
|
||||
```
|
||||
|
||||
#### `CustodySlashing`
|
||||
|
@ -180,13 +178,8 @@ class EarlyDerivedSecretReveal(Container):
|
|||
mask: Bytes32
|
||||
```
|
||||
|
||||
|
||||
## Helpers
|
||||
|
||||
### `get_block_data_merkle_root`
|
||||
|
||||
`get_block_data_merkle_root(data: ByteList) -> Root` is the function that returns the Merkle root of the block data without the length mix-in.
|
||||
|
||||
### `replace_empty_or_append`
|
||||
|
||||
```python
|
||||
|
@ -272,12 +265,12 @@ def universal_hash_function(data_chunks: Sequence[bytes], secrets: Sequence[int]
|
|||
### `compute_custody_bit`
|
||||
|
||||
```python
|
||||
def compute_custody_bit(key: BLSSignature, data: ByteList[MAX_SHARD_BLOCK_SIZE]) -> bit:
|
||||
def compute_custody_bit(key: BLSSignature, data: ByteList) -> bit:
|
||||
custody_atoms = get_custody_atoms(data)
|
||||
secrets = get_custody_secrets(key)
|
||||
uhf = universal_hash_function(custody_atoms, secrets)
|
||||
legendre_bits = [legendre_bit(uhf + secrets[0] + i, CUSTODY_PRIME) for i in range(CUSTODY_PROBABILITY_EXPONENT)]
|
||||
return all(legendre_bits)
|
||||
return bit(all(legendre_bits))
|
||||
```
|
||||
|
||||
### `get_randao_epoch_for_custody_period`
|
||||
|
@ -323,7 +316,7 @@ def process_chunk_challenge(state: BeaconState, challenge: CustodyChunkChallenge
|
|||
# Verify the attestation
|
||||
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, challenge.attestation))
|
||||
# Verify it is not too late to challenge the attestation
|
||||
max_attestation_challenge_epoch = challenge.attestation.data.target.epoch + MAX_CHUNK_CHALLENGE_DELAY
|
||||
max_attestation_challenge_epoch = Epoch(challenge.attestation.data.target.epoch + MAX_CHUNK_CHALLENGE_DELAY)
|
||||
assert get_current_epoch(state) <= max_attestation_challenge_epoch
|
||||
# Verify it is not too late to challenge the responder
|
||||
responder = state.validators[challenge.responder_index]
|
||||
|
@ -381,7 +374,7 @@ def process_chunk_challenge_response(state: BeaconState,
|
|||
assert is_valid_merkle_branch(
|
||||
leaf=hash_tree_root(response.chunk),
|
||||
branch=response.branch,
|
||||
depth=CUSTODY_RESPONSE_DEPTH,
|
||||
depth=CUSTODY_RESPONSE_DEPTH + 1, # Add 1 for the List length mix-in
|
||||
index=response.chunk_index,
|
||||
root=challenge.data_root,
|
||||
)
|
||||
|
@ -445,7 +438,7 @@ def process_early_derived_secret_reveal(state: BeaconState, reveal: EarlyDerived
|
|||
Note that this function mutates ``state``.
|
||||
"""
|
||||
revealed_validator = state.validators[reveal.revealed_index]
|
||||
derived_secret_location = reveal.epoch % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS
|
||||
derived_secret_location = uint64(reveal.epoch % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS)
|
||||
|
||||
assert reveal.epoch >= get_current_epoch(state) + RANDAO_PENALTY_EPOCHS
|
||||
assert reveal.epoch < get_current_epoch(state) + EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS
|
||||
|
@ -522,12 +515,8 @@ def process_custody_slashing(state: BeaconState, signed_custody_slashing: Signed
|
|||
shard_transition = custody_slashing.shard_transition
|
||||
assert hash_tree_root(shard_transition) == attestation.data.shard_transition_root
|
||||
# Verify that the provided data matches the shard-transition
|
||||
assert (
|
||||
get_block_data_merkle_root(custody_slashing.data)
|
||||
== shard_transition.shard_data_roots[custody_slashing.data_index]
|
||||
)
|
||||
assert len(custody_slashing.data) == shard_transition.shard_block_lengths[custody_slashing.data_index]
|
||||
|
||||
assert hash_tree_root(custody_slashing.data) == shard_transition.shard_data_roots[custody_slashing.data_index]
|
||||
# Verify existence and participation of claimed malefactor
|
||||
attesters = get_attesting_indices(state, attestation.data, attestation.aggregation_bits)
|
||||
assert custody_slashing.malefactor_index in attesters
|
||||
|
|
|
@ -9,8 +9,13 @@
|
|||
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [Helpers](#helpers)
|
||||
- [Extended `LatestMessage`](#extended-latestmessage)
|
||||
- [Updated data structures](#updated-data-structures)
|
||||
- [Extended `Store`](#extended-store)
|
||||
- [New data structures](#new-data-structures)
|
||||
- [`ShardLatestMessage`](#shardlatestmessage)
|
||||
- [`ShardStore`](#shardstore)
|
||||
- [Updated helpers](#updated-helpers)
|
||||
- [Updated `get_forkchoice_store`](#updated-get_forkchoice_store)
|
||||
- [Updated `update_latest_messages`](#updated-update_latest_messages)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
@ -20,17 +25,72 @@
|
|||
|
||||
This document is the beacon chain fork choice spec for part of Ethereum 2.0 Phase 1.
|
||||
|
||||
### Helpers
|
||||
### Updated data structures
|
||||
|
||||
#### Extended `LatestMessage`
|
||||
#### Extended `Store`
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Store(object):
|
||||
time: uint64
|
||||
genesis_time: uint64
|
||||
justified_checkpoint: Checkpoint
|
||||
finalized_checkpoint: Checkpoint
|
||||
best_justified_checkpoint: Checkpoint
|
||||
blocks: Dict[Root, BeaconBlock] = field(default_factory=dict)
|
||||
block_states: Dict[Root, BeaconState] = field(default_factory=dict)
|
||||
checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict)
|
||||
latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict)
|
||||
shard_stores: Dict[Shard, ShardStore] = field(default_factory=dict)
|
||||
```
|
||||
|
||||
### New data structures
|
||||
|
||||
#### `ShardLatestMessage`
|
||||
|
||||
```python
|
||||
@dataclass(eq=True, frozen=True)
|
||||
class LatestMessage(object):
|
||||
class ShardLatestMessage(object):
|
||||
epoch: Epoch
|
||||
root: Root
|
||||
```
|
||||
|
||||
#### `ShardStore`
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class ShardStore:
|
||||
shard: Shard
|
||||
shard_root: Root
|
||||
signed_blocks: Dict[Root, SignedShardBlock] = field(default_factory=dict)
|
||||
block_states: Dict[Root, ShardState] = field(default_factory=dict)
|
||||
latest_messages: Dict[ValidatorIndex, ShardLatestMessage] = field(default_factory=dict)
|
||||
```
|
||||
|
||||
### Updated helpers
|
||||
|
||||
#### Updated `get_forkchoice_store`
|
||||
|
||||
```python
|
||||
def get_forkchoice_store(anchor_state: BeaconState, anchor_block: BeaconBlock) -> Store:
|
||||
assert anchor_block.state_root == hash_tree_root(anchor_state)
|
||||
anchor_root = hash_tree_root(anchor_block)
|
||||
anchor_epoch = get_current_epoch(anchor_state)
|
||||
justified_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
|
||||
finalized_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
|
||||
return Store(
|
||||
time=anchor_state.genesis_time + SECONDS_PER_SLOT * anchor_state.slot,
|
||||
genesis_time=anchor_state.genesis_time,
|
||||
justified_checkpoint=justified_checkpoint,
|
||||
finalized_checkpoint=finalized_checkpoint,
|
||||
best_justified_checkpoint=justified_checkpoint,
|
||||
blocks={anchor_root: copy(anchor_block)},
|
||||
block_states={anchor_root: anchor_state.copy()},
|
||||
checkpoint_states={justified_checkpoint: anchor_state.copy()},
|
||||
shard_stores={
|
||||
Shard(shard): get_forkchoice_shard_store(anchor_state, Shard(shard))
|
||||
for shard in range(get_active_shard_count(anchor_state))
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
#### Updated `update_latest_messages`
|
||||
|
@ -43,7 +103,7 @@ def update_latest_messages(store: Store, attesting_indices: Sequence[ValidatorIn
|
|||
shard = attestation.data.shard
|
||||
for i in attesting_indices:
|
||||
if i not in store.latest_messages or target.epoch > store.latest_messages[i].epoch:
|
||||
store.latest_messages[i] = LatestMessage(
|
||||
epoch=target.epoch, root=beacon_block_root, shard=shard, shard_root=attestation.data.shard_head_root
|
||||
)
|
||||
store.latest_messages[i] = LatestMessage(epoch=target.epoch, root=beacon_block_root)
|
||||
shard_latest_message = ShardLatestMessage(epoch=target.epoch, root=attestation.data.shard_head_root)
|
||||
store.shard_stores[shard].latest_messages[i] = shard_latest_message
|
||||
```
|
||||
|
|
|
@ -35,18 +35,17 @@ Warning: this configuration is not definitive.
|
|||
| Name | Value |
|
||||
| - | - |
|
||||
| `PHASE_1_FORK_VERSION` | `Version('0x01000000')` |
|
||||
| `PHASE_1_GENESIS_SLOT` | `2**5` **TBD** |
|
||||
| `INITIAL_ACTIVE_SHARDS` | `2**6` (= 64) |
|
||||
| `PHASE_1_FORK_SLOT` | `Slot(0)` **TBD** |
|
||||
|
||||
## Fork to Phase 1
|
||||
|
||||
### Fork trigger
|
||||
|
||||
TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at slot `PHASE_1_GENESIS_SLOT`, where `PHASE_1_GENESIS_SLOT % SLOTS_PER_EPOCH == 0`.
|
||||
TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at slot `PHASE_1_FORK_SLOT`, where `PHASE_1_FORK_SLOT % SLOTS_PER_EPOCH == 0`.
|
||||
|
||||
### Upgrading the state
|
||||
|
||||
After `process_slots` of Phase 0 finishes, if `state.slot == PHASE_1_GENESIS_SLOT`, an irregular state change is made to upgrade to Phase 1.
|
||||
After `process_slots` of Phase 0 finishes, if `state.slot == PHASE_1_FORK_SLOT`, an irregular state change is made to upgrade to Phase 1.
|
||||
|
||||
```python
|
||||
def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState:
|
||||
|
@ -102,7 +101,7 @@ def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState:
|
|||
current_epoch_start_shard=Shard(0),
|
||||
shard_states=List[ShardState, MAX_SHARDS](
|
||||
ShardState(
|
||||
slot=pre.slot,
|
||||
slot=compute_previous_slot(pre.slot),
|
||||
gasprice=MIN_GASPRICE,
|
||||
latest_block_root=Root(),
|
||||
) for i in range(INITIAL_ACTIVE_SHARDS)
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
- [Introduction](#introduction)
|
||||
- [Fork choice](#fork-choice)
|
||||
- [Helpers](#helpers)
|
||||
- [`ShardStore`](#shardstore)
|
||||
- [`get_forkchoice_shard_store`](#get_forkchoice_shard_store)
|
||||
- [`get_shard_latest_attesting_balance`](#get_shard_latest_attesting_balance)
|
||||
- [`get_shard_head`](#get_shard_head)
|
||||
|
@ -30,16 +29,6 @@ This document is the shard chain fork choice spec for part of Ethereum 2.0 Phase
|
|||
|
||||
### Helpers
|
||||
|
||||
#### `ShardStore`
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class ShardStore:
|
||||
shard: Shard
|
||||
signed_blocks: Dict[Root, SignedShardBlock] = field(default_factory=dict)
|
||||
block_states: Dict[Root, ShardState] = field(default_factory=dict)
|
||||
```
|
||||
|
||||
#### `get_forkchoice_shard_store`
|
||||
|
||||
```python
|
||||
|
@ -48,7 +37,7 @@ def get_forkchoice_shard_store(anchor_state: BeaconState, shard: Shard) -> Shard
|
|||
shard=shard,
|
||||
signed_blocks={
|
||||
anchor_state.shard_states[shard].latest_block_root: SignedShardBlock(
|
||||
message=ShardBlock(slot=anchor_state.slot, shard=shard)
|
||||
message=ShardBlock(slot=compute_previous_slot(anchor_state.slot), shard=shard)
|
||||
)
|
||||
},
|
||||
block_states={anchor_state.shard_states[shard].latest_block_root: anchor_state.copy().shard_states[shard]},
|
||||
|
@ -58,18 +47,21 @@ def get_forkchoice_shard_store(anchor_state: BeaconState, shard: Shard) -> Shard
|
|||
#### `get_shard_latest_attesting_balance`
|
||||
|
||||
```python
|
||||
def get_shard_latest_attesting_balance(store: Store, shard_store: ShardStore, root: Root) -> Gwei:
|
||||
def get_shard_latest_attesting_balance(store: Store, shard: Shard, root: Root) -> Gwei:
|
||||
shard_store = store.shard_stores[shard]
|
||||
state = store.checkpoint_states[store.justified_checkpoint]
|
||||
active_indices = get_active_validator_indices(state, get_current_epoch(state))
|
||||
return Gwei(sum(
|
||||
state.validators[i].effective_balance for i in active_indices
|
||||
if (
|
||||
i in store.latest_messages
|
||||
i in shard_store.latest_messages
|
||||
# TODO: check the latest message logic: currently, validator's previous vote of another shard
|
||||
# would be ignored once their newer vote is accepted. Check if it makes sense.
|
||||
and store.latest_messages[i].shard == shard_store.shard
|
||||
and get_shard_ancestor(
|
||||
store, shard_store, store.latest_messages[i].shard_root, shard_store.signed_blocks[root].message.slot
|
||||
store,
|
||||
shard,
|
||||
shard_store.latest_messages[i].root,
|
||||
shard_store.signed_blocks[root].message.slot,
|
||||
) == root
|
||||
)
|
||||
))
|
||||
|
@ -78,10 +70,14 @@ def get_shard_latest_attesting_balance(store: Store, shard_store: ShardStore, ro
|
|||
#### `get_shard_head`
|
||||
|
||||
```python
|
||||
def get_shard_head(store: Store, shard_store: ShardStore) -> Root:
|
||||
def get_shard_head(store: Store, shard: Shard) -> Root:
|
||||
# Execute the LMD-GHOST fork choice
|
||||
"""
|
||||
Execute the LMD-GHOST fork choice.
|
||||
"""
|
||||
shard_store = store.shard_stores[shard]
|
||||
beacon_head_root = get_head(store)
|
||||
shard_head_state = store.block_states[beacon_head_root].shard_states[shard_store.shard]
|
||||
shard_head_state = store.block_states[beacon_head_root].shard_states[shard]
|
||||
shard_head_root = shard_head_state.latest_block_root
|
||||
shard_blocks = {
|
||||
root: signed_shard_block.message for root, signed_shard_block in shard_store.signed_blocks.items()
|
||||
|
@ -97,17 +93,18 @@ def get_shard_head(store: Store, shard_store: ShardStore) -> Root:
|
|||
return shard_head_root
|
||||
# Sort by latest attesting balance with ties broken lexicographically
|
||||
shard_head_root = max(
|
||||
children, key=lambda root: (get_shard_latest_attesting_balance(store, shard_store, root), root)
|
||||
children, key=lambda root: (get_shard_latest_attesting_balance(store, shard, root), root)
|
||||
)
|
||||
```
|
||||
|
||||
#### `get_shard_ancestor`
|
||||
|
||||
```python
|
||||
def get_shard_ancestor(store: Store, shard_store: ShardStore, root: Root, slot: Slot) -> Root:
|
||||
def get_shard_ancestor(store: Store, shard: Shard, root: Root, slot: Slot) -> Root:
|
||||
shard_store = store.shard_stores[shard]
|
||||
block = shard_store.signed_blocks[root].message
|
||||
if block.slot > slot:
|
||||
return get_shard_ancestor(store, shard_store, block.shard_parent_root, slot)
|
||||
return get_shard_ancestor(store, shard, block.shard_parent_root, slot)
|
||||
elif block.slot == slot:
|
||||
return root
|
||||
else:
|
||||
|
@ -118,17 +115,17 @@ def get_shard_ancestor(store: Store, shard_store: ShardStore, root: Root, slot:
|
|||
#### `get_pending_shard_blocks`
|
||||
|
||||
```python
|
||||
def get_pending_shard_blocks(store: Store, shard_store: ShardStore) -> Sequence[SignedShardBlock]:
|
||||
def get_pending_shard_blocks(store: Store, shard: Shard) -> Sequence[SignedShardBlock]:
|
||||
"""
|
||||
Return the canonical shard block branch that has not yet been crosslinked.
|
||||
"""
|
||||
shard = shard_store.shard
|
||||
shard_store = store.shard_stores[shard]
|
||||
|
||||
beacon_head_root = get_head(store)
|
||||
beacon_head_state = store.block_states[beacon_head_root]
|
||||
latest_shard_block_root = beacon_head_state.shard_states[shard].latest_block_root
|
||||
|
||||
shard_head_root = get_shard_head(store, shard_store)
|
||||
shard_head_root = get_shard_head(store, shard)
|
||||
root = shard_head_root
|
||||
signed_shard_blocks = []
|
||||
while root != latest_shard_block_root:
|
||||
|
@ -145,13 +142,10 @@ def get_pending_shard_blocks(store: Store, shard_store: ShardStore) -> Sequence[
|
|||
#### `on_shard_block`
|
||||
|
||||
```python
|
||||
def on_shard_block(store: Store, shard_store: ShardStore, signed_shard_block: SignedShardBlock) -> None:
|
||||
def on_shard_block(store: Store, signed_shard_block: SignedShardBlock) -> None:
|
||||
shard_block = signed_shard_block.message
|
||||
shard = shard_store.shard
|
||||
|
||||
# Check shard
|
||||
# TODO: check it in networking spec
|
||||
assert shard_block.shard == shard
|
||||
shard = shard_block.shard
|
||||
shard_store = store.shard_stores[shard]
|
||||
|
||||
# Check shard parent exists
|
||||
assert shard_block.shard_parent_root in shard_store.block_states
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
- [`FullAttestation`](#fullattestation)
|
||||
- [Timing](#timing)
|
||||
- [Attestation data](#attestation-data)
|
||||
- [Head shard root](#head-shard-root)
|
||||
- [Shard head root](#shard-head-root)
|
||||
- [Shard transition](#shard-transition)
|
||||
- [Construct attestation](#construct-attestation)
|
||||
- [Attestation Aggregation](#attestation-aggregation)
|
||||
|
@ -267,9 +267,9 @@ A validator should create and broadcast the `attestation` to the associated atte
|
|||
|
||||
*Note*: We assume that the fork choice only follows branches with valid `offset_slots` with respect to the most recent beacon state shard transition for the queried shard.
|
||||
|
||||
##### Head shard root
|
||||
##### Shard head root
|
||||
|
||||
Set `attestation_data.shard_head_root = hash_tree_root(shard_head_block)`.
|
||||
If `attestation_data.slot == GENESIS_SLOT`, set `attestation_data.shard_head_root = Root()`. Otherwise, set `attestation_data.shard_head_root = hash_tree_root(shard_head_block)`.
|
||||
|
||||
##### Shard transition
|
||||
|
||||
|
@ -281,9 +281,9 @@ def get_shard_transition_fields(
|
|||
shard: Shard,
|
||||
shard_blocks: Sequence[SignedShardBlock],
|
||||
) -> Tuple[Sequence[uint64], Sequence[Root], Sequence[ShardState]]:
|
||||
shard_states = []
|
||||
shard_data_roots = []
|
||||
shard_block_lengths = []
|
||||
shard_block_lengths = [] # type: PyList[uint64]
|
||||
shard_data_roots = [] # type: PyList[Root]
|
||||
shard_states = [] # type: PyList[ShardState]
|
||||
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
shard_block_slots = [shard_block.message.slot for shard_block in shard_blocks]
|
||||
|
@ -294,14 +294,14 @@ def get_shard_transition_fields(
|
|||
for slot in offset_slots:
|
||||
if slot in shard_block_slots:
|
||||
shard_block = shard_blocks[shard_block_slots.index(slot)]
|
||||
shard_data_roots.append(get_block_data_merkle_root(shard_block.message.body))
|
||||
shard_data_roots.append(hash_tree_root(shard_block.message.body))
|
||||
else:
|
||||
shard_block = SignedShardBlock(message=ShardBlock(slot=slot, shard=shard))
|
||||
shard_data_roots.append(Root())
|
||||
shard_state = shard_state.copy()
|
||||
process_shard_block(shard_state, shard_block.message)
|
||||
shard_states.append(shard_state)
|
||||
shard_block_lengths.append(len(shard_block.message.body))
|
||||
shard_block_lengths.append(uint64(len(shard_block.message.body)))
|
||||
|
||||
return shard_block_lengths, shard_data_roots, shard_states
|
||||
```
|
||||
|
@ -310,6 +310,10 @@ def get_shard_transition_fields(
|
|||
def get_shard_transition(beacon_state: BeaconState,
|
||||
shard: Shard,
|
||||
shard_blocks: Sequence[SignedShardBlock]) -> ShardTransition:
|
||||
# NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors.
|
||||
if beacon_state.slot == GENESIS_SLOT:
|
||||
return ShardTransition()
|
||||
|
||||
offset_slots = compute_offset_slots(
|
||||
get_latest_slot_for_shard(beacon_state, shard),
|
||||
Slot(beacon_state.slot + 1),
|
||||
|
|
|
@ -165,7 +165,7 @@ variable_lengths = [len(part) for part in variable_parts]
|
|||
assert sum(fixed_lengths + variable_lengths) < 2**(BYTES_PER_LENGTH_OFFSET * BITS_PER_BYTE)
|
||||
|
||||
# Interleave offsets of variable-size parts with fixed-size parts
|
||||
variable_offsets = [serialize(sum(fixed_lengths + variable_lengths[:i])) for i in range(len(value))]
|
||||
variable_offsets = [serialize(uint32(sum(fixed_lengths + variable_lengths[:i]))) for i in range(len(value))]
|
||||
fixed_parts = [part if part != None else variable_offsets[i] for i, part in enumerate(fixed_parts)]
|
||||
|
||||
# Return the concatenation of the fixed-size parts (offsets interleaved) with the variable-size parts
|
||||
|
|
|
@ -2,6 +2,7 @@ import argparse
|
|||
from pathlib import Path
|
||||
import sys
|
||||
from typing import Iterable, AnyStr, Any, Callable
|
||||
import traceback
|
||||
|
||||
from ruamel.yaml import (
|
||||
YAML,
|
||||
|
@ -9,6 +10,13 @@ from ruamel.yaml import (
|
|||
|
||||
from gen_base.gen_typing import TestProvider
|
||||
|
||||
from eth2spec.test import context
|
||||
from eth2spec.test.exceptions import SkippedTest
|
||||
|
||||
|
||||
# Flag that the runner does NOT run test via pytest
|
||||
context.is_pytest = False
|
||||
|
||||
|
||||
def validate_output_dir(path_str):
|
||||
path = Path(path_str)
|
||||
|
@ -134,14 +142,20 @@ def run_generator(generator_name, test_providers: Iterable[TestProvider]):
|
|||
|
||||
written_part = False
|
||||
meta = dict()
|
||||
for (name, out_kind, data) in test_case.case_fn():
|
||||
written_part = True
|
||||
if out_kind == "meta":
|
||||
meta[name] = data
|
||||
if out_kind == "data":
|
||||
output_part("data", name, dump_yaml_fn(data, name, file_mode, yaml))
|
||||
if out_kind == "ssz":
|
||||
output_part("ssz", name, dump_ssz_fn(data, name, file_mode))
|
||||
|
||||
try:
|
||||
for (name, out_kind, data) in test_case.case_fn():
|
||||
written_part = True
|
||||
if out_kind == "meta":
|
||||
meta[name] = data
|
||||
if out_kind == "data":
|
||||
output_part("data", name, dump_yaml_fn(data, name, file_mode, yaml))
|
||||
if out_kind == "ssz":
|
||||
output_part("ssz", name, dump_ssz_fn(data, name, file_mode))
|
||||
except SkippedTest as e:
|
||||
print(e)
|
||||
continue
|
||||
|
||||
# Once all meta data is collected (if any), write it to a meta data file.
|
||||
if len(meta) != 0:
|
||||
written_part = True
|
||||
|
@ -152,6 +166,7 @@ def run_generator(generator_name, test_providers: Iterable[TestProvider]):
|
|||
|
||||
except Exception as e:
|
||||
print(f"ERROR: failed to generate vector(s) for test {case_dir}: {e}")
|
||||
traceback.print_exc()
|
||||
print(f"completed {generator_name}")
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
ruamel.yaml==0.16.5
|
||||
eth-utils==1.6.0
|
||||
pytest>=4.4
|
||||
|
|
|
@ -5,6 +5,7 @@ setup(
|
|||
packages=['gen_base', 'gen_from_tests'],
|
||||
install_requires=[
|
||||
"ruamel.yaml==0.16.5",
|
||||
"eth-utils==1.6.0"
|
||||
"eth-utils==1.6.0",
|
||||
"pytest>=4.4",
|
||||
]
|
||||
)
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.12.2
|
||||
0.12.3
|
|
@ -54,6 +54,8 @@ def load_config_file(configs_dir: str, presets_name: str) -> Dict[str, Any]:
|
|||
out[k] = [int(item) if item.isdigit() else item for item in v]
|
||||
elif isinstance(v, str) and v.startswith("0x"):
|
||||
out[k] = bytes.fromhex(v[2:])
|
||||
elif k == "CONFIG_NAME":
|
||||
out[k] = str(v)
|
||||
else:
|
||||
out[k] = int(v)
|
||||
return out
|
||||
|
|
|
@ -68,9 +68,8 @@ def get_random_ssz_object(rng: Random,
|
|||
else:
|
||||
return typ(get_random_bytes_list(rng, rng.randint(0, min(max_bytes_length, typ.limit()))))
|
||||
if issubclass(typ, ByteVector):
|
||||
# Sanity, don't generate absurdly big random values
|
||||
# If a client is aiming to performance-test, they should create a benchmark suite.
|
||||
assert typ.type_byte_length() <= max_bytes_length
|
||||
# Random byte vectors can be bigger than max bytes size, e.g. custody chunk data.
|
||||
# No max-bytes-length limitation here.
|
||||
if mode == RandomizationMode.mode_zero:
|
||||
return typ(b'\x00' * typ.type_byte_length())
|
||||
elif mode == RandomizationMode.mode_max:
|
||||
|
|
|
@ -31,7 +31,7 @@ def pytest_addoption(parser):
|
|||
help="config: make the pyspec use the specified configuration"
|
||||
)
|
||||
parser.addoption(
|
||||
"--disable-bls", action="store_true",
|
||||
"--disable-bls", action="store_true", default=False,
|
||||
help="bls-default: make tests that are not dependent on BLS run without BLS"
|
||||
)
|
||||
parser.addoption(
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import pytest
|
||||
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.utils import bls
|
||||
|
||||
from .exceptions import SkippedTest
|
||||
from .helpers.genesis import create_genesis_state
|
||||
|
||||
from .utils import vector_test, with_meta_tags
|
||||
|
||||
from random import Random
|
||||
|
@ -22,11 +24,16 @@ def reload_specs():
|
|||
# Some of the Spec module functionality is exposed here to deal with phase-specific changes.
|
||||
|
||||
SpecForkName = NewType("SpecForkName", str)
|
||||
ConfigName = NewType("ConfigName", str)
|
||||
|
||||
PHASE0 = SpecForkName('phase0')
|
||||
PHASE1 = SpecForkName('phase1')
|
||||
ALL_PHASES = (PHASE0, PHASE1)
|
||||
|
||||
MAINNET = ConfigName('mainnet')
|
||||
MINIMAL = ConfigName('minimal')
|
||||
|
||||
|
||||
# TODO: currently phases are defined as python modules.
|
||||
# It would be better if they would be more well-defined interfaces for stronger typing.
|
||||
|
||||
|
@ -63,9 +70,6 @@ def _prepare_state(balances_fn: Callable[[Any], Sequence[int]], threshold_fn: Ca
|
|||
# TODO: instead of upgrading a test phase0 genesis state we can also write a phase1 state helper.
|
||||
# Decide based on performance/consistency results later.
|
||||
state = phases[PHASE1].upgrade_to_phase1(state)
|
||||
# Shard state slot must lag behind BeaconState slot by at least 1
|
||||
# Will handle this more elegantly with fork mechanics
|
||||
spec.process_slots(state, state.slot + 1)
|
||||
|
||||
return state
|
||||
|
||||
|
@ -154,6 +158,15 @@ def low_single_balance(spec):
|
|||
return [1]
|
||||
|
||||
|
||||
def large_validator_set(spec):
|
||||
"""
|
||||
Helper method to create a large series of default balances.
|
||||
Usage: `@with_custom_state(balances_fn=default_balances, ...)`
|
||||
"""
|
||||
num_validators = 2 * spec.SLOTS_PER_EPOCH * spec.MAX_COMMITTEES_PER_SLOT * spec.TARGET_COMMITTEE_SIZE
|
||||
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
|
||||
|
||||
|
||||
def single_phase(fn):
|
||||
"""
|
||||
Decorator that filters out the phases data.
|
||||
|
@ -178,6 +191,17 @@ def single_phase(fn):
|
|||
DEFAULT_BLS_ACTIVE = True
|
||||
|
||||
|
||||
is_pytest = True
|
||||
|
||||
|
||||
def dump_skipping_message(reason: str) -> None:
|
||||
message = f"[Skipped test] {reason}"
|
||||
if is_pytest:
|
||||
pytest.skip(message)
|
||||
else:
|
||||
raise SkippedTest(message)
|
||||
|
||||
|
||||
def spec_test(fn):
|
||||
# Bls switch must be wrapped by vector_test,
|
||||
# to fully go through the yielded bls switch data, before setting back the BLS setting.
|
||||
|
@ -249,6 +273,24 @@ def bls_switch(fn):
|
|||
return entry
|
||||
|
||||
|
||||
def disable_process_reveal_deadlines(fn):
|
||||
"""
|
||||
Decorator to make a function execute with `process_reveal_deadlines` OFF.
|
||||
This is for testing long-range epochs transition without considering the reveal-deadline slashing effect.
|
||||
"""
|
||||
def entry(*args, spec: Spec, **kw):
|
||||
if hasattr(spec, 'process_reveal_deadlines'):
|
||||
old_state = spec.process_reveal_deadlines
|
||||
spec.process_reveal_deadlines = lambda state: None
|
||||
|
||||
yield from fn(*args, spec=spec, **kw)
|
||||
|
||||
if hasattr(spec, 'process_reveal_deadlines'):
|
||||
spec.process_reveal_deadlines = old_state
|
||||
|
||||
return with_meta_tags({'reveal_deadlines_setting': 1})(entry)
|
||||
|
||||
|
||||
def with_all_phases(fn):
|
||||
"""
|
||||
A decorator for running a test with every phase
|
||||
|
@ -278,7 +320,8 @@ def with_phases(phases, other_phases=None):
|
|||
if 'phase' in kw:
|
||||
phase = kw.pop('phase')
|
||||
if phase not in phases:
|
||||
return
|
||||
dump_skipping_message(f"doesn't support this fork: {phase}")
|
||||
return None
|
||||
run_phases = [phase]
|
||||
|
||||
available_phases = set(run_phases)
|
||||
|
@ -303,3 +346,33 @@ def with_phases(phases, other_phases=None):
|
|||
return ret
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
def with_configs(configs, reason=None):
|
||||
def decorator(fn):
|
||||
def wrapper(*args, spec: Spec, **kw):
|
||||
available_configs = set(configs)
|
||||
if spec.CONFIG_NAME not in available_configs:
|
||||
message = f"doesn't support this config: {spec.CONFIG_NAME}."
|
||||
if reason is not None:
|
||||
message = f"{message} Reason: {reason}"
|
||||
dump_skipping_message(message)
|
||||
return None
|
||||
|
||||
return fn(*args, spec=spec, **kw)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
def only_full_crosslink(fn):
|
||||
def is_full_crosslink(spec, state):
|
||||
epoch = spec.compute_epoch_at_slot(state.slot)
|
||||
return spec.get_committee_count_per_slot(state, epoch) >= spec.get_active_shard_count(state)
|
||||
|
||||
def wrapper(*args, spec: Spec, state: Any, **kw):
|
||||
# TODO: update condition to "phase1+" if we have phase2
|
||||
if spec.fork == PHASE1 and not is_full_crosslink(spec, state):
|
||||
dump_skipping_message("only for full crosslink")
|
||||
return None
|
||||
return fn(*args, spec=spec, state=state, **kw)
|
||||
return wrapper
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
class SkippedTest(Exception):
|
||||
...
|
|
@ -69,7 +69,7 @@ def build_attestation_data(spec, state, slot, index, shard=None, shard_transitio
|
|||
source_epoch = state.current_justified_checkpoint.epoch
|
||||
source_root = state.current_justified_checkpoint.root
|
||||
|
||||
attestation_data = spec.AttestationData(
|
||||
data = spec.AttestationData(
|
||||
slot=slot,
|
||||
index=index,
|
||||
beacon_block_root=block_root,
|
||||
|
@ -79,23 +79,27 @@ def build_attestation_data(spec, state, slot, index, shard=None, shard_transitio
|
|||
|
||||
if spec.fork == PHASE1:
|
||||
if shard is None:
|
||||
shard = spec.compute_shard_from_committee_index(state, attestation_data.index, attestation_data.slot)
|
||||
attestation_data.shard = shard
|
||||
shard = spec.compute_shard_from_committee_index(state, data.index, data.slot)
|
||||
data.shard = shard
|
||||
|
||||
if shard_transition is not None:
|
||||
last_offset_index = len(shard_transition.shard_data_roots) - 1
|
||||
attestation_data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root
|
||||
attestation_data.shard_transition_root = shard_transition.hash_tree_root()
|
||||
data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root
|
||||
data.shard_transition_root = shard_transition.hash_tree_root()
|
||||
else:
|
||||
if on_time:
|
||||
shard_transition = spec.get_shard_transition(state, shard, shard_blocks=[])
|
||||
last_offset_index = len(shard_transition.shard_data_roots) - 1
|
||||
attestation_data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root
|
||||
attestation_data.shard_transition_root = shard_transition.hash_tree_root()
|
||||
if data.slot == spec.GENESIS_SLOT:
|
||||
data.shard_head_root = spec.Root()
|
||||
data.shard_transition_root = spec.ShardTransition().hash_tree_root()
|
||||
else:
|
||||
shard_transition = spec.get_shard_transition(state, shard, shard_blocks=[])
|
||||
last_offset_index = len(shard_transition.shard_data_roots) - 1
|
||||
data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root
|
||||
data.shard_transition_root = shard_transition.hash_tree_root()
|
||||
else:
|
||||
attestation_data.shard_head_root = state.shard_states[shard].latest_block_root
|
||||
attestation_data.shard_transition_root = spec.Root()
|
||||
return attestation_data
|
||||
data.shard_head_root = state.shard_states[shard].latest_block_root
|
||||
data.shard_transition_root = spec.Root()
|
||||
return data
|
||||
|
||||
|
||||
def get_valid_on_time_attestation(spec, state, slot=None, index=None, shard_transition=None, signed=False):
|
||||
|
@ -189,19 +193,6 @@ def sign_indexed_attestation(spec, state, indexed_attestation):
|
|||
indexed_attestation.signature = sign_aggregate_attestation(spec, state, data, participants)
|
||||
|
||||
|
||||
def get_attestation_custody_signature(spec, state, attestation_data, block_index, bit, privkey):
|
||||
domain = spec.get_domain(state, spec.DOMAIN_BEACON_ATTESTER, attestation_data.target.epoch)
|
||||
signing_root = spec.compute_signing_root(
|
||||
spec.AttestationCustodyBitWrapper(
|
||||
attestation_data_root=attestation_data.hash_tree_root(),
|
||||
block_index=block_index,
|
||||
bit=bit,
|
||||
),
|
||||
domain,
|
||||
)
|
||||
return bls.Sign(privkey, signing_root)
|
||||
|
||||
|
||||
def sign_attestation(spec, state, attestation):
|
||||
participants = spec.get_attesting_indices(
|
||||
state,
|
||||
|
|
|
@ -113,7 +113,7 @@ def get_valid_chunk_challenge(spec, state, attestation, shard_transition, data_i
|
|||
def custody_chunkify(spec, x):
|
||||
chunks = [bytes(x[i:i + spec.BYTES_PER_CUSTODY_CHUNK]) for i in range(0, len(x), spec.BYTES_PER_CUSTODY_CHUNK)]
|
||||
chunks[-1] = chunks[-1].ljust(spec.BYTES_PER_CUSTODY_CHUNK, b"\0")
|
||||
return chunks
|
||||
return [ByteVector[spec.BYTES_PER_CUSTODY_CHUNK](c) for c in chunks]
|
||||
|
||||
|
||||
def build_proof(anchor, leaf_index):
|
||||
|
@ -149,12 +149,14 @@ def get_valid_custody_chunk_response(spec, state, chunk_challenge, challenge_ind
|
|||
|
||||
chunk_index = chunk_challenge.chunk_index
|
||||
|
||||
data_branch = build_proof(custody_data_block.get_backing().get_left(), chunk_index + 2**spec.CUSTODY_RESPONSE_DEPTH)
|
||||
leaf_index = chunk_index + 2**spec.CUSTODY_RESPONSE_DEPTH
|
||||
serialized_length = len(custody_data_block).to_bytes(32, 'little')
|
||||
data_branch = build_proof(custody_data_block.get_backing().get_left(), leaf_index) + [serialized_length]
|
||||
|
||||
return spec.CustodyChunkResponse(
|
||||
challenge_index=challenge_index,
|
||||
chunk_index=chunk_index,
|
||||
chunk=ByteVector[spec.BYTES_PER_CUSTODY_CHUNK](chunks[chunk_index]),
|
||||
chunk=chunks[chunk_index],
|
||||
branch=data_branch,
|
||||
)
|
||||
|
||||
|
@ -165,7 +167,7 @@ def get_custody_test_vector(bytelength, offset=0):
|
|||
|
||||
|
||||
def get_sample_shard_transition(spec, start_slot, block_lengths):
|
||||
b = [spec.get_block_data_merkle_root(ByteList[spec.MAX_SHARD_BLOCK_SIZE](get_custody_test_vector(x)))
|
||||
b = [spec.hash_tree_root(ByteList[spec.MAX_SHARD_BLOCK_SIZE](get_custody_test_vector(x)))
|
||||
for x in block_lengths]
|
||||
shard_transition = spec.ShardTransition(
|
||||
start_slot=start_slot,
|
||||
|
@ -177,15 +179,6 @@ def get_sample_shard_transition(spec, start_slot, block_lengths):
|
|||
return shard_transition
|
||||
|
||||
|
||||
def get_custody_secret(spec, state, validator_index, epoch=None):
|
||||
period = spec.get_custody_period_for_validator(validator_index, epoch if epoch is not None
|
||||
else spec.get_current_epoch(state))
|
||||
epoch_to_sign = spec.get_randao_epoch_for_custody_period(period, validator_index)
|
||||
domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch_to_sign)
|
||||
signing_root = spec.compute_signing_root(spec.Epoch(epoch_to_sign), domain)
|
||||
return bls.Sign(privkeys[validator_index], signing_root)
|
||||
|
||||
|
||||
def get_custody_slashable_test_vector(spec, custody_secret, length, slashable=True):
|
||||
test_vector = get_custody_test_vector(length)
|
||||
offset = 0
|
||||
|
@ -200,5 +193,5 @@ def get_custody_slashable_shard_transition(spec, start_slot, block_lengths, cust
|
|||
slashable_test_vector = get_custody_slashable_test_vector(spec, custody_secret,
|
||||
block_lengths[0], slashable=slashable)
|
||||
block_data = ByteList[spec.MAX_SHARD_BLOCK_SIZE](slashable_test_vector)
|
||||
shard_transition.shard_data_roots[0] = spec.get_block_data_merkle_root(block_data)
|
||||
shard_transition.shard_data_roots[0] = spec.hash_tree_root(block_data)
|
||||
return shard_transition, slashable_test_vector
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
from eth2spec.phase0 import spec as phase0_spec
|
||||
|
||||
|
||||
def get_anchor_root(spec, state):
|
||||
anchor_block_header = state.latest_block_header.copy()
|
||||
if anchor_block_header.state_root == spec.Bytes32():
|
||||
|
@ -25,3 +28,10 @@ def add_attestation_to_store(spec, store, attestation):
|
|||
spec.on_tick(store, next_epoch_time)
|
||||
|
||||
spec.on_attestation(store, attestation)
|
||||
|
||||
|
||||
def get_genesis_forkchoice_store(spec, genesis_state):
|
||||
assert genesis_state.slot == spec.GENESIS_SLOT
|
||||
# The genesis block must be a Phase 0 `BeaconBlock`
|
||||
genesis_block = phase0_spec.BeaconBlock(state_root=genesis_state.hash_tree_root())
|
||||
return spec.get_forkchoice_store(genesis_state, genesis_block)
|
||||
|
|
|
@ -35,8 +35,3 @@ def get_shard_transition_of_committee(spec, state, committee_index, shard_blocks
|
|||
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot)
|
||||
shard_transition = spec.get_shard_transition(state, shard, shard_blocks=shard_blocks)
|
||||
return shard_transition
|
||||
|
||||
|
||||
def is_full_crosslink(spec, state):
|
||||
epoch = spec.compute_epoch_at_slot(state.slot)
|
||||
return spec.get_committee_count_per_slot(state, epoch) >= spec.get_active_shard_count(state)
|
||||
|
|
|
@ -42,12 +42,10 @@ def transition_to_slot_via_block(spec, state, slot):
|
|||
|
||||
def transition_to_valid_shard_slot(spec, state):
|
||||
"""
|
||||
Transition to slot `spec.PHASE_1_GENESIS_SLOT + 1` and fork at `spec.PHASE_1_GENESIS_SLOT`.
|
||||
Transition to slot `spec.PHASE_1_FORK_SLOT + 1` and fork at `spec.PHASE_1_FORK_SLOT`.
|
||||
"""
|
||||
transition_to(spec, state, spec.PHASE_1_GENESIS_SLOT)
|
||||
state = spec.upgrade_to_phase1(state) # `upgrade_to_phase1` is a pure function
|
||||
transition_to(spec, state, spec.PHASE_1_FORK_SLOT)
|
||||
next_slot(spec, state)
|
||||
return state
|
||||
|
||||
|
||||
def next_epoch(spec, state):
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
spec_state_test, expect_assertion_error, always_bls, with_all_phases, with_phases
|
||||
spec_state_test, expect_assertion_error, always_bls, with_all_phases
|
||||
)
|
||||
from eth2spec.test.helpers.attestations import sign_indexed_attestation
|
||||
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, \
|
||||
|
@ -194,10 +193,7 @@ def test_participants_already_slashed(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
# Some of the following tests are phase0 only: phase 1 lists participants with bitfields instead of index list.
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_high_index(spec, state):
|
||||
|
@ -210,7 +206,7 @@ def test_att1_high_index(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_high_index(spec, state):
|
||||
|
@ -223,7 +219,7 @@ def test_att2_high_index(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_empty_indices(spec, state):
|
||||
|
@ -235,7 +231,7 @@ def test_att1_empty_indices(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_empty_indices(spec, state):
|
||||
|
@ -247,7 +243,7 @@ def test_att2_empty_indices(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_all_empty_indices(spec, state):
|
||||
|
@ -262,7 +258,7 @@ def test_all_empty_indices(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_bad_extra_index(spec, state):
|
||||
|
@ -278,7 +274,7 @@ def test_att1_bad_extra_index(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_bad_replaced_index(spec, state):
|
||||
|
@ -294,7 +290,7 @@ def test_att1_bad_replaced_index(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_bad_extra_index(spec, state):
|
||||
|
@ -310,7 +306,7 @@ def test_att2_bad_extra_index(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_bad_replaced_index(spec, state):
|
||||
|
@ -326,7 +322,7 @@ def test_att2_bad_replaced_index(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_duplicate_index_normal_signed(spec, state):
|
||||
|
@ -346,7 +342,7 @@ def test_att1_duplicate_index_normal_signed(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_duplicate_index_normal_signed(spec, state):
|
||||
|
@ -366,7 +362,7 @@ def test_att2_duplicate_index_normal_signed(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_duplicate_index_double_signed(spec, state):
|
||||
|
@ -381,7 +377,7 @@ def test_att1_duplicate_index_double_signed(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_duplicate_index_double_signed(spec, state):
|
||||
|
@ -396,7 +392,7 @@ def test_att2_duplicate_index_double_signed(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_unsorted_att_1(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
|
||||
|
@ -409,7 +405,7 @@ def test_unsorted_att_1(spec, state):
|
|||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_unsorted_att_2(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False)
|
||||
|
|
|
@ -79,6 +79,20 @@ def test_invalid_sig_1_and_2(spec, state):
|
|||
yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_invalid_sig_1_and_2_swap(spec, state):
|
||||
# Get valid signatures for the slashings
|
||||
proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True)
|
||||
|
||||
# But swap them
|
||||
signature_1 = proposer_slashing.signed_header_1.signature
|
||||
proposer_slashing.signed_header_1.signature = proposer_slashing.signed_header_2.signature
|
||||
proposer_slashing.signed_header_2.signature = signature_1
|
||||
yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_invalid_proposer_index(spec, state):
|
||||
|
@ -122,11 +136,26 @@ def test_epochs_are_different(spec, state):
|
|||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_headers_are_same(spec, state):
|
||||
def test_headers_are_same_sigs_are_same(spec, state):
|
||||
proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=False)
|
||||
|
||||
# set headers to be the same
|
||||
proposer_slashing.signed_header_2 = proposer_slashing.signed_header_1
|
||||
proposer_slashing.signed_header_2 = proposer_slashing.signed_header_1.copy()
|
||||
|
||||
yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_headers_are_same_sigs_are_different(spec, state):
|
||||
proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=False)
|
||||
|
||||
# set headers to be the same
|
||||
proposer_slashing.signed_header_2 = proposer_slashing.signed_header_1.copy()
|
||||
# but signatures to be different
|
||||
proposer_slashing.signed_header_2.signature = proposer_slashing.signed_header_2.signature[:-1] + b'\x00'
|
||||
|
||||
assert proposer_slashing.signed_header_1.signature != proposer_slashing.signed_header_2.signature
|
||||
|
||||
yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
spec_state_test, spec_test,
|
||||
with_all_phases, with_phases, single_phase,
|
||||
with_all_phases, single_phase,
|
||||
with_custom_state,
|
||||
zero_activation_threshold,
|
||||
misc_balances, low_single_balance,
|
||||
|
@ -25,7 +24,7 @@ def run_process_rewards_and_penalties(spec, state):
|
|||
yield from run_epoch_processing_with(spec, state, 'process_rewards_and_penalties')
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_genesis_epoch_no_attestations_no_penalties(spec, state):
|
||||
pre_state = state.copy()
|
||||
|
@ -38,7 +37,7 @@ def test_genesis_epoch_no_attestations_no_penalties(spec, state):
|
|||
assert state.balances[index] == pre_state.balances[index]
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_genesis_epoch_full_attestations_no_rewards(spec, state):
|
||||
attestations = []
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from eth2spec.test.context import PHASE0, spec_state_test, with_all_phases, with_phases
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.helpers.state import next_epoch_via_block
|
||||
from eth2spec.test.helpers.attestations import next_epoch_with_attestations
|
||||
|
||||
|
@ -28,7 +28,7 @@ def check_finality(spec,
|
|||
assert state.finalized_checkpoint == prev_state.finalized_checkpoint
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_finality_no_updates_at_genesis(spec, state):
|
||||
assert spec.get_current_epoch(state) == spec.GENESIS_EPOCH
|
||||
|
|
|
@ -21,8 +21,14 @@ from eth2spec.test.helpers.deposits import prepare_state_and_deposit
|
|||
from eth2spec.test.helpers.shard_transitions import get_shard_transition_of_committee
|
||||
|
||||
from eth2spec.test.context import (
|
||||
PHASE0, PHASE1,
|
||||
spec_state_test, with_all_phases, expect_assertion_error, always_bls, with_phases,
|
||||
PHASE0, PHASE1, MINIMAL,
|
||||
spec_test, spec_state_test, dump_skipping_message,
|
||||
with_phases, with_all_phases, single_phase,
|
||||
expect_assertion_error, always_bls,
|
||||
disable_process_reveal_deadlines,
|
||||
with_configs,
|
||||
with_custom_state,
|
||||
large_validator_set,
|
||||
)
|
||||
|
||||
|
||||
|
@ -70,6 +76,7 @@ def test_same_slot_block_transition(spec, state):
|
|||
def test_empty_block_transition(spec, state):
|
||||
pre_slot = state.slot
|
||||
pre_eth1_votes = len(state.eth1_data_votes)
|
||||
pre_mix = spec.get_randao_mix(state, spec.get_current_epoch(state))
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
|
@ -82,7 +89,32 @@ def test_empty_block_transition(spec, state):
|
|||
|
||||
assert len(state.eth1_data_votes) == pre_eth1_votes + 1
|
||||
assert spec.get_block_root_at_slot(state, pre_slot) == signed_block.message.parent_root
|
||||
assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.Bytes32()
|
||||
assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != pre_mix
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_configs([MINIMAL],
|
||||
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||
@single_phase
|
||||
def test_empty_block_transition_large_validator_set(spec, state):
|
||||
pre_slot = state.slot
|
||||
pre_eth1_votes = len(state.eth1_data_votes)
|
||||
pre_mix = spec.get_randao_mix(state, spec.get_current_epoch(state))
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert len(state.eth1_data_votes) == pre_eth1_votes + 1
|
||||
assert spec.get_block_root_at_slot(state, pre_slot) == signed_block.message.parent_root
|
||||
assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != pre_mix
|
||||
|
||||
|
||||
def process_and_sign_block_without_header_validations(spec, state, block):
|
||||
|
@ -288,13 +320,34 @@ def test_empty_epoch_transition(spec, state):
|
|||
assert spec.get_block_root_at_slot(state, slot) == block.parent_root
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_configs([MINIMAL],
|
||||
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
|
||||
@spec_test
|
||||
@with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||
@single_phase
|
||||
def test_empty_epoch_transition_large_validator_set(spec, state):
|
||||
pre_slot = state.slot
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
assert state.slot == block.slot
|
||||
for slot in range(pre_slot, state.slot):
|
||||
assert spec.get_block_root_at_slot(state, slot) == block.parent_root
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_empty_epoch_transition_not_finalizing(spec, state):
|
||||
# Don't run for non-minimal configs, it takes very long, and the effect
|
||||
# of calling finalization/justification is just the same as with the minimal configuration.
|
||||
if spec.SLOTS_PER_EPOCH > 8:
|
||||
return
|
||||
return dump_skipping_message("Skip mainnet config for saving time."
|
||||
" Minimal config suffice to cover the target-of-test.")
|
||||
|
||||
# copy for later balance lookups.
|
||||
pre_balances = list(state.balances)
|
||||
|
@ -480,9 +533,8 @@ def test_attester_slashing(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_duplicate_attester_slashing(spec, state):
|
||||
# Skip test if config cannot handle multiple AttesterSlashings per block
|
||||
if spec.MAX_ATTESTER_SLASHINGS < 2:
|
||||
return
|
||||
return dump_skipping_message("Skip test if config cannot handle multiple AttesterSlashings per block")
|
||||
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
|
||||
attester_slashings = [attester_slashing, attester_slashing.copy()]
|
||||
|
@ -506,12 +558,11 @@ def test_duplicate_attester_slashing(spec, state):
|
|||
|
||||
# All AttesterSlashing tests should be adopted for Phase 1 but helper support is not yet there
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_multiple_attester_slashings_no_overlap(spec, state):
|
||||
# Skip test if config cannot handle multiple AttesterSlashings per block
|
||||
if spec.MAX_ATTESTER_SLASHINGS < 2:
|
||||
return
|
||||
return dump_skipping_message("Skip test if config cannot handle multiple AttesterSlashings per block")
|
||||
|
||||
# copy for later balance lookups.
|
||||
pre_state = state.copy()
|
||||
|
@ -547,12 +598,11 @@ def test_multiple_attester_slashings_no_overlap(spec, state):
|
|||
check_attester_slashing_effect(spec, pre_state, state, full_indices)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_multiple_attester_slashings_partial_overlap(spec, state):
|
||||
# Skip test if config cannot handle multiple AttesterSlashings per block
|
||||
if spec.MAX_ATTESTER_SLASHINGS < 2:
|
||||
return
|
||||
return dump_skipping_message("Skip test if config cannot handle multiple AttesterSlashings per block")
|
||||
|
||||
# copy for later balance lookups.
|
||||
pre_state = state.copy()
|
||||
|
@ -762,8 +812,9 @@ def prepare_signed_exits(spec, state, indices):
|
|||
# exceeding the minimal-config randao mixes memory size.
|
||||
# Applies to all voluntary-exit sanity block tests.
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
def test_voluntary_exit(spec, state):
|
||||
validator_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
|
||||
|
@ -790,7 +841,7 @@ def test_voluntary_exit(spec, state):
|
|||
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_double_validator_exit_same_block(spec, state):
|
||||
validator_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
|
@ -811,8 +862,9 @@ def test_double_validator_exit_same_block(spec, state):
|
|||
yield 'post', None
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
def test_multiple_different_validator_exits_same_block(spec, state):
|
||||
validator_indices = [
|
||||
spec.get_active_validator_indices(state, spec.get_current_epoch(state))[i]
|
||||
|
@ -888,9 +940,9 @@ def test_historical_batch(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_eth1_data_votes_consensus(spec, state):
|
||||
# Don't run when it will take very, very long to simulate. Minimal configuration suffices.
|
||||
if spec.EPOCHS_PER_ETH1_VOTING_PERIOD > 2:
|
||||
return
|
||||
return dump_skipping_message("Skip test if config with longer `EPOCHS_PER_ETH1_VOTING_PERIOD` for saving time."
|
||||
" Minimal config suffice to cover the target-of-test.")
|
||||
|
||||
voting_period_slots = spec.EPOCHS_PER_ETH1_VOTING_PERIOD * spec.SLOTS_PER_EPOCH
|
||||
|
||||
|
@ -932,9 +984,9 @@ def test_eth1_data_votes_consensus(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_eth1_data_votes_no_consensus(spec, state):
|
||||
# Don't run when it will take very, very long to simulate. Minimal configuration suffices.
|
||||
if spec.EPOCHS_PER_ETH1_VOTING_PERIOD > 2:
|
||||
return
|
||||
return dump_skipping_message("Skip test if config with longer `EPOCHS_PER_ETH1_VOTING_PERIOD` for saving time."
|
||||
" Minimal config suffice to cover the target-of-test.")
|
||||
|
||||
voting_period_slots = spec.EPOCHS_PER_ETH1_VOTING_PERIOD * spec.SLOTS_PER_EPOCH
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
from eth2spec.test.context import with_all_phases, spec_state_test
|
||||
from eth2spec.test.helpers.attestations import get_valid_attestation, next_epoch_with_attestations
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||
from eth2spec.test.helpers.fork_choice import add_attestation_to_store, add_block_to_store, get_anchor_root
|
||||
from eth2spec.test.helpers.fork_choice import (
|
||||
add_attestation_to_store,
|
||||
add_block_to_store, get_anchor_root,
|
||||
get_genesis_forkchoice_store,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_epoch,
|
||||
state_transition_and_sign_block,
|
||||
|
@ -12,7 +16,7 @@ from eth2spec.test.helpers.state import (
|
|||
@spec_state_test
|
||||
def test_genesis(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
anchor_root = get_anchor_root(spec, state)
|
||||
assert spec.get_head(store) == anchor_root
|
||||
|
||||
|
@ -21,7 +25,7 @@ def test_genesis(spec, state):
|
|||
@spec_state_test
|
||||
def test_chain_no_attestations(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
anchor_root = get_anchor_root(spec, state)
|
||||
assert spec.get_head(store) == anchor_root
|
||||
|
||||
|
@ -44,7 +48,7 @@ def test_split_tie_breaker_no_attestations(spec, state):
|
|||
genesis_state = state.copy()
|
||||
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
anchor_root = get_anchor_root(spec, state)
|
||||
assert spec.get_head(store) == anchor_root
|
||||
|
||||
|
@ -72,13 +76,13 @@ def test_shorter_chain_but_heavier_weight(spec, state):
|
|||
genesis_state = state.copy()
|
||||
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
anchor_root = get_anchor_root(spec, state)
|
||||
assert spec.get_head(store) == anchor_root
|
||||
|
||||
# build longer tree
|
||||
long_state = genesis_state.copy()
|
||||
for i in range(3):
|
||||
for _ in range(3):
|
||||
long_block = build_empty_block_for_next_slot(spec, long_state)
|
||||
signed_long_block = state_transition_and_sign_block(spec, long_state, long_block)
|
||||
add_block_to_store(spec, store, signed_long_block)
|
||||
|
@ -100,7 +104,7 @@ def test_shorter_chain_but_heavier_weight(spec, state):
|
|||
@spec_state_test
|
||||
def test_filtered_block_tree(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
anchor_root = get_anchor_root(spec, state)
|
||||
|
||||
# transition state past initial couple of epochs
|
||||
|
|
|
@ -2,6 +2,7 @@ from eth2spec.test.context import PHASE0, with_all_phases, spec_state_test
|
|||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||
from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation
|
||||
from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block, next_epoch, next_slot
|
||||
from eth2spec.test.helpers.fork_choice import get_genesis_forkchoice_store
|
||||
|
||||
|
||||
def run_on_attestation(spec, state, store, attestation, valid=True):
|
||||
|
@ -26,9 +27,12 @@ def run_on_attestation(spec, state, store, attestation, valid=True):
|
|||
latest_message = spec.LatestMessage(
|
||||
epoch=attestation.data.target.epoch,
|
||||
root=attestation.data.beacon_block_root,
|
||||
shard=attestation.data.shard,
|
||||
shard_root=attestation.data.shard_head_root,
|
||||
)
|
||||
shard_latest_message = spec.ShardLatestMessage(
|
||||
epoch=attestation.data.target.epoch,
|
||||
root=attestation.data.shard_head_root,
|
||||
)
|
||||
assert store.shard_stores[attestation.data.shard].latest_messages[sample_index] == shard_latest_message
|
||||
|
||||
assert (
|
||||
store.latest_messages[sample_index] == latest_message
|
||||
|
@ -38,7 +42,7 @@ def run_on_attestation(spec, state, store, attestation, valid=True):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_current_epoch(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * 2)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
@ -57,7 +61,7 @@ def test_on_attestation_current_epoch(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_previous_epoch(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
@ -76,7 +80,7 @@ def test_on_attestation_previous_epoch(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_past_epoch(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
|
||||
# move time forward 2 epochs
|
||||
time = store.time + 2 * spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
|
||||
|
@ -98,7 +102,7 @@ def test_on_attestation_past_epoch(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_mismatched_target_and_slot(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
|
@ -121,7 +125,7 @@ def test_on_attestation_mismatched_target_and_slot(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_inconsistent_target_and_head(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
spec.on_tick(store, store.time + 2 * spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH)
|
||||
|
||||
# Create chain 1 as empty chain between genesis and start of 1st epoch
|
||||
|
@ -159,7 +163,7 @@ def test_on_attestation_inconsistent_target_and_head(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_target_block_not_in_store(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = store.time + spec.SECONDS_PER_SLOT * (spec.SLOTS_PER_EPOCH + 1)
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -181,7 +185,7 @@ def test_on_attestation_target_block_not_in_store(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_target_checkpoint_not_in_store(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = store.time + spec.SECONDS_PER_SLOT * (spec.SLOTS_PER_EPOCH + 1)
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -206,7 +210,7 @@ def test_on_attestation_target_checkpoint_not_in_store(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_target_checkpoint_not_in_store_diff_slot(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = store.time + spec.SECONDS_PER_SLOT * (spec.SLOTS_PER_EPOCH + 1)
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -233,7 +237,7 @@ def test_on_attestation_target_checkpoint_not_in_store_diff_slot(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_beacon_block_not_in_store(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = store.time + spec.SECONDS_PER_SLOT * (spec.SLOTS_PER_EPOCH + 1)
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -262,7 +266,7 @@ def test_on_attestation_beacon_block_not_in_store(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_future_epoch(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = store.time + 3 * spec.SECONDS_PER_SLOT
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -282,7 +286,7 @@ def test_on_attestation_future_epoch(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_future_block(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = store.time + spec.SECONDS_PER_SLOT * 5
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -302,7 +306,7 @@ def test_on_attestation_future_block(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_same_slot(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = store.time + spec.SECONDS_PER_SLOT
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -318,7 +322,7 @@ def test_on_attestation_same_slot(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_on_attestation_invalid_attestation(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = store.time + 3 * spec.SECONDS_PER_SLOT
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@ from copy import deepcopy
|
|||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
|
||||
from eth2spec.test.context import with_all_phases, spec_state_test
|
||||
from eth2spec.test.helpers.attestations import next_epoch_with_attestations
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block, transition_unsigned_block, \
|
||||
build_empty_block
|
||||
from eth2spec.test.helpers.attestations import next_epoch_with_attestations
|
||||
from eth2spec.test.helpers.state import next_epoch, state_transition_and_sign_block
|
||||
from eth2spec.test.helpers.fork_choice import get_genesis_forkchoice_store
|
||||
from eth2spec.test.helpers.state import next_epoch, state_transition_and_sign_block, transition_to
|
||||
|
||||
|
||||
def run_on_block(spec, store, signed_block, valid=True):
|
||||
|
@ -37,7 +38,7 @@ def apply_next_epoch_with_attestations(spec, state, store):
|
|||
@spec_state_test
|
||||
def test_basic(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
assert store.time == time
|
||||
|
@ -61,7 +62,7 @@ def test_basic(spec, state):
|
|||
@spec_state_test
|
||||
def test_on_block_checkpoints(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -87,7 +88,7 @@ def test_on_block_checkpoints(spec, state):
|
|||
@spec_state_test
|
||||
def test_on_block_future_block(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
|
||||
# do not tick time
|
||||
|
||||
|
@ -101,7 +102,7 @@ def test_on_block_future_block(spec, state):
|
|||
@spec_state_test
|
||||
def test_on_block_bad_parent_root(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -121,7 +122,7 @@ def test_on_block_bad_parent_root(spec, state):
|
|||
@spec_state_test
|
||||
def test_on_block_before_finalized(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -140,7 +141,7 @@ def test_on_block_before_finalized(spec, state):
|
|||
@spec_state_test
|
||||
def test_on_block_finalized_skip_slots(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -160,16 +161,18 @@ def test_on_block_finalized_skip_slots(spec, state):
|
|||
@spec_state_test
|
||||
def test_on_block_finalized_skip_slots_not_in_skip_chain(spec, state):
|
||||
# Initialization
|
||||
next_epoch(spec, state)
|
||||
store = spec.get_forkchoice_store(state)
|
||||
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH - 1)
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
transition_unsigned_block(spec, state, block)
|
||||
block.state_root = state.hash_tree_root()
|
||||
store = spec.get_forkchoice_store(state, block)
|
||||
store.finalized_checkpoint = spec.Checkpoint(
|
||||
epoch=store.finalized_checkpoint.epoch + 2,
|
||||
root=store.finalized_checkpoint.root
|
||||
)
|
||||
|
||||
# First transition through the epoch to ensure no skipped slots
|
||||
state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store)
|
||||
state, store, _ = apply_next_epoch_with_attestations(spec, state, store)
|
||||
|
||||
# Now build a block at later slot than finalized epoch
|
||||
# Includes finalized block in chain, but not at appropriate skip slot
|
||||
|
@ -183,7 +186,7 @@ def test_on_block_finalized_skip_slots_not_in_skip_chain(spec, state):
|
|||
@spec_state_test
|
||||
def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = 0
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -214,7 +217,7 @@ def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state):
|
|||
@spec_state_test
|
||||
def test_on_block_outside_safe_slots_and_multiple_better_justified(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = 0
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
@ -264,7 +267,7 @@ def test_on_block_outside_safe_slots_and_multiple_better_justified(spec, state):
|
|||
@spec_state_test
|
||||
def test_on_block_outside_safe_slots_but_finality(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from eth2spec.test.context import with_all_phases, spec_state_test
|
||||
from eth2spec.test.helpers.fork_choice import get_genesis_forkchoice_store
|
||||
|
||||
|
||||
def run_on_tick(spec, store, time, new_justified_checkpoint=False):
|
||||
|
@ -19,14 +20,14 @@ def run_on_tick(spec, store, time, new_justified_checkpoint=False):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_basic(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
run_on_tick(spec, store, store.time + 1)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_update_justified_single(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
next_epoch = spec.get_current_epoch(state) + 1
|
||||
next_epoch_start_slot = spec.compute_start_slot_at_epoch(next_epoch)
|
||||
seconds_until_next_epoch = next_epoch_start_slot * spec.SECONDS_PER_SLOT - store.time
|
||||
|
@ -42,7 +43,7 @@ def test_update_justified_single(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_no_update_same_slot_at_epoch_boundary(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
seconds_per_epoch = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
|
||||
|
||||
store.best_justified_checkpoint = spec.Checkpoint(
|
||||
|
@ -59,7 +60,7 @@ def test_no_update_same_slot_at_epoch_boundary(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_no_update_not_epoch_boundary(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
|
||||
store.best_justified_checkpoint = spec.Checkpoint(
|
||||
epoch=store.justified_checkpoint.epoch + 1,
|
||||
|
@ -72,7 +73,7 @@ def test_no_update_not_epoch_boundary(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_no_update_new_justified_equal_epoch(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
seconds_per_epoch = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
|
||||
|
||||
store.best_justified_checkpoint = spec.Checkpoint(
|
||||
|
@ -91,7 +92,7 @@ def test_no_update_new_justified_equal_epoch(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_no_update_new_justified_later_epoch(spec, state):
|
||||
store = spec.get_forkchoice_store(state)
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
seconds_per_epoch = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
|
||||
|
||||
store.best_justified_checkpoint = spec.Checkpoint(
|
||||
|
|
|
@ -6,12 +6,15 @@ from eth2spec.test.helpers.custody import (
|
|||
from eth2spec.test.helpers.attestations import (
|
||||
get_valid_on_time_attestation,
|
||||
)
|
||||
from eth2spec.test.helpers.state import transition_to
|
||||
from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard_slot
|
||||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
with_all_phases_except,
|
||||
spec_state_test,
|
||||
MINIMAL,
|
||||
expect_assertion_error,
|
||||
disable_process_reveal_deadlines,
|
||||
spec_state_test,
|
||||
with_all_phases_except,
|
||||
with_configs,
|
||||
)
|
||||
from eth2spec.test.phase0.block_processing.test_process_attestation import run_attestation_processing
|
||||
|
||||
|
@ -67,8 +70,11 @@ def run_custody_chunk_response_processing(spec, state, custody_response, valid=T
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
@disable_process_reveal_deadlines
|
||||
def test_challenge_appended(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
|
@ -88,8 +94,11 @@ def test_challenge_appended(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_challenge_empty_element_replaced(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
|
@ -111,8 +120,11 @@ def test_challenge_empty_element_replaced(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_duplicate_challenge(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
|
@ -134,8 +146,11 @@ def test_duplicate_challenge(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_second_challenge(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
|
@ -159,7 +174,10 @@ def test_second_challenge(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_multiple_epochs_custody(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 3)
|
||||
|
||||
shard = 0
|
||||
|
@ -181,7 +199,10 @@ def test_multiple_epochs_custody(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_many_epochs_custody(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 20)
|
||||
|
||||
shard = 0
|
||||
|
@ -203,7 +224,10 @@ def test_many_epochs_custody(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_off_chain_attestation(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
|
||||
shard = 0
|
||||
|
@ -221,7 +245,10 @@ def test_off_chain_attestation(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_custody_response(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
|
||||
shard = 0
|
||||
|
@ -250,7 +277,41 @@ def test_custody_response(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_custody_response_chunk_index_2(spec, state):
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True,
|
||||
shard_transition=shard_transition)
|
||||
|
||||
transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
||||
|
||||
_, _, _ = run_attestation_processing(spec, state, attestation)
|
||||
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1))
|
||||
|
||||
challenge = get_valid_chunk_challenge(spec, state, attestation, shard_transition, chunk_index=2)
|
||||
|
||||
_, _, _ = run_chunk_challenge_processing(spec, state, challenge)
|
||||
|
||||
chunk_challenge_index = state.custody_chunk_challenge_index - 1
|
||||
|
||||
custody_response = get_valid_custody_chunk_response(
|
||||
spec, state, challenge, chunk_challenge_index, block_length_or_custody_data=2**15 // 3)
|
||||
|
||||
yield from run_custody_chunk_response_processing(spec, state, custody_response)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_custody_response_multiple_epochs(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 3)
|
||||
|
||||
shard = 0
|
||||
|
@ -279,7 +340,10 @@ def test_custody_response_multiple_epochs(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_custody_response_many_epochs(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 20)
|
||||
|
||||
shard = 0
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
from eth2spec.test.helpers.custody import (
|
||||
get_valid_custody_slashing,
|
||||
get_custody_secret,
|
||||
get_custody_slashable_shard_transition,
|
||||
)
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
get_valid_on_time_attestation,
|
||||
)
|
||||
from eth2spec.test.helpers.keys import privkeys
|
||||
from eth2spec.utils.ssz.ssz_typing import ByteList
|
||||
from eth2spec.test.helpers.state import get_balance, transition_to
|
||||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
MINIMAL,
|
||||
with_all_phases_except,
|
||||
spec_state_test,
|
||||
expect_assertion_error,
|
||||
disable_process_reveal_deadlines,
|
||||
with_configs,
|
||||
)
|
||||
from eth2spec.test.phase0.block_processing.test_process_attestation import run_attestation_processing
|
||||
|
||||
|
@ -63,6 +66,7 @@ def run_standard_custody_slashing_test(spec,
|
|||
slashing_message_data=None,
|
||||
correct=True,
|
||||
valid=True):
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
if shard_lateness is None:
|
||||
shard_lateness = spec.SLOTS_PER_EPOCH
|
||||
transition_to(spec, state, state.slot + shard_lateness)
|
||||
|
@ -76,7 +80,12 @@ def run_standard_custody_slashing_test(spec,
|
|||
if block_lengths is None:
|
||||
block_lengths = [2**15 // 3] * len(offset_slots)
|
||||
|
||||
custody_secret = get_custody_secret(spec, state, validator_index)
|
||||
custody_secret = spec.get_custody_secret(
|
||||
state,
|
||||
validator_index,
|
||||
privkeys[validator_index],
|
||||
spec.get_current_epoch(state),
|
||||
)
|
||||
shard_transition, slashable_test_vector = get_custody_slashable_shard_transition(
|
||||
spec,
|
||||
state.slot,
|
||||
|
@ -105,30 +114,40 @@ def run_standard_custody_slashing_test(spec,
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_custody_slashing(spec, state):
|
||||
yield from run_standard_custody_slashing_test(spec, state)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_incorrect_custody_slashing(spec, state):
|
||||
yield from run_standard_custody_slashing_test(spec, state, correct=False)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_multiple_epochs_custody(spec, state):
|
||||
yield from run_standard_custody_slashing_test(spec, state, shard_lateness=spec.SLOTS_PER_EPOCH * 3)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_many_epochs_custody(spec, state):
|
||||
yield from run_standard_custody_slashing_test(spec, state, shard_lateness=spec.SLOTS_PER_EPOCH * 10)
|
||||
yield from run_standard_custody_slashing_test(spec, state, shard_lateness=spec.SLOTS_PER_EPOCH * 5)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@disable_process_reveal_deadlines
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_invalid_custody_slashing(spec, state):
|
||||
yield from run_standard_custody_slashing_test(
|
||||
spec,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
with_all_phases_except,
|
||||
only_full_crosslink,
|
||||
spec_state_test,
|
||||
)
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
|
@ -10,7 +11,6 @@ from eth2spec.test.helpers.attestations import (
|
|||
)
|
||||
from eth2spec.test.helpers.shard_transitions import (
|
||||
run_shard_transitions_processing,
|
||||
is_full_crosslink,
|
||||
)
|
||||
from eth2spec.test.helpers.shard_block import (
|
||||
build_shard_block,
|
||||
|
@ -22,7 +22,7 @@ from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard
|
|||
|
||||
|
||||
def get_initial_env(spec, state, target_len_offset_slot):
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
committee_index = spec.CommitteeIndex(0)
|
||||
target_shard_slot = state.slot + target_len_offset_slot - 1
|
||||
shard = spec.compute_shard_from_committee_index(state, committee_index, target_shard_slot)
|
||||
|
@ -92,31 +92,22 @@ def run_successful_crosslink_tests(spec, state, target_len_offset_slot):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_basic_crosslinks(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# Skip this test
|
||||
return
|
||||
|
||||
yield from run_successful_crosslink_tests(spec, state, target_len_offset_slot=1)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_multiple_offset_slots(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# Skip this test
|
||||
return
|
||||
|
||||
yield from run_successful_crosslink_tests(spec, state, target_len_offset_slot=2)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_no_winning_root(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# Skip this test
|
||||
return
|
||||
|
||||
state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot=1)
|
||||
init_slot = state.slot
|
||||
|
||||
|
@ -163,11 +154,8 @@ def test_no_winning_root(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_wrong_shard_transition_root(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# Skip this test
|
||||
return
|
||||
|
||||
state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot=1)
|
||||
init_slot = state.slot
|
||||
|
||||
|
|
|
@ -5,11 +5,13 @@ from eth2spec.test.helpers.custody import (
|
|||
from eth2spec.test.helpers.attestations import (
|
||||
get_valid_on_time_attestation,
|
||||
)
|
||||
from eth2spec.test.helpers.state import transition_to
|
||||
from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard_slot
|
||||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
with_all_phases_except,
|
||||
MINIMAL,
|
||||
spec_state_test,
|
||||
with_all_phases_except,
|
||||
with_configs,
|
||||
)
|
||||
from eth2spec.test.phase0.block_processing.test_process_attestation import run_attestation_processing
|
||||
from eth2spec.test.phase0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
||||
|
@ -25,8 +27,10 @@ def run_process_challenge_deadlines(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_validator_slashed_after_chunk_challenge(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
|
|
|
@ -10,7 +10,7 @@ from eth2spec.test.helpers.custody import (
|
|||
from eth2spec.test.helpers.attestations import (
|
||||
get_valid_on_time_attestation,
|
||||
)
|
||||
from eth2spec.test.helpers.state import next_epoch_via_block, transition_to
|
||||
from eth2spec.test.helpers.state import next_epoch_via_block, transition_to, transition_to_valid_shard_slot
|
||||
from eth2spec.test.context import (
|
||||
with_all_phases_except,
|
||||
spec_state_test,
|
||||
|
@ -32,6 +32,8 @@ def run_process_custody_final_updates(spec, state):
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_validator_withdrawal_delay(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
spec.initiate_validator_exit(state, 0)
|
||||
assert state.validators[0].withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||
|
||||
|
@ -43,6 +45,8 @@ def test_validator_withdrawal_delay(spec, state):
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_validator_withdrawal_reenable_after_custody_reveal(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
spec.initiate_validator_exit(state, 0)
|
||||
assert state.validators[0].withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||
|
||||
|
@ -66,7 +70,8 @@ def test_validator_withdrawal_reenable_after_custody_reveal(spec, state):
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_validator_withdrawal_suspend_after_chunk_challenge(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
|
@ -114,7 +119,8 @@ def test_validator_withdrawal_suspend_after_chunk_challenge(spec, state):
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_validator_withdrawal_resume_after_chunk_challenge_response(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
|
|
|
@ -4,7 +4,9 @@ from eth2spec.test.helpers.custody import (
|
|||
from eth2spec.test.helpers.state import transition_to
|
||||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
MINIMAL,
|
||||
with_all_phases_except,
|
||||
with_configs,
|
||||
spec_state_test,
|
||||
)
|
||||
from eth2spec.test.phase0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
||||
|
@ -17,6 +19,7 @@ def run_process_challenge_deadlines(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_validator_slashed_after_reveal_deadline(spec, state):
|
||||
assert state.validators[0].slashed == 0
|
||||
transition_to(spec, state, spec.get_randao_epoch_for_custody_period(0, 0) * spec.SLOTS_PER_EPOCH)
|
||||
|
@ -36,6 +39,7 @@ def test_validator_slashed_after_reveal_deadline(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@with_configs([MINIMAL], reason="too slow")
|
||||
def test_validator_not_slashed_after_reveal(spec, state):
|
||||
transition_to(spec, state, spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH)
|
||||
custody_key_reveal = get_valid_custody_key_reveal(spec, state)
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
from typing import Dict, Sequence
|
||||
|
||||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
PHASE0, MINIMAL,
|
||||
with_all_phases_except,
|
||||
spec_state_test,
|
||||
only_full_crosslink,
|
||||
with_configs,
|
||||
)
|
||||
from eth2spec.test.helpers.attestations import get_valid_on_time_attestation
|
||||
from eth2spec.test.helpers.block import build_empty_block
|
||||
from eth2spec.test.helpers.custody import (
|
||||
get_custody_secret,
|
||||
get_custody_slashable_test_vector,
|
||||
get_valid_chunk_challenge,
|
||||
get_valid_custody_chunk_response,
|
||||
|
@ -16,13 +17,13 @@ from eth2spec.test.helpers.custody import (
|
|||
get_valid_custody_slashing,
|
||||
get_valid_early_derived_secret_reveal,
|
||||
)
|
||||
from eth2spec.test.helpers.keys import privkeys
|
||||
from eth2spec.test.helpers.shard_block import (
|
||||
build_shard_block,
|
||||
get_committee_index_of_shard,
|
||||
get_sample_shard_block_body,
|
||||
get_shard_transitions,
|
||||
)
|
||||
from eth2spec.test.helpers.shard_transitions import is_full_crosslink
|
||||
from eth2spec.test.helpers.state import state_transition_and_sign_block, transition_to_valid_shard_slot, transition_to
|
||||
|
||||
|
||||
|
@ -99,13 +100,9 @@ def run_beacon_block_with_shard_blocks(spec, state, target_len_offset_slot, comm
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_process_beacon_block_with_normal_shard_transition(spec, state):
|
||||
# NOTE: this test is only for full crosslink (minimal config), not for mainnet
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
|
||||
target_len_offset_slot = 1
|
||||
committee_index = spec.CommitteeIndex(0)
|
||||
|
@ -117,13 +114,9 @@ def test_process_beacon_block_with_normal_shard_transition(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_process_beacon_block_with_empty_proposal_transition(spec, state):
|
||||
# NOTE: this test is only for full crosslink (minimal config), not for mainnet
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
|
||||
target_len_offset_slot = 1
|
||||
committee_index = spec.CommitteeIndex(0)
|
||||
|
@ -140,13 +133,9 @@ def test_process_beacon_block_with_empty_proposal_transition(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_with_shard_transition_with_custody_challenge_and_response(spec, state):
|
||||
# NOTE: this test is only for full crosslink (minimal config), not for mainnet
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
|
||||
# build shard block
|
||||
shard = 0
|
||||
|
@ -178,8 +167,9 @@ def test_with_shard_transition_with_custody_challenge_and_response(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@with_configs([MINIMAL])
|
||||
def test_custody_key_reveal(spec, state):
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH)
|
||||
|
||||
block = build_empty_block(spec, state, slot=state.slot + 1)
|
||||
|
@ -192,7 +182,7 @@ def test_custody_key_reveal(spec, state):
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_early_derived_secret_reveal(spec, state):
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
block = build_empty_block(spec, state, slot=state.slot + 1)
|
||||
early_derived_secret_reveal = get_valid_early_derived_secret_reveal(spec, state)
|
||||
block.body.early_derived_secret_reveals = [early_derived_secret_reveal]
|
||||
|
@ -202,20 +192,21 @@ def test_early_derived_secret_reveal(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_custody_slashing(spec, state):
|
||||
# NOTE: this test is only for full crosslink (minimal config), not for mainnet
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
|
||||
# Build shard block
|
||||
shard = 0
|
||||
committee_index = get_committee_index_of_shard(spec, state, state.slot, shard)
|
||||
# Create slashable shard block body
|
||||
validator_index = spec.get_beacon_committee(state, state.slot, committee_index)[0]
|
||||
custody_secret = get_custody_secret(spec, state, validator_index)
|
||||
custody_secret = spec.get_custody_secret(
|
||||
state,
|
||||
validator_index,
|
||||
privkeys[validator_index],
|
||||
spec.get_current_epoch(state),
|
||||
)
|
||||
slashable_body = get_custody_slashable_test_vector(spec, custody_secret, length=100, slashable=True)
|
||||
shard_block = build_shard_block(spec, state, shard, body=slashable_body, slot=state.slot, signed=True)
|
||||
shard_block_dict: Dict[spec.Shard, Sequence[spec.SignedShardBlock]] = {shard: [shard_block]}
|
||||
|
|
|
@ -4,12 +4,12 @@ from eth2spec.test.context import (
|
|||
expect_assertion_error,
|
||||
spec_state_test,
|
||||
with_all_phases_except,
|
||||
only_full_crosslink,
|
||||
)
|
||||
from eth2spec.test.helpers.shard_block import (
|
||||
build_shard_block,
|
||||
sign_shard_block,
|
||||
)
|
||||
from eth2spec.test.helpers.shard_transitions import is_full_crosslink
|
||||
from eth2spec.test.helpers.state import next_slot, transition_to_valid_shard_slot, transition_to
|
||||
|
||||
|
||||
|
@ -46,15 +46,14 @@ def run_shard_blocks(spec, shard_state, signed_shard_block, beacon_parent_state,
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
@only_full_crosslink
|
||||
def test_valid_shard_block(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
signed_shard_block = build_shard_block(spec, state, shard, slot=beacon_state.slot, signed=True)
|
||||
|
||||
yield from run_shard_blocks(spec, shard_state, signed_shard_block, beacon_state)
|
||||
|
||||
|
@ -66,12 +65,11 @@ def test_valid_shard_block(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_invalid_shard_parent_root(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
|
@ -83,12 +81,10 @@ def test_invalid_shard_parent_root(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_invalid_beacon_parent_root(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
|
@ -100,12 +96,10 @@ def test_invalid_beacon_parent_root(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_invalid_slot(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
|
@ -118,12 +112,10 @@ def test_invalid_slot(spec, state):
|
|||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_invalid_proposer_index(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
|
@ -141,13 +133,10 @@ def test_invalid_proposer_index(spec, state):
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
@only_full_crosslink
|
||||
def test_out_of_bound_offset(spec, state):
|
||||
# TODO: Handle this edge case properly
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
slot = (
|
||||
beacon_state.shard_states[shard].slot
|
||||
|
@ -165,12 +154,10 @@ def test_out_of_bound_offset(spec, state):
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
@only_full_crosslink
|
||||
def test_invalid_offset(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
# 4 is not in `SHARD_BLOCK_OFFSETS`
|
||||
shard = 0
|
||||
slot = beacon_state.shard_states[shard].slot + 4
|
||||
|
@ -186,12 +173,10 @@ def test_invalid_offset(spec, state):
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
@only_full_crosslink
|
||||
def test_empty_block_body(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, body=b'', signed=True)
|
||||
|
@ -207,12 +192,10 @@ def test_empty_block_body(spec, state):
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
@only_full_crosslink
|
||||
def test_invalid_signature(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=False)
|
||||
|
@ -228,12 +211,10 @@ def test_invalid_signature(spec, state):
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
@only_full_crosslink
|
||||
def test_max_offset(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
slot = beacon_state.shard_states[shard].slot + spec.SHARD_BLOCK_OFFSETS[spec.MAX_SHARD_BLOCKS_PER_ATTESTATION - 1]
|
||||
transition_to(spec, beacon_state, slot)
|
||||
|
@ -247,13 +228,11 @@ def test_max_offset(spec, state):
|
|||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
@only_full_crosslink
|
||||
def test_pending_shard_parent_block(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
# Block N
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block_1 = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
|
||||
from eth2spec.test.context import PHASE0, spec_state_test, with_all_phases_except, never_bls, only_full_crosslink
|
||||
from eth2spec.test.helpers.attestations import get_valid_on_time_attestation
|
||||
from eth2spec.test.helpers.shard_block import (
|
||||
build_shard_block,
|
||||
get_shard_transitions,
|
||||
get_committee_index_of_shard,
|
||||
)
|
||||
from eth2spec.test.helpers.fork_choice import add_block_to_store, get_anchor_root, get_genesis_forkchoice_store
|
||||
from eth2spec.test.helpers.state import state_transition_and_sign_block
|
||||
from eth2spec.test.helpers.block import build_empty_block
|
||||
|
||||
|
||||
def run_on_shard_block(spec, store, signed_block, valid=True):
|
||||
shard = signed_block.message.shard
|
||||
if not valid:
|
||||
try:
|
||||
spec.on_shard_block(store, signed_block)
|
||||
except AssertionError:
|
||||
return
|
||||
else:
|
||||
assert False
|
||||
|
||||
spec.on_shard_block(store, signed_block)
|
||||
shard_store = store.shard_stores[shard]
|
||||
assert shard_store.signed_blocks[hash_tree_root(signed_block.message)] == signed_block
|
||||
|
||||
|
||||
def initialize_store(spec, state, shards):
|
||||
store = get_genesis_forkchoice_store(spec, state)
|
||||
anchor_root = get_anchor_root(spec, state)
|
||||
assert spec.get_head(store) == anchor_root
|
||||
|
||||
for shard in shards:
|
||||
shard_head_root = spec.get_shard_head(store, shard)
|
||||
assert shard_head_root == state.shard_states[shard].latest_block_root
|
||||
shard_store = store.shard_stores[shard]
|
||||
assert shard_store.block_states[shard_head_root].slot == 0
|
||||
assert shard_store.block_states[shard_head_root] == state.shard_states[shard]
|
||||
|
||||
return store
|
||||
|
||||
|
||||
def create_and_apply_shard_block(spec, store, shard, beacon_parent_state, shard_blocks_buffer):
|
||||
body = b'\x56' * 4
|
||||
shard_head_root = spec.get_shard_head(store, shard)
|
||||
shard_store = store.shard_stores[shard]
|
||||
shard_parent_state = shard_store.block_states[shard_head_root]
|
||||
assert shard_parent_state.slot != beacon_parent_state.slot
|
||||
shard_block = build_shard_block(
|
||||
spec, beacon_parent_state, shard,
|
||||
shard_parent_state=shard_parent_state, slot=beacon_parent_state.slot, body=body, signed=True
|
||||
)
|
||||
shard_blocks_buffer.append(shard_block)
|
||||
run_on_shard_block(spec, store, shard_block)
|
||||
assert spec.get_shard_head(store, shard) == shard_block.message.hash_tree_root()
|
||||
|
||||
|
||||
def check_pending_shard_blocks(spec, store, shard, shard_blocks_buffer):
|
||||
pending_shard_blocks = spec.get_pending_shard_blocks(store, shard)
|
||||
assert pending_shard_blocks == shard_blocks_buffer
|
||||
|
||||
|
||||
def is_in_offset_sets(spec, beacon_head_state, shard):
|
||||
offset_slots = spec.compute_offset_slots(
|
||||
beacon_head_state.shard_states[shard].slot, beacon_head_state.slot + 1
|
||||
)
|
||||
return beacon_head_state.slot in offset_slots
|
||||
|
||||
|
||||
def create_attestation_for_shard_blocks(spec, beacon_parent_state, shard, committee_index, blocks,
|
||||
filter_participant_set=None):
|
||||
shard_transition = spec.get_shard_transition(beacon_parent_state, shard, blocks)
|
||||
attestation = get_valid_on_time_attestation(
|
||||
spec,
|
||||
beacon_parent_state,
|
||||
index=committee_index,
|
||||
shard_transition=shard_transition,
|
||||
signed=True,
|
||||
)
|
||||
return attestation
|
||||
|
||||
|
||||
def create_beacon_block_with_shard_transition(
|
||||
spec, state, store, shard, shard_blocks_buffer, is_checking_pending_shard_blocks=True):
|
||||
beacon_block = build_empty_block(spec, state, slot=state.slot + 1)
|
||||
committee_index = get_committee_index_of_shard(spec, state, state.slot, shard)
|
||||
has_shard_committee = committee_index is not None # has committee of `shard` at this slot
|
||||
|
||||
beacon_block = build_empty_block(spec, state, slot=state.slot + 1)
|
||||
|
||||
# If next slot has committee of `shard`, add `shard_transtion` to the proposing beacon block
|
||||
if has_shard_committee and len(shard_blocks_buffer) > 0:
|
||||
# Sanity check `get_pending_shard_blocks`
|
||||
# Assert that the pending shard blocks set in the store equal to shard_blocks_buffer
|
||||
if is_checking_pending_shard_blocks:
|
||||
check_pending_shard_blocks(spec, store, shard, shard_blocks_buffer)
|
||||
# Use temporary next state to get ShardTransition of shard block
|
||||
shard_transitions = get_shard_transitions(spec, state, shard_block_dict={shard: shard_blocks_buffer})
|
||||
shard_transition = shard_transitions[shard]
|
||||
attestation = get_valid_on_time_attestation(
|
||||
spec,
|
||||
state,
|
||||
index=committee_index,
|
||||
shard_transition=shard_transition,
|
||||
signed=True,
|
||||
)
|
||||
assert attestation.data.shard == shard
|
||||
beacon_block.body.attestations = [attestation]
|
||||
beacon_block.body.shard_transitions = shard_transitions
|
||||
|
||||
# Clear buffer
|
||||
shard_blocks_buffer.clear()
|
||||
|
||||
return beacon_block
|
||||
|
||||
|
||||
def apply_all_attestation_to_store(spec, store, attestations):
|
||||
for attestation in attestations:
|
||||
spec.on_attestation(store, attestation)
|
||||
|
||||
|
||||
def apply_beacon_block_to_store(spec, state, store, beacon_block):
|
||||
signed_beacon_block = state_transition_and_sign_block(spec, state, beacon_block) # transition!
|
||||
store.time = store.time + spec.SECONDS_PER_SLOT
|
||||
add_block_to_store(spec, store, signed_beacon_block)
|
||||
apply_all_attestation_to_store(spec, store, signed_beacon_block.message.body.attestations)
|
||||
|
||||
|
||||
def create_and_apply_beacon_and_shard_blocks(spec, state, store, shard, shard_blocks_buffer,
|
||||
is_checking_pending_shard_blocks=True):
|
||||
beacon_block = create_beacon_block_with_shard_transition(
|
||||
spec, state, store, shard, shard_blocks_buffer,
|
||||
is_checking_pending_shard_blocks=is_checking_pending_shard_blocks
|
||||
)
|
||||
apply_beacon_block_to_store(spec, state, store, beacon_block)
|
||||
|
||||
# On shard block at the transitioned `state.slot`
|
||||
if is_in_offset_sets(spec, state, shard):
|
||||
# The created shard block would be appended to `shard_blocks_buffer`
|
||||
create_and_apply_shard_block(spec, store, shard, state, shard_blocks_buffer)
|
||||
|
||||
has_shard_committee = get_committee_index_of_shard(spec, state, state.slot, shard) is not None
|
||||
return has_shard_committee
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@never_bls # Set to never_bls for testing `check_pending_shard_blocks`
|
||||
def test_basic(spec, state):
|
||||
spec.PHASE_1_GENESIS_SLOT = 0 # NOTE: mock genesis slot here
|
||||
state = spec.upgrade_to_phase1(state)
|
||||
shard = spec.Shard(1)
|
||||
|
||||
# Initialization
|
||||
store = initialize_store(spec, state, [shard])
|
||||
|
||||
# For mainnet config, it's possible that only one committee of `shard` per epoch.
|
||||
# we set this counter to test more rounds.
|
||||
shard_committee_counter = 2
|
||||
shard_blocks_buffer = [] # the accumulated shard blocks that haven't been crosslinked yet
|
||||
while shard_committee_counter > 0:
|
||||
has_shard_committee = create_and_apply_beacon_and_shard_blocks(spec, state, store, shard, shard_blocks_buffer)
|
||||
if has_shard_committee:
|
||||
shard_committee_counter -= 1
|
||||
|
||||
|
||||
def create_simple_fork(spec, state, store, shard):
|
||||
# Beacon block
|
||||
beacon_block = create_beacon_block_with_shard_transition(spec, state, store, shard, [])
|
||||
apply_beacon_block_to_store(spec, state, store, beacon_block)
|
||||
|
||||
beacon_head_root = spec.get_head(store)
|
||||
assert beacon_head_root == beacon_block.hash_tree_root()
|
||||
beacon_parent_state = store.block_states[beacon_head_root]
|
||||
shard_store = store.shard_stores[shard]
|
||||
shard_parent_state = shard_store.block_states[spec.get_shard_head(store, shard)]
|
||||
|
||||
# Shard block A
|
||||
body = b'\x56' * 4
|
||||
forking_block_child = build_shard_block(
|
||||
spec, beacon_parent_state, shard,
|
||||
shard_parent_state=shard_parent_state, slot=beacon_parent_state.slot, body=body, signed=True
|
||||
)
|
||||
run_on_shard_block(spec, store, forking_block_child)
|
||||
|
||||
# Shard block B
|
||||
body = b'\x78' * 4 # different body
|
||||
shard_block_b = build_shard_block(
|
||||
spec, beacon_parent_state, shard,
|
||||
shard_parent_state=shard_parent_state, slot=beacon_parent_state.slot, body=body, signed=True
|
||||
)
|
||||
run_on_shard_block(spec, store, shard_block_b)
|
||||
|
||||
# Set forking_block
|
||||
current_head = spec.get_shard_head(store, shard)
|
||||
if current_head == forking_block_child.message.hash_tree_root():
|
||||
head_block = forking_block_child
|
||||
forking_block = shard_block_b
|
||||
else:
|
||||
assert current_head == shard_block_b.message.hash_tree_root()
|
||||
head_block = shard_block_b
|
||||
forking_block = forking_block_child
|
||||
|
||||
return head_block, forking_block
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_shard_simple_fork(spec, state):
|
||||
spec.PHASE_1_GENESIS_SLOT = 0 # NOTE: mock genesis slot here
|
||||
state = spec.upgrade_to_phase1(state)
|
||||
shard = spec.Shard(1)
|
||||
|
||||
# Initialization
|
||||
store = initialize_store(spec, state, [shard])
|
||||
|
||||
# Create fork
|
||||
_, forking_block = create_simple_fork(spec, state, store, shard)
|
||||
|
||||
# Vote for forking_block
|
||||
state = store.block_states[spec.get_head(store)].copy()
|
||||
beacon_block = create_beacon_block_with_shard_transition(spec, state, store, shard, [forking_block],
|
||||
is_checking_pending_shard_blocks=False)
|
||||
store.time = store.time + spec.SECONDS_PER_SLOT
|
||||
apply_all_attestation_to_store(spec, store, beacon_block.body.attestations)
|
||||
|
||||
# Head block has been changed
|
||||
assert spec.get_shard_head(store, shard) == forking_block.message.hash_tree_root()
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@only_full_crosslink
|
||||
def test_shard_latest_messages_for_different_shards(spec, state):
|
||||
spec.PHASE_1_GENESIS_SLOT = 0 # NOTE: mock genesis slot here
|
||||
state = spec.upgrade_to_phase1(state)
|
||||
shard_0 = spec.Shard(0)
|
||||
shard_1 = spec.Shard(1)
|
||||
|
||||
# Initialization
|
||||
store = initialize_store(spec, state, [shard_0, shard_1])
|
||||
|
||||
# Shard 0 ----------------------------------
|
||||
# Create fork on shard 0
|
||||
_, forking_block = create_simple_fork(spec, state, store, shard_0)
|
||||
|
||||
# Vote for forking_block on shard 0
|
||||
state = store.block_states[spec.get_head(store)].copy()
|
||||
beacon_block = create_beacon_block_with_shard_transition(spec, state, store, shard_0, [forking_block],
|
||||
is_checking_pending_shard_blocks=False)
|
||||
store.time = store.time + spec.SECONDS_PER_SLOT
|
||||
apply_all_attestation_to_store(spec, store, beacon_block.body.attestations)
|
||||
|
||||
# Head block of shard 0 has been changed due to the shard latest messages
|
||||
assert spec.get_shard_head(store, shard_0) == forking_block.message.hash_tree_root()
|
||||
|
||||
# Shard 1 ----------------------------------
|
||||
# Run shard 1 after 1~2 epochs
|
||||
shard_committee_counter = 2
|
||||
shard_blocks_buffer = [] # the accumulated shard blocks that haven't been crosslinked yet
|
||||
while shard_committee_counter > 0:
|
||||
has_shard_committee = create_and_apply_beacon_and_shard_blocks(
|
||||
spec, state, store, shard_1, shard_blocks_buffer
|
||||
)
|
||||
if has_shard_committee:
|
||||
shard_committee_counter -= 1
|
||||
|
||||
# Go back to see shard 0 ----------------------------------
|
||||
# The head block of shard 0 should be unchanged.
|
||||
assert spec.get_shard_head(store, shard_0) == forking_block.message.hash_tree_root()
|
|
@ -1,129 +0,0 @@
|
|||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
|
||||
from eth2spec.test.context import PHASE0, spec_state_test, with_all_phases_except, never_bls
|
||||
from eth2spec.test.helpers.attestations import get_valid_on_time_attestation
|
||||
from eth2spec.test.helpers.shard_block import (
|
||||
build_shard_block,
|
||||
get_shard_transitions,
|
||||
get_committee_index_of_shard,
|
||||
)
|
||||
from eth2spec.test.helpers.fork_choice import add_block_to_store, get_anchor_root
|
||||
from eth2spec.test.helpers.state import state_transition_and_sign_block
|
||||
from eth2spec.test.helpers.block import build_empty_block
|
||||
|
||||
|
||||
def run_on_shard_block(spec, store, shard_store, signed_block, valid=True):
|
||||
if not valid:
|
||||
try:
|
||||
spec.on_shard_block(store, shard_store, signed_block)
|
||||
except AssertionError:
|
||||
return
|
||||
else:
|
||||
assert False
|
||||
|
||||
spec.on_shard_block(store, shard_store, signed_block)
|
||||
assert shard_store.signed_blocks[hash_tree_root(signed_block.message)] == signed_block
|
||||
|
||||
|
||||
def apply_shard_block(spec, store, shard_store, beacon_parent_state, shard_blocks_buffer):
|
||||
shard = shard_store.shard
|
||||
body = b'\x56' * 4
|
||||
shard_head_root = spec.get_shard_head(store, shard_store)
|
||||
shard_parent_state = shard_store.block_states[shard_head_root]
|
||||
assert shard_parent_state.slot != beacon_parent_state.slot
|
||||
shard_block = build_shard_block(
|
||||
spec, beacon_parent_state, shard,
|
||||
shard_parent_state=shard_parent_state, slot=beacon_parent_state.slot, body=body, signed=True
|
||||
)
|
||||
shard_blocks_buffer.append(shard_block)
|
||||
run_on_shard_block(spec, store, shard_store, shard_block)
|
||||
assert spec.get_shard_head(store, shard_store) == shard_block.message.hash_tree_root()
|
||||
|
||||
|
||||
def check_pending_shard_blocks(spec, store, shard_store, shard_blocks_buffer):
|
||||
pending_shard_blocks = spec.get_pending_shard_blocks(store, shard_store)
|
||||
assert pending_shard_blocks == shard_blocks_buffer
|
||||
|
||||
|
||||
def is_in_offset_sets(spec, beacon_head_state, shard):
|
||||
offset_slots = spec.compute_offset_slots(
|
||||
beacon_head_state.shard_states[shard].slot, beacon_head_state.slot + 1
|
||||
)
|
||||
return beacon_head_state.slot in offset_slots
|
||||
|
||||
|
||||
def apply_shard_and_beacon(spec, state, store, shard_store, shard_blocks_buffer):
|
||||
store.time = store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
|
||||
|
||||
shard = shard_store.shard
|
||||
committee_index = get_committee_index_of_shard(spec, state, state.slot, shard)
|
||||
has_shard_committee = committee_index is not None # has committee of `shard` at this slot
|
||||
|
||||
beacon_block = build_empty_block(spec, state, slot=state.slot + 1)
|
||||
|
||||
# If next slot has committee of `shard`, add `shard_transtion` to the proposing beacon block
|
||||
if has_shard_committee and len(shard_blocks_buffer) > 0:
|
||||
# Sanity check `get_pending_shard_blocks` function
|
||||
check_pending_shard_blocks(spec, store, shard_store, shard_blocks_buffer)
|
||||
# Use temporary next state to get ShardTransition of shard block
|
||||
shard_transitions = get_shard_transitions(
|
||||
spec,
|
||||
state,
|
||||
shard_block_dict={shard: shard_blocks_buffer},
|
||||
)
|
||||
shard_transition = shard_transitions[shard]
|
||||
attestation = get_valid_on_time_attestation(
|
||||
spec,
|
||||
state,
|
||||
index=committee_index,
|
||||
shard_transition=shard_transition,
|
||||
signed=False,
|
||||
)
|
||||
assert attestation.data.shard == shard
|
||||
beacon_block.body.attestations = [attestation]
|
||||
beacon_block.body.shard_transitions = shard_transitions
|
||||
|
||||
# Clear buffer
|
||||
shard_blocks_buffer.clear()
|
||||
|
||||
signed_beacon_block = state_transition_and_sign_block(spec, state, beacon_block) # transition!
|
||||
add_block_to_store(spec, store, signed_beacon_block)
|
||||
assert spec.get_head(store) == beacon_block.hash_tree_root()
|
||||
|
||||
# On shard block at transitioned `state.slot`
|
||||
if is_in_offset_sets(spec, state, shard):
|
||||
# The created shard block would be appended to `shard_blocks_buffer`
|
||||
apply_shard_block(spec, store, shard_store, state, shard_blocks_buffer)
|
||||
|
||||
return has_shard_committee
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@never_bls # Set to never_bls for testing `check_pending_shard_blocks`
|
||||
def test_basic(spec, state):
|
||||
spec.PHASE_1_GENESIS_SLOT = 0 # NOTE: mock genesis slot here
|
||||
state = spec.upgrade_to_phase1(state)
|
||||
shard = spec.Shard(1)
|
||||
|
||||
# Initialization
|
||||
store = spec.get_forkchoice_store(state)
|
||||
anchor_root = get_anchor_root(spec, state)
|
||||
assert spec.get_head(store) == anchor_root
|
||||
|
||||
shard_store = spec.get_forkchoice_shard_store(state, shard)
|
||||
shard_head_root = spec.get_shard_head(store, shard_store)
|
||||
assert shard_head_root == state.shard_states[shard].latest_block_root
|
||||
assert shard_store.block_states[shard_head_root].slot == 1
|
||||
assert shard_store.block_states[shard_head_root] == state.shard_states[shard]
|
||||
|
||||
# For mainnet config, it's possible that only one committee of `shard` per epoch.
|
||||
# we set this counter to test more rounds.
|
||||
shard_committee_counter = 2
|
||||
shard_blocks_buffer = []
|
||||
while shard_committee_counter > 0:
|
||||
has_shard_committee = apply_shard_and_beacon(
|
||||
spec, state, store, shard_store, shard_blocks_buffer
|
||||
)
|
||||
if has_shard_committee:
|
||||
shard_committee_counter -= 1
|
|
@ -74,3 +74,16 @@ def test_get_start_shard_previous_slot(spec, state):
|
|||
- spec.get_committee_count_delta(state, start_slot=slot, stop_slot=current_epoch_start_slot)
|
||||
) % active_shard_count
|
||||
assert start_shard == expected_start_shard
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_get_start_shard_far_past_epoch(spec, state):
|
||||
initial_epoch = spec.get_current_epoch(state)
|
||||
initial_start_slot = spec.compute_start_slot_at_epoch(initial_epoch)
|
||||
initial_start_shard = state.current_epoch_start_shard
|
||||
|
||||
for _ in range(spec.MAX_SHARDS + 2):
|
||||
next_epoch(spec, state)
|
||||
|
||||
assert spec.get_start_shard(state, initial_start_slot) == initial_start_shard
|
||||
|
|
|
@ -165,6 +165,9 @@ bls_setting: int -- optional, can have 3 different values:
|
|||
but there is no change of outcome when running the test if BLS is ON or OFF.
|
||||
1: known as "BLS required" - if the test validity is strictly dependent on BLS being ON
|
||||
2: known as "BLS ignored" - if the test validity is strictly dependent on BLS being OFF
|
||||
reveal_deadlines_setting: -- optional, can have 2 different values:
|
||||
0: default, `process_reveal_deadlines` is ON.
|
||||
1: `process_reveal_deadlines` is OFF.
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -7,9 +7,10 @@ Sanity tests to cover a series of one or more blocks being processed, aiming to
|
|||
### `meta.yaml`
|
||||
|
||||
```yaml
|
||||
description: string -- Optional. Description of test case, purely for debugging purposes.
|
||||
bls_setting: int -- see general test-format spec.
|
||||
blocks_count: int -- the number of blocks processed in this test.
|
||||
description: string -- Optional. Description of test case, purely for debugging purposes.
|
||||
bls_setting: int -- see general test-format spec.
|
||||
reveal_deadlines_setting: int -- see general test-format spec.
|
||||
blocks_count: int -- the number of blocks processed in this test.
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -1,24 +1,17 @@
|
|||
from typing import Iterable
|
||||
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.test.phase0.epoch_processing import (
|
||||
test_process_final_updates,
|
||||
test_process_justification_and_finalization,
|
||||
test_process_registry_updates,
|
||||
test_process_rewards_and_penalties,
|
||||
test_process_slashings
|
||||
)
|
||||
from gen_base import gen_runner, gen_typing
|
||||
from gen_from_tests.gen import generate_from_tests
|
||||
from importlib import reload
|
||||
from importlib import reload, import_module
|
||||
from eth2spec.config import config_util
|
||||
from eth2spec.test.context import PHASE0
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.test.context import PHASE0, PHASE1
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
|
||||
def create_provider(fork_name: str, handler_name: str,
|
||||
tests_src_mod_name: str, config_name: str) -> gen_typing.TestProvider:
|
||||
def prepare_fn(configs_path: str) -> str:
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
reload(spec_phase0)
|
||||
|
@ -27,27 +20,40 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
|
|||
return config_name
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
tests_src = import_module(tests_src_mod_name)
|
||||
return generate_from_tests(
|
||||
runner_name='epoch_processing',
|
||||
handler_name=handler_name,
|
||||
src=tests_src,
|
||||
fork_name=PHASE0,
|
||||
fork_name=fork_name,
|
||||
)
|
||||
|
||||
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
gen_runner.run_generator("epoch_processing", [
|
||||
create_provider('final_updates', test_process_final_updates, 'minimal'),
|
||||
create_provider('final_updates', test_process_final_updates, 'mainnet'),
|
||||
create_provider('justification_and_finalization', test_process_justification_and_finalization, 'minimal'),
|
||||
create_provider('justification_and_finalization', test_process_justification_and_finalization, 'mainnet'),
|
||||
create_provider('registry_updates', test_process_registry_updates, 'minimal'),
|
||||
create_provider('registry_updates', test_process_registry_updates, 'mainnet'),
|
||||
create_provider('rewards_and_penalties', test_process_rewards_and_penalties, 'minimal'),
|
||||
# runs full epochs filled with data, with uncached ssz. Disabled for now.
|
||||
# create_provider('rewards_and_penalties', test_process_rewards_and_penalties, 'mainnet'),
|
||||
create_provider('slashings', test_process_slashings, 'minimal'),
|
||||
create_provider('slashings', test_process_slashings, 'mainnet'),
|
||||
phase_0_mods = {key: 'eth2spec.test.phase0.epoch_processing.test_process_' + key for key in [
|
||||
'final_updates',
|
||||
'justification_and_finalization',
|
||||
'registry_updates',
|
||||
'rewards_and_penalties',
|
||||
'slashings',
|
||||
]}
|
||||
phase_1_mods = {**{key: 'eth2spec.test.phase1.epoch_processing.test_process_' + key for key in [
|
||||
'challenge_deadlines',
|
||||
'custody_final_updates',
|
||||
'reveal_deadlines',
|
||||
]}, **phase_0_mods} # also run the previous phase 0 tests (but against phase 1 spec)
|
||||
|
||||
gen_runner.run_generator(f"epoch_processing", [
|
||||
create_provider(PHASE0, key, mod_name, 'minimal') for key, mod_name in phase_0_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"epoch_processing", [
|
||||
create_provider(PHASE0, key, mod_name, 'mainnet') for key, mod_name in phase_0_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"epoch_processing", [
|
||||
create_provider(PHASE1, key, mod_name, 'minimal') for key, mod_name in phase_1_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"epoch_processing", [
|
||||
create_provider(PHASE1, key, mod_name, 'mainnet') for key, mod_name in phase_1_mods.items()
|
||||
])
|
||||
|
|
|
@ -4,7 +4,7 @@ from importlib import reload
|
|||
from gen_base import gen_runner, gen_typing
|
||||
from gen_from_tests.gen import generate_from_tests
|
||||
|
||||
from eth2spec.test.context import PHASE0
|
||||
from eth2spec.test.context import PHASE0, PHASE1
|
||||
from eth2spec.test.phase0.finality import test_finality
|
||||
from eth2spec.config import config_util
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
|
@ -12,7 +12,7 @@ from eth2spec.phase1 import spec as spec_phase1
|
|||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
def create_provider(fork_name: str, handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
|
||||
def prepare_fn(configs_path: str) -> str:
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
|
@ -26,14 +26,18 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
|
|||
runner_name='finality',
|
||||
handler_name=handler_name,
|
||||
src=tests_src,
|
||||
fork_name=PHASE0,
|
||||
fork_name=fork_name,
|
||||
)
|
||||
|
||||
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# No additional phase 1 specific rewards tests, yet.
|
||||
key = 'finality'
|
||||
gen_runner.run_generator("finality", [
|
||||
create_provider('finality', test_finality, 'minimal'),
|
||||
create_provider('finality', test_finality, 'mainnet'),
|
||||
create_provider(PHASE0, 'finality', test_finality, 'minimal'),
|
||||
create_provider(PHASE0, 'finality', test_finality, 'mainnet'),
|
||||
create_provider(PHASE1, 'finality', test_finality, 'minimal'),
|
||||
create_provider(PHASE1, 'finality', test_finality, 'mainnet'),
|
||||
])
|
||||
|
|
|
@ -1,26 +1,17 @@
|
|||
from typing import Iterable
|
||||
|
||||
from eth2spec.test.phase0.block_processing import (
|
||||
test_process_attestation,
|
||||
test_process_attester_slashing,
|
||||
test_process_block_header,
|
||||
test_process_deposit,
|
||||
test_process_proposer_slashing,
|
||||
test_process_voluntary_exit,
|
||||
)
|
||||
|
||||
from gen_base import gen_runner, gen_typing
|
||||
from gen_from_tests.gen import generate_from_tests
|
||||
from importlib import reload
|
||||
from importlib import reload, import_module
|
||||
from eth2spec.config import config_util
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.test.context import PHASE0
|
||||
from eth2spec.test.context import PHASE0, PHASE1
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
|
||||
def create_provider(fork_name: str, handler_name: str,
|
||||
tests_src_mod_name: str, config_name: str) -> gen_typing.TestProvider:
|
||||
def prepare_fn(configs_path: str) -> str:
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
reload(spec_phase0)
|
||||
|
@ -29,28 +20,44 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
|
|||
return config_name
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
tests_src = import_module(tests_src_mod_name)
|
||||
return generate_from_tests(
|
||||
runner_name='operations',
|
||||
handler_name=handler_name,
|
||||
src=tests_src,
|
||||
fork_name=PHASE0,
|
||||
fork_name=fork_name,
|
||||
)
|
||||
|
||||
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
gen_runner.run_generator("operations", [
|
||||
create_provider('attestation', test_process_attestation, 'minimal'),
|
||||
create_provider('attestation', test_process_attestation, 'mainnet'),
|
||||
create_provider('attester_slashing', test_process_attester_slashing, 'minimal'),
|
||||
create_provider('attester_slashing', test_process_attester_slashing, 'mainnet'),
|
||||
create_provider('block_header', test_process_block_header, 'minimal'),
|
||||
create_provider('block_header', test_process_block_header, 'mainnet'),
|
||||
create_provider('deposit', test_process_deposit, 'minimal'),
|
||||
create_provider('deposit', test_process_deposit, 'mainnet'),
|
||||
create_provider('proposer_slashing', test_process_proposer_slashing, 'minimal'),
|
||||
create_provider('proposer_slashing', test_process_proposer_slashing, 'mainnet'),
|
||||
create_provider('voluntary_exit', test_process_voluntary_exit, 'minimal'),
|
||||
create_provider('voluntary_exit', test_process_voluntary_exit, 'mainnet'),
|
||||
phase_0_mods = {key: 'eth2spec.test.phase0.block_processing.test_process_' + key for key in [
|
||||
'attestation',
|
||||
'attester_slashing',
|
||||
'block_header',
|
||||
'deposit',
|
||||
'proposer_slashing',
|
||||
'voluntary_exit',
|
||||
]}
|
||||
phase_1_mods = {**{key: 'eth2spec.test.phase1.block_processing.test_process_' + key for key in [
|
||||
'attestation',
|
||||
'chunk_challenge',
|
||||
'custody_key_reveal',
|
||||
'custody_slashing',
|
||||
'early_derived_secret_reveal',
|
||||
'shard_transition',
|
||||
]}, **phase_0_mods} # also run the previous phase 0 tests (but against phase 1 spec)
|
||||
|
||||
gen_runner.run_generator(f"operations", [
|
||||
create_provider(PHASE0, key, mod_name, 'minimal') for key, mod_name in phase_0_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"operations", [
|
||||
create_provider(PHASE0, key, mod_name, 'mainnet') for key, mod_name in phase_0_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"operations", [
|
||||
create_provider(PHASE1, key, mod_name, 'minimal') for key, mod_name in phase_1_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"operations", [
|
||||
create_provider(PHASE1, key, mod_name, 'mainnet') for key, mod_name in phase_1_mods.items()
|
||||
])
|
||||
|
|
|
@ -1,22 +1,17 @@
|
|||
from typing import Iterable
|
||||
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.test.phase0.rewards import (
|
||||
test_basic,
|
||||
test_leak,
|
||||
test_random,
|
||||
)
|
||||
from gen_base import gen_runner, gen_typing
|
||||
from gen_from_tests.gen import generate_from_tests
|
||||
from importlib import reload
|
||||
from importlib import reload, import_module
|
||||
from eth2spec.config import config_util
|
||||
from eth2spec.test.context import PHASE0
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.test.context import PHASE0, PHASE1
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def create_provider(tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
|
||||
def create_provider(fork_name: str, handler_name: str,
|
||||
tests_src_mod_name: str, config_name: str) -> gen_typing.TestProvider:
|
||||
def prepare_fn(configs_path: str) -> str:
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
reload(spec_phase0)
|
||||
|
@ -25,22 +20,35 @@ def create_provider(tests_src, config_name: str) -> gen_typing.TestProvider:
|
|||
return config_name
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
tests_src = import_module(tests_src_mod_name)
|
||||
return generate_from_tests(
|
||||
runner_name='rewards',
|
||||
handler_name='core',
|
||||
handler_name=handler_name,
|
||||
src=tests_src,
|
||||
fork_name=PHASE0,
|
||||
fork_name=fork_name,
|
||||
)
|
||||
|
||||
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
gen_runner.run_generator("rewards", [
|
||||
create_provider(test_basic, 'minimal'),
|
||||
create_provider(test_basic, 'mainnet'),
|
||||
create_provider(test_leak, 'minimal'),
|
||||
create_provider(test_leak, 'mainnet'),
|
||||
create_provider(test_random, 'minimal'),
|
||||
create_provider(test_random, 'mainnet'),
|
||||
phase_0_mods = {key: 'eth2spec.test.phase0.rewards.test_' + key for key in [
|
||||
'basic',
|
||||
'leak',
|
||||
'random',
|
||||
]}
|
||||
# No additional phase 1 specific rewards tests, yet.
|
||||
phase_1_mods = phase_0_mods
|
||||
|
||||
gen_runner.run_generator(f"rewards", [
|
||||
create_provider(PHASE0, key, mod_name, 'minimal') for key, mod_name in phase_0_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"rewards", [
|
||||
create_provider(PHASE0, key, mod_name, 'mainnet') for key, mod_name in phase_0_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"rewards", [
|
||||
create_provider(PHASE1, key, mod_name, 'minimal') for key, mod_name in phase_1_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"rewards", [
|
||||
create_provider(PHASE1, key, mod_name, 'mainnet') for key, mod_name in phase_1_mods.items()
|
||||
])
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
from typing import Iterable
|
||||
from importlib import reload
|
||||
|
||||
from gen_base import gen_runner, gen_typing
|
||||
from gen_from_tests.gen import generate_from_tests
|
||||
|
||||
from eth2spec.test.context import PHASE0
|
||||
from eth2spec.test.phase0.sanity import test_blocks, test_slots
|
||||
from importlib import reload, import_module
|
||||
from eth2spec.config import config_util
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.test.context import PHASE0, PHASE1
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
|
||||
def create_provider(fork_name: str, handler_name: str,
|
||||
tests_src_mod_name: str, config_name: str) -> gen_typing.TestProvider:
|
||||
def prepare_fn(configs_path: str) -> str:
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
reload(spec_phase0)
|
||||
|
@ -22,20 +20,36 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
|
|||
return config_name
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
tests_src = import_module(tests_src_mod_name)
|
||||
return generate_from_tests(
|
||||
runner_name='sanity',
|
||||
handler_name=handler_name,
|
||||
src=tests_src,
|
||||
fork_name=PHASE0,
|
||||
fork_name=fork_name,
|
||||
)
|
||||
|
||||
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
gen_runner.run_generator("sanity", [
|
||||
create_provider('blocks', test_blocks, 'minimal'),
|
||||
create_provider('blocks', test_blocks, 'mainnet'),
|
||||
create_provider('slots', test_slots, 'minimal'),
|
||||
create_provider('slots', test_slots, 'mainnet'),
|
||||
phase_0_mods = {key: 'eth2spec.test.phase0.sanity.test_' + key for key in [
|
||||
'blocks',
|
||||
'slots',
|
||||
]}
|
||||
phase_1_mods = {**{key: 'eth2spec.test.phase1.sanity.test_' + key for key in [
|
||||
'blocks', # more phase 1 specific block tests
|
||||
'shard_blocks',
|
||||
]}, **phase_0_mods} # also run the previous phase 0 tests (but against phase 1 spec)
|
||||
|
||||
gen_runner.run_generator(f"sanity", [
|
||||
create_provider(PHASE0, key, mod_name, 'minimal') for key, mod_name in phase_0_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"sanity", [
|
||||
create_provider(PHASE0, key, mod_name, 'mainnet') for key, mod_name in phase_0_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"sanity", [
|
||||
create_provider(PHASE1, key, mod_name, 'minimal') for key, mod_name in phase_1_mods.items()
|
||||
])
|
||||
gen_runner.run_generator(f"sanity", [
|
||||
create_provider(PHASE1, key, mod_name, 'mainnet') for key, mod_name in phase_1_mods.items()
|
||||
])
|
||||
|
|
|
@ -12,4 +12,3 @@ def invalid_cases():
|
|||
yield "byte_rev_nibble", invalid_test_case(lambda: b'\x10')
|
||||
yield "byte_0x80", invalid_test_case(lambda: b'\x80')
|
||||
yield "byte_full", invalid_test_case(lambda: b'\xff')
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ def valid_cases():
|
|||
def mod_offset(b: bytes, offset_index: int, change: Callable[[int], int]):
|
||||
return b[:offset_index] + \
|
||||
(change(int.from_bytes(b[offset_index:offset_index + 4], byteorder='little')) & 0xffffffff) \
|
||||
.to_bytes(length=4, byteorder='little') + \
|
||||
.to_bytes(length=4, byteorder='little') + \
|
||||
b[offset_index + 4:]
|
||||
|
||||
|
||||
|
|
|
@ -7,8 +7,9 @@ from gen_base import gen_runner, gen_typing
|
|||
|
||||
from eth2spec.debug import random_value, encode
|
||||
from eth2spec.config import config_util
|
||||
from eth2spec.phase0 import spec
|
||||
from eth2spec.test.context import PHASE0
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.test.context import PHASE0, PHASE1
|
||||
from eth2spec.utils.ssz.ssz_typing import Container
|
||||
from eth2spec.utils.ssz.ssz_impl import (
|
||||
hash_tree_root,
|
||||
|
@ -16,11 +17,12 @@ from eth2spec.utils.ssz.ssz_impl import (
|
|||
)
|
||||
|
||||
|
||||
MAX_BYTES_LENGTH = 100
|
||||
MAX_BYTES_LENGTH = 1000
|
||||
MAX_LIST_LENGTH = 10
|
||||
|
||||
|
||||
def create_test_case(rng: Random, typ, mode: random_value.RandomizationMode, chaos: bool) -> Iterable[gen_typing.TestCasePart]:
|
||||
def create_test_case(rng: Random, typ,
|
||||
mode: random_value.RandomizationMode, chaos: bool) -> Iterable[gen_typing.TestCasePart]:
|
||||
value = random_value.get_random_ssz_object(rng, typ, MAX_BYTES_LENGTH, MAX_LIST_LENGTH, mode, chaos)
|
||||
yield "value", "data", encode.encode(value)
|
||||
yield "serialized", "ssz", serialize(value)
|
||||
|
@ -30,14 +32,15 @@ def create_test_case(rng: Random, typ, mode: random_value.RandomizationMode, cha
|
|||
yield "roots", "data", roots_data
|
||||
|
||||
|
||||
def get_spec_ssz_types():
|
||||
def get_spec_ssz_types(spec):
|
||||
return [
|
||||
(name, value) for (name, value) in getmembers(spec, isclass)
|
||||
if issubclass(value, Container) and value != Container # only the subclasses, not the imported base class
|
||||
]
|
||||
|
||||
|
||||
def ssz_static_cases(seed: int, name, ssz_type, mode: random_value.RandomizationMode, chaos: bool, count: int):
|
||||
def ssz_static_cases(fork_name: str, seed: int, name, ssz_type,
|
||||
mode: random_value.RandomizationMode, chaos: bool, count: int):
|
||||
random_mode_name = mode.to_name()
|
||||
|
||||
# Reproducible RNG
|
||||
|
@ -45,7 +48,7 @@ def ssz_static_cases(seed: int, name, ssz_type, mode: random_value.Randomization
|
|||
|
||||
for i in range(count):
|
||||
yield gen_typing.TestCase(
|
||||
fork_name=PHASE0,
|
||||
fork_name=fork_name,
|
||||
runner_name='ssz_static',
|
||||
handler_name=name,
|
||||
suite_name=f"ssz_{random_mode_name}{'_chaos' if chaos else ''}",
|
||||
|
@ -54,19 +57,23 @@ def ssz_static_cases(seed: int, name, ssz_type, mode: random_value.Randomization
|
|||
)
|
||||
|
||||
|
||||
def create_provider(config_name: str, seed: int, mode: random_value.RandomizationMode, chaos: bool,
|
||||
def create_provider(fork_name, config_name: str, seed: int, mode: random_value.RandomizationMode, chaos: bool,
|
||||
cases_if_random: int) -> gen_typing.TestProvider:
|
||||
def prepare_fn(configs_path: str) -> str:
|
||||
# Apply changes to presets, this affects some of the vector types.
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
reload(spec)
|
||||
reload(spec_phase0)
|
||||
reload(spec_phase1)
|
||||
return config_name
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
count = cases_if_random if chaos or mode.is_changing() else 1
|
||||
spec = spec_phase0
|
||||
if fork_name == PHASE1:
|
||||
spec = spec_phase1
|
||||
|
||||
for (i, (name, ssz_type)) in enumerate(get_spec_ssz_types()):
|
||||
yield from ssz_static_cases(seed * 1000 + i, name, ssz_type, mode, chaos, count)
|
||||
for (i, (name, ssz_type)) in enumerate(get_spec_ssz_types(spec)):
|
||||
yield from ssz_static_cases(fork_name, seed * 1000 + i, name, ssz_type, mode, chaos, count)
|
||||
|
||||
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
|
||||
|
||||
|
@ -80,10 +87,11 @@ if __name__ == "__main__":
|
|||
seed += 1
|
||||
settings.append((seed, "minimal", random_value.RandomizationMode.mode_random, True, 30))
|
||||
seed += 1
|
||||
settings.append((seed, "mainnet", random_value.RandomizationMode.mode_random, False, 5))
|
||||
seed += 1
|
||||
# settings.append((seed, "mainnet", random_value.RandomizationMode.mode_random, False, 5))
|
||||
# seed += 1
|
||||
|
||||
gen_runner.run_generator("ssz_static", [
|
||||
create_provider(config_name, seed, mode, chaos, cases_if_random)
|
||||
for (seed, config_name, mode, chaos, cases_if_random) in settings
|
||||
])
|
||||
for fork in [PHASE0, PHASE1]:
|
||||
gen_runner.run_generator("ssz_static", [
|
||||
create_provider(fork, config_name, seed, mode, chaos, cases_if_random)
|
||||
for (seed, config_name, mode, chaos, cases_if_random) in settings
|
||||
])
|
||||
|
|
Loading…
Reference in New Issue