Merges dev into carl-patch-1

This commit is contained in:
Carl Beekhuizen 2019-04-23 13:39:19 +02:00
commit fe01b6f231
No known key found for this signature in database
GPG Key ID: D05CA176D0020646
21 changed files with 240 additions and 273 deletions

View File

@ -1,89 +1,97 @@
version: 2.1
commands:
restore_cached_venv:
description: "Restores a cached venv"
parameters:
reqs_checksum:
type: string
default: "1234"
venv_name:
type: string
default: "default-name"
steps:
- restore_cache:
keys:
- << parameters.venv_name >>-venv-<< parameters.reqs_checksum >>
# fallback to using the latest cache if no exact match is found
- << parameters.venv_name >>-venv-
save_cached_venv:
description: "Saves a venv into a cache"
parameters:
reqs_checksum:
type: string
default: "1234"
venv_path:
type: string
default: "venv"
venv_name:
type: string
default: "default-name"
steps:
- save_cache:
key: << parameters.venv_name >>-venv-<< parameters.reqs_checksum >>
paths: << parameters.venv_path >>
jobs:
build:
checkout_specs:
docker:
- image: circleci/python:3.6
working_directory: ~/repo
working_directory: ~/specs-repo
steps:
# Restore git repo at point close to target branch/revision, to speed up checkout
- restore_cache:
keys:
- v1-specs-repo-{{ .Branch }}-{{ .Revision }}
- v1-specs-repo-{{ .Branch }}-
- v1-specs-repo-
- checkout
- run:
name: Build pyspec
command: make pyspec
name: Clean up git repo to reduce cache size
command: git gc
# Save the git checkout as a cache, to make cloning next time faster.
- save_cache:
key: v1-specs-repo-{{ .Branch }}-{{ .Revision }}
paths:
- ~/specs-repo
install_test:
docker:
- image: circleci/python:3.6
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v1-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_cached_venv:
venv_name: v1-pyspec
reqs_checksum: '{{ checksum "test_libs/pyspec/requirements.txt" }}'
- run:
name: Install pyspec requirements
command: make install_test
- save_cached_venv:
venv_name: v1-pyspec
reqs_checksum: '{{ checksum "test_libs/pyspec/requirements.txt" }}'
venv_path: ./test_libs/pyspec/venv
test:
docker:
- image: circleci/python:3.6
working_directory: ~/specs-repo
steps:
- restore_cache:
key: v1-specs-repo-{{ .Branch }}-{{ .Revision }}
- restore_cached_venv:
venv_name: v1-pyspec
reqs_checksum: '{{ checksum "test_libs/pyspec/requirements.txt" }}'
- run:
name: Run py-tests
command: make test
# TODO see #928: decide on CI triggering of yaml tests building,
# and destination of output (new yaml tests LFS-configured repository)
#
# - run:
# name: Generate YAML tests
# command: make gen_yaml_tests
#
# - store_artifacts:
# path: test-reports
# destination: test-reports
#
# - run:
# name: Save YAML tests for deployment
# command: |
# mkdir /tmp/workspace
# cp -r yaml_tests /tmp/workspace/
# git log -1 >> /tmp/workspace/latest_commit_message
# - persist_to_workspace:
# root: /tmp/workspace
# paths:
# - yaml_tests
# - latest_commit_message
# commit:
# docker:
# - image: circleci/python:3.6
# steps:
# - attach_workspace:
# at: /tmp/workspace
# - add_ssh_keys:
# fingerprints:
# - "01:85:b6:36:96:a6:84:72:e4:9b:4e:38:ee:21:97:fa"
# - run:
# name: Checkout test repository
# command: |
# ssh-keyscan -H github.com >> ~/.ssh/known_hosts
# git clone git@github.com:ethereum/eth2.0-tests.git
# - run:
# name: Commit and push generated YAML tests
# command: |
# cd eth2.0-tests
# git config user.name 'eth2TestGenBot'
# git config user.email '47188154+eth2TestGenBot@users.noreply.github.com'
# for filename in /tmp/workspace/yaml_tests/*; do
# rm -rf $(basename $filename)
# cp -r $filename .
# done
# git add .
# if git diff --cached --exit-code >& /dev/null; then
# echo "No changes to commit"
# else
# echo -e "Update generated tests\n\nLatest commit message from eth2.0-specs:\n" > commit_message
# cat /tmp/workspace/latest_commit_message >> commit_message
# git commit -F commit_message
# git push origin master
# fi
#workflows:
# version: 2.1
#
# build_and_commit:
# jobs:
# - build:
# filters:
# tags:
# only: /.*/
# - commit:
# requires:
# - build
# filters:
# tags:
# only: /.*/
# branches:
# ignore: /.*/
command: make citest
- store_test_results:
path: test_libs/pyspec/test-reports
workflows:
version: 2.1
test_spec:
jobs:
- checkout_specs
- install_test:
requires:
- checkout_specs
- test:
requires:
- install_test

2
.gitignore vendored
View File

@ -8,7 +8,7 @@ venv
build/
output/
yaml_tests/
eth2.0-spec-tests/
.pytest_cache
# Dynamically built from Markdown spec

View File

@ -2,7 +2,7 @@ SPEC_DIR = ./specs
SCRIPT_DIR = ./scripts
TEST_LIBS_DIR = ./test_libs
PY_SPEC_DIR = $(TEST_LIBS_DIR)/pyspec
YAML_TEST_DIR = ./yaml_tests
YAML_TEST_DIR = ./eth2.0-spec-tests/tests
GENERATOR_DIR = ./test_generators
CONFIGS_DIR = ./configs
@ -16,7 +16,7 @@ PY_SPEC_PHASE_0_TARGETS = $(PY_SPEC_DIR)/eth2spec/phase0/spec.py
PY_SPEC_ALL_TARGETS = $(PY_SPEC_PHASE_0_TARGETS)
.PHONY: clean all test gen_yaml_tests pyspec phase0
.PHONY: clean all test citest gen_yaml_tests pyspec phase0 install_test
all: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_DIR) $(YAML_TEST_TARGETS)
@ -27,11 +27,17 @@ clean:
rm -rf $(PY_SPEC_ALL_TARGETS)
# "make gen_yaml_tests" to run generators
gen_yaml_tests: $(YAML_TEST_DIR) $(YAML_TEST_TARGETS)
gen_yaml_tests: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_TARGETS)
# installs the packages to run pyspec tests
install_test:
cd $(PY_SPEC_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt;
# runs a limited set of tests against a minimal config
test: $(PY_SPEC_ALL_TARGETS)
cd $(PY_SPEC_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt; python -m pytest -m minimal_config .
cd $(PY_SPEC_DIR); . venv/bin/activate; python -m pytest -m minimal_config .
citest: $(PY_SPEC_ALL_TARGETS)
cd $(PY_SPEC_DIR); mkdir -p test-reports/eth2spec; . venv/bin/activate; python -m pytest --junitxml=test-reports/eth2spec/test_results.xml -m minimal_config .
# "make pyspec" to create the pyspec for all phases.
pyspec: $(PY_SPEC_ALL_TARGETS)
@ -48,26 +54,32 @@ CURRENT_DIR = ${CURDIR}
# The function that builds a set of suite files, by calling a generator for the given type (param 1)
define build_yaml_tests
$(info running generator $(1))
# Create the output
mkdir -p $(YAML_TEST_DIR)$(1)
# 1) Create a virtual environment
# 2) Activate the venv, this is where dependencies are installed for the generator
# 3) Install all the necessary requirements
# 4) Run the generator. The generator is assumed to have an "main.py" file.
# 5) We output to the tests dir (generator program should accept a "-o <filepath>" argument.
cd $(GENERATOR_DIR)$(1); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements.txt; python3 main.py -o $(CURRENT_DIR)/$(YAML_TEST_DIR)$(1) -c $(CURRENT_DIR)/$(CONFIGS_DIR)
$(info generator $(1) finished)
# Started!
# Create output directory
# Navigate to the generator
# Create a virtual environment, if it does not exist already
# Activate the venv, this is where dependencies are installed for the generator
# Install all the necessary requirements
# Run the generator. The generator is assumed to have an "main.py" file.
# We output to the tests dir (generator program should accept a "-o <filepath>" argument.
echo "generator $(1) started"; \
mkdir -p $(YAML_TEST_DIR)$(1); \
cd $(GENERATOR_DIR)$(1); \
if ! test -d venv; then python3 -m venv venv; fi; \
. venv/bin/activate; \
pip3 install -r requirements.txt; \
python3 main.py -o $(CURRENT_DIR)/$(YAML_TEST_DIR)$(1) -c $(CURRENT_DIR)/$(CONFIGS_DIR); \
echo "generator $(1) finished"
endef
# The tests dir itself is simply build by creating the directory (recursively creating deeper directories if necessary)
$(YAML_TEST_DIR):
$(info creating directory, to output yaml targets to: ${YAML_TEST_TARGETS})
mkdir -p $@
$(YAML_TEST_DIR)/:
$(info ignoring duplicate yaml tests dir)
# For any target within the tests dir, build it using the build_yaml_tests function.
# (creation of output dir is a dependency)
$(YAML_TEST_DIR)%: $(YAML_TEST_DIR)
$(YAML_TEST_DIR)%: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_DIR)
$(call build_yaml_tests,$*)

View File

@ -42,8 +42,8 @@ HIGH_BALANCE_INCREMENT: 1000000000
# Initial values
# ---------------------------------------------------------------
GENESIS_FORK_VERSION: 0x00000000
# 2**32, GENESIS_EPOCH is derived from this constant
GENESIS_SLOT: 4294967296
# 0, GENESIS_EPOCH is derived from this constant
GENESIS_SLOT: 0
GENESIS_START_SHARD: 0
# 2**64 - 1
FAR_FUTURE_EPOCH: 18446744073709551615
@ -116,7 +116,7 @@ MAX_TRANSFERS: 16
# Signature domains
# ---------------------------------------------------------------
DOMAIN_BEACON_BLOCK: 0
DOMAIN_BEACON_PROPOSER: 0
DOMAIN_RANDAO: 1
DOMAIN_ATTESTATION: 2
DOMAIN_DEPOSIT: 3

View File

@ -42,8 +42,8 @@ HIGH_BALANCE_INCREMENT: 1000000000
# Initial values
# ---------------------------------------------------------------
GENESIS_FORK_VERSION: 0x00000000
# 2**32, GENESIS_EPOCH is derived from this constant
GENESIS_SLOT: 4294967296
# 0, GENESIS_EPOCH is derived from this constant
GENESIS_SLOT: 0
GENESIS_START_SHARD: 0
# 2**64 - 1
FAR_FUTURE_EPOCH: 18446744073709551615
@ -116,7 +116,7 @@ MAX_TRANSFERS: 16
# Signature domains
# ---------------------------------------------------------------
DOMAIN_BEACON_BLOCK: 0
DOMAIN_BEACON_PROPOSER: 0
DOMAIN_RANDAO: 1
DOMAIN_ATTESTATION: 2
DOMAIN_DEPOSIT: 3

View File

@ -88,7 +88,7 @@ def hash_to_G2(message_hash: Bytes32, domain: uint64) -> [uint384]:
`modular_squareroot(x)` returns a solution `y` to `y**2 % q == x`, and `None` if none exists. If there are two solutions the one with higher imaginary component is favored; if both solutions have equal imaginary component the one with higher real component is favored (note that this is equivalent to saying that the single solution with either imaginary component > p/2 or imaginary component zero and real component > p/2 is favored).
The following is a sample implementation; implementers are free to implement modular square roots as they wish. Note that `x2 = -x1` is an _additive modular inverse_ so real and imaginary coefficients remain in `[0 .. q-1]`. `coerce_to_int(element: Fq) -> int` is a function that takes Fq element `element` (ie. integers `mod q`) and converts it to a regular integer.
The following is a sample implementation; implementers are free to implement modular square roots as they wish. Note that `x2 = -x1` is an _additive modular inverse_ so real and imaginary coefficients remain in `[0 .. q-1]`. `coerce_to_int(element: Fq) -> int` is a function that takes Fq element `element` (i.e. integers `mod q`) and converts it to a regular integer.
```python
Fq2_order = q ** 2 - 1

View File

@ -51,7 +51,6 @@
- [`hash`](#hash)
- [`hash_tree_root`](#hash_tree_root)
- [`signing_root`](#signing_root)
- [`get_temporary_block_header`](#get_temporary_block_header)
- [`slot_to_epoch`](#slot_to_epoch)
- [`get_previous_epoch`](#get_previous_epoch)
- [`get_current_epoch`](#get_current_epoch)
@ -95,7 +94,6 @@
- [`bls_verify_multiple`](#bls_verify_multiple)
- [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys)
- [Routines for updating validator status](#routines-for-updating-validator-status)
- [`activate_validator`](#activate_validator)
- [`initiate_validator_exit`](#initiate_validator_exit)
- [`slash_validator`](#slash_validator)
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
@ -201,13 +199,10 @@ These configurations are updated for releases, but may be out of sync during `de
| Name | Value |
| - | - |
| `GENESIS_FORK_VERSION` | `int_to_bytes4(0)` |
| `GENESIS_SLOT` | `0` |
| `GENESIS_EPOCH` | `0` |
| `GENESIS_START_SHARD` | `0` |
| `FAR_FUTURE_EPOCH` | `2**64 - 1` |
| `ZERO_HASH` | `int_to_bytes32(0)` |
| `EMPTY_SIGNATURE` | `int_to_bytes96(0)` |
| `BLS_WITHDRAWAL_PREFIX_BYTE` | `int_to_bytes1(0)` |
### Time parameters
@ -245,7 +240,7 @@ These configurations are updated for releases, but may be out of sync during `de
| `INACTIVITY_PENALTY_QUOTIENT` | `2**24` (= 16,777,216) |
| `MIN_PENALTY_QUOTIENT` | `2**5` (= 32) |
* The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It corresponds to ~2.54% annual interest assuming 10 million participating ETH in every epoch.
* **The `BASE_REWARD_QUOTIENT` is NOT final. Once all other protocol details are finalized it will be adjusted, to target a theoretical maximum total issuance of `2**21` ETH per year if `2**27` ETH is validating (and therefore `2**20` per year if `2**25` ETH is validating, etc etc)**
* The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (~18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating [validators](#dfn-validator) to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline [validators](#dfn-validator) after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)` so after `INVERSE_SQRT_E_DROP_TIME` epochs it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`.
### Max operations per block
@ -257,7 +252,7 @@ These configurations are updated for releases, but may be out of sync during `de
| `MAX_ATTESTATIONS` | `2**7` (= 128) |
| `MAX_DEPOSITS` | `2**4` (= 16) |
| `MAX_VOLUNTARY_EXITS` | `2**4` (= 16) |
| `MAX_TRANSFERS` | `2**4` (= 16) |
| `MAX_TRANSFERS` | `0` |
### Signature domains
@ -299,7 +294,7 @@ The types are defined topologically to aid in facilitating an executable version
'epoch': 'uint64',
# Root of the previous crosslink
'previous_crosslink_root': 'bytes32',
# Shard data since the previous crosslink
# Root of the crosslinked shard data since the previous crosslink
'crosslink_data_root': 'bytes32',
}
```
@ -640,23 +635,6 @@ Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethere
`def signing_root(object: SSZContainer) -> Bytes32` is a function defined in the [SimpleSerialize spec](../simple-serialize.md#self-signed-containers) to compute signing messages.
### `get_temporary_block_header`
```python
def get_temporary_block_header(block: BeaconBlock) -> BeaconBlockHeader:
"""
Return the block header corresponding to a block with ``state_root`` set to ``ZERO_HASH``.
"""
return BeaconBlockHeader(
slot=block.slot,
previous_block_root=block.previous_block_root,
state_root=ZERO_HASH,
block_body_root=hash_tree_root(block.body),
# signing_root(block) is used for block id purposes so signature is a stub
signature=EMPTY_SIGNATURE,
)
```
### `slot_to_epoch`
```python
@ -1226,22 +1204,6 @@ def get_churn_limit(state: BeaconState) -> int:
Note: All functions in this section mutate `state`.
#### `activate_validator`
```python
def activate_validator(state: BeaconState, index: ValidatorIndex) -> None:
"""
Activate the validator of the given ``index``.
Note that this function mutates ``state``.
"""
validator = state.validator_registry[index]
if state.slot == GENESIS_SLOT:
validator.activation_eligibility_epoch = GENESIS_EPOCH
validator.activation_epoch = GENESIS_EPOCH
else:
validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state))
```
#### `initiate_validator_exit`
```python
@ -1257,7 +1219,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
# Compute exit queue epoch
exit_epochs = [v.exit_epoch for v in state.validator_registry if v.exit_epoch != FAR_FUTURE_EPOCH]
exit_queue_epoch = sorted(exit_epochs + [get_delayed_activation_exit_epoch(get_current_epoch(state))])[-1]
exit_queue_epoch = max(exit_epochs + [get_delayed_activation_exit_epoch(get_current_epoch(state))])
exit_queue_churn = len([v for v in state.validator_registry if v.exit_epoch == exit_queue_epoch])
if exit_queue_churn >= get_churn_limit(state):
exit_queue_epoch += 1
@ -1297,7 +1259,7 @@ The initial deployment phases of Ethereum 2.0 are implemented without consensus
### Deposit arguments
The deposit contract has a single `deposit` function which takes as argument a SimpleSerialize'd `DepositData`.
The deposit contract has a single `deposit` function which takes as argument the `DepositData` elements.
### Withdrawal credentials
@ -1332,7 +1294,7 @@ For convenience, we provide the interface to the contract here:
* `__init__()`: initializes the contract
* `get_deposit_root() -> bytes32`: returns the current root of the deposit tree
* `deposit(bytes[512])`: adds a deposit instance to the deposit tree, incorporating the input argument and the value transferred in the given call. Note: the amount of value transferred *must* be at least `MIN_DEPOSIT_AMOUNT`. Each of these constants are specified in units of Gwei.
* `deposit(pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96])`: adds a deposit instance to the deposit tree, incorporating the input arguments and the value transferred in the given call. Note: the amount of value transferred *must* be at least `MIN_DEPOSIT_AMOUNT`. Each of these constants are specified in units of Gwei.
## On genesis
@ -1345,35 +1307,7 @@ When enough full deposits have been made to the deposit contract, an `Eth2Genesi
* `genesis_eth1_data.deposit_count` is the `deposit_count` contained in the `Eth2Genesis` log.
* `genesis_eth1_data.block_hash` is the hash of the Ethereum 1.0 block that emitted the `Eth2Genesis` log.
* Let `genesis_state = get_genesis_beacon_state(genesis_validator_deposits, genesis_time, genesis_eth1_data)`.
* Let `genesis_block = get_empty_block()`.
* Set `genesis_block.state_root = hash_tree_root(genesis_state)`.
```python
def get_empty_block() -> BeaconBlock:
"""
Get an empty ``BeaconBlock``.
"""
return BeaconBlock(
slot=GENESIS_SLOT,
previous_block_root=ZERO_HASH,
state_root=ZERO_HASH,
body=BeaconBlockBody(
randao_reveal=EMPTY_SIGNATURE,
eth1_data=Eth1Data(
deposit_root=ZERO_HASH,
deposit_count=0,
block_hash=ZERO_HASH,
),
proposer_slashings=[],
attester_slashings=[],
attestations=[],
deposits=[],
voluntary_exits=[],
transfers=[],
),
signature=EMPTY_SIGNATURE,
)
```
* Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`.
```python
def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
@ -1382,59 +1316,17 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
"""
Get the genesis ``BeaconState``.
"""
state = BeaconState(
# Misc
slot=GENESIS_SLOT,
genesis_time=genesis_time,
fork=Fork(
previous_version=GENESIS_FORK_VERSION,
current_version=GENESIS_FORK_VERSION,
epoch=GENESIS_EPOCH,
),
# Validator registry
validator_registry=[],
balances=[],
# Randomness and committees
latest_randao_mixes=Vector([ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)]),
latest_start_shard=GENESIS_START_SHARD,
# Finality
previous_epoch_attestations=[],
current_epoch_attestations=[],
previous_justified_epoch=GENESIS_EPOCH,
current_justified_epoch=GENESIS_EPOCH,
previous_justified_root=ZERO_HASH,
current_justified_root=ZERO_HASH,
justification_bitfield=0,
finalized_epoch=GENESIS_EPOCH,
finalized_root=ZERO_HASH,
# Recent state
current_crosslinks=Vector([Crosslink(epoch=GENESIS_EPOCH, previous_crosslink_root=ZERO_HASH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)]),
previous_crosslinks=Vector([Crosslink(epoch=GENESIS_EPOCH, previous_crosslink_root=ZERO_HASH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)]),
latest_block_roots=Vector([ZERO_HASH for _ in range(SLOTS_PER_HISTORICAL_ROOT)]),
latest_state_roots=Vector([ZERO_HASH for _ in range(SLOTS_PER_HISTORICAL_ROOT)]),
latest_active_index_roots=Vector([ZERO_HASH for _ in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH)]),
latest_slashed_balances=Vector([0 for _ in range(LATEST_SLASHED_EXIT_LENGTH)]),
latest_block_header=get_temporary_block_header(get_empty_block()),
historical_roots=[],
# Ethereum 1.0 chain data
latest_eth1_data=genesis_eth1_data,
eth1_data_votes=[],
deposit_index=0,
)
state = BeaconState(genesis_time=genesis_time, latest_eth1_data=genesis_eth1_data)
# Process genesis deposits
for deposit in genesis_validator_deposits:
process_deposit(state, deposit)
# Process genesis activations
for index in range(len(state.validator_registry)):
for index, validator in enumerate(state.validator_registry):
if get_effective_balance(state, index) >= MAX_EFFECTIVE_BALANCE:
activate_validator(state, index)
validator.activation_eligibility_epoch = GENESIS_EPOCH
validator.activation_epoch = GENESIS_EPOCH
genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH))
for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH):
@ -1837,14 +1729,16 @@ def process_registry_updates(state: BeaconState) -> None:
if is_active_validator(validator, get_current_epoch(state)) and balance < EJECTION_BALANCE:
initiate_validator_exit(state, index)
# Process activations
# Queue validators eligible for activation and not dequeued for activation prior to finalized epoch
activation_queue = sorted([
index for index, validator in enumerate(state.validator_registry) if
validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and
validator.activation_epoch >= get_delayed_activation_exit_epoch(state.finalized_epoch)
], key=lambda index: state.validator_registry[index].activation_eligibility_epoch)
# Dequeued validators for activation up to churn limit (without resetting activation epoch)
for index in activation_queue[:get_churn_limit(state)]:
activate_validator(state, index)
if validator.activation_epoch == FAR_FUTURE_EPOCH:
validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state))
```
#### Slashings
@ -1929,7 +1823,11 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
# Verify that the parent matches
assert block.previous_block_root == signing_root(state.latest_block_header)
# Save current block as the new latest block
state.latest_block_header = get_temporary_block_header(block)
state.latest_block_header = BeaconBlockHeader(
slot=block.slot,
previous_block_root=block.previous_block_root,
block_body_root=hash_tree_root(block.body),
)
# Verify proposer is not slashed
proposer = state.validator_registry[get_beacon_proposer_index(state)]
assert not proposer.slashed
@ -2101,7 +1999,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
# Verify the Merkle branch
merkle_branch_is_valid = verify_merkle_branch(
leaf=hash(serialize(deposit.data)), # 48 + 32 + 8 + 96 = 184 bytes serialization
leaf=hash_tree_root(deposit.data),
proof=deposit.proof,
depth=DEPOSIT_CONTRACT_TREE_DEPTH,
index=deposit.index,
@ -2132,8 +2030,6 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
activation_epoch=FAR_FUTURE_EPOCH,
exit_epoch=FAR_FUTURE_EPOCH,
withdrawable_epoch=FAR_FUTURE_EPOCH,
slashed=False,
high_balance=0
)
# Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled.

View File

@ -28,9 +28,12 @@
- [`BeaconState`](#beaconstate)
- [`BeaconBlockBody`](#beaconblockbody)
- [Helpers](#helpers)
- [`typeof`](#typeof)
- [`empty`](#empty)
- [`get_crosslink_chunk_count`](#get_crosslink_chunk_count)
- [`get_custody_chunk_bit`](#get_custody_chunk_bit)
- [`epoch_to_custody_period`](#epoch_to_custody_period)
- [`replace_empty_or_append`](#replace_empty_or_append)
- [`verify_custody_key`](#verify_custody_key)
- [Per-block processing](#per-block-processing)
- [Operations](#operations)
@ -203,6 +206,14 @@ Add the following fields to the end of the specified container objects. Fields w
## Helpers
### `typeof`
The `typeof` function accepts and SSZ object as a single input and returns the corresponding SSZ type.
### `empty`
The `empty` function accepts and SSZ type as input and returns an object of that type with all fields initialized to default values.
### `get_crosslink_chunk_count`
```python
@ -229,6 +240,18 @@ def epoch_to_custody_period(epoch: Epoch) -> int:
return epoch // EPOCHS_PER_CUSTODY_PERIOD
```
### `replace_empty_or_append`
```python
def replace_empty_or_append(list: List[Any], new_element: Any) -> int:
for i in range(len(list)):
if list[i] == empty(typeof(new_element)):
list[i] = new_element
return i
list.append(new_element)
return len(list) - 1
```
### `verify_custody_key`
```python
@ -321,7 +344,7 @@ def process_chunk_challenge(state: BeaconState,
depth = math.log2(next_power_of_two(get_custody_chunk_count(challenge.attestation)))
assert challenge.chunk_index < 2**depth
# Add new chunk challenge record
state.custody_chunk_challenge_records.append(CustodyChunkChallengeRecord(
new_record = CustodyChunkChallengeRecord(
challenge_index=state.custody_challenge_index,
challenger_index=get_beacon_proposer_index(state),
responder_index=challenge.responder_index
@ -329,7 +352,9 @@ def process_chunk_challenge(state: BeaconState,
crosslink_data_root=challenge.attestation.data.crosslink_data_root,
depth=depth,
chunk_index=challenge.chunk_index,
))
)
replace_empty_or_append(state.custody_chunk_challenge_records, new_record)
state.custody_challenge_index += 1
# Postpone responder withdrawability
responder.withdrawable_epoch = FAR_FUTURE_EPOCH
@ -385,7 +410,7 @@ def process_bit_challenge(state: BeaconState,
custody_bit = get_bitfield_bit(attestation.custody_bitfield, attesters.index(responder_index))
assert custody_bit != chunk_bits_xor
# Add new bit challenge record
state.custody_bit_challenge_records.append(CustodyBitChallengeRecord(
new_record = CustodyBitChallengeRecord(
challenge_index=state.custody_challenge_index,
challenger_index=challenge.challenger_index,
responder_index=challenge.responder_index,
@ -393,7 +418,8 @@ def process_bit_challenge(state: BeaconState,
crosslink_data_root=challenge.attestation.crosslink_data_root,
chunk_bits=challenge.chunk_bits,
responder_key=challenge.responder_key,
))
)
replace_empty_or_append(state.custody_bit_challenge_records, new_record)
state.custody_challenge_index += 1
# Postpone responder withdrawability
responder.withdrawable_epoch = FAR_FUTURE_EPOCH
@ -434,7 +460,8 @@ def process_chunk_challenge_response(state: BeaconState,
root=challenge.crosslink_data_root,
)
# Clear the challenge
state.custody_chunk_challenge_records.remove(challenge)
records = state.custody_chunk_challenge_records
records[records.index(challenge)] = CustodyChunkChallengeRecord()
# Reward the proposer
proposer_index = get_beacon_proposer_index(state)
increase_balance(state, proposer_index, base_reward(state, index) // MINOR_REWARD_QUOTIENT)
@ -457,7 +484,8 @@ def process_bit_challenge_response(state: BeaconState,
# Verify the chunk bit does not match the challenge chunk bit
assert get_custody_chunk_bit(challenge.responder_key, response.chunk) != get_bitfield_bit(challenge.chunk_bits, response.chunk_index)
# Clear the challenge
state.custody_bit_challenge_records.remove(challenge)
records = state.custody_bit_challenge_records
records[records.index(challenge)] = CustodyBitChallengeRecord()
# Slash challenger
slash_validator(state, challenge.challenger_index, challenge.responder_index)
```
@ -471,12 +499,14 @@ def process_challenge_deadlines(state: BeaconState) -> None:
for challenge in state.custody_chunk_challenge_records:
if get_current_epoch(state) > challenge.deadline:
slash_validator(state, challenge.responder_index, challenge.challenger_index)
state.custody_chunk_challenge_records.remove(challenge)
records = state.custody_chunk_challenge_records
records[records.index(challenge)] = CustodyChunkChallengeRecord()
for challenge in state.custody_bit_challenge_records:
if get_current_epoch(state) > challenge.deadline:
slash_validator(state, challenge.responder_index, challenge.challenger_index)
state.custody_bit_challenge_records.remove(challenge)
records = state.custody_bit_challenge_records
records[records.index(challenge)] = CustodyBitChallengeRecord()
```
In `process_penalties_and_exits`, change the definition of `eligible` to the following (note that it is not a pure function because `state` is declared in the surrounding scope):

View File

@ -102,7 +102,7 @@ def get_generalized_indices(obj: Any, path: List[int], root: int=1) -> List[int]
## Merkle multiproofs
We define a Merkle multiproof as a minimal subset of nodes in a Merkle tree needed to fully authenticate that a set of nodes actually are part of a Merkle tree with some specified root, at a particular set of generalized indices. For example, here is the Merkle multiproof for positions 0, 1, 6 in an 8-node Merkle tree (ie. generalized indices 8, 9, 14):
We define a Merkle multiproof as a minimal subset of nodes in a Merkle tree needed to fully authenticate that a set of nodes actually are part of a Merkle tree with some specified root, at a particular set of generalized indices. For example, here is the Merkle multiproof for positions 0, 1, 6 in an 8-node Merkle tree (i.e. generalized indices 8, 9, 14):
```
.

View File

@ -27,7 +27,7 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers
### Expansions
We define an "expansion" of an object as an object where a field in an object that is meant to represent the `hash_tree_root` of another object is replaced by the object. Note that defining expansions is not a consensus-layer-change; it is merely a "re-interpretation" of the object. Particularly, the `hash_tree_root` of an expansion of an object is identical to that of the original object, and we can define expansions where, given a complete history, it is always possible to compute the expansion of any object in the history. The opposite of an expansion is a "summary" (eg. `BeaconBlockHeader` is a summary of `BeaconBlock`).
We define an "expansion" of an object as an object where a field in an object that is meant to represent the `hash_tree_root` of another object is replaced by the object. Note that defining expansions is not a consensus-layer-change; it is merely a "re-interpretation" of the object. Particularly, the `hash_tree_root` of an expansion of an object is identical to that of the original object, and we can define expansions where, given a complete history, it is always possible to compute the expansion of any object in the history. The opposite of an expansion is a "summary" (e.g. `BeaconBlockHeader` is a summary of `BeaconBlock`).
We define two expansions:

View File

@ -9,6 +9,7 @@ This is a **work in progress** describing typing, serialization and Merkleizatio
- [Basic types](#basic-types)
- [Composite types](#composite-types)
- [Aliases](#aliases)
- [Default values](#default-values)
- [Serialization](#serialization)
- [`"uintN"`](#uintn)
- [`"bool"`](#bool)
@ -33,11 +34,11 @@ This is a **work in progress** describing typing, serialization and Merkleizatio
### Composite types
* **container**: ordered heterogenous collection of values
* **container**: ordered heterogeneous collection of values
* key-pair curly bracket notation `{}`, e.g. `{"foo": "uint64", "bar": "bool"}`
* **vector**: ordered fixed-length homogeneous collection of values
* angle bracket notation `[type, N]`, e.g. `["uint64", N]`
* **list**: ordered variable-length homogenous collection of values
* **list**: ordered variable-length homogeneous collection of values
* angle bracket notation `[type]`, e.g. `["uint64"]`
We recursively define "variable-size" types to be lists and all types that contains a variable-size type. All other types are said to be "fixed-size".
@ -50,6 +51,10 @@ For convenience we alias:
* `"bytes"` to `["byte"]` (this is *not* a basic type)
* `"bytesN"` to `["byte", N]` (this is *not* a basic type)
### Default values
The default value of a type upon initialization is recursively defined using `0` for `"uintN"`, `False` for `"bool"`, and `[]` for lists.
## Serialization
We recursively define the `serialize` function which consumes an object `value` (of the type specified) and returns a bytestring of type `"bytes"`.

View File

@ -13,6 +13,7 @@ type_name: string -- string, object name, formatted as in spec. E.g. "BeaconBlo
value: dynamic -- the YAML-encoded value, of the type specified by type_name.
serialized: bytes -- string, SSZ-serialized data, hex encoded, with prefix 0x
root: bytes32 -- string, hash-tree-root of the value, hex encoded, with prefix 0x
signing_root: bytes32 -- string, signing-root of the value, hex encoded, with prefix 0x. Optional, present if type contains ``signature`` field
```
## Condition
@ -20,4 +21,12 @@ root: bytes32 -- string, hash-tree-root of the value, hex encoded, with pre
A test-runner can implement the following assertions:
- Serialization: After parsing the `value`, SSZ-serialize it: the output should match `serialized`
- Hash-tree-root: After parsing the `value`, Hash-tree-root it: the output should match `root`
- Optionally also check signing-root, if present.
- Deserialization: SSZ-deserialize the `serialized` value, and see if it matches the parsed `value`
## References
**`serialized`**: [SSZ serialization](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#serialization)
**`root`** - [hash_tree_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#merkleization) function
**`signing_root`** - [signing_root](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#self-signed-containers) function

View File

@ -60,7 +60,7 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers
## Introduction
This document represents the expected behavior of an "honest validator" with respect to Phase 0 of the Ethereum 2.0 protocol. This document does not distinguish between a "node" (ie. the functionality of following and reading the beacon chain) and a "validator client" (ie. the functionality of actively participating in consensus). The separation of concerns between these (potentially) two pieces of software is left as a design decision that is out of scope.
This document represents the expected behavior of an "honest validator" with respect to Phase 0 of the Ethereum 2.0 protocol. This document does not distinguish between a "node" (i.e. the functionality of following and reading the beacon chain) and a "validator client" (i.e. the functionality of actively participating in consensus). The separation of concerns between these (potentially) two pieces of software is left as a design decision that is out of scope.
A validator is an entity that participates in the consensus of the Ethereum 2.0 protocol. This is an optional role for users in which they can post ETH as collateral and verify and attest to the validity of blocks to seek financial returns in exchange for building and securing the protocol. This is similar to proof of work networks in which a miner provides collateral in the form of hardware/hash-power to seek returns in exchange for building and securing the protocol.
@ -101,11 +101,10 @@ In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW
To submit a deposit:
* Pack the validator's [initialization parameters](#initialization) into `deposit_data`, a [`DepositData`](../core/0_beacon-chain.md#depositdata) SSZ object.
* Let `proof_of_possession` be the result of `bls_sign` of the `signing_root(deposit_data)` with `domain=DOMAIN_DEPOSIT`.
* Set `deposit_data.proof_of_possession = proof_of_possession`.
* Let `amount` be the amount in Gwei to be deposited by the validator where `MIN_DEPOSIT_AMOUNT <= amount <= MAX_DEPOSIT_AMOUNT`.
* Set `deposit_data.amount = amount`.
* Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `deposit(deposit_input: bytes[512])` along with `serialize(deposit_data)` as the singular `bytes` input along with a deposit of `amount` Gwei.
* Let `signature` be the result of `bls_sign` of the `signing_root(deposit_data)` with `domain=DOMAIN_DEPOSIT`.
* Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `def deposit(pubkey: bytes[48], withdrawal_credentials: bytes[32], signature: bytes[96])` along with a deposit of `amount` Gwei.
_Note_: Deposits made for the same `pubkey` are treated as for the same validator. A singular `Validator` will be added to `state.validator_registry` with each additional deposit amount added to the validator's balance. A validator can only be activated when total deposits for the validator pubkey meet or exceed `MAX_DEPOSIT_AMOUNT`.
@ -141,7 +140,7 @@ A validator has two primary responsibilities to the beacon chain -- [proposing b
A validator is expected to propose a [`BeaconBlock`](../core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `get_beacon_proposer_index(state, slot)` returns the validator's `validator_index`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator is to create, sign, and broadcast a `block` that is a child of `parent` and that executes a valid [beacon chain state transition](../core/0_beacon-chain.md#beacon-chain-state-transition-function).
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 (eg. at 312500 validators = 10 million ETH, that's once per ~3 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 312500 validators = 10 million ETH, that's once per ~3 weeks).
#### Block header

View File

@ -28,9 +28,12 @@ make clean
This runs all the generators.
```bash
make gen_yaml_tests
make -j 4 gen_yaml_tests
```
The `-j N` flag makes the generators run in parallel, with `N` being the amount of cores.
### Running a single generator
The make file auto-detects generators in the `test_generators/` directory,

View File

@ -24,13 +24,12 @@ def build_deposit_data(state,
pubkey=pubkey,
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + withdrawal_cred[1:],
amount=amount,
proof_of_possession=spec.EMPTY_SIGNATURE,
)
deposit_data.proof_of_possession = bls.sign(
message_hash=signing_root(deposit_data),
privkey=privkey,
domain=spec.get_domain(
state.fork,
state,
spec.get_current_epoch(state),
spec.DOMAIN_DEPOSIT,
)
@ -47,7 +46,7 @@ def build_deposit(state,
deposit_data = build_deposit_data(state, pubkey, withdrawal_cred, privkey, amount)
item = spec.hash(deposit_data.serialize())
item = deposit_data.hash_tree_root()
index = len(deposit_data_leaves)
deposit_data_leaves.append(item)
tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves))
@ -70,7 +69,7 @@ def build_deposit_for_index(initial_validator_count: int, index: int) -> Tuple[s
)
state = genesis.create_genesis_state(genesis_deposits)
deposit_data_leaves = [spec.hash(dep.data.serialize()) for dep in genesis_deposits]
deposit_data_leaves = [dep.data.hash_tree_root() for dep in genesis_deposits]
deposit = build_deposit(
state,

View File

@ -4,7 +4,7 @@ from typing import List
def create_genesis_state(deposits: List[spec.Deposit]) -> spec.BeaconState:
deposit_root = get_merkle_root((tuple([spec.hash(dep.data.serialize()) for dep in deposits])))
deposit_root = get_merkle_root((tuple([(dep.data.hash_tree_root()) for dep in deposits])))
return spec.get_genesis_beacon_state(
deposits,
@ -32,7 +32,7 @@ def create_deposits(pubkeys: List[spec.BLSPubkey], withdrawal_cred: List[spec.By
]
# Fill tree with existing deposits
deposit_data_leaves = [spec.hash(data.serialize()) for data in deposit_data]
deposit_data_leaves = [data.hash_tree_root() for data in deposit_data]
tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves))
return [

View File

@ -2,3 +2,5 @@
The purpose of this test-generator is to provide test-vectors for the most important applications of SSZ:
the serialization and hashing of ETH 2.0 data types
Test-format documentation can be found [here](../../specs/test_formats/ssz_static/README.md).

View File

@ -2,7 +2,11 @@ from random import Random
from eth2spec.debug import random_value, encode
from eth2spec.phase0 import spec
from eth2spec.utils.minimal_ssz import hash_tree_root, serialize
from eth2spec.utils.minimal_ssz import (
hash_tree_root,
signing_root,
serialize,
)
from eth_utils import (
to_tuple, to_dict
)
@ -21,6 +25,8 @@ def create_test_case(rng: Random, name: str, mode: random_value.RandomizationMod
yield "value", encode.encode(value, typ)
yield "serialized", '0x' + serialize(value).hex()
yield "root", '0x' + hash_tree_root(value).hex()
if hasattr(value, "signature"):
yield "signing_root", '0x' + signing_root(value).hex()
@to_tuple

View File

@ -19,6 +19,8 @@ Or, to build a single file, specify the path, e.g. `make test_libs/pyspec/eth2sp
## Py-tests
After building, you can install the dependencies for running the `pyspec` tests with `make install_test`
These tests are not intended for client-consumption.
These tests are sanity tests, to verify if the spec itself is consistent.
@ -38,8 +40,9 @@ python3 -m venv venv
. venv/bin/activate
pip3 install -r requirements.txt
```
Note: make sure to run `make pyspec` from the root of the specs repository,
Note: make sure to run `make -B pyspec` from the root of the specs repository,
to build the parts of the pyspec module derived from the markdown specs.
The `-B` flag may be helpful to force-overwrite the `pyspec` output after you made a change to the markdown source files.
Run the tests:
```

View File

@ -9,13 +9,13 @@ import eth2spec.phase0.spec as spec
from eth2spec.utils.minimal_ssz import signing_root
from eth2spec.phase0.spec import (
# constants
EMPTY_SIGNATURE,
ZERO_HASH,
# SSZ
Attestation,
AttestationData,
AttestationDataAndCustodyBit,
AttesterSlashing,
BeaconBlock,
BeaconBlockHeader,
Deposit,
DepositData,
@ -32,7 +32,6 @@ from eth2spec.phase0.spec import (
get_crosslink_committees_at_slot,
get_current_epoch,
get_domain,
get_empty_block,
get_epoch_start_slot,
get_genesis_beacon_state,
get_previous_epoch,
@ -82,7 +81,7 @@ def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves=N
amount=spec.MAX_EFFECTIVE_BALANCE,
signature=signature,
)
item = hash(deposit_data.serialize())
item = deposit_data.hash_tree_root()
deposit_data_leaves.append(item)
tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves))
root = get_merkle_root((tuple(deposit_data_leaves)))
@ -117,7 +116,7 @@ def create_genesis_state(num_validators, deposit_data_leaves=None):
def build_empty_block_for_next_slot(state):
empty_block = get_empty_block()
empty_block = BeaconBlock()
empty_block.slot = state.slot + 1
previous_block_header = deepcopy(state.latest_block_header)
if previous_block_header.state_root == spec.ZERO_HASH:
@ -132,7 +131,6 @@ def build_deposit_data(state, pubkey, privkey, amount):
# insecurely use pubkey as withdrawal key as well
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:],
amount=amount,
signature=EMPTY_SIGNATURE,
)
signature = bls.sign(
message_hash=signing_root(deposit_data),
@ -187,7 +185,6 @@ def build_voluntary_exit(state, epoch, validator_index, privkey):
voluntary_exit = VoluntaryExit(
epoch=epoch,
validator_index=validator_index,
signature=EMPTY_SIGNATURE,
)
voluntary_exit.signature = bls.sign(
message_hash=signing_root(voluntary_exit),
@ -209,7 +206,7 @@ def build_deposit(state,
amount):
deposit_data = build_deposit_data(state, pubkey, privkey, amount)
item = hash(deposit_data.serialize())
item = deposit_data.hash_tree_root()
index = len(deposit_data_leaves)
deposit_data_leaves.append(item)
tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves))
@ -237,7 +234,6 @@ def get_valid_proposer_slashing(state):
previous_block_root=ZERO_HASH,
state_root=ZERO_HASH,
block_body_root=ZERO_HASH,
signature=EMPTY_SIGNATURE,
)
header_2 = deepcopy(header_1)
header_2.previous_block_root = b'\x02' * 32
@ -306,7 +302,6 @@ def get_valid_attestation(state, slot=None):
aggregation_bitfield=aggregation_bitfield,
data=attestation_data,
custody_bitfield=custody_bitfield,
aggregate_signature=EMPTY_SIGNATURE,
)
participants = get_attesting_indices(
state,
@ -352,7 +347,7 @@ def get_valid_transfer(state, slot=None, sender_index=None, amount=None, fee=Non
fee=fee,
slot=slot,
pubkey=transfer_pubkey,
signature=EMPTY_SIGNATURE,
signature=ZERO_HASH,
)
transfer.signature = bls.sign(
message_hash=signing_root(transfer),

View File

@ -8,7 +8,6 @@ import eth2spec.phase0.spec as spec
from eth2spec.utils.minimal_ssz import signing_root
from eth2spec.phase0.spec import (
# constants
EMPTY_SIGNATURE,
ZERO_HASH,
# SSZ
Deposit,
@ -236,7 +235,7 @@ def test_deposit_in_block(state):
privkey = privkeys[index]
deposit_data = build_deposit_data(pre_state, pubkey, privkey, spec.MAX_EFFECTIVE_BALANCE)
item = hash(deposit_data.serialize())
item = deposit_data.hash_tree_root()
test_deposit_data_leaves.append(item)
tree = calc_merkle_tree_from_leaves(tuple(test_deposit_data_leaves))
root = get_merkle_root((tuple(test_deposit_data_leaves)))
@ -275,7 +274,7 @@ def test_deposit_top_up(state):
deposit_data = build_deposit_data(pre_state, pubkey, privkey, amount)
merkle_index = len(test_deposit_data_leaves)
item = hash(deposit_data.serialize())
item = deposit_data.hash_tree_root()
test_deposit_data_leaves.append(item)
tree = calc_merkle_tree_from_leaves(tuple(test_deposit_data_leaves))
root = get_merkle_root((tuple(test_deposit_data_leaves)))
@ -350,7 +349,6 @@ def test_voluntary_exit(state):
voluntary_exit = VoluntaryExit(
epoch=get_current_epoch(pre_state),
validator_index=validator_index,
signature=EMPTY_SIGNATURE,
)
voluntary_exit.signature = bls.sign(
message_hash=signing_root(voluntary_exit),
@ -382,7 +380,10 @@ def test_voluntary_exit(state):
return pre_state, [initiate_exit_block, exit_block], post_state
def test_transfer(state):
def test_transfer(state, config):
# overwrite default 0 to test
spec.MAX_TRANSFERS = 1
pre_state = deepcopy(state)
current_epoch = get_current_epoch(pre_state)
sender_index = get_active_validator_indices(pre_state, current_epoch)[-1]
@ -398,7 +399,6 @@ def test_transfer(state):
fee=0,
slot=pre_state.slot + 1,
pubkey=transfer_pubkey,
signature=EMPTY_SIGNATURE,
)
transfer.signature = bls.sign(
message_hash=signing_root(transfer),