Merge pull request #1131 from ethereum/v06x-to-dev

V06x to dev
This commit is contained in:
Danny Ryan 2019-05-28 15:59:49 -06:00 committed by GitHub
commit a994a5d835
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
82 changed files with 2958 additions and 2258 deletions

View File

@ -34,7 +34,7 @@ install_test:
cd $(PY_SPEC_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements-testing.txt;
test: $(PY_SPEC_ALL_TARGETS)
cd $(PY_SPEC_DIR); . venv/bin/activate; python -m pytest .
cd $(PY_SPEC_DIR); . venv/bin/activate; python -m pytest eth2spec
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 .

View File

@ -13,17 +13,9 @@ from typing import (
NewType,
Tuple,
)
from eth2spec.utils.minimal_ssz import (
SSZType,
hash_tree_root,
signing_root,
)
from eth2spec.utils.bls_stub import (
bls_aggregate_pubkeys,
bls_verify,
bls_verify_multiple,
)
from eth2spec.utils.minimal_ssz import *
from eth2spec.utils.hash_function import hash
from eth2spec.utils.bls import *
# stub, will get overwritten by real var

View File

@ -1248,7 +1248,7 @@ def state_transition(state: BeaconState, block: BeaconBlock, validate_state_root
```python
def process_slots(state: BeaconState, slot: Slot) -> None:
assert state.slot < slot
assert state.slot <= slot
while state.slot < slot:
process_slot(state)
# Process epoch on the first slot of the next epoch
@ -1775,7 +1775,8 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
amount = deposit.data.amount
validator_pubkeys = [v.pubkey for v in state.validator_registry]
if pubkey not in validator_pubkeys:
# Verify the deposit signature (proof of possession)
# Verify the deposit signature (proof of possession).
# Invalid signatures are allowed by the deposit contract, and hence included on-chain, but must not be processed.
# Note: deposits are valid across forks, hence the deposit domain is retrieved directly from `bls_domain`
if not bls_verify(
pubkey, signing_root(deposit.data), deposit.data.signature, bls_domain(DOMAIN_DEPOSIT)

View File

@ -186,6 +186,18 @@ To prevent parsing of hundreds of different YAML files to test a specific test t
... <--- more test types
```
## Common test-case properties
Some test-case formats share some common key-value pair patterns, and these are documented here:
```
bls_setting: int -- optional, can have 3 different values:
0: (default, applies if key-value pair is absent). Free to choose either BLS ON or OFF.
Tests are generated with valid BLS data in this case,
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
```
## Note for implementers

View File

@ -0,0 +1,29 @@
# Epoch processing tests
The different epoch sub-transitions are tested individually with test handlers.
The format is similar to block-processing state-transition tests.
There is no "change" factor however, the transitions are pure functions with just the pre-state as input.
Hence, the format is shared between each test-handler. (See test condition documentation on how to run the tests.)
## Test case format
```yaml
description: string -- description of test case, purely for debugging purposes
bls_setting: int -- see general test-format spec.
pre: BeaconState -- state before running the sub-transition
post: BeaconState -- state after applying the epoch sub-transition.
```
## Condition
A handler of the `epoch_processing` test-runner should process these cases,
calling the corresponding processing implementation.
Sub-transitions:
| *`sub-transition-name`* | *`processing call`* |
|-------------------------|-----------------------------------|
| `crosslinks` | `process_crosslinks(state)` |
| `registry_updates` | `process_registry_updates(state)` |
The resulting state should match the expected `post` state.

View File

@ -2,9 +2,34 @@
The different kinds of operations ("transactions") are tested individually with test handlers.
The tested operation kinds are:
- [`deposits`](./deposits.md)
- More tests are work-in-progress.
## Test case format
```yaml
description: string -- description of test case, purely for debugging purposes
bls_setting: int -- see general test-format spec.
pre: BeaconState -- state before applying the operation
<operation-name>: <operation-object> -- the YAML encoded operation, e.g. a "ProposerSlashing", or "Deposit".
post: BeaconState -- state after applying the operation. No value if operation processing is aborted.
```
## Condition
A handler of the `operations` test-runner should process these cases,
calling the corresponding processing implementation.
Operations:
| *`operation-name`* | *`operation-object`* | *`input name`* | *`processing call`* |
|-------------------------|----------------------|----------------------|--------------------------------------------------------|
| `attestation` | `Attestation` | `attestation` | `process_attestation(state, attestation)` |
| `attester_slashing` | `AttesterSlashing` | `attester_slashing` | `process_attester_slashing(state, attester_slashing)` |
| `block_header` | `Block` | `block` | `process_block_header(state, block)` |
| `deposit` | `Deposit` | `deposit` | `process_deposit(state, deposit)` |
| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` |
| `transfer` | `Transfer` | `transfer` | `process_transfer(state, transfer)` |
| `voluntary_exit` | `VoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` |
Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here.
The resulting state should match the expected `post` state, or if the `post` state is left blank,
the handler should reject the input operation as invalid.

View File

@ -1,18 +0,0 @@
# Test format: Deposit operations
A deposit is a form of an operation (or "transaction"), modifying the state.
## Test case format
```yaml
description: string -- description of test case, purely for debugging purposes
pre: BeaconState -- state before applying the deposit
deposit: Deposit -- the deposit
post: BeaconState -- state after applying the deposit. No value if deposit processing is aborted.
```
## Condition
A `deposits` handler of the `operations` should process these cases,
calling the implementation of the `process_deposit(state, deposit)` functionality described in the spec.
The resulting state should match the expected `post` state, or if the `post` state is left blank, the handler should reject the inputs as invalid.

View File

@ -0,0 +1,7 @@
# Sanity tests
The aim of the sanity tests is to set a base-line on what really needs to pass, i.e. the essentials.
There are two handlers, documented individually:
- [`slots`](./slots.md): transitions of one or more slots (and epoch transitions within)
- [`blocks`](./blocks.md): transitions triggered by one or more blocks

View File

@ -0,0 +1,18 @@
# Sanity blocks testing
Sanity tests to cover a series of one or more blocks being processed, aiming to cover common changes.
## Test case format
```yaml
description: string -- description of test case, purely for debugging purposes
bls_setting: int -- see general test-format spec.
pre: BeaconState -- state before running through the transitions triggered by the blocks.
blocks: [BeaconBlock] -- blocks to process, in given order, following the main transition function (i.e. process slot and epoch transitions in between blocks as normal)
post: BeaconState -- state after applying all the transitions triggered by the blocks.
```
## Condition
The resulting state should match the expected `post` state, or if the `post` state is left blank,
the handler should reject the series of blocks as invalid.

View File

@ -0,0 +1,23 @@
# Sanity slots testing
Sanity tests to cover a series of one or more empty-slot transitions being processed, aiming to cover common changes.
## Test case format
```yaml
description: string -- description of test case, purely for debugging purposes
bls_setting: int -- see general test-format spec.
pre: BeaconState -- state before running through the transitions.
slots: N -- amount of slots to process, N being a positive numer.
post: BeaconState -- state after applying all the transitions.
```
The transition with pure time, no blocks, is known as `state_transition_to(state, slot)` in the spec.
This runs state-caching (pure slot transition) and epoch processing (every E slots).
To process the data, call `state_transition_to(pre, pre.slot + N)`. And see if `pre` mutated into the equivalent of `post`.
## Condition
The resulting state should match the expected `post` state.

View File

@ -9,11 +9,11 @@ This test-format ensures these direct serializations are covered.
## Test case format
```yaml
type_name: string -- string, object name, formatted as in spec. E.g. "BeaconBlock"
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
SomeObjectName: -- key, object name, formatted as in spec. E.g. "BeaconBlock".
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

View File

@ -58,7 +58,7 @@ It's recommended to extend the base-generator.
Create a `requirements.txt` in the root of your generator directory:
```
eth-utils==1.4.1
eth-utils==1.6.0
../../test_libs/gen_helpers
../../test_libs/config_helpers
../../test_libs/pyspec

View File

@ -1,3 +1,3 @@
py-ecc==1.7.0
eth-utils==1.4.1
eth-utils==1.6.0
../../test_libs/gen_helpers

View File

@ -0,0 +1,11 @@
# Epoch processing
Epoch processing covers the sub-transitions during an epoch change.
An epoch-processing test-runner can consume these sub-transition test-suites,
and handle different kinds of epoch sub-transitions by processing the cases using the specified test handler.
Information on the format of the tests can be found in the [epoch-processing test formats documentation](../../specs/test_formats/epoch_processing/README.md).

View File

@ -0,0 +1,38 @@
from typing import Callable, Iterable
from eth2spec.phase0 import spec
from eth2spec.test.epoch_processing import (
test_process_crosslinks,
test_process_registry_updates
)
from gen_base import gen_runner, gen_suite, gen_typing
from gen_from_tests.gen import generate_from_tests
from preset_loader import loader
def create_suite(transition_name: str, config_name: str, get_cases: Callable[[], Iterable[gen_typing.TestCase]]) \
-> Callable[[str], gen_typing.TestSuiteOutput]:
def suite_definition(configs_path: str) -> gen_typing.TestSuiteOutput:
presets = loader.load_presets(configs_path, config_name)
spec.apply_constants_preset(presets)
return ("%s_%s" % (transition_name, config_name), transition_name, gen_suite.render_suite(
title="%s epoch processing" % transition_name,
summary="Test suite for %s type epoch processing" % transition_name,
forks_timeline="testing",
forks=["phase0"],
config=config_name,
runner="epoch_processing",
handler=transition_name,
test_cases=get_cases()))
return suite_definition
if __name__ == "__main__":
gen_runner.run_generator("epoch_processing", [
create_suite('crosslinks', 'minimal', lambda: generate_from_tests(test_process_crosslinks)),
create_suite('crosslinks', 'mainnet', lambda: generate_from_tests(test_process_crosslinks)),
create_suite('registry_updates', 'minimal', lambda: generate_from_tests(test_process_registry_updates)),
create_suite('registry_updates', 'mainnet', lambda: generate_from_tests(test_process_registry_updates)),
])

View File

@ -0,0 +1,4 @@
eth-utils==1.6.0
../../test_libs/gen_helpers
../../test_libs/config_helpers
../../test_libs/pyspec

View File

@ -3,7 +3,6 @@
Operations (or "transactions" in previous spec iterations),
are atomic changes to the state, introduced by embedding in blocks.
This generator provides a series of test suites, divided into handler, for each operation type.
An operation test-runner can consume these operation test-suites,
and handle different kinds of operations by processing the cases using the specified test handler.

View File

@ -1,180 +0,0 @@
from eth2spec.phase0 import spec
from eth_utils import (
to_dict, to_tuple
)
from gen_base import gen_suite, gen_typing
from preset_loader import loader
from eth2spec.debug.encode import encode
from eth2spec.utils.minimal_ssz import signing_root
from eth2spec.utils.merkle_minimal import get_merkle_root, calc_merkle_tree_from_leaves, get_merkle_proof
from typing import List, Tuple
import genesis
import keys
from py_ecc import bls
def build_deposit_data(state,
pubkey: spec.BLSPubkey,
withdrawal_cred: spec.Bytes32,
privkey: int,
amount: int):
deposit_data = spec.DepositData(
pubkey=pubkey,
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + withdrawal_cred[1:],
amount=amount,
)
deposit_data.proof_of_possession = bls.sign(
message_hash=signing_root(deposit_data),
privkey=privkey,
domain=spec.get_domain(
state,
spec.get_current_epoch(state),
spec.DOMAIN_DEPOSIT,
)
)
return deposit_data
def build_deposit(state,
deposit_data_leaves: List[spec.Bytes32],
pubkey: spec.BLSPubkey,
withdrawal_cred: spec.Bytes32,
privkey: int,
amount: int) -> spec.Deposit:
deposit_data = build_deposit_data(state, pubkey, withdrawal_cred, privkey, amount)
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))
proof = list(get_merkle_proof(tree, item_index=index))
deposit = spec.Deposit(
proof=list(proof),
index=index,
data=deposit_data,
)
assert spec.verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, index, get_merkle_root(tuple(deposit_data_leaves)))
return deposit
def build_deposit_for_index(initial_validator_count: int, index: int) -> Tuple[spec.Deposit, spec.BeaconState]:
genesis_deposits = genesis.create_deposits(
keys.pubkeys[:initial_validator_count],
keys.withdrawal_creds[:initial_validator_count]
)
state = genesis.create_genesis_state(genesis_deposits)
deposit_data_leaves = [dep.data.hash_tree_root() for dep in genesis_deposits]
deposit = build_deposit(
state,
deposit_data_leaves,
keys.pubkeys[index],
keys.withdrawal_creds[index],
keys.privkeys[index],
spec.MAX_EFFECTIVE_BALANCE,
)
state.latest_eth1_data.deposit_root = get_merkle_root(tuple(deposit_data_leaves))
state.latest_eth1_data.deposit_count = len(deposit_data_leaves)
return deposit, state
@to_dict
def valid_deposit():
new_dep, state = build_deposit_for_index(10, 10)
yield 'description', 'valid deposit to add new validator'
yield 'pre', encode(state, spec.BeaconState)
yield 'deposit', encode(new_dep, spec.Deposit)
spec.process_deposit(state, new_dep)
yield 'post', encode(state, spec.BeaconState)
@to_dict
def valid_topup():
new_dep, state = build_deposit_for_index(10, 3)
yield 'description', 'valid deposit to top-up existing validator'
yield 'pre', encode(state, spec.BeaconState)
yield 'deposit', encode(new_dep, spec.Deposit)
spec.process_deposit(state, new_dep)
yield 'post', encode(state, spec.BeaconState)
@to_dict
def invalid_deposit_index():
new_dep, state = build_deposit_for_index(10, 10)
# Mess up deposit index, 1 too small
state.deposit_index = 9
yield 'description', 'invalid deposit index'
yield 'pre', encode(state, spec.BeaconState)
yield 'deposit', encode(new_dep, spec.Deposit)
try:
spec.process_deposit(state, new_dep)
except AssertionError:
# expected
yield 'post', None
return
raise Exception('invalid_deposit_index has unexpectedly allowed deposit')
@to_dict
def invalid_deposit_proof():
new_dep, state = build_deposit_for_index(10, 10)
# Make deposit proof invalid (at bottom of proof)
new_dep.proof[-1] = spec.ZERO_HASH
yield 'description', 'invalid deposit proof'
yield 'pre', encode(state, spec.BeaconState)
yield 'deposit', encode(new_dep, spec.Deposit)
try:
spec.process_deposit(state, new_dep)
except AssertionError:
# expected
yield 'post', None
return
raise Exception('invalid_deposit_index has unexpectedly allowed deposit')
@to_tuple
def deposit_cases():
yield valid_deposit()
yield valid_topup()
yield invalid_deposit_index()
yield invalid_deposit_proof()
def mini_deposits_suite(configs_path: str) -> gen_typing.TestSuiteOutput:
presets = loader.load_presets(configs_path, 'minimal')
spec.apply_constants_preset(presets)
return ("deposit_minimal", "deposits", gen_suite.render_suite(
title="deposit operation",
summary="Test suite for deposit type operation processing",
forks_timeline="testing",
forks=["phase0"],
config="minimal",
runner="operations",
handler="deposits",
test_cases=deposit_cases()))
def full_deposits_suite(configs_path: str) -> gen_typing.TestSuiteOutput:
presets = loader.load_presets(configs_path, 'mainnet')
spec.apply_constants_preset(presets)
return ("deposit_full", "deposits", gen_suite.render_suite(
title="deposit operation",
summary="Test suite for deposit type operation processing",
forks_timeline="mainnet",
forks=["phase0"],
config="mainnet",
runner="operations",
handler="deposits",
test_cases=deposit_cases()))

View File

@ -1,44 +0,0 @@
from eth2spec.phase0 import spec
from eth2spec.utils.merkle_minimal import get_merkle_root, calc_merkle_tree_from_leaves, get_merkle_proof
from typing import List
def create_genesis_state(deposits: List[spec.Deposit]) -> spec.BeaconState:
deposit_root = get_merkle_root((tuple([(dep.data.hash_tree_root()) for dep in deposits])))
return spec.get_genesis_beacon_state(
deposits,
genesis_time=0,
genesis_eth1_data=spec.Eth1Data(
deposit_root=deposit_root,
deposit_count=len(deposits),
block_hash=spec.ZERO_HASH,
),
)
def create_deposits(pubkeys: List[spec.BLSPubkey], withdrawal_cred: List[spec.Bytes32]) -> List[spec.Deposit]:
# Mock proof of possession
proof_of_possession = b'\x33' * 96
deposit_data = [
spec.DepositData(
pubkey=pubkeys[i],
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + withdrawal_cred[i][1:],
amount=spec.MAX_EFFECTIVE_BALANCE,
proof_of_possession=proof_of_possession,
) for i in range(len(pubkeys))
]
# Fill tree with existing deposits
deposit_data_leaves = [data.hash_tree_root() for data in deposit_data]
tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves))
return [
spec.Deposit(
proof=list(get_merkle_proof(tree, item_index=i)),
index=i,
data=deposit_data[i]
) for i in range(len(deposit_data))
]

View File

@ -1,7 +0,0 @@
from py_ecc import bls
from eth2spec.phase0.spec import hash
privkeys = list(range(1, 101))
pubkeys = [bls.privtopub(k) for k in privkeys]
# Insecure, but easier to follow
withdrawal_creds = [hash(bls.privtopub(k)) for k in privkeys]

View File

@ -1,9 +1,53 @@
from gen_base import gen_runner
from typing import Callable, Iterable
from eth2spec.test.block_processing import (
test_process_attestation,
test_process_attester_slashing,
test_process_block_header,
test_process_deposit,
test_process_proposer_slashing,
test_process_transfer,
test_process_voluntary_exit
)
from gen_base import gen_runner, gen_suite, gen_typing
from gen_from_tests.gen import generate_from_tests
from preset_loader import loader
from eth2spec.phase0 import spec
def create_suite(operation_name: str, config_name: str, get_cases: Callable[[], Iterable[gen_typing.TestCase]]) \
-> Callable[[str], gen_typing.TestSuiteOutput]:
def suite_definition(configs_path: str) -> gen_typing.TestSuiteOutput:
presets = loader.load_presets(configs_path, config_name)
spec.apply_constants_preset(presets)
return ("%s_%s" % (operation_name, config_name), operation_name, gen_suite.render_suite(
title="%s operation" % operation_name,
summary="Test suite for %s type operation processing" % operation_name,
forks_timeline="testing",
forks=["phase0"],
config=config_name,
runner="operations",
handler=operation_name,
test_cases=get_cases()))
return suite_definition
from deposits import mini_deposits_suite, full_deposits_suite
if __name__ == "__main__":
gen_runner.run_generator("operations", [
mini_deposits_suite,
full_deposits_suite
create_suite('attestation', 'minimal', lambda: generate_from_tests(test_process_attestation)),
create_suite('attestation', 'mainnet', lambda: generate_from_tests(test_process_attestation)),
create_suite('attester_slashing', 'minimal', lambda: generate_from_tests(test_process_attester_slashing)),
create_suite('attester_slashing', 'mainnet', lambda: generate_from_tests(test_process_attester_slashing)),
create_suite('block_header', 'minimal', lambda: generate_from_tests(test_process_block_header)),
create_suite('block_header', 'mainnet', lambda: generate_from_tests(test_process_block_header)),
create_suite('deposit', 'minimal', lambda: generate_from_tests(test_process_deposit)),
create_suite('deposit', 'mainnet', lambda: generate_from_tests(test_process_deposit)),
create_suite('proposer_slashing', 'minimal', lambda: generate_from_tests(test_process_proposer_slashing)),
create_suite('proposer_slashing', 'mainnet', lambda: generate_from_tests(test_process_proposer_slashing)),
create_suite('transfer', 'minimal', lambda: generate_from_tests(test_process_transfer)),
create_suite('transfer', 'mainnet', lambda: generate_from_tests(test_process_transfer)),
create_suite('voluntary_exit', 'minimal', lambda: generate_from_tests(test_process_voluntary_exit)),
create_suite('voluntary_exit', 'mainnet', lambda: generate_from_tests(test_process_voluntary_exit)),
])

View File

@ -1,5 +1,4 @@
eth-utils==1.4.1
eth-utils==1.6.0
../../test_libs/gen_helpers
../../test_libs/config_helpers
../../test_libs/pyspec
py_ecc
../../test_libs/pyspec

View File

@ -0,0 +1,8 @@
# Sanity tests
Sanity tests cover regular state-transitions in a common block-list format, to ensure the basics work.
Information on the format of the tests can be found in the [sanity test formats documentation](../../specs/test_formats/sanity/README.md).

View File

@ -0,0 +1,35 @@
from typing import Callable, Iterable
from eth2spec.test.sanity import test_blocks, test_slots
from gen_base import gen_runner, gen_suite, gen_typing
from gen_from_tests.gen import generate_from_tests
from preset_loader import loader
from eth2spec.phase0 import spec
def create_suite(handler_name: str, config_name: str, get_cases: Callable[[], Iterable[gen_typing.TestCase]]) \
-> Callable[[str], gen_typing.TestSuiteOutput]:
def suite_definition(configs_path: str) -> gen_typing.TestSuiteOutput:
presets = loader.load_presets(configs_path, config_name)
spec.apply_constants_preset(presets)
return ("%sanity_s_%s" % (handler_name, config_name), handler_name, gen_suite.render_suite(
title="sanity testing",
summary="Sanity test suite, %s type, generated from pytests" % handler_name,
forks_timeline="testing",
forks=["phase0"],
config=config_name,
runner="sanity",
handler=handler_name,
test_cases=get_cases()))
return suite_definition
if __name__ == "__main__":
gen_runner.run_generator("sanity", [
create_suite('blocks', 'minimal', lambda: generate_from_tests(test_blocks)),
create_suite('blocks', 'mainnet', lambda: generate_from_tests(test_blocks)),
create_suite('slots', 'minimal', lambda: generate_from_tests(test_slots)),
create_suite('slots', 'mainnet', lambda: generate_from_tests(test_slots)),
])

View File

@ -0,0 +1,4 @@
eth-utils==1.6.0
../../test_libs/gen_helpers
../../test_libs/config_helpers
../../test_libs/pyspec

View File

@ -10,7 +10,7 @@ from preset_loader import loader
def shuffling_case(seed: spec.Bytes32, count: int):
yield 'seed', '0x' + seed.hex()
yield 'count', count
yield 'shuffled', [spec.get_permuted_index(i, count, seed) for i in range(count)]
yield 'shuffled', [spec.get_shuffled_index(i, count, seed) for i in range(count)]
@to_tuple

View File

@ -1,4 +1,4 @@
eth-utils==1.4.1
eth-utils==1.6.0
../../test_libs/gen_helpers
../../test_libs/config_helpers
../../test_libs/pyspec

View File

@ -1,4 +1,4 @@
eth-utils==1.4.1
eth-utils==1.6.0
../../test_libs/gen_helpers
../../test_libs/config_helpers
ssz==0.1.0a2

View File

@ -18,10 +18,7 @@ MAX_LIST_LENGTH = 10
@to_dict
def create_test_case(rng: Random, name: str, mode: random_value.RandomizationMode, chaos: bool):
typ = spec.get_ssz_type_by_name(name)
value = random_value.get_random_ssz_object(rng, typ, MAX_BYTES_LENGTH, MAX_LIST_LENGTH, mode, chaos)
yield "type_name", name
def create_test_case_contents(value, typ):
yield "value", encode.encode(value, typ)
yield "serialized", '0x' + serialize(value).hex()
yield "root", '0x' + hash_tree_root(value).hex()
@ -29,6 +26,13 @@ def create_test_case(rng: Random, name: str, mode: random_value.RandomizationMod
yield "signing_root", '0x' + signing_root(value).hex()
@to_dict
def create_test_case(rng: Random, name: str, mode: random_value.RandomizationMode, chaos: bool):
typ = spec.get_ssz_type_by_name(name)
value = random_value.get_random_ssz_object(rng, typ, MAX_BYTES_LENGTH, MAX_LIST_LENGTH, mode, chaos)
yield name, create_test_case_contents(value, typ)
@to_tuple
def ssz_static_cases(rng: Random, mode: random_value.RandomizationMode, chaos: bool, count: int):
for type_name in spec.ssz_types:

View File

@ -1,4 +1,4 @@
eth-utils==1.4.1
eth-utils==1.6.0
../../test_libs/gen_helpers
../../test_libs/config_helpers
../../test_libs/pyspec

View File

@ -1 +1 @@
ruamel.yaml==0.15.87
ruamel.yaml==0.15.96

View File

@ -4,6 +4,6 @@ setup(
name='config_helpers',
packages=['preset_loader'],
install_requires=[
"ruamel.yaml==0.15.87"
"ruamel.yaml==0.15.96"
]
)

View File

@ -0,0 +1,25 @@
from inspect import getmembers, isfunction
def generate_from_tests(src, bls_active=True):
"""
Generate a list of test cases by running tests from the given src in generator-mode.
:param src: to retrieve tests from (discovered using inspect.getmembers)
:param bls_active: optional, to override BLS switch preference. Defaults to True.
:return: the list of test cases.
"""
fn_names = [
name for (name, _) in getmembers(src, isfunction)
if name.startswith('test_')
]
out = []
print("generating test vectors from tests source: %s" % src.__name__)
for name in fn_names:
tfn = getattr(src, name)
try:
test_case = tfn(generator_mode=True, bls_active=bls_active)
# If no test case data is returned, the test is ignored.
if test_case is not None:
out.append(test_case)
except AssertionError:
print("ERROR: failed to generate vector from test: %s (src: %s)" % (name, src.__name__))
return out

View File

@ -1,2 +1,2 @@
ruamel.yaml==0.15.87
eth-utils==1.4.1
ruamel.yaml==0.15.96
eth-utils==1.6.0

View File

@ -2,9 +2,9 @@ from distutils.core import setup
setup(
name='gen_helpers',
packages=['gen_base'],
packages=['gen_base', 'gen_from_tests'],
install_requires=[
"ruamel.yaml==0.15.87",
"eth-utils==1.4.1"
"ruamel.yaml==0.15.96",
"eth-utils==1.6.0"
]
)

View File

@ -46,8 +46,9 @@ The `-B` flag may be helpful to force-overwrite the `pyspec` output after you ma
Run the tests:
```
pytest --config=minimal
pytest --config=minimal eth2spec
```
Note the package-name, this is to locate the tests.
## Contributing

View File

@ -0,0 +1,301 @@
from copy import deepcopy
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_current_epoch,
process_attestation,
process_slots,
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls
from eth2spec.test.helpers.attestations import (
get_valid_attestation,
sign_attestation,
)
from eth2spec.test.helpers.state import (
next_epoch,
next_slot,
)
from eth2spec.test.helpers.block import apply_empty_block
def run_attestation_processing(state, attestation, valid=True):
"""
Run ``process_attestation``, yielding:
- pre-state ('pre')
- attestation ('attestation')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
# yield pre-state
yield 'pre', state
yield 'attestation', attestation
# If the attestation is invalid, processing is aborted, and there is no post-state.
if not valid:
expect_assertion_error(lambda: process_attestation(state, attestation))
yield 'post', None
return
current_epoch_count = len(state.current_epoch_attestations)
previous_epoch_count = len(state.previous_epoch_attestations)
# process attestation
process_attestation(state, attestation)
# Make sure the attestation has been processed
if attestation.data.target_epoch == get_current_epoch(state):
assert len(state.current_epoch_attestations) == current_epoch_count + 1
else:
assert len(state.previous_epoch_attestations) == previous_epoch_count + 1
# yield post-state
yield 'post', state
@spec_state_test
def test_success(state):
attestation = get_valid_attestation(state, signed=True)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
yield from run_attestation_processing(state, attestation)
@spec_state_test
def test_success_previous_epoch(state):
attestation = get_valid_attestation(state, signed=True)
next_epoch(state)
apply_empty_block(state)
yield from run_attestation_processing(state, attestation)
@spec_state_test
def test_success_since_max_epochs_per_crosslink(state):
for _ in range(spec.MAX_EPOCHS_PER_CROSSLINK + 2):
next_epoch(state)
apply_empty_block(state)
attestation = get_valid_attestation(state, signed=True)
data = attestation.data
# test logic sanity check: make sure the attestation only includes MAX_EPOCHS_PER_CROSSLINK epochs
assert data.crosslink.end_epoch - data.crosslink.start_epoch == spec.MAX_EPOCHS_PER_CROSSLINK
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
apply_empty_block(state)
yield from run_attestation_processing(state, attestation)
@always_bls
@spec_state_test
def test_invalid_attestation_signature(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_before_inclusion_delay(state):
attestation = get_valid_attestation(state, signed=True)
# do not increment slot to allow for inclusion delay
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_after_epoch_slots(state):
attestation = get_valid_attestation(state, signed=True)
# increment past latest inclusion slot
process_slots(state, state.slot + spec.SLOTS_PER_EPOCH + 1)
apply_empty_block(state)
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_old_source_epoch(state):
state.slot = spec.SLOTS_PER_EPOCH * 5
state.finalized_epoch = 2
state.previous_justified_epoch = 3
state.current_justified_epoch = 4
attestation = get_valid_attestation(state, slot=(spec.SLOTS_PER_EPOCH * 3) + 1)
# test logic sanity check: make sure the attestation is pointing to oldest known source epoch
assert attestation.data.source_epoch == state.previous_justified_epoch
# Now go beyond that, it will be invalid
attestation.data.source_epoch -= 1
sign_attestation(state, attestation)
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_wrong_shard(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.data.crosslink.shard += 1
sign_attestation(state, attestation)
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_new_source_epoch(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.data.source_epoch += 1
sign_attestation(state, attestation)
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_source_root_is_target_root(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.data.source_root = attestation.data.target_root
sign_attestation(state, attestation)
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_invalid_current_source_root(state):
state.slot = spec.SLOTS_PER_EPOCH * 5
state.finalized_epoch = 2
state.previous_justified_epoch = 3
state.previous_justified_root = b'\x01' * 32
state.current_justified_epoch = 4
state.current_justified_root = b'\xff' * 32
attestation = get_valid_attestation(state, slot=(spec.SLOTS_PER_EPOCH * 3) + 1)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
# Test logic sanity checks:
assert state.current_justified_root != state.previous_justified_root
assert attestation.data.source_root == state.previous_justified_root
# Make attestation source root invalid: should be previous justified, not current one
attestation.data.source_root = state.current_justified_root
sign_attestation(state, attestation)
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_bad_source_root(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.data.source_root = b'\x42' * 32
sign_attestation(state, attestation)
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_non_zero_crosslink_data_root(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.data.crosslink.data_root = b'\x42' * 32
sign_attestation(state, attestation)
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_bad_parent_crosslink(state):
next_epoch(state)
apply_empty_block(state)
attestation = get_valid_attestation(state, signed=True)
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
apply_empty_block(state)
attestation.data.crosslink.parent_root = b'\x27' * 32
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_bad_crosslink_start_epoch(state):
next_epoch(state)
apply_empty_block(state)
attestation = get_valid_attestation(state, signed=True)
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
apply_empty_block(state)
attestation.data.crosslink.start_epoch += 1
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_bad_crosslink_end_epoch(state):
next_epoch(state)
apply_empty_block(state)
attestation = get_valid_attestation(state, signed=True)
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
apply_empty_block(state)
attestation.data.crosslink.end_epoch += 1
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_inconsistent_bitfields(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.custody_bitfield = deepcopy(attestation.aggregation_bitfield) + b'\x00'
sign_attestation(state, attestation)
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_non_empty_custody_bitfield(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.custody_bitfield = deepcopy(attestation.aggregation_bitfield)
sign_attestation(state, attestation)
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
def test_empty_aggregation_bitfield(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.aggregation_bitfield = b'\x00' * len(attestation.aggregation_bitfield)
sign_attestation(state, attestation)
yield from run_attestation_processing(state, attestation)

View File

@ -0,0 +1,149 @@
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_beacon_proposer_index,
process_attester_slashing,
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls
from eth2spec.test.helpers.attestations import sign_indexed_attestation
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing
from eth2spec.test.helpers.block import apply_empty_block
from eth2spec.test.helpers.state import (
get_balance,
next_epoch,
)
def run_attester_slashing_processing(state, attester_slashing, valid=True):
"""
Run ``process_attester_slashing``, yielding:
- pre-state ('pre')
- attester_slashing ('attester_slashing')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
yield 'pre', state
yield 'attester_slashing', attester_slashing
if not valid:
expect_assertion_error(lambda: process_attester_slashing(state, attester_slashing))
yield 'post', None
return
slashed_index = attester_slashing.attestation_1.custody_bit_0_indices[0]
pre_slashed_balance = get_balance(state, slashed_index)
proposer_index = get_beacon_proposer_index(state)
pre_proposer_balance = get_balance(state, proposer_index)
# Process slashing
process_attester_slashing(state, attester_slashing)
slashed_validator = state.validator_registry[slashed_index]
# Check slashing
assert slashed_validator.slashed
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
if slashed_index != proposer_index:
# lost whistleblower reward
assert get_balance(state, slashed_index) < pre_slashed_balance
# gained whistleblower reward
assert get_balance(state, proposer_index) > pre_proposer_balance
else:
# gained rewards for all slashings, which may include others. And only lost that of themselves.
# Netto at least 0, if more people where slashed, a balance increase.
assert get_balance(state, slashed_index) >= pre_slashed_balance
yield 'post', state
@spec_state_test
def test_success_double(state):
attester_slashing = get_valid_attester_slashing(state, signed_1=True, signed_2=True)
yield from run_attester_slashing_processing(state, attester_slashing)
@spec_state_test
def test_success_surround(state):
next_epoch(state)
apply_empty_block(state)
state.current_justified_epoch += 1
attester_slashing = get_valid_attester_slashing(state, signed_1=False, signed_2=True)
# set attestion1 to surround attestation 2
attester_slashing.attestation_1.data.source_epoch = attester_slashing.attestation_2.data.source_epoch - 1
attester_slashing.attestation_1.data.target_epoch = attester_slashing.attestation_2.data.target_epoch + 1
sign_indexed_attestation(state, attester_slashing.attestation_1)
yield from run_attester_slashing_processing(state, attester_slashing)
@always_bls
@spec_state_test
def test_invalid_sig_1(state):
attester_slashing = get_valid_attester_slashing(state, signed_1=False, signed_2=True)
yield from run_attester_slashing_processing(state, attester_slashing, False)
@always_bls
@spec_state_test
def test_invalid_sig_2(state):
attester_slashing = get_valid_attester_slashing(state, signed_1=True, signed_2=False)
yield from run_attester_slashing_processing(state, attester_slashing, False)
@always_bls
@spec_state_test
def test_invalid_sig_1_and_2(state):
attester_slashing = get_valid_attester_slashing(state, signed_1=False, signed_2=False)
yield from run_attester_slashing_processing(state, attester_slashing, False)
@spec_state_test
def test_same_data(state):
attester_slashing = get_valid_attester_slashing(state, signed_1=False, signed_2=True)
attester_slashing.attestation_1.data = attester_slashing.attestation_2.data
sign_indexed_attestation(state, attester_slashing.attestation_1)
yield from run_attester_slashing_processing(state, attester_slashing, False)
@spec_state_test
def test_no_double_or_surround(state):
attester_slashing = get_valid_attester_slashing(state, signed_1=False, signed_2=True)
attester_slashing.attestation_1.data.target_epoch += 1
sign_indexed_attestation(state, attester_slashing.attestation_1)
yield from run_attester_slashing_processing(state, attester_slashing, False)
@spec_state_test
def test_participants_already_slashed(state):
attester_slashing = get_valid_attester_slashing(state, signed_1=True, signed_2=True)
# set all indices to slashed
attestation_1 = attester_slashing.attestation_1
validator_indices = attestation_1.custody_bit_0_indices + attestation_1.custody_bit_1_indices
for index in validator_indices:
state.validator_registry[index].slashed = True
yield from run_attester_slashing_processing(state, attester_slashing, False)
@spec_state_test
def test_custody_bit_0_and_1(state):
attester_slashing = get_valid_attester_slashing(state, signed_1=False, signed_2=True)
attester_slashing.attestation_1.custody_bit_1_indices = (
attester_slashing.attestation_1.custody_bit_0_indices
)
sign_indexed_attestation(state, attester_slashing.attestation_1)
yield from run_attester_slashing_processing(state, attester_slashing, False)

View File

@ -0,0 +1,85 @@
from copy import deepcopy
from eth2spec.phase0.spec import (
get_beacon_proposer_index,
process_slots,
process_block_header,
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot,
sign_block
)
from eth2spec.test.helpers.state import next_slot
def prepare_state_for_header_processing(state):
process_slots(state, state.slot + 1)
def run_block_header_processing(state, block, valid=True):
"""
Run ``process_block_header``, yielding:
- pre-state ('pre')
- block ('block')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
prepare_state_for_header_processing(state)
yield 'pre', state
yield 'block', block
if not valid:
expect_assertion_error(lambda: process_block_header(state, block))
yield 'post', None
return
process_block_header(state, block)
yield 'post', state
@spec_state_test
def test_success_block_header(state):
block = build_empty_block_for_next_slot(state, signed=True)
yield from run_block_header_processing(state, block)
@always_bls
@spec_state_test
def test_invalid_sig_block_header(state):
block = build_empty_block_for_next_slot(state)
yield from run_block_header_processing(state, block, valid=False)
@spec_state_test
def test_invalid_slot_block_header(state):
block = build_empty_block_for_next_slot(state)
block.slot = state.slot + 2 # invalid slot
sign_block(state, block)
yield from run_block_header_processing(state, block, valid=False)
@spec_state_test
def test_invalid_parent_root(state):
block = build_empty_block_for_next_slot(state)
block.parent_root = b'\12' * 32 # invalid prev root
sign_block(state, block)
yield from run_block_header_processing(state, block, valid=False)
@spec_state_test
def test_proposer_slashed(state):
# use stub state to get proposer index of next slot
stub_state = deepcopy(state)
next_slot(stub_state)
proposer_index = get_beacon_proposer_index(stub_state)
# set proposer to slashed
state.validator_registry[proposer_index].slashed = True
block = build_empty_block_for_next_slot(state, signed=True)
yield from run_block_header_processing(state, block, valid=False)

View File

@ -0,0 +1,124 @@
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import process_deposit
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls
from eth2spec.test.helpers.deposits import prepare_state_and_deposit, sign_deposit_data
from eth2spec.test.helpers.state import get_balance
from eth2spec.test.helpers.keys import privkeys
def run_deposit_processing(state, deposit, validator_index, valid=True, effective=True):
"""
Run ``process_deposit``, yielding:
- pre-state ('pre')
- deposit ('deposit')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
pre_validator_count = len(state.validator_registry)
pre_balance = 0
if validator_index < pre_validator_count:
pre_balance = get_balance(state, validator_index)
else:
# if it is a new validator, it should be right at the end of the current registry.
assert validator_index == pre_validator_count
yield 'pre', state
yield 'deposit', deposit
if not valid:
expect_assertion_error(lambda: process_deposit(state, deposit))
yield 'post', None
return
process_deposit(state, deposit)
yield 'post', state
if not effective:
assert len(state.validator_registry) == pre_validator_count
assert len(state.balances) == pre_validator_count
if validator_index < pre_validator_count:
assert get_balance(state, validator_index) == pre_balance
else:
if validator_index < pre_validator_count:
# top-up
assert len(state.validator_registry) == pre_validator_count
assert len(state.balances) == pre_validator_count
else:
# new validator
assert len(state.validator_registry) == pre_validator_count + 1
assert len(state.balances) == pre_validator_count + 1
assert get_balance(state, validator_index) == pre_balance + deposit.data.amount
assert state.deposit_index == state.latest_eth1_data.deposit_count
@spec_state_test
def test_new_deposit(state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validator_registry)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(state, validator_index, amount, signed=True)
yield from run_deposit_processing(state, deposit, validator_index)
@always_bls
@spec_state_test
def test_invalid_sig_new_deposit(state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validator_registry)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(state, validator_index, amount)
yield from run_deposit_processing(state, deposit, validator_index, valid=True, effective=False)
@spec_state_test
def test_success_top_up(state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(state, validator_index, amount, signed=True)
yield from run_deposit_processing(state, deposit, validator_index)
@always_bls
@spec_state_test
def test_invalid_sig_top_up(state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(state, validator_index, amount)
# invalid signatures, in top-ups, are allowed!
yield from run_deposit_processing(state, deposit, validator_index, valid=True, effective=True)
@spec_state_test
def test_wrong_index(state):
validator_index = len(state.validator_registry)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(state, validator_index, amount)
# mess up deposit_index
deposit.index = state.deposit_index + 1
sign_deposit_data(state, deposit.data, privkeys[validator_index])
yield from run_deposit_processing(state, deposit, validator_index, valid=False)
# TODO: test invalid signature
@spec_state_test
def test_bad_merkle_proof(state):
validator_index = len(state.validator_registry)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(state, validator_index, amount)
# mess up merkle branch
deposit.proof[-1] = spec.ZERO_HASH
sign_deposit_data(state, deposit.data, privkeys[validator_index])
yield from run_deposit_processing(state, deposit, validator_index, valid=False)

View File

@ -0,0 +1,137 @@
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_current_epoch,
process_proposer_slashing,
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls
from eth2spec.test.helpers.block_header import sign_block_header
from eth2spec.test.helpers.keys import privkeys
from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing
from eth2spec.test.helpers.state import get_balance
def run_proposer_slashing_processing(state, proposer_slashing, valid=True):
"""
Run ``process_proposer_slashing``, yielding:
- pre-state ('pre')
- proposer_slashing ('proposer_slashing')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
yield 'pre', state
yield 'proposer_slashing', proposer_slashing
if not valid:
expect_assertion_error(lambda: process_proposer_slashing(state, proposer_slashing))
yield 'post', None
return
pre_proposer_balance = get_balance(state, proposer_slashing.proposer_index)
process_proposer_slashing(state, proposer_slashing)
yield 'post', state
# check if slashed
slashed_validator = state.validator_registry[proposer_slashing.proposer_index]
assert slashed_validator.slashed
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
# lost whistleblower reward
assert (
get_balance(state, proposer_slashing.proposer_index) <
pre_proposer_balance
)
@spec_state_test
def test_success(state):
proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=True)
yield from run_proposer_slashing_processing(state, proposer_slashing)
@always_bls
@spec_state_test
def test_invalid_sig_1(state):
proposer_slashing = get_valid_proposer_slashing(state, signed_1=False, signed_2=True)
yield from run_proposer_slashing_processing(state, proposer_slashing, False)
@always_bls
@spec_state_test
def test_invalid_sig_2(state):
proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=False)
yield from run_proposer_slashing_processing(state, proposer_slashing, False)
@always_bls
@spec_state_test
def test_invalid_sig_1_and_2(state):
proposer_slashing = get_valid_proposer_slashing(state, signed_1=False, signed_2=False)
yield from run_proposer_slashing_processing(state, proposer_slashing, False)
@spec_state_test
def test_invalid_proposer_index(state):
proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=True)
# Index just too high (by 1)
proposer_slashing.proposer_index = len(state.validator_registry)
yield from run_proposer_slashing_processing(state, proposer_slashing, False)
@spec_state_test
def test_epochs_are_different(state):
proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=False)
# set slots to be in different epochs
proposer_slashing.header_2.slot += spec.SLOTS_PER_EPOCH
sign_block_header(state, proposer_slashing.header_2, privkeys[proposer_slashing.proposer_index])
yield from run_proposer_slashing_processing(state, proposer_slashing, False)
@spec_state_test
def test_headers_are_same(state):
proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=False)
# set headers to be the same
proposer_slashing.header_2 = proposer_slashing.header_1
yield from run_proposer_slashing_processing(state, proposer_slashing, False)
@spec_state_test
def test_proposer_is_not_activated(state):
proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=True)
# set proposer to be not active yet
state.validator_registry[proposer_slashing.proposer_index].activation_epoch = get_current_epoch(state) + 1
yield from run_proposer_slashing_processing(state, proposer_slashing, False)
@spec_state_test
def test_proposer_is_slashed(state):
proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=True)
# set proposer to slashed
state.validator_registry[proposer_slashing.proposer_index].slashed = True
yield from run_proposer_slashing_processing(state, proposer_slashing, False)
@spec_state_test
def test_proposer_is_withdrawn(state):
proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=True)
# move 1 epoch into future, to allow for past withdrawable epoch
state.slot += spec.SLOTS_PER_EPOCH
# set proposer withdrawable_epoch in past
current_epoch = get_current_epoch(state)
proposer_index = proposer_slashing.proposer_index
state.validator_registry[proposer_index].withdrawable_epoch = current_epoch - 1
yield from run_proposer_slashing_processing(state, proposer_slashing, False)

View File

@ -0,0 +1,172 @@
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_active_validator_indices,
get_beacon_proposer_index,
get_current_epoch,
process_transfer,
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls
from eth2spec.test.helpers.state import next_epoch
from eth2spec.test.helpers.block import apply_empty_block
from eth2spec.test.helpers.transfers import get_valid_transfer
def run_transfer_processing(state, transfer, valid=True):
"""
Run ``process_transfer``, yielding:
- pre-state ('pre')
- transfer ('transfer')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
proposer_index = get_beacon_proposer_index(state)
pre_transfer_sender_balance = state.balances[transfer.sender]
pre_transfer_recipient_balance = state.balances[transfer.recipient]
pre_transfer_proposer_balance = state.balances[proposer_index]
yield 'pre', state
yield 'transfer', transfer
if not valid:
expect_assertion_error(lambda: process_transfer(state, transfer))
yield 'post', None
return
process_transfer(state, transfer)
yield 'post', state
sender_balance = state.balances[transfer.sender]
recipient_balance = state.balances[transfer.recipient]
assert sender_balance == pre_transfer_sender_balance - transfer.amount - transfer.fee
assert recipient_balance == pre_transfer_recipient_balance + transfer.amount
assert state.balances[proposer_index] == pre_transfer_proposer_balance + transfer.fee
@spec_state_test
def test_success_non_activated(state):
transfer = get_valid_transfer(state, signed=True)
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
yield from run_transfer_processing(state, transfer)
@spec_state_test
def test_success_withdrawable(state):
next_epoch(state)
apply_empty_block(state)
transfer = get_valid_transfer(state, signed=True)
# withdrawable_epoch in past so can transfer
state.validator_registry[transfer.sender].withdrawable_epoch = get_current_epoch(state) - 1
yield from run_transfer_processing(state, transfer)
@spec_state_test
def test_success_active_above_max_effective(state):
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + 1
transfer = get_valid_transfer(state, sender_index=sender_index, amount=1, fee=0, signed=True)
yield from run_transfer_processing(state, transfer)
@spec_state_test
def test_success_active_above_max_effective_fee(state):
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + 1
transfer = get_valid_transfer(state, sender_index=sender_index, amount=0, fee=1, signed=True)
yield from run_transfer_processing(state, transfer)
@always_bls
@spec_state_test
def test_invalid_signature(state):
transfer = get_valid_transfer(state)
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
yield from run_transfer_processing(state, transfer, False)
@spec_state_test
def test_active_but_transfer_past_effective_balance(state):
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
amount = spec.MAX_EFFECTIVE_BALANCE // 32
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0, signed=True)
yield from run_transfer_processing(state, transfer, False)
@spec_state_test
def test_incorrect_slot(state):
transfer = get_valid_transfer(state, slot=state.slot + 1, signed=True)
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_transfer_processing(state, transfer, False)
@spec_state_test
def test_insufficient_balance_for_fee(state):
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE
transfer = get_valid_transfer(state, sender_index=sender_index, amount=0, fee=1, signed=True)
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_transfer_processing(state, transfer, False)
@spec_state_test
def test_insufficient_balance(state):
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE
transfer = get_valid_transfer(state, sender_index=sender_index, amount=1, fee=0, signed=True)
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_transfer_processing(state, transfer, False)
@spec_state_test
def test_no_dust_sender(state):
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
balance = state.balances[sender_index]
transfer = get_valid_transfer(state, sender_index=sender_index, amount=balance - spec.MIN_DEPOSIT_AMOUNT + 1, fee=0, signed=True)
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_transfer_processing(state, transfer, False)
@spec_state_test
def test_no_dust_recipient(state):
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + 1
transfer = get_valid_transfer(state, sender_index=sender_index, amount=1, fee=0, signed=True)
state.balances[transfer.recipient] = 0
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_transfer_processing(state, transfer, False)
@spec_state_test
def test_invalid_pubkey(state):
transfer = get_valid_transfer(state, signed=True)
state.validator_registry[transfer.sender].withdrawal_credentials = spec.ZERO_HASH
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
yield from run_transfer_processing(state, transfer, False)

View File

@ -0,0 +1,225 @@
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_active_validator_indices,
get_churn_limit,
get_current_epoch,
process_voluntary_exit,
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.voluntary_exits import build_voluntary_exit, sign_voluntary_exit
def run_voluntary_exit_processing(state, voluntary_exit, valid=True):
"""
Run ``process_voluntary_exit``, yielding:
- pre-state ('pre')
- voluntary_exit ('voluntary_exit')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
validator_index = voluntary_exit.validator_index
yield 'pre', state
yield 'voluntary_exit', voluntary_exit
if not valid:
expect_assertion_error(lambda: process_voluntary_exit(state, voluntary_exit))
yield 'post', None
return
pre_exit_epoch = state.validator_registry[validator_index].exit_epoch
process_voluntary_exit(state, voluntary_exit)
yield 'post', state
assert pre_exit_epoch == spec.FAR_FUTURE_EPOCH
assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
@spec_state_test
def test_success(state):
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(state, current_epoch, validator_index, privkey, signed=True)
yield from run_voluntary_exit_processing(state, voluntary_exit)
@always_bls
@spec_state_test
def test_invalid_signature(state):
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(state, current_epoch, validator_index, privkey)
yield from run_voluntary_exit_processing(state, voluntary_exit, False)
@spec_state_test
def test_success_exit_queue(state):
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = get_current_epoch(state)
# exit `MAX_EXITS_PER_EPOCH`
initial_indices = get_active_validator_indices(state, current_epoch)[:get_churn_limit(state)]
# Prepare a bunch of exits, based on the current state
exit_queue = []
for index in initial_indices:
privkey = pubkey_to_privkey[state.validator_registry[index].pubkey]
exit_queue.append(build_voluntary_exit(
state,
current_epoch,
index,
privkey,
signed=True,
))
# Now run all the exits
for voluntary_exit in exit_queue:
# the function yields data, but we are just interested in running it here, ignore yields.
for _ in run_voluntary_exit_processing(state, voluntary_exit):
continue
# exit an additional validator
validator_index = get_active_validator_indices(state, current_epoch)[-1]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
validator_index,
privkey,
signed=True,
)
# This is the interesting part of the test: on a pre-state with a full exit queue,
# when processing an additional exit, it results in an exit in a later epoch
yield from run_voluntary_exit_processing(state, voluntary_exit)
assert (
state.validator_registry[validator_index].exit_epoch ==
state.validator_registry[initial_indices[0]].exit_epoch + 1
)
@spec_state_test
def test_validator_exit_in_future(state):
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
validator_index,
privkey,
signed=False,
)
voluntary_exit.epoch += 1
sign_voluntary_exit(state, voluntary_exit, privkey)
yield from run_voluntary_exit_processing(state, voluntary_exit, False)
@spec_state_test
def test_validator_invalid_validator_index(state):
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
validator_index,
privkey,
signed=False,
)
voluntary_exit.validator_index = len(state.validator_registry)
sign_voluntary_exit(state, voluntary_exit, privkey)
yield from run_voluntary_exit_processing(state, voluntary_exit, False)
@spec_state_test
def test_validator_not_active(state):
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
# build and test voluntary exit
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
validator_index,
privkey,
signed=True,
)
yield from run_voluntary_exit_processing(state, voluntary_exit, False)
@spec_state_test
def test_validator_already_exited(state):
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
# but validator already has exited
state.validator_registry[validator_index].exit_epoch = current_epoch + 2
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
validator_index,
privkey,
signed=True,
)
yield from run_voluntary_exit_processing(state, voluntary_exit, False)
@spec_state_test
def test_validator_not_active_long_enough(state):
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
validator_index,
privkey,
signed=True,
)
assert (
current_epoch - state.validator_registry[validator_index].activation_epoch <
spec.PERSISTENT_COMMITTEE_PERIOD
)
yield from run_voluntary_exit_processing(state, voluntary_exit, False)

View File

@ -0,0 +1,36 @@
from eth2spec.phase0 import spec
# We import pytest only when it's present, i.e. when we are running tests.
# The test-cases themselves can be generated without installing pytest.
def module_exists(module_name):
try:
__import__(module_name)
except ImportError:
return False
else:
return True
def fixture(*args, **kwargs):
if module_exists("pytest"):
import pytest
return pytest.fixture(*args, **kwargs)
else:
def ignore():
pass
return ignore
def pytest_addoption(parser):
parser.addoption(
"--config", action="store", default="minimal", help="config: make the pyspec use the specified configuration"
)
@fixture(autouse=True)
def config(request):
config_name = request.config.getoption("--config")
from preset_loader import loader
presets = loader.load_presets('../../configs/', config_name)
spec.apply_constants_preset(presets)

View File

@ -0,0 +1,82 @@
from eth2spec.phase0 import spec
from eth2spec.utils import bls
from .helpers.genesis import create_genesis_state
from .utils import spectest, with_args, with_tags
# Provides a genesis state as first argument to the function decorated with this
with_state = with_args(lambda: [create_genesis_state(spec.SLOTS_PER_EPOCH * 8)])
# BLS is turned off by default *for performance purposes during TESTING*.
# The runner of the test can indicate the preferred setting (test generators prefer BLS to be ON).
# - Some tests are marked as BLS-requiring, and ignore this setting.
# (tests that express differences caused by BLS, e.g. invalid signatures being rejected)
# - Some other tests are marked as BLS-ignoring, and ignore this setting.
# (tests that are heavily performance impacted / require unsigned state transitions)
# - Most tests respect the BLS setting.
DEFAULT_BLS_ACTIVE = False
# shorthand for decorating @with_state @spectest()
def spec_state_test(fn):
return with_state(bls_switch(spectest()(fn)))
def expect_assertion_error(fn):
bad = False
try:
fn()
bad = True
except AssertionError:
pass
except IndexError:
# Index errors are special; the spec is not explicit on bound checking, an IndexError is like a failed assert.
pass
if bad:
raise AssertionError('expected an assertion error, but got none.')
# Tags a test to be ignoring BLS for it to pass.
bls_ignored = with_tags({'bls_setting': 2})
def never_bls(fn):
"""
Decorator to apply on ``bls_switch`` decorator to force BLS de-activation. Useful to mark tests as BLS-ignorant.
"""
def entry(*args, **kw):
# override bls setting
kw['bls_active'] = False
return fn(*args, **kw)
return bls_ignored(entry)
# Tags a test to be requiring BLS for it to pass.
bls_required = with_tags({'bls_setting': 1})
def always_bls(fn):
"""
Decorator to apply on ``bls_switch`` decorator to force BLS activation. Useful to mark tests as BLS-dependent.
"""
def entry(*args, **kw):
# override bls setting
kw['bls_active'] = True
return fn(*args, **kw)
return bls_required(entry)
def bls_switch(fn):
"""
Decorator to make a function execute with BLS ON, or BLS off.
Based on an optional bool argument ``bls_active``, passed to the function at runtime.
"""
def entry(*args, **kw):
old_state = bls.bls_active
bls.bls_active = kw.pop('bls_active', DEFAULT_BLS_ACTIVE)
out = fn(*args, **kw)
bls.bls_active = old_state
return out
return entry

View File

@ -1,5 +1,4 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
@ -9,106 +8,123 @@ from eth2spec.phase0.spec import (
process_crosslinks,
state_transition,
)
from tests.helpers import (
from eth2spec.test.context import spec_state_test
from eth2spec.test.helpers.state import (
next_epoch,
next_slot
)
from eth2spec.test.helpers.block import apply_empty_block, sign_block
from eth2spec.test.helpers.attestations import (
add_attestation_to_state,
build_empty_block_for_next_slot,
fill_aggregate_attestation,
get_crosslink_committee,
get_valid_attestation,
next_epoch,
next_slot,
set_bitfield_bit,
sign_attestation,
)
# mark entire file as 'crosslinks'
pytestmark = pytest.mark.crosslinks
def run_process_crosslinks(state, valid=True):
"""
Run ``process_crosslinks``, yielding:
- pre-state ('pre')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
# transition state to slot before state transition
slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1
block = build_empty_block_for_next_slot(state)
block.slot = slot
sign_block(state, block)
state_transition(state, block)
# cache state before epoch transition
process_slot(state)
post_state = deepcopy(state)
process_crosslinks(post_state)
return state, post_state
yield 'pre', state
process_crosslinks(state)
yield 'post', state
@spec_state_test
def test_no_attestations(state):
pre_state, post_state = run_process_crosslinks(state)
yield from run_process_crosslinks(state)
for shard in range(spec.SHARD_COUNT):
assert post_state.previous_crosslinks[shard] == post_state.current_crosslinks[shard]
return pre_state, post_state
assert state.previous_crosslinks[shard] == state.current_crosslinks[shard]
@spec_state_test
def test_single_crosslink_update_from_current_epoch(state):
next_epoch(state)
attestation = get_valid_attestation(state)
attestation = get_valid_attestation(state, signed=True)
fill_aggregate_attestation(state, attestation)
add_attestation_to_state(state, attestation, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
assert len(state.current_epoch_attestations) == 1
pre_state, post_state = run_process_crosslinks(state)
shard = attestation.data.crosslink.shard
assert post_state.previous_crosslinks[shard] != post_state.current_crosslinks[shard]
assert pre_state.current_crosslinks[shard] != post_state.current_crosslinks[shard]
pre_crosslink = deepcopy(state.current_crosslinks[shard])
return pre_state, post_state
yield from run_process_crosslinks(state)
assert state.previous_crosslinks[shard] != state.current_crosslinks[shard]
assert pre_crosslink != state.current_crosslinks[shard]
@spec_state_test
def test_single_crosslink_update_from_previous_epoch(state):
next_epoch(state)
attestation = get_valid_attestation(state)
attestation = get_valid_attestation(state, signed=True)
fill_aggregate_attestation(state, attestation)
add_attestation_to_state(state, attestation, state.slot + spec.SLOTS_PER_EPOCH)
assert len(state.previous_epoch_attestations) == 1
pre_state, post_state = run_process_crosslinks(state)
shard = attestation.data.crosslink.shard
pre_crosslink = deepcopy(state.current_crosslinks[shard])
crosslink_deltas = get_crosslink_deltas(state)
shard = attestation.data.crosslink.shard
assert post_state.previous_crosslinks[shard] != post_state.current_crosslinks[shard]
assert pre_state.current_crosslinks[shard] != post_state.current_crosslinks[shard]
yield from run_process_crosslinks(state)
assert state.previous_crosslinks[shard] != state.current_crosslinks[shard]
assert pre_crosslink != state.current_crosslinks[shard]
# ensure rewarded
for index in get_crosslink_committee(state, attestation.data.target_epoch, attestation.data.crosslink.shard):
assert crosslink_deltas[0][index] > 0
assert crosslink_deltas[1][index] == 0
return pre_state, post_state
@spec_state_test
def test_double_late_crosslink(state):
if spec.get_epoch_committee_count(state, spec.get_current_epoch(state)) < spec.SHARD_COUNT:
print("warning: ignoring test, test-assumptions are incompatible with configuration")
return
next_epoch(state)
state.slot += 4
attestation_1 = get_valid_attestation(state)
attestation_1 = get_valid_attestation(state, signed=True)
fill_aggregate_attestation(state, attestation_1)
# add attestation_1 in the next epoch
# add attestation_1 to next epoch
next_epoch(state)
add_attestation_to_state(state, attestation_1, state.slot + 1)
for slot in range(spec.SLOTS_PER_EPOCH):
attestation_2 = get_valid_attestation(state)
if attestation_2.data.crosslink.shard == attestation_1.data.crosslink.shard:
sign_attestation(state, attestation_2)
break
next_slot(state)
apply_empty_block(state)
fill_aggregate_attestation(state, attestation_2)
# add attestation_2 in the next epoch after attestation_1 has
@ -119,16 +135,15 @@ def test_double_late_crosslink(state):
assert len(state.previous_epoch_attestations) == 1
assert len(state.current_epoch_attestations) == 0
pre_state, post_state = run_process_crosslinks(state)
crosslink_deltas = get_crosslink_deltas(state)
yield from run_process_crosslinks(state)
shard = attestation_2.data.crosslink.shard
# ensure that the current crosslinks were not updated by the second attestation
assert post_state.previous_crosslinks[shard] == post_state.current_crosslinks[shard]
assert state.previous_crosslinks[shard] == state.current_crosslinks[shard]
# ensure no reward, only penalties for the failed crosslink
for index in get_crosslink_committee(state, attestation_2.data.target_epoch, attestation_2.data.crosslink.shard):
assert crosslink_deltas[0][index] == 0
assert crosslink_deltas[1][index] > 0
return pre_state, post_state

View File

@ -1,21 +1,44 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_current_epoch,
is_active_validator,
process_registry_updates
)
from tests.helpers import (
next_epoch,
)
# mark entire file as 'state'
pytestmark = pytest.mark.state
from eth2spec.phase0.spec import state_transition
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
from eth2spec.test.helpers.state import next_epoch
from eth2spec.test.context import spec_state_test
def run_process_registry_updates(state, valid=True):
"""
Run ``process_crosslinks``, yielding:
- pre-state ('pre')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
# transition state to slot before state transition
slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1
block = build_empty_block_for_next_slot(state)
block.slot = slot
sign_block(state, block)
state_transition(state, block)
# cache state before epoch transition
spec.process_slot(state)
# process components of epoch transition before registry update
spec.process_justification_and_finalization(state)
spec.process_crosslinks(state)
spec.process_rewards_and_penalties(state)
yield 'pre', state
process_registry_updates(state)
yield 'post', state
@spec_state_test
def test_activation(state):
index = 0
assert is_active_validator(state.validator_registry[index], get_current_epoch(state))
@ -26,12 +49,10 @@ def test_activation(state):
state.validator_registry[index].effective_balance = spec.MAX_EFFECTIVE_BALANCE
assert not is_active_validator(state.validator_registry[index], get_current_epoch(state))
pre_state = deepcopy(state)
blocks = []
for _ in range(spec.ACTIVATION_EXIT_DELAY + 1):
block = next_epoch(state)
blocks.append(block)
next_epoch(state)
yield from run_process_registry_updates(state)
assert state.validator_registry[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
assert state.validator_registry[index].activation_epoch != spec.FAR_FUTURE_EPOCH
@ -40,9 +61,8 @@ def test_activation(state):
get_current_epoch(state),
)
return pre_state, blocks, state
@spec_state_test
def test_ejection(state):
index = 0
assert is_active_validator(state.validator_registry[index], get_current_epoch(state))
@ -51,17 +71,13 @@ def test_ejection(state):
# Mock an ejection
state.validator_registry[index].effective_balance = spec.EJECTION_BALANCE
pre_state = deepcopy(state)
blocks = []
for _ in range(spec.ACTIVATION_EXIT_DELAY + 1):
block = next_epoch(state)
blocks.append(block)
next_epoch(state)
yield from run_process_registry_updates(state)
assert state.validator_registry[index].exit_epoch != spec.FAR_FUTURE_EPOCH
assert not is_active_validator(
state.validator_registry[index],
get_current_epoch(state),
)
return pre_state, blocks, state

View File

@ -0,0 +1,161 @@
from typing import List
# Access constants from spec pkg reference.
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
Attestation,
AttestationData,
AttestationDataAndCustodyBit,
Crosslink,
get_epoch_start_slot, get_block_root, get_current_epoch, get_previous_epoch, slot_to_epoch,
get_crosslink_committee, get_domain, IndexedAttestation, get_attesting_indices, BeaconState, get_block_root_at_slot,
get_epoch_start_shard, get_epoch_committee_count,
state_transition, process_slots,
)
from eth2spec.test.helpers.bitfields import set_bitfield_bit
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils.bls import bls_sign, bls_aggregate_signatures
from eth2spec.utils.minimal_ssz import hash_tree_root
def build_attestation_data(state, slot, shard):
assert state.slot >= slot
if slot == state.slot:
block_root = build_empty_block_for_next_slot(state).parent_root
else:
block_root = get_block_root_at_slot(state, slot)
current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
if slot < current_epoch_start_slot:
epoch_boundary_root = get_block_root(state, get_previous_epoch(state))
elif slot == current_epoch_start_slot:
epoch_boundary_root = block_root
else:
epoch_boundary_root = get_block_root(state, get_current_epoch(state))
if slot < current_epoch_start_slot:
justified_epoch = state.previous_justified_epoch
justified_block_root = state.previous_justified_root
else:
justified_epoch = state.current_justified_epoch
justified_block_root = state.current_justified_root
if slot_to_epoch(slot) == get_current_epoch(state):
parent_crosslink = state.current_crosslinks[shard]
else:
parent_crosslink = state.previous_crosslinks[shard]
return AttestationData(
beacon_block_root=block_root,
source_epoch=justified_epoch,
source_root=justified_block_root,
target_epoch=slot_to_epoch(slot),
target_root=epoch_boundary_root,
crosslink=Crosslink(
shard=shard,
start_epoch=parent_crosslink.end_epoch,
end_epoch=min(slot_to_epoch(slot), parent_crosslink.end_epoch + spec.MAX_EPOCHS_PER_CROSSLINK),
data_root=spec.ZERO_HASH,
parent_root=hash_tree_root(parent_crosslink),
),
)
def get_valid_attestation(state, slot=None, signed=False):
if slot is None:
slot = state.slot
epoch = slot_to_epoch(slot)
epoch_start_shard = get_epoch_start_shard(state, epoch)
committees_per_slot = get_epoch_committee_count(state, epoch) // spec.SLOTS_PER_EPOCH
shard = (epoch_start_shard + committees_per_slot * (slot % spec.SLOTS_PER_EPOCH)) % spec.SHARD_COUNT
attestation_data = build_attestation_data(state, slot, shard)
crosslink_committee = get_crosslink_committee(
state,
attestation_data.target_epoch,
attestation_data.crosslink.shard
)
committee_size = len(crosslink_committee)
bitfield_length = (committee_size + 7) // 8
aggregation_bitfield = b'\x00' * bitfield_length
custody_bitfield = b'\x00' * bitfield_length
attestation = Attestation(
aggregation_bitfield=aggregation_bitfield,
data=attestation_data,
custody_bitfield=custody_bitfield,
)
fill_aggregate_attestation(state, attestation)
if signed:
sign_attestation(state, attestation)
return attestation
def sign_aggregate_attestation(state: BeaconState, data: AttestationData, participants: List[int]):
signatures = []
for validator_index in participants:
privkey = privkeys[validator_index]
signatures.append(
get_attestation_signature(
state,
data,
privkey
)
)
return bls_aggregate_signatures(signatures)
def sign_indexed_attestation(state, indexed_attestation: IndexedAttestation):
participants = indexed_attestation.custody_bit_0_indices + indexed_attestation.custody_bit_1_indices
indexed_attestation.signature = sign_aggregate_attestation(state, indexed_attestation.data, participants)
def sign_attestation(state, attestation: Attestation):
participants = get_attesting_indices(
state,
attestation.data,
attestation.aggregation_bitfield,
)
attestation.signature = sign_aggregate_attestation(state, attestation.data, participants)
def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0):
message_hash = AttestationDataAndCustodyBit(
data=attestation_data,
custody_bit=custody_bit,
).hash_tree_root()
return bls_sign(
message_hash=message_hash,
privkey=privkey,
domain=get_domain(
state=state,
domain_type=spec.DOMAIN_ATTESTATION,
message_epoch=attestation_data.target_epoch,
)
)
def fill_aggregate_attestation(state, attestation):
crosslink_committee = get_crosslink_committee(
state,
attestation.data.target_epoch,
attestation.data.crosslink.shard,
)
for i in range(len(crosslink_committee)):
attestation.aggregation_bitfield = set_bitfield_bit(attestation.aggregation_bitfield, i)
def add_attestation_to_state(state, attestation, slot):
block = build_empty_block_for_next_slot(state)
block.slot = slot
block.body.attestations.append(attestation)
process_slots(state, block.slot)
sign_block(state, block)
state_transition(state, block)

View File

@ -0,0 +1,19 @@
from copy import deepcopy
from eth2spec.phase0.spec import AttesterSlashing, convert_to_indexed
from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation
def get_valid_attester_slashing(state, signed_1=False, signed_2=False):
attestation_1 = get_valid_attestation(state, signed=signed_1)
attestation_2 = deepcopy(attestation_1)
attestation_2.data.target_root = b'\x01' * 32
if signed_2:
sign_attestation(state, attestation_2)
return AttesterSlashing(
attestation_1=convert_to_indexed(state, attestation_1),
attestation_2=convert_to_indexed(state, attestation_2),
)

View File

@ -0,0 +1,11 @@
def set_bitfield_bit(bitfield, i):
"""
Set the bit in ``bitfield`` at position ``i`` to ``1``.
"""
byte_index = i // 8
bit_index = i % 8
return (
bitfield[:byte_index] +
bytes([bitfield[byte_index] | (1 << bit_index)]) +
bitfield[byte_index + 1:]
)

View File

@ -0,0 +1,80 @@
from copy import deepcopy
from eth2spec.phase0 import spec
from eth2spec.phase0.spec import (
BeaconBlock,
get_beacon_proposer_index, slot_to_epoch, get_domain,
process_slots, state_transition,
)
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils.bls import bls_sign, only_with_bls
from eth2spec.utils.minimal_ssz import signing_root, hash_tree_root
# Fully ignore the function if BLS is off, beacon-proposer index calculation is slow.
@only_with_bls()
def sign_block(state, block, proposer_index=None):
assert state.slot <= block.slot
if proposer_index is None:
if block.slot == state.slot:
proposer_index = get_beacon_proposer_index(state)
else:
if slot_to_epoch(state.slot) + 1 > slot_to_epoch(block.slot):
print("warning: block slot far away, and no proposer index manually given."
" Signing block is slow due to transition for proposer index calculation.")
# use stub state to get proposer index of future slot
stub_state = deepcopy(state)
process_slots(stub_state, block.slot)
proposer_index = get_beacon_proposer_index(stub_state)
privkey = privkeys[proposer_index]
block.body.randao_reveal = bls_sign(
privkey=privkey,
message_hash=hash_tree_root(slot_to_epoch(block.slot)),
domain=get_domain(
state,
message_epoch=slot_to_epoch(block.slot),
domain_type=spec.DOMAIN_RANDAO,
)
)
block.signature = bls_sign(
message_hash=signing_root(block),
privkey=privkey,
domain=get_domain(
state,
spec.DOMAIN_BEACON_PROPOSER,
slot_to_epoch(block.slot)))
def apply_empty_block(state):
"""
Transition via an empty block (on current slot, assuming no block has been applied yet).
:return: the empty block that triggered the transition.
"""
block = build_empty_block(state, signed=True)
state_transition(state, block)
return block
def build_empty_block(state, slot=None, signed=False):
if slot is None:
slot = state.slot
empty_block = BeaconBlock()
empty_block.slot = slot
empty_block.body.eth1_data.deposit_count = state.deposit_index
previous_block_header = deepcopy(state.latest_block_header)
if previous_block_header.state_root == spec.ZERO_HASH:
previous_block_header.state_root = state.hash_tree_root()
empty_block.parent_root = signing_root(previous_block_header)
if signed:
sign_block(state, empty_block)
return empty_block
def build_empty_block_for_next_slot(state, signed=False):
return build_empty_block(state, state.slot + 1, signed=signed)

View File

@ -0,0 +1,18 @@
# Access constants from spec pkg reference.
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import get_domain
from eth2spec.utils.bls import bls_sign
from eth2spec.utils.minimal_ssz import signing_root
def sign_block_header(state, header, privkey):
domain = get_domain(
state=state,
domain_type=spec.DOMAIN_BEACON_PROPOSER,
)
header.signature = bls_sign(
message_hash=signing_root(header),
privkey=privkey,
domain=domain,
)

View File

@ -0,0 +1,81 @@
# Access constants from spec pkg reference.
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import get_domain, DepositData, verify_merkle_branch, Deposit, ZERO_HASH
from eth2spec.test.helpers.keys import pubkeys, privkeys
from eth2spec.utils.bls import bls_sign
from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merkle_root, get_merkle_proof
from eth2spec.utils.minimal_ssz import signing_root
def build_deposit_data(state, pubkey, privkey, amount, signed=False):
deposit_data = DepositData(
pubkey=pubkey,
# insecurely use pubkey as withdrawal key as well
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(pubkey)[1:],
amount=amount,
)
if signed:
sign_deposit_data(state, deposit_data, privkey)
return deposit_data
def sign_deposit_data(state, deposit_data, privkey):
signature = bls_sign(
message_hash=signing_root(deposit_data),
privkey=privkey,
domain=get_domain(
state,
spec.DOMAIN_DEPOSIT,
)
)
deposit_data.signature = signature
def build_deposit(state,
deposit_data_leaves,
pubkey,
privkey,
amount,
signed):
deposit_data = build_deposit_data(state, pubkey, privkey, amount, signed)
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))
root = get_merkle_root((tuple(deposit_data_leaves)))
proof = list(get_merkle_proof(tree, item_index=index))
assert verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, index, root)
deposit = Deposit(
proof=list(proof),
index=index,
data=deposit_data,
)
return deposit, root, deposit_data_leaves
def prepare_state_and_deposit(state, validator_index, amount, signed=False):
"""
Prepare the state for the deposit, and create a deposit for the given validator, depositing the given amount.
"""
pre_validator_count = len(state.validator_registry)
# fill previous deposits with zero-hash
deposit_data_leaves = [ZERO_HASH] * pre_validator_count
pubkey = pubkeys[validator_index]
privkey = privkeys[validator_index]
deposit, root, deposit_data_leaves = build_deposit(
state,
deposit_data_leaves,
pubkey,
privkey,
amount,
signed
)
state.latest_eth1_data.deposit_root = root
state.latest_eth1_data.deposit_count = len(deposit_data_leaves)
return deposit

View File

@ -0,0 +1,51 @@
# Access constants from spec pkg reference.
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import Eth1Data, ZERO_HASH, get_active_validator_indices
from eth2spec.test.helpers.keys import pubkeys
from eth2spec.utils.minimal_ssz import hash_tree_root
def build_mock_validator(i: int, balance: int):
pubkey = pubkeys[i]
# insecurely use pubkey as withdrawal key as well
withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(pubkey)[1:]
return spec.Validator(
pubkey=pubkeys[i],
withdrawal_credentials=withdrawal_credentials,
activation_eligibility_epoch=spec.FAR_FUTURE_EPOCH,
activation_epoch=spec.FAR_FUTURE_EPOCH,
exit_epoch=spec.FAR_FUTURE_EPOCH,
withdrawable_epoch=spec.FAR_FUTURE_EPOCH,
effective_balance=min(balance - balance % spec.EFFECTIVE_BALANCE_INCREMENT, spec.MAX_EFFECTIVE_BALANCE)
)
def create_genesis_state(num_validators):
deposit_root = b'\x42' * 32
state = spec.BeaconState(
genesis_time=0,
deposit_index=num_validators,
latest_eth1_data=Eth1Data(
deposit_root=deposit_root,
deposit_count=num_validators,
block_hash=ZERO_HASH,
))
# We "hack" in the initial validators,
# as it is much faster than creating and processing genesis deposits for every single test case.
state.balances = [spec.MAX_EFFECTIVE_BALANCE] * num_validators
state.validator_registry = [build_mock_validator(i, state.balances[i]) for i in range(num_validators)]
# Process genesis activations
for validator in state.validator_registry:
if validator.effective_balance >= spec.MAX_EFFECTIVE_BALANCE:
validator.activation_eligibility_epoch = spec.GENESIS_EPOCH
validator.activation_epoch = spec.GENESIS_EPOCH
genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, spec.GENESIS_EPOCH))
for index in range(spec.LATEST_ACTIVE_INDEX_ROOTS_LENGTH):
state.latest_active_index_roots[index] = genesis_active_index_root
return state

View File

@ -0,0 +1,6 @@
from py_ecc import bls
from eth2spec.phase0 import spec
privkeys = [i + 1 for i in range(spec.SLOTS_PER_EPOCH * 16)]
pubkeys = [bls.privtopub(privkey) for privkey in privkeys]
pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)}

View File

@ -0,0 +1,35 @@
from copy import deepcopy
from eth2spec.phase0.spec import (
get_current_epoch, get_active_validator_indices, BeaconBlockHeader, ProposerSlashing
)
from eth2spec.test.helpers.block_header import sign_block_header
from eth2spec.test.helpers.keys import pubkey_to_privkey
def get_valid_proposer_slashing(state, signed_1=False, signed_2=False):
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[-1]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
slot = state.slot
header_1 = BeaconBlockHeader(
slot=slot,
parent_root=b'\x33' * 32,
state_root=b'\x44' * 32,
block_body_root=b'\x55' * 32,
)
header_2 = deepcopy(header_1)
header_2.parent_root = b'\x99' * 32
header_2.slot = slot + 1
if signed_1:
sign_block_header(state, header_1, privkey)
if signed_2:
sign_block_header(state, header_2, privkey)
return ProposerSlashing(
proposer_index=validator_index,
header_1=header_1,
header_2=header_2,
)

View File

@ -0,0 +1,31 @@
# Access constants from spec pkg reference.
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import process_slots
def get_balance(state, index):
return state.balances[index]
def next_slot(state):
"""
Transition to the next slot.
"""
process_slots(state, state.slot + 1)
def next_epoch(state):
"""
Transition to the start slot of the next epoch
"""
slot = state.slot + spec.SLOTS_PER_EPOCH - (state.slot % spec.SLOTS_PER_EPOCH)
process_slots(state, slot)
def get_state_root(state, slot) -> bytes:
"""
Return the state root at a recent ``slot``.
"""
assert slot < state.slot <= slot + spec.SLOTS_PER_HISTORICAL_ROOT
return state.latest_state_roots[slot % spec.SLOTS_PER_HISTORICAL_ROOT]

View File

@ -0,0 +1,55 @@
# Access constants from spec pkg reference.
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import get_current_epoch, get_active_validator_indices, Transfer, get_domain
from eth2spec.test.helpers.keys import pubkeys, privkeys
from eth2spec.test.helpers.state import get_balance
from eth2spec.utils.bls import bls_sign
from eth2spec.utils.minimal_ssz import signing_root
def get_valid_transfer(state, slot=None, sender_index=None, amount=None, fee=None, signed=False):
if slot is None:
slot = state.slot
current_epoch = get_current_epoch(state)
if sender_index is None:
sender_index = get_active_validator_indices(state, current_epoch)[-1]
recipient_index = get_active_validator_indices(state, current_epoch)[0]
transfer_pubkey = pubkeys[-1]
transfer_privkey = privkeys[-1]
if fee is None:
fee = get_balance(state, sender_index) // 32
if amount is None:
amount = get_balance(state, sender_index) - fee
transfer = Transfer(
sender=sender_index,
recipient=recipient_index,
amount=amount,
fee=fee,
slot=slot,
pubkey=transfer_pubkey,
)
if signed:
sign_transfer(state, transfer, transfer_privkey)
# ensure withdrawal_credentials reproducible
state.validator_registry[transfer.sender].withdrawal_credentials = (
spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(transfer.pubkey)[1:]
)
return transfer
def sign_transfer(state, transfer, privkey):
transfer.signature = bls_sign(
message_hash=signing_root(transfer),
privkey=privkey,
domain=get_domain(
state=state,
domain_type=spec.DOMAIN_TRANSFER,
message_epoch=get_current_epoch(state),
)
)
return transfer

View File

@ -0,0 +1,28 @@
# Access constants from spec pkg reference.
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import VoluntaryExit, get_domain
from eth2spec.utils.bls import bls_sign
from eth2spec.utils.minimal_ssz import signing_root
def build_voluntary_exit(state, epoch, validator_index, privkey, signed=False):
voluntary_exit = VoluntaryExit(
epoch=epoch,
validator_index=validator_index,
)
if signed:
sign_voluntary_exit(state, voluntary_exit, privkey)
return voluntary_exit
def sign_voluntary_exit(state, voluntary_exit, privkey):
voluntary_exit.signature = bls_sign(
message_hash=signing_root(voluntary_exit),
privkey=privkey,
domain=get_domain(
state=state,
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
message_epoch=voluntary_exit.epoch,
)
)

View File

@ -0,0 +1,406 @@
from copy import deepcopy
import eth2spec.phase0.spec as spec
from eth2spec.utils.bls import bls_sign
from eth2spec.utils.minimal_ssz import signing_root
from eth2spec.phase0.spec import (
# SSZ
VoluntaryExit,
# functions
get_active_validator_indices,
get_beacon_proposer_index,
get_block_root_at_slot,
get_current_epoch,
get_domain,
state_transition,
)
from eth2spec.test.helpers.state import get_balance
from eth2spec.test.helpers.transfers import get_valid_transfer
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
from eth2spec.test.helpers.keys import privkeys, pubkeys
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing
from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing
from eth2spec.test.helpers.attestations import get_valid_attestation
from eth2spec.test.helpers.deposits import prepare_state_and_deposit
from eth2spec.test.context import spec_state_test, never_bls
@never_bls
@spec_state_test
def test_empty_block_transition(state):
pre_slot = state.slot
pre_eth1_votes = len(state.eth1_data_votes)
yield 'pre', state
block = build_empty_block_for_next_slot(state, signed=True)
yield 'blocks', [block], [spec.BeaconBlock]
state_transition(state, block)
yield 'post', state
assert len(state.eth1_data_votes) == pre_eth1_votes + 1
assert get_block_root_at_slot(state, pre_slot) == block.parent_root
@never_bls
@spec_state_test
def test_skipped_slots(state):
pre_slot = state.slot
yield 'pre', state
block = build_empty_block_for_next_slot(state)
block.slot += 3
sign_block(state, block)
yield 'blocks', [block], [spec.BeaconBlock]
state_transition(state, block)
yield 'post', state
assert state.slot == block.slot
for slot in range(pre_slot, state.slot):
assert get_block_root_at_slot(state, slot) == block.parent_root
@spec_state_test
def test_empty_epoch_transition(state):
pre_slot = state.slot
yield 'pre', state
block = build_empty_block_for_next_slot(state)
block.slot += spec.SLOTS_PER_EPOCH
sign_block(state, block)
yield 'blocks', [block], [spec.BeaconBlock]
state_transition(state, block)
yield 'post', state
assert state.slot == block.slot
for slot in range(pre_slot, state.slot):
assert get_block_root_at_slot(state, slot) == block.parent_root
# @spec_state_test
# def test_empty_epoch_transition_not_finalizing(state):
# # copy for later balance lookups.
# pre_state = deepcopy(state)
# yield 'pre', state
#
# block = build_empty_block_for_next_slot(state)
# block.slot += spec.SLOTS_PER_EPOCH * 5
# sign_block(state, block, proposer_index=0)
# yield 'blocks', [block], [spec.BeaconBlock]
#
# state_transition(state, block)
# yield 'post', state
#
# assert state.slot == block.slot
# assert state.finalized_epoch < get_current_epoch(state) - 4
# for index in range(len(state.validator_registry)):
# assert get_balance(state, index) < get_balance(pre_state, index)
@spec_state_test
def test_proposer_slashing(state):
# copy for later balance lookups.
pre_state = deepcopy(state)
proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=True)
validator_index = proposer_slashing.proposer_index
assert not state.validator_registry[validator_index].slashed
yield 'pre', state
#
# Add to state via block transition
#
block = build_empty_block_for_next_slot(state)
block.body.proposer_slashings.append(proposer_slashing)
sign_block(state, block)
yield 'blocks', [block], [spec.BeaconBlock]
state_transition(state, block)
yield 'post', state
# check if slashed
slashed_validator = state.validator_registry[validator_index]
assert slashed_validator.slashed
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
# lost whistleblower reward
assert get_balance(state, validator_index) < get_balance(pre_state, validator_index)
@spec_state_test
def test_attester_slashing(state):
# copy for later balance lookups.
pre_state = deepcopy(state)
attester_slashing = get_valid_attester_slashing(state, signed_1=True, signed_2=True)
validator_index = (attester_slashing.attestation_1.custody_bit_0_indices +
attester_slashing.attestation_1.custody_bit_1_indices)[0]
assert not state.validator_registry[validator_index].slashed
yield 'pre', state
#
# Add to state via block transition
#
block = build_empty_block_for_next_slot(state)
block.body.attester_slashings.append(attester_slashing)
sign_block(state, block)
yield 'blocks', [block], [spec.BeaconBlock]
state_transition(state, block)
yield 'post', state
slashed_validator = state.validator_registry[validator_index]
assert slashed_validator.slashed
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
# lost whistleblower reward
assert get_balance(state, validator_index) < get_balance(pre_state, validator_index)
proposer_index = get_beacon_proposer_index(state)
# gained whistleblower reward
assert (
get_balance(state, proposer_index) >
get_balance(pre_state, proposer_index)
)
# TODO update functions below to be like above, i.e. with @spec_state_test and yielding data to put into the test vector
@spec_state_test
def test_deposit_in_block(state):
initial_registry_len = len(state.validator_registry)
initial_balances_len = len(state.balances)
validator_index = len(state.validator_registry)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(state, validator_index, amount, signed=True)
yield 'pre', state
block = build_empty_block_for_next_slot(state)
block.body.deposits.append(deposit)
sign_block(state, block)
yield 'blocks', [block], [spec.BeaconBlock]
state_transition(state, block)
yield 'post', state
assert len(state.validator_registry) == initial_registry_len + 1
assert len(state.balances) == initial_balances_len + 1
assert get_balance(state, validator_index) == spec.MAX_EFFECTIVE_BALANCE
assert state.validator_registry[validator_index].pubkey == pubkeys[validator_index]
@spec_state_test
def test_deposit_top_up(state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(state, validator_index, amount)
initial_registry_len = len(state.validator_registry)
initial_balances_len = len(state.balances)
validator_pre_balance = get_balance(state, validator_index)
yield 'pre', state
block = build_empty_block_for_next_slot(state)
block.body.deposits.append(deposit)
sign_block(state, block)
yield 'blocks', [block], [spec.BeaconBlock]
state_transition(state, block)
yield 'post', state
assert len(state.validator_registry) == initial_registry_len
assert len(state.balances) == initial_balances_len
assert get_balance(state, validator_index) == validator_pre_balance + amount
@spec_state_test
def test_attestation(state):
state.slot = spec.SLOTS_PER_EPOCH
yield 'pre', state
attestation = get_valid_attestation(state, signed=True)
# Add to state via block transition
pre_current_attestations_len = len(state.current_epoch_attestations)
attestation_block = build_empty_block_for_next_slot(state)
attestation_block.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation_block.body.attestations.append(attestation)
sign_block(state, attestation_block)
state_transition(state, attestation_block)
assert len(state.current_epoch_attestations) == pre_current_attestations_len + 1
# Epoch transition should move to previous_epoch_attestations
pre_current_attestations_root = spec.hash_tree_root(state.current_epoch_attestations)
epoch_block = build_empty_block_for_next_slot(state)
epoch_block.slot += spec.SLOTS_PER_EPOCH
sign_block(state, epoch_block)
state_transition(state, epoch_block)
yield 'blocks', [attestation_block, epoch_block], [spec.BeaconBlock]
yield 'post', state
assert len(state.current_epoch_attestations) == 0
assert spec.hash_tree_root(state.previous_epoch_attestations) == pre_current_attestations_root
@spec_state_test
def test_voluntary_exit(state):
validator_index = get_active_validator_indices(
state,
get_current_epoch(state)
)[-1]
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
yield 'pre', state
voluntary_exit = VoluntaryExit(
epoch=get_current_epoch(state),
validator_index=validator_index,
)
voluntary_exit.signature = bls_sign(
message_hash=signing_root(voluntary_exit),
privkey=privkeys[validator_index],
domain=get_domain(
state=state,
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
)
)
# Add to state via block transition
initiate_exit_block = build_empty_block_for_next_slot(state)
initiate_exit_block.body.voluntary_exits.append(voluntary_exit)
sign_block(state, initiate_exit_block)
state_transition(state, initiate_exit_block)
assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
# Process within epoch transition
exit_block = build_empty_block_for_next_slot(state)
exit_block.slot += spec.SLOTS_PER_EPOCH
sign_block(state, exit_block)
state_transition(state, exit_block)
yield 'blocks', [initiate_exit_block, exit_block], [spec.BeaconBlock]
yield 'post', state
assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
@spec_state_test
def test_transfer(state):
# overwrite default 0 to test
spec.MAX_TRANSFERS = 1
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
amount = get_balance(state, sender_index)
transfer = get_valid_transfer(state, state.slot + 1, sender_index, amount, signed=True)
recipient_index = transfer.recipient
pre_transfer_recipient_balance = get_balance(state, recipient_index)
# un-activate so validator can transfer
state.validator_registry[sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
yield 'pre', state
# Add to state via block transition
block = build_empty_block_for_next_slot(state)
block.body.transfers.append(transfer)
sign_block(state, block)
yield 'blocks', [block], [spec.BeaconBlock]
state_transition(state, block)
yield 'post', state
sender_balance = get_balance(state, sender_index)
recipient_balance = get_balance(state, recipient_index)
assert sender_balance == 0
assert recipient_balance == pre_transfer_recipient_balance + amount
@spec_state_test
def test_balance_driven_status_transitions(state):
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[-1]
assert state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
# set validator balance to below ejection threshold
state.validator_registry[validator_index].effective_balance = spec.EJECTION_BALANCE
yield 'pre', state
# trigger epoch transition
block = build_empty_block_for_next_slot(state)
block.slot += spec.SLOTS_PER_EPOCH
sign_block(state, block)
state_transition(state, block)
yield 'blocks', [block], [spec.BeaconBlock]
yield 'post', state
assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
@spec_state_test
def test_historical_batch(state):
state.slot += spec.SLOTS_PER_HISTORICAL_ROOT - (state.slot % spec.SLOTS_PER_HISTORICAL_ROOT) - 1
pre_historical_roots_len = len(state.historical_roots)
yield 'pre', state
block = build_empty_block_for_next_slot(state, signed=True)
state_transition(state, block)
yield 'blocks', [block], [spec.BeaconBlock]
yield 'post', state
assert state.slot == block.slot
assert get_current_epoch(state) % (spec.SLOTS_PER_HISTORICAL_ROOT // spec.SLOTS_PER_EPOCH) == 0
assert len(state.historical_roots) == pre_historical_roots_len + 1
# @spec_state_test
# def test_eth1_data_votes(state):
# yield 'pre', state
#
# expected_votes = 0
# assert len(state.eth1_data_votes) == expected_votes
#
# blocks = []
# for _ in range(spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1):
# block = build_empty_block_for_next_slot(state)
# state_transition(state, block)
# expected_votes += 1
# assert len(state.eth1_data_votes) == expected_votes
# blocks.append(block)
#
# block = build_empty_block_for_next_slot(state)
# blocks.append(block)
#
# state_transition(state, block)
#
# yield 'blocks', [block], [spec.BeaconBlock]
# yield 'post', state
#
# assert state.slot % spec.SLOTS_PER_ETH1_VOTING_PERIOD == 0
# assert len(state.eth1_data_votes) == 1

View File

@ -0,0 +1,58 @@
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import process_slots
from eth2spec.test.helpers.state import get_state_root
from eth2spec.test.context import spec_state_test
@spec_state_test
def test_slots_1(state):
pre_slot = state.slot
pre_root = state.hash_tree_root()
yield 'pre', state
slots = 1
yield 'slots', slots
process_slots(state, state.slot + slots)
yield 'post', state
assert state.slot == pre_slot + 1
assert get_state_root(state, pre_slot) == pre_root
@spec_state_test
def test_slots_2(state):
yield 'pre', state
slots = 2
yield 'slots', slots
process_slots(state, state.slot + slots)
yield 'post', state
@spec_state_test
def test_empty_epoch(state):
yield 'pre', state
slots = spec.SLOTS_PER_EPOCH
yield 'slots', slots
process_slots(state, state.slot + slots)
yield 'post', state
@spec_state_test
def test_double_empty_epoch(state):
yield 'pre', state
slots = spec.SLOTS_PER_EPOCH * 2
yield 'slots', slots
process_slots(state, state.slot + slots)
yield 'post', state
@spec_state_test
def test_over_epoch_boundary(state):
process_slots(state, state.slot + (spec.SLOTS_PER_EPOCH // 2))
yield 'pre', state
slots = spec.SLOTS_PER_EPOCH
yield 'slots', slots
process_slots(state, state.slot + slots)
yield 'post', state

View File

@ -1,20 +1,14 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from .helpers import (
build_empty_block_for_next_slot,
fill_aggregate_attestation,
from eth2spec.phase0.spec import (
get_current_epoch,
get_epoch_start_slot,
get_valid_attestation,
next_epoch,
)
# mark entire file as 'state'
pytestmark = pytest.mark.state
from .context import spec_state_test, never_bls
from .helpers.state import next_epoch
from .helpers.block import build_empty_block_for_next_slot, apply_empty_block
from .helpers.attestations import get_valid_attestation
def check_finality(state,
@ -55,13 +49,11 @@ def next_epoch_with_attestations(state,
slot_to_attest = post_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1
if slot_to_attest >= get_epoch_start_slot(get_current_epoch(post_state)):
cur_attestation = get_valid_attestation(post_state, slot_to_attest)
fill_aggregate_attestation(post_state, cur_attestation)
block.body.attestations.append(cur_attestation)
if fill_prev_epoch:
slot_to_attest = post_state.slot - spec.SLOTS_PER_EPOCH + 1
prev_attestation = get_valid_attestation(post_state, slot_to_attest)
fill_aggregate_attestation(post_state, prev_attestation)
block.body.attestations.append(prev_attestation)
spec.state_transition(post_state, block)
@ -70,126 +62,140 @@ def next_epoch_with_attestations(state,
return state, blocks, post_state
@never_bls
@spec_state_test
def test_finality_rule_4(state):
test_state = deepcopy(state)
yield 'pre', state
blocks = []
for epoch in range(4):
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
prev_state, new_blocks, state = next_epoch_with_attestations(state, True, False)
blocks += new_blocks
# justification/finalization skipped at GENESIS_EPOCH
if epoch == 0:
check_finality(test_state, prev_state, False, False, False)
check_finality(state, prev_state, False, False, False)
# justification/finalization skipped at GENESIS_EPOCH + 1
elif epoch == 1:
check_finality(test_state, prev_state, False, False, False)
check_finality(state, prev_state, False, False, False)
elif epoch == 2:
check_finality(test_state, prev_state, True, False, False)
check_finality(state, prev_state, True, False, False)
elif epoch >= 3:
# rule 4 of finality
check_finality(test_state, prev_state, True, True, True)
assert test_state.finalized_epoch == prev_state.current_justified_epoch
assert test_state.finalized_root == prev_state.current_justified_root
check_finality(state, prev_state, True, True, True)
assert state.finalized_epoch == prev_state.current_justified_epoch
assert state.finalized_root == prev_state.current_justified_root
return state, blocks, test_state
yield 'blocks', blocks, [spec.BeaconBlock]
yield 'post', state
@never_bls
@spec_state_test
def test_finality_rule_1(state):
# get past first two epochs that finality does not run on
next_epoch(state)
apply_empty_block(state)
next_epoch(state)
apply_empty_block(state)
pre_state = deepcopy(state)
test_state = deepcopy(state)
yield 'pre', state
blocks = []
for epoch in range(3):
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, True)
prev_state, new_blocks, state = next_epoch_with_attestations(state, False, True)
blocks += new_blocks
if epoch == 0:
check_finality(test_state, prev_state, True, False, False)
check_finality(state, prev_state, True, False, False)
elif epoch == 1:
check_finality(test_state, prev_state, True, True, False)
check_finality(state, prev_state, True, True, False)
elif epoch == 2:
# finalized by rule 1
check_finality(test_state, prev_state, True, True, True)
assert test_state.finalized_epoch == prev_state.previous_justified_epoch
assert test_state.finalized_root == prev_state.previous_justified_root
check_finality(state, prev_state, True, True, True)
assert state.finalized_epoch == prev_state.previous_justified_epoch
assert state.finalized_root == prev_state.previous_justified_root
return pre_state, blocks, test_state
yield 'blocks', blocks, [spec.BeaconBlock]
yield 'post', state
@never_bls
@spec_state_test
def test_finality_rule_2(state):
# get past first two epochs that finality does not run on
next_epoch(state)
apply_empty_block(state)
next_epoch(state)
apply_empty_block(state)
pre_state = deepcopy(state)
test_state = deepcopy(state)
yield 'pre', state
blocks = []
for epoch in range(3):
if epoch == 0:
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
check_finality(test_state, prev_state, True, False, False)
prev_state, new_blocks, state = next_epoch_with_attestations(state, True, False)
check_finality(state, prev_state, True, False, False)
elif epoch == 1:
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, False)
check_finality(test_state, prev_state, False, True, False)
prev_state, new_blocks, state = next_epoch_with_attestations(state, False, False)
check_finality(state, prev_state, False, True, False)
elif epoch == 2:
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, True)
prev_state, new_blocks, state = next_epoch_with_attestations(state, False, True)
# finalized by rule 2
check_finality(test_state, prev_state, True, False, True)
assert test_state.finalized_epoch == prev_state.previous_justified_epoch
assert test_state.finalized_root == prev_state.previous_justified_root
check_finality(state, prev_state, True, False, True)
assert state.finalized_epoch == prev_state.previous_justified_epoch
assert state.finalized_root == prev_state.previous_justified_root
blocks += new_blocks
return pre_state, blocks, test_state
yield 'blocks', blocks, [spec.BeaconBlock]
yield 'post', state
@never_bls
@spec_state_test
def test_finality_rule_3(state):
"""
Test scenario described here
https://github.com/ethereum/eth2.0-specs/issues/611#issuecomment-463612892
"""
# get past first two epochs that finality does not run on
next_epoch(state)
apply_empty_block(state)
next_epoch(state)
apply_empty_block(state)
pre_state = deepcopy(state)
test_state = deepcopy(state)
yield 'pre', state
blocks = []
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
prev_state, new_blocks, state = next_epoch_with_attestations(state, True, False)
blocks += new_blocks
check_finality(test_state, prev_state, True, False, False)
check_finality(state, prev_state, True, False, False)
# In epoch N, JE is set to N, prev JE is set to N-1
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
prev_state, new_blocks, state = next_epoch_with_attestations(state, True, False)
blocks += new_blocks
check_finality(test_state, prev_state, True, True, True)
check_finality(state, prev_state, True, True, True)
# In epoch N+1, JE is N, prev JE is N-1, and not enough messages get in to do anything
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, False)
prev_state, new_blocks, state = next_epoch_with_attestations(state, False, False)
blocks += new_blocks
check_finality(test_state, prev_state, False, True, False)
check_finality(state, prev_state, False, True, False)
# In epoch N+2, JE is N, prev JE is N, and enough messages from the previous epoch get in to justify N+1.
# N+1 now becomes the JE. Not enough messages from epoch N+2 itself get in to justify N+2
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, True)
prev_state, new_blocks, state = next_epoch_with_attestations(state, False, True)
blocks += new_blocks
# rule 2
check_finality(test_state, prev_state, True, False, True)
check_finality(state, prev_state, True, False, True)
# In epoch N+3, LJE is N+1, prev LJE is N, and enough messages get in to justify epochs N+2 and N+3.
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, True)
prev_state, new_blocks, state = next_epoch_with_attestations(state, True, True)
blocks += new_blocks
# rule 3
check_finality(test_state, prev_state, True, True, True)
assert test_state.finalized_epoch == prev_state.current_justified_epoch
assert test_state.finalized_root == prev_state.current_justified_root
check_finality(state, prev_state, True, True, True)
assert state.finalized_epoch == prev_state.current_justified_epoch
assert state.finalized_root == prev_state.current_justified_root
return pre_state, blocks, test_state
yield 'blocks', blocks, [spec.BeaconBlock]
yield 'post', state

View File

@ -0,0 +1,80 @@
from typing import Dict, Any, Callable, Iterable
from eth2spec.debug.encode import encode
def spectest(description: str = None):
def runner(fn):
# this wraps the function, to hide that the function actually is yielding data, instead of returning once.
def entry(*args, **kw):
# check generator mode, may be None/else.
# "pop" removes it, so it is not passed to the inner function.
if kw.pop('generator_mode', False) is True:
out = {}
if description is None:
# fall back on function name for test description
name = fn.__name__
if name.startswith('test_'):
name = name[5:]
out['description'] = name
else:
# description can be explicit
out['description'] = description
has_contents = False
# put all generated data into a dict.
for data in fn(*args, **kw):
has_contents = True
# If there is a type argument, encode it as that type.
if len(data) == 3:
(key, value, typ) = data
out[key] = encode(value, typ)
else:
# Otherwise, try to infer the type, but keep it as-is if it's not a SSZ container.
(key, value) = data
if hasattr(value.__class__, 'fields'):
out[key] = encode(value, value.__class__)
else:
out[key] = value
if has_contents:
return out
else:
return None
else:
# just complete the function, ignore all yielded data, we are not using it
for _ in fn(*args, **kw):
continue
return None
return entry
return runner
def with_tags(tags: Dict[str, Any]):
"""
Decorator factory, adds tags (key, value) pairs to the output of the function.
Useful to build test-vector annotations with.
This decorator is applied after the ``spectest`` decorator is applied.
:param tags: dict of tags
:return: Decorator.
"""
def runner(fn):
def entry(*args, **kw):
fn_out = fn(*args, **kw)
# do not add tags if the function is not returning a dict at all (i.e. not in generator mode)
if fn_out is None:
return None
return {**tags, **fn_out}
return entry
return runner
def with_args(create_args: Callable[[], Iterable[Any]]):
"""
Decorator factory, adds given extra arguments to the decorated function.
:param create_args: function to create arguments with.
:return: Decorator.
"""
def runner(fn):
# this wraps the function, to hide that the function actually yielding data.
def entry(*args, **kw):
return fn(*(list(create_args()) + list(args)), **kw)
return entry
return runner

View File

@ -0,0 +1,46 @@
from py_ecc import bls
# Flag to make BLS active or not. Used for testing, do not ignore BLS in production unless you know what you are doing.
bls_active = True
STUB_SIGNATURE = b'\x11' * 96
STUB_PUBKEY = b'\x22' * 48
def only_with_bls(alt_return=None):
"""
Decorator factory to make a function only run when BLS is active. Otherwise return the default.
"""
def runner(fn):
def entry(*args, **kw):
if bls_active:
return fn(*args, **kw)
else:
return alt_return
return entry
return runner
@only_with_bls(alt_return=True)
def bls_verify(pubkey, message_hash, signature, domain):
return bls.verify(message_hash=message_hash, pubkey=pubkey, signature=signature, domain=domain)
@only_with_bls(alt_return=True)
def bls_verify_multiple(pubkeys, message_hashes, signature, domain):
return bls.verify_multiple(pubkeys, message_hashes, signature, domain)
@only_with_bls(alt_return=STUB_PUBKEY)
def bls_aggregate_pubkeys(pubkeys):
return bls.aggregate_pubkeys(pubkeys)
@only_with_bls(alt_return=STUB_SIGNATURE)
def bls_aggregate_signatures(signatures):
return bls.aggregate_signatures(signatures)
@only_with_bls(alt_return=STUB_SIGNATURE)
def bls_sign(message_hash, privkey, domain):
return bls.sign(message_hash=message_hash, privkey=privkey, domain=domain)

View File

@ -1,12 +0,0 @@
def bls_verify(pubkey, message_hash, signature, domain):
return True
def bls_verify_multiple(pubkeys, message_hashes, signature, domain):
return True
def bls_aggregate_pubkeys(pubkeys):
return b'\x42' * 48

View File

@ -1,6 +1,4 @@
from hashlib import sha256
# from eth_utils import keccak
def hash(x): return sha256(x).digest()
# def hash(x): return keccak(x)

View File

@ -1,195 +0,0 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_current_epoch,
process_attestation,
slot_to_epoch,
state_transition,
)
from tests.helpers import (
build_empty_block_for_next_slot,
get_valid_attestation,
next_epoch,
next_slot,
)
# mark entire file as 'attestations'
pytestmark = pytest.mark.attestations
def run_attestation_processing(state, attestation, valid=True):
"""
Run ``process_attestation`` returning the pre and post state.
If ``valid == False``, run expecting ``AssertionError``
"""
post_state = deepcopy(state)
if not valid:
with pytest.raises(AssertionError):
process_attestation(post_state, attestation)
return state, None
process_attestation(post_state, attestation)
current_epoch = get_current_epoch(state)
if attestation.data.target_epoch == current_epoch:
assert len(post_state.current_epoch_attestations) == len(state.current_epoch_attestations) + 1
else:
assert len(post_state.previous_epoch_attestations) == len(state.previous_epoch_attestations) + 1
return state, post_state
def test_success(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
pre_state, post_state = run_attestation_processing(state, attestation)
return pre_state, attestation, post_state
def test_success_prevous_epoch(state):
attestation = get_valid_attestation(state)
block = build_empty_block_for_next_slot(state)
block.slot = state.slot + spec.SLOTS_PER_EPOCH
state_transition(state, block)
pre_state, post_state = run_attestation_processing(state, attestation)
return pre_state, attestation, post_state
def test_success_since_max_epochs_per_crosslink(state):
for _ in range(spec.MAX_EPOCHS_PER_CROSSLINK + 2):
next_epoch(state)
attestation = get_valid_attestation(state)
data = attestation.data
assert data.crosslink.end_epoch - data.crosslink.start_epoch == spec.MAX_EPOCHS_PER_CROSSLINK
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
pre_state, post_state = run_attestation_processing(state, attestation)
return pre_state, attestation, post_state
def test_before_inclusion_delay(state):
attestation = get_valid_attestation(state)
# do not increment slot to allow for inclusion delay
pre_state, post_state = run_attestation_processing(state, attestation, False)
return pre_state, attestation, post_state
def test_after_epoch_slots(state):
attestation = get_valid_attestation(state)
block = build_empty_block_for_next_slot(state)
# increment past latest inclusion slot
block.slot = state.slot + spec.SLOTS_PER_EPOCH + 1
state_transition(state, block)
pre_state, post_state = run_attestation_processing(state, attestation, False)
return pre_state, attestation, post_state
def test_bad_source_epoch(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.data.source_epoch += 10
pre_state, post_state = run_attestation_processing(state, attestation, False)
return pre_state, attestation, post_state
def test_bad_source_root(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.data.source_root = b'\x42' * 32
pre_state, post_state = run_attestation_processing(state, attestation, False)
return pre_state, attestation, post_state
def test_non_zero_crosslink_data_root(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.data.crosslink.data_root = b'\x42' * 32
pre_state, post_state = run_attestation_processing(state, attestation, False)
return pre_state, attestation, post_state
def test_bad_previous_crosslink(state):
next_epoch(state)
attestation = get_valid_attestation(state)
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
attestation.data.crosslink.parent_root = b'\x27' * 32
pre_state, post_state = run_attestation_processing(state, attestation, False)
return pre_state, attestation, post_state
def test_bad_crosslink_start_epoch(state):
next_epoch(state)
attestation = get_valid_attestation(state)
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
attestation.data.crosslink.start_epoch += 1
pre_state, post_state = run_attestation_processing(state, attestation, False)
return pre_state, attestation, post_state
def test_bad_crosslink_end_epoch(state):
next_epoch(state)
attestation = get_valid_attestation(state)
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
attestation.data.crosslink.end_epoch += 1
pre_state, post_state = run_attestation_processing(state, attestation, False)
return pre_state, attestation, post_state
def test_non_empty_custody_bitfield(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.custody_bitfield = deepcopy(attestation.aggregation_bitfield)
pre_state, post_state = run_attestation_processing(state, attestation, False)
return pre_state, attestation, post_state
def test_empty_aggregation_bitfield(state):
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation.aggregation_bitfield = b'\x00' * len(attestation.aggregation_bitfield)
pre_state, post_state = run_attestation_processing(state, attestation)
return pre_state, attestation, post_state

View File

@ -1,117 +0,0 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_beacon_proposer_index,
process_attester_slashing,
)
from tests.helpers import (
get_balance,
get_valid_attester_slashing,
next_epoch,
)
# mark entire file as 'attester_slashing'
pytestmark = pytest.mark.attester_slashings
def run_attester_slashing_processing(state, attester_slashing, valid=True):
"""
Run ``process_attester_slashing`` returning the pre and post state.
If ``valid == False``, run expecting ``AssertionError``
"""
post_state = deepcopy(state)
if not valid:
with pytest.raises(AssertionError):
process_attester_slashing(post_state, attester_slashing)
return state, None
process_attester_slashing(post_state, attester_slashing)
slashed_index = attester_slashing.attestation_1.custody_bit_0_indices[0]
slashed_validator = post_state.validator_registry[slashed_index]
assert slashed_validator.slashed
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
# lost whistleblower reward
assert (
get_balance(post_state, slashed_index) <
get_balance(state, slashed_index)
)
proposer_index = get_beacon_proposer_index(state)
# gained whistleblower reward
assert (
get_balance(post_state, proposer_index) >
get_balance(state, proposer_index)
)
return state, post_state
def test_success_double(state):
attester_slashing = get_valid_attester_slashing(state)
pre_state, post_state = run_attester_slashing_processing(state, attester_slashing)
return pre_state, attester_slashing, post_state
def test_success_surround(state):
next_epoch(state)
state.current_justified_epoch += 1
attester_slashing = get_valid_attester_slashing(state)
# set attestion1 to surround attestation 2
attester_slashing.attestation_1.data.source_epoch = attester_slashing.attestation_2.data.source_epoch - 1
attester_slashing.attestation_1.data.target_epoch = attester_slashing.attestation_2.data.target_epoch + 1
pre_state, post_state = run_attester_slashing_processing(state, attester_slashing)
return pre_state, attester_slashing, post_state
def test_same_data(state):
attester_slashing = get_valid_attester_slashing(state)
attester_slashing.attestation_1.data = attester_slashing.attestation_2.data
pre_state, post_state = run_attester_slashing_processing(state, attester_slashing, False)
return pre_state, attester_slashing, post_state
def test_no_double_or_surround(state):
attester_slashing = get_valid_attester_slashing(state)
attester_slashing.attestation_1.data.target_epoch += 1
pre_state, post_state = run_attester_slashing_processing(state, attester_slashing, False)
return pre_state, attester_slashing, post_state
def test_participants_already_slashed(state):
attester_slashing = get_valid_attester_slashing(state)
# set all indices to slashed
attestation_1 = attester_slashing.attestation_1
validator_indices = attestation_1.custody_bit_0_indices + attestation_1.custody_bit_1_indices
for index in validator_indices:
state.validator_registry[index].slashed = True
pre_state, post_state = run_attester_slashing_processing(state, attester_slashing, False)
return pre_state, attester_slashing, post_state
def test_custody_bit_0_and_1(state):
attester_slashing = get_valid_attester_slashing(state)
attester_slashing.attestation_1.custody_bit_1_indices = (
attester_slashing.attestation_1.custody_bit_0_indices
)
pre_state, post_state = run_attester_slashing_processing(state, attester_slashing, False)
return pre_state, attester_slashing, post_state

View File

@ -1,76 +0,0 @@
from copy import deepcopy
import pytest
from eth2spec.phase0.spec import (
get_beacon_proposer_index,
process_slot,
process_block_header,
)
from tests.helpers import (
advance_slot,
build_empty_block_for_next_slot,
next_slot,
)
# mark entire file as 'header'
pytestmark = pytest.mark.header
def prepare_state_for_header_processing(state):
process_slot(state)
advance_slot(state)
def run_block_header_processing(state, block, valid=True):
"""
Run ``process_block_header`` returning the pre and post state.
If ``valid == False``, run expecting ``AssertionError``
"""
prepare_state_for_header_processing(state)
post_state = deepcopy(state)
if not valid:
with pytest.raises(AssertionError):
process_block_header(post_state, block)
return state, None
process_block_header(post_state, block)
return state, post_state
def test_success(state):
block = build_empty_block_for_next_slot(state)
pre_state, post_state = run_block_header_processing(state, block)
return state, block, post_state
def test_invalid_slot(state):
block = build_empty_block_for_next_slot(state)
block.slot = state.slot + 2 # invalid slot
pre_state, post_state = run_block_header_processing(state, block, valid=False)
return pre_state, block, None
def test_invalid_parent_block_root(state):
block = build_empty_block_for_next_slot(state)
block.parent_root = b'\12' * 32 # invalid prev root
pre_state, post_state = run_block_header_processing(state, block, valid=False)
return pre_state, block, None
def test_proposer_slashed(state):
# use stub state to get proposer index of next slot
stub_state = deepcopy(state)
next_slot(stub_state)
proposer_index = get_beacon_proposer_index(stub_state)
# set proposer to slashed
state.validator_registry[proposer_index].slashed = True
block = build_empty_block_for_next_slot(state)
pre_state, post_state = run_block_header_processing(state, block, valid=False)
return pre_state, block, None

View File

@ -1,141 +0,0 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
ZERO_HASH,
process_deposit,
)
from tests.helpers import (
get_balance,
build_deposit,
privkeys,
pubkeys,
)
# mark entire file as 'deposits'
pytestmark = pytest.mark.deposits
def test_success(state):
pre_state = deepcopy(state)
# fill previous deposits with zero-hash
deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
index = len(deposit_data_leaves)
pubkey = pubkeys[index]
privkey = privkeys[index]
deposit, root, deposit_data_leaves = build_deposit(
pre_state,
deposit_data_leaves,
pubkey,
privkey,
spec.MAX_EFFECTIVE_BALANCE,
)
pre_state.latest_eth1_data.deposit_root = root
pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves)
post_state = deepcopy(pre_state)
process_deposit(post_state, deposit)
assert len(post_state.validator_registry) == len(state.validator_registry) + 1
assert len(post_state.balances) == len(state.balances) + 1
assert post_state.validator_registry[index].pubkey == pubkeys[index]
assert get_balance(post_state, index) == spec.MAX_EFFECTIVE_BALANCE
assert post_state.deposit_index == post_state.latest_eth1_data.deposit_count
return pre_state, deposit, post_state
def test_success_top_up(state):
pre_state = deepcopy(state)
deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
pubkey = pubkeys[validator_index]
privkey = privkeys[validator_index]
deposit, root, deposit_data_leaves = build_deposit(
pre_state,
deposit_data_leaves,
pubkey,
privkey,
amount,
)
pre_state.latest_eth1_data.deposit_root = root
pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves)
pre_balance = get_balance(pre_state, validator_index)
post_state = deepcopy(pre_state)
process_deposit(post_state, deposit)
assert len(post_state.validator_registry) == len(state.validator_registry)
assert len(post_state.balances) == len(state.balances)
assert post_state.deposit_index == post_state.latest_eth1_data.deposit_count
assert get_balance(post_state, validator_index) == pre_balance + amount
return pre_state, deposit, post_state
def test_wrong_index(state):
pre_state = deepcopy(state)
deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
index = len(deposit_data_leaves)
pubkey = pubkeys[index]
privkey = privkeys[index]
deposit, root, deposit_data_leaves = build_deposit(
pre_state,
deposit_data_leaves,
pubkey,
privkey,
spec.MAX_EFFECTIVE_BALANCE,
)
# mess up deposit_index
deposit.index = pre_state.deposit_index + 1
pre_state.latest_eth1_data.deposit_root = root
pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves)
post_state = deepcopy(pre_state)
with pytest.raises(AssertionError):
process_deposit(post_state, deposit)
return pre_state, deposit, None
def test_bad_merkle_proof(state):
pre_state = deepcopy(state)
deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
index = len(deposit_data_leaves)
pubkey = pubkeys[index]
privkey = privkeys[index]
deposit, root, deposit_data_leaves = build_deposit(
pre_state,
deposit_data_leaves,
pubkey,
privkey,
spec.MAX_EFFECTIVE_BALANCE,
)
# mess up merkle branch
deposit.proof[-1] = spec.ZERO_HASH
pre_state.latest_eth1_data.deposit_root = root
pre_state.latest_eth1_data.deposit_count = len(deposit_data_leaves)
post_state = deepcopy(pre_state)
with pytest.raises(AssertionError):
process_deposit(post_state, deposit)
return pre_state, deposit, None

View File

@ -1,96 +0,0 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_current_epoch,
process_proposer_slashing,
)
from tests.helpers import (
get_balance,
get_valid_proposer_slashing,
)
# mark entire file as 'proposer_slashings'
pytestmark = pytest.mark.proposer_slashings
def run_proposer_slashing_processing(state, proposer_slashing, valid=True):
"""
Run ``process_proposer_slashing`` returning the pre and post state.
If ``valid == False``, run expecting ``AssertionError``
"""
post_state = deepcopy(state)
if not valid:
with pytest.raises(AssertionError):
process_proposer_slashing(post_state, proposer_slashing)
return state, None
process_proposer_slashing(post_state, proposer_slashing)
slashed_validator = post_state.validator_registry[proposer_slashing.proposer_index]
assert slashed_validator.slashed
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
# lost whistleblower reward
assert (
get_balance(post_state, proposer_slashing.proposer_index) <
get_balance(state, proposer_slashing.proposer_index)
)
return state, post_state
def test_success(state):
proposer_slashing = get_valid_proposer_slashing(state)
pre_state, post_state = run_proposer_slashing_processing(state, proposer_slashing)
return pre_state, proposer_slashing, post_state
def test_epochs_are_different(state):
proposer_slashing = get_valid_proposer_slashing(state)
# set slots to be in different epochs
proposer_slashing.header_2.slot += spec.SLOTS_PER_EPOCH
pre_state, post_state = run_proposer_slashing_processing(state, proposer_slashing, False)
return pre_state, proposer_slashing, post_state
def test_headers_are_same(state):
proposer_slashing = get_valid_proposer_slashing(state)
# set headers to be the same
proposer_slashing.header_2 = proposer_slashing.header_1
pre_state, post_state = run_proposer_slashing_processing(state, proposer_slashing, False)
return pre_state, proposer_slashing, post_state
def test_proposer_is_slashed(state):
proposer_slashing = get_valid_proposer_slashing(state)
# set proposer to slashed
state.validator_registry[proposer_slashing.proposer_index].slashed = True
pre_state, post_state = run_proposer_slashing_processing(state, proposer_slashing, False)
return pre_state, proposer_slashing, post_state
def test_proposer_is_withdrawn(state):
proposer_slashing = get_valid_proposer_slashing(state)
# set proposer withdrawable_epoch in past
current_epoch = get_current_epoch(state)
proposer_index = proposer_slashing.proposer_index
state.validator_registry[proposer_index].withdrawable_epoch = current_epoch - 1
pre_state, post_state = run_proposer_slashing_processing(state, proposer_slashing, False)
return pre_state, proposer_slashing, post_state

View File

@ -1,141 +0,0 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_active_validator_indices,
get_beacon_proposer_index,
get_current_epoch,
process_transfer,
)
from tests.helpers import (
get_valid_transfer,
next_epoch,
)
# mark entire file as 'transfers'
pytestmark = pytest.mark.transfers
def run_transfer_processing(state, transfer, valid=True):
"""
Run ``process_transfer`` returning the pre and post state.
If ``valid == False``, run expecting ``AssertionError``
"""
post_state = deepcopy(state)
if not valid:
with pytest.raises(AssertionError):
process_transfer(post_state, transfer)
return state, None
process_transfer(post_state, transfer)
proposer_index = get_beacon_proposer_index(state)
pre_transfer_sender_balance = state.balances[transfer.sender]
pre_transfer_recipient_balance = state.balances[transfer.recipient]
pre_transfer_proposer_balance = state.balances[proposer_index]
sender_balance = post_state.balances[transfer.sender]
recipient_balance = post_state.balances[transfer.recipient]
assert sender_balance == pre_transfer_sender_balance - transfer.amount - transfer.fee
assert recipient_balance == pre_transfer_recipient_balance + transfer.amount
assert post_state.balances[proposer_index] == pre_transfer_proposer_balance + transfer.fee
return state, post_state
def test_success_non_activated(state):
transfer = get_valid_transfer(state)
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
pre_state, post_state = run_transfer_processing(state, transfer)
return pre_state, transfer, post_state
def test_success_withdrawable(state):
next_epoch(state)
transfer = get_valid_transfer(state)
# withdrawable_epoch in past so can transfer
state.validator_registry[transfer.sender].withdrawable_epoch = get_current_epoch(state) - 1
pre_state, post_state = run_transfer_processing(state, transfer)
return pre_state, transfer, post_state
def test_success_active_above_max_effective(state):
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
amount = spec.MAX_EFFECTIVE_BALANCE // 32
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + amount
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0)
pre_state, post_state = run_transfer_processing(state, transfer)
return pre_state, transfer, post_state
def test_active_but_transfer_past_effective_balance(state):
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
amount = spec.MAX_EFFECTIVE_BALANCE // 32
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0)
pre_state, post_state = run_transfer_processing(state, transfer, False)
return pre_state, transfer, post_state
def test_incorrect_slot(state):
transfer = get_valid_transfer(state, slot=state.slot+1)
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
pre_state, post_state = run_transfer_processing(state, transfer, False)
return pre_state, transfer, post_state
def test_insufficient_balance(state):
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
amount = spec.MAX_EFFECTIVE_BALANCE
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE
transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount + 1, fee=0)
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
pre_state, post_state = run_transfer_processing(state, transfer, False)
return pre_state, transfer, post_state
def test_no_dust(state):
sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1]
balance = state.balances[sender_index]
transfer = get_valid_transfer(state, sender_index=sender_index, amount=balance - spec.MIN_DEPOSIT_AMOUNT + 1, fee=0)
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
pre_state, post_state = run_transfer_processing(state, transfer, False)
return pre_state, transfer, post_state
def test_invalid_pubkey(state):
transfer = get_valid_transfer(state)
state.validator_registry[transfer.sender].withdrawal_credentials = spec.ZERO_HASH
# un-activate so validator can transfer
state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH
pre_state, post_state = run_transfer_processing(state, transfer, False)
return pre_state, transfer, post_state

View File

@ -1,163 +0,0 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_active_validator_indices,
get_churn_limit,
get_current_epoch,
process_voluntary_exit,
)
from tests.helpers import (
build_voluntary_exit,
pubkey_to_privkey,
)
# mark entire file as 'voluntary_exits'
pytestmark = pytest.mark.voluntary_exits
def run_voluntary_exit_processing(state, voluntary_exit, valid=True):
"""
Run ``process_voluntary_exit`` returning the pre and post state.
If ``valid == False``, run expecting ``AssertionError``
"""
post_state = deepcopy(state)
if not valid:
with pytest.raises(AssertionError):
process_voluntary_exit(post_state, voluntary_exit)
return state, None
process_voluntary_exit(post_state, voluntary_exit)
validator_index = voluntary_exit.validator_index
assert state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
return state, post_state
def test_success(state):
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
validator_index,
privkey,
)
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit)
return pre_state, voluntary_exit, post_state
def test_success_exit_queue(state):
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = get_current_epoch(state)
# exit `MAX_EXITS_PER_EPOCH`
initial_indices = get_active_validator_indices(state, current_epoch)[:get_churn_limit(state)]
post_state = state
for index in initial_indices:
privkey = pubkey_to_privkey[state.validator_registry[index].pubkey]
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
index,
privkey,
)
pre_state, post_state = run_voluntary_exit_processing(post_state, voluntary_exit)
# exit an additional validator
validator_index = get_active_validator_indices(state, current_epoch)[-1]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
validator_index,
privkey,
)
pre_state, post_state = run_voluntary_exit_processing(post_state, voluntary_exit)
assert (
post_state.validator_registry[validator_index].exit_epoch ==
post_state.validator_registry[initial_indices[0]].exit_epoch + 1
)
return pre_state, voluntary_exit, post_state
def test_validator_not_active(state):
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
#
# build and test voluntary exit
#
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
validator_index,
privkey,
)
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False)
return pre_state, voluntary_exit, post_state
def test_validator_already_exited(state):
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit
state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
# but validator already has exited
state.validator_registry[validator_index].exit_epoch = current_epoch + 2
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
validator_index,
privkey,
)
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False)
return pre_state, voluntary_exit, post_state
def test_validator_not_active_long_enough(state):
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[0]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
validator_index,
privkey,
)
assert (
current_epoch - state.validator_registry[validator_index].activation_epoch <
spec.PERSISTENT_COMMITTEE_PERIOD
)
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False)
return pre_state, voluntary_exit, post_state

View File

@ -1,36 +0,0 @@
import pytest
from eth2spec.phase0 import spec
from preset_loader import loader
from .helpers import (
create_genesis_state,
)
def pytest_addoption(parser):
parser.addoption(
"--config", action="store", default="minimal", help="config: make the pyspec use the specified configuration"
)
@pytest.fixture(autouse=True)
def config(request):
config_name = request.config.getoption("--config")
presets = loader.load_presets('../../configs/', config_name)
spec.apply_constants_preset(presets)
@pytest.fixture
def num_validators(config):
return spec.SLOTS_PER_EPOCH * 8
@pytest.fixture
def deposit_data_leaves():
return list()
@pytest.fixture
def state(num_validators, deposit_data_leaves):
return create_genesis_state(num_validators, deposit_data_leaves)

View File

@ -1,429 +0,0 @@
from copy import deepcopy
from py_ecc import bls
import eth2spec.phase0.spec as spec
from eth2spec.utils.minimal_ssz import signing_root
from eth2spec.phase0.spec import (
# constants
ZERO_HASH,
MAX_EPOCHS_PER_CROSSLINK,
# SSZ
Attestation,
AttestationData,
AttestationDataAndCustodyBit,
AttesterSlashing,
BeaconBlock,
BeaconBlockHeader,
Crosslink,
Deposit,
DepositData,
Eth1Data,
ProposerSlashing,
Transfer,
VoluntaryExit,
# functions
convert_to_indexed,
bls_domain,
get_active_validator_indices,
get_attesting_indices,
get_block_root,
get_block_root_at_slot,
get_crosslink_committee,
get_current_epoch,
get_domain,
get_epoch_start_slot,
get_genesis_beacon_state,
get_previous_epoch,
get_shard_delta,
hash_tree_root,
slot_to_epoch,
state_transition,
verify_merkle_branch,
hash,
)
from eth2spec.utils.merkle_minimal import (
calc_merkle_tree_from_leaves,
get_merkle_proof,
get_merkle_root,
)
privkeys = [i + 1 for i in range(1024)]
pubkeys = [bls.privtopub(privkey) for privkey in privkeys]
pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)}
def advance_slot(state) -> None:
state.slot += 1
def get_balance(state, index):
return state.balances[index]
def set_bitfield_bit(bitfield, i):
"""
Set the bit in ``bitfield`` at position ``i`` to ``1``.
"""
byte_index = i // 8
bit_index = i % 8
return (
bitfield[:byte_index] +
bytes([bitfield[byte_index] | (1 << bit_index)]) +
bitfield[byte_index+1:]
)
def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves=None):
if not deposit_data_leaves:
deposit_data_leaves = []
signature = b'\x33' * 96
deposit_data_list = []
for i in range(num_validators):
pubkey = pubkeys[i]
deposit_data = DepositData(
pubkey=pubkey,
# insecurely use pubkey as withdrawal key as well
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:],
amount=spec.MAX_EFFECTIVE_BALANCE,
signature=signature,
)
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)))
proof = list(get_merkle_proof(tree, item_index=i))
assert verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, i, root)
deposit_data_list.append(deposit_data)
genesis_validator_deposits = []
for i in range(num_validators):
genesis_validator_deposits.append(Deposit(
proof=list(get_merkle_proof(tree, item_index=i)),
index=i,
data=deposit_data_list[i]
))
return genesis_validator_deposits, root
def create_genesis_state(num_validators, deposit_data_leaves=None):
initial_deposits, deposit_root = create_mock_genesis_validator_deposits(
num_validators,
deposit_data_leaves,
)
return get_genesis_beacon_state(
initial_deposits,
genesis_time=0,
genesis_eth1_data=Eth1Data(
deposit_root=deposit_root,
deposit_count=len(initial_deposits),
block_hash=spec.ZERO_HASH,
),
)
def build_empty_block_for_next_slot(state):
empty_block = BeaconBlock()
empty_block.slot = state.slot + 1
empty_block.body.eth1_data.deposit_count = state.deposit_index
previous_block_header = deepcopy(state.latest_block_header)
if previous_block_header.state_root == spec.ZERO_HASH:
previous_block_header.state_root = state.hash_tree_root()
empty_block.parent_root = signing_root(previous_block_header)
return empty_block
def build_deposit_data(state, pubkey, privkey, amount):
deposit_data = DepositData(
pubkey=pubkey,
# insecurely use pubkey as withdrawal key as well
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:],
amount=amount,
)
signature = bls.sign(
message_hash=signing_root(deposit_data),
privkey=privkey,
domain=bls_domain(spec.DOMAIN_DEPOSIT),
)
deposit_data.signature = signature
return deposit_data
def build_attestation_data(state, slot, shard):
assert state.slot >= slot
if slot == state.slot:
block_root = build_empty_block_for_next_slot(state).parent_root
else:
block_root = get_block_root_at_slot(state, slot)
current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
if slot < current_epoch_start_slot:
epoch_boundary_root = get_block_root(state, get_previous_epoch(state))
elif slot == current_epoch_start_slot:
epoch_boundary_root = block_root
else:
epoch_boundary_root = get_block_root(state, get_current_epoch(state))
if slot < current_epoch_start_slot:
justified_epoch = state.previous_justified_epoch
justified_block_root = state.previous_justified_root
else:
justified_epoch = state.current_justified_epoch
justified_block_root = state.current_justified_root
crosslinks = state.current_crosslinks if slot_to_epoch(slot) == get_current_epoch(state) else state.previous_crosslinks
parent_crosslink = crosslinks[shard]
return AttestationData(
beacon_block_root=block_root,
source_epoch=justified_epoch,
source_root=justified_block_root,
target_epoch=slot_to_epoch(slot),
target_root=epoch_boundary_root,
crosslink=Crosslink(
shard=shard,
start_epoch=parent_crosslink.end_epoch,
end_epoch=min(slot_to_epoch(slot), parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK),
data_root=spec.ZERO_HASH,
parent_root=hash_tree_root(parent_crosslink),
),
)
def build_voluntary_exit(state, epoch, validator_index, privkey):
voluntary_exit = VoluntaryExit(
epoch=epoch,
validator_index=validator_index,
)
voluntary_exit.signature = bls.sign(
message_hash=signing_root(voluntary_exit),
privkey=privkey,
domain=get_domain(
state=state,
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
message_epoch=epoch,
)
)
return voluntary_exit
def build_deposit(state,
deposit_data_leaves,
pubkey,
privkey,
amount):
deposit_data = build_deposit_data(state, pubkey, privkey, amount)
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))
root = get_merkle_root((tuple(deposit_data_leaves)))
proof = list(get_merkle_proof(tree, item_index=index))
assert verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, index, root)
deposit = Deposit(
proof=list(proof),
index=index,
data=deposit_data,
)
return deposit, root, deposit_data_leaves
def get_valid_proposer_slashing(state):
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[-1]
privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey]
slot = state.slot
header_1 = BeaconBlockHeader(
slot=slot,
parent_root=ZERO_HASH,
state_root=ZERO_HASH,
body_root=ZERO_HASH,
)
header_2 = deepcopy(header_1)
header_2.parent_root = b'\x02' * 32
header_2.slot = slot + 1
domain = get_domain(
state=state,
domain_type=spec.DOMAIN_BEACON_PROPOSER,
)
header_1.signature = bls.sign(
message_hash=signing_root(header_1),
privkey=privkey,
domain=domain,
)
header_2.signature = bls.sign(
message_hash=signing_root(header_2),
privkey=privkey,
domain=domain,
)
return ProposerSlashing(
proposer_index=validator_index,
header_1=header_1,
header_2=header_2,
)
def get_valid_attester_slashing(state):
attestation_1 = get_valid_attestation(state)
attestation_2 = deepcopy(attestation_1)
attestation_2.data.target_root = b'\x01' * 32
return AttesterSlashing(
attestation_1=convert_to_indexed(state, attestation_1),
attestation_2=convert_to_indexed(state, attestation_2),
)
def get_valid_attestation(state, slot=None):
if slot is None:
slot = state.slot
if slot_to_epoch(slot) == get_current_epoch(state):
shard = (state.latest_start_shard + slot) % spec.SLOTS_PER_EPOCH
else:
previous_shard_delta = get_shard_delta(state, get_previous_epoch(state))
shard = (state.latest_start_shard - previous_shard_delta + slot) % spec.SHARD_COUNT
attestation_data = build_attestation_data(state, slot, shard)
crosslink_committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.crosslink.shard)
committee_size = len(crosslink_committee)
bitfield_length = (committee_size + 7) // 8
aggregation_bitfield = b'\xC0' + b'\x00' * (bitfield_length - 1)
custody_bitfield = b'\x00' * bitfield_length
attestation = Attestation(
aggregation_bitfield=aggregation_bitfield,
data=attestation_data,
custody_bitfield=custody_bitfield,
)
participants = get_attesting_indices(
state,
attestation.data,
attestation.aggregation_bitfield,
)
assert len(participants) == 2
signatures = []
for validator_index in participants:
privkey = privkeys[validator_index]
signatures.append(
get_attestation_signature(
state,
attestation.data,
privkey
)
)
attestation.aggregation_signature = bls.aggregate_signatures(signatures)
return attestation
def get_valid_transfer(state, slot=None, sender_index=None, amount=None, fee=None):
if slot is None:
slot = state.slot
current_epoch = get_current_epoch(state)
if sender_index is None:
sender_index = get_active_validator_indices(state, current_epoch)[-1]
recipient_index = get_active_validator_indices(state, current_epoch)[0]
transfer_pubkey = pubkeys[-1]
transfer_privkey = privkeys[-1]
if fee is None:
fee = get_balance(state, sender_index) // 32
if amount is None:
amount = get_balance(state, sender_index) - fee
transfer = Transfer(
sender=sender_index,
recipient=recipient_index,
amount=amount,
fee=fee,
slot=slot,
pubkey=transfer_pubkey,
signature=ZERO_HASH,
)
transfer.signature = bls.sign(
message_hash=signing_root(transfer),
privkey=transfer_privkey,
domain=get_domain(
state=state,
domain_type=spec.DOMAIN_TRANSFER,
message_epoch=get_current_epoch(state),
)
)
# ensure withdrawal_credentials reproducable
state.validator_registry[transfer.sender].withdrawal_credentials = (
spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(transfer.pubkey)[1:]
)
return transfer
def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0):
message_hash = AttestationDataAndCustodyBit(
data=attestation_data,
custody_bit=custody_bit,
).hash_tree_root()
return bls.sign(
message_hash=message_hash,
privkey=privkey,
domain=get_domain(
state=state,
domain_type=spec.DOMAIN_ATTESTATION,
message_epoch=attestation_data.target_epoch,
)
)
def fill_aggregate_attestation(state, attestation):
crosslink_committee = get_crosslink_committee(state, attestation.data.target_epoch, attestation.data.crosslink.shard)
for i in range(len(crosslink_committee)):
attestation.aggregation_bitfield = set_bitfield_bit(attestation.aggregation_bitfield, i)
def add_attestation_to_state(state, attestation, slot):
block = build_empty_block_for_next_slot(state)
block.slot = slot
block.body.attestations.append(attestation)
state_transition(state, block)
def next_slot(state):
"""
Transition to the next slot via an empty block.
Return the empty block that triggered the transition.
"""
block = build_empty_block_for_next_slot(state)
state_transition(state, block)
return block
def next_epoch(state):
"""
Transition to the start slot of the next epoch via an empty block.
Return the empty block that triggered the transition.
"""
block = build_empty_block_for_next_slot(state)
block.slot += spec.SLOTS_PER_EPOCH - (state.slot % spec.SLOTS_PER_EPOCH)
state_transition(state, block)
return block
def get_state_root(state, slot) -> bytes:
"""
Return the state root at a recent ``slot``.
"""
assert slot < state.slot <= slot + spec.SLOTS_PER_HISTORICAL_ROOT
return state.latest_state_roots[slot % spec.SLOTS_PER_HISTORICAL_ROOT]

View File

@ -1,436 +0,0 @@
from copy import deepcopy
import pytest
from py_ecc import bls
import eth2spec.phase0.spec as spec
from eth2spec.utils.minimal_ssz import signing_root
from eth2spec.phase0.spec import (
# constants
ZERO_HASH,
SLOTS_PER_HISTORICAL_ROOT,
# SSZ
Deposit,
Transfer,
VoluntaryExit,
# functions
get_active_validator_indices,
get_beacon_proposer_index,
get_block_root_at_slot,
get_current_epoch,
get_domain,
process_slot,
verify_merkle_branch,
state_transition,
hash,
)
from eth2spec.utils.merkle_minimal import (
calc_merkle_tree_from_leaves,
get_merkle_proof,
get_merkle_root,
)
from .helpers import (
advance_slot,
get_balance,
build_deposit_data,
build_empty_block_for_next_slot,
fill_aggregate_attestation,
get_state_root,
get_valid_attestation,
get_valid_attester_slashing,
get_valid_proposer_slashing,
next_slot,
privkeys,
pubkeys,
)
# mark entire file as 'sanity'
pytestmark = pytest.mark.sanity
def test_slot_transition(state):
test_state = deepcopy(state)
process_slot(test_state)
advance_slot(test_state)
assert test_state.slot == state.slot + 1
assert get_state_root(test_state, state.slot) == state.hash_tree_root()
return test_state
def test_empty_block_transition(state):
test_state = deepcopy(state)
block = build_empty_block_for_next_slot(test_state)
state_transition(test_state, block)
assert len(test_state.eth1_data_votes) == len(state.eth1_data_votes) + 1
assert get_block_root_at_slot(test_state, state.slot) == block.parent_root
return state, [block], test_state
def test_skipped_slots(state):
test_state = deepcopy(state)
block = build_empty_block_for_next_slot(test_state)
block.slot += 3
state_transition(test_state, block)
assert test_state.slot == block.slot
for slot in range(state.slot, test_state.slot):
assert get_block_root_at_slot(test_state, slot) == block.parent_root
return state, [block], test_state
def test_empty_epoch_transition(state):
test_state = deepcopy(state)
block = build_empty_block_for_next_slot(test_state)
block.slot += spec.SLOTS_PER_EPOCH
state_transition(test_state, block)
assert test_state.slot == block.slot
for slot in range(state.slot, test_state.slot):
assert get_block_root_at_slot(test_state, slot) == block.parent_root
return state, [block], test_state
def test_empty_epoch_transition_not_finalizing(state):
test_state = deepcopy(state)
block = build_empty_block_for_next_slot(test_state)
block.slot += spec.SLOTS_PER_EPOCH * 5
state_transition(test_state, block)
assert test_state.slot == block.slot
assert test_state.finalized_epoch < get_current_epoch(test_state) - 4
for index in range(len(test_state.validator_registry)):
assert get_balance(test_state, index) < get_balance(state, index)
return state, [block], test_state
def test_proposer_slashing(state):
test_state = deepcopy(state)
proposer_slashing = get_valid_proposer_slashing(state)
validator_index = proposer_slashing.proposer_index
#
# Add to state via block transition
#
block = build_empty_block_for_next_slot(test_state)
block.body.proposer_slashings.append(proposer_slashing)
state_transition(test_state, block)
assert not state.validator_registry[validator_index].slashed
slashed_validator = test_state.validator_registry[validator_index]
assert slashed_validator.slashed
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
# lost whistleblower reward
assert get_balance(test_state, validator_index) < get_balance(state, validator_index)
return state, [block], test_state
def test_attester_slashing(state):
test_state = deepcopy(state)
attester_slashing = get_valid_attester_slashing(state)
validator_index = attester_slashing.attestation_1.custody_bit_0_indices[0]
#
# Add to state via block transition
#
block = build_empty_block_for_next_slot(test_state)
block.body.attester_slashings.append(attester_slashing)
state_transition(test_state, block)
assert not state.validator_registry[validator_index].slashed
slashed_validator = test_state.validator_registry[validator_index]
assert slashed_validator.slashed
assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
# lost whistleblower reward
assert get_balance(test_state, validator_index) < get_balance(state, validator_index)
proposer_index = get_beacon_proposer_index(test_state)
# gained whistleblower reward
assert (
get_balance(test_state, proposer_index) >
get_balance(state, proposer_index)
)
return state, [block], test_state
def test_deposit_in_block(state):
pre_state = deepcopy(state)
test_deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
index = len(test_deposit_data_leaves)
pubkey = pubkeys[index]
privkey = privkeys[index]
deposit_data = build_deposit_data(pre_state, pubkey, privkey, spec.MAX_EFFECTIVE_BALANCE)
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)))
proof = list(get_merkle_proof(tree, item_index=index))
assert verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, index, root)
deposit = Deposit(
proof=list(proof),
index=index,
data=deposit_data,
)
pre_state.latest_eth1_data.deposit_root = root
pre_state.latest_eth1_data.deposit_count = len(test_deposit_data_leaves)
post_state = deepcopy(pre_state)
block = build_empty_block_for_next_slot(post_state)
block.body.deposits.append(deposit)
state_transition(post_state, block)
assert len(post_state.validator_registry) == len(state.validator_registry) + 1
assert len(post_state.balances) == len(state.balances) + 1
assert get_balance(post_state, index) == spec.MAX_EFFECTIVE_BALANCE
assert post_state.validator_registry[index].pubkey == pubkeys[index]
return pre_state, [block], post_state
def test_deposit_top_up(state):
pre_state = deepcopy(state)
test_deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
pubkey = pubkeys[validator_index]
privkey = privkeys[validator_index]
deposit_data = build_deposit_data(pre_state, pubkey, privkey, amount)
merkle_index = len(test_deposit_data_leaves)
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)))
proof = list(get_merkle_proof(tree, item_index=merkle_index))
assert verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, merkle_index, root)
deposit = Deposit(
proof=list(proof),
index=merkle_index,
data=deposit_data,
)
pre_state.latest_eth1_data.deposit_root = root
pre_state.latest_eth1_data.deposit_count = len(test_deposit_data_leaves)
block = build_empty_block_for_next_slot(pre_state)
block.body.deposits.append(deposit)
pre_balance = get_balance(pre_state, validator_index)
post_state = deepcopy(pre_state)
state_transition(post_state, block)
assert len(post_state.validator_registry) == len(pre_state.validator_registry)
assert len(post_state.balances) == len(pre_state.balances)
assert get_balance(post_state, validator_index) == pre_balance + amount
return pre_state, [block], post_state
def test_attestation(state):
state.slot = spec.SLOTS_PER_EPOCH
test_state = deepcopy(state)
attestation = get_valid_attestation(state)
#
# Add to state via block transition
#
attestation_block = build_empty_block_for_next_slot(test_state)
attestation_block.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
attestation_block.body.attestations.append(attestation)
state_transition(test_state, attestation_block)
assert len(test_state.current_epoch_attestations) == len(state.current_epoch_attestations) + 1
#
# Epoch transition should move to previous_epoch_attestations
#
pre_current_epoch_attestations = deepcopy(test_state.current_epoch_attestations)
epoch_block = build_empty_block_for_next_slot(test_state)
epoch_block.slot += spec.SLOTS_PER_EPOCH
state_transition(test_state, epoch_block)
assert len(test_state.current_epoch_attestations) == 0
assert test_state.previous_epoch_attestations == pre_current_epoch_attestations
return state, [attestation_block, epoch_block], test_state
def test_voluntary_exit(state):
pre_state = deepcopy(state)
validator_index = get_active_validator_indices(
pre_state,
get_current_epoch(pre_state)
)[-1]
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
post_state = deepcopy(pre_state)
voluntary_exit = VoluntaryExit(
epoch=get_current_epoch(pre_state),
validator_index=validator_index,
)
voluntary_exit.signature = bls.sign(
message_hash=signing_root(voluntary_exit),
privkey=privkeys[validator_index],
domain=get_domain(
state=pre_state,
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
)
)
#
# Add to state via block transition
#
initiate_exit_block = build_empty_block_for_next_slot(post_state)
initiate_exit_block.body.voluntary_exits.append(voluntary_exit)
state_transition(post_state, initiate_exit_block)
assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
#
# Process within epoch transition
#
exit_block = build_empty_block_for_next_slot(post_state)
exit_block.slot += spec.SLOTS_PER_EPOCH
state_transition(post_state, exit_block)
assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
return pre_state, [initiate_exit_block, exit_block], post_state
def test_transfer(state):
# 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]
recipient_index = get_active_validator_indices(pre_state, current_epoch)[0]
transfer_pubkey = pubkeys[-1]
transfer_privkey = privkeys[-1]
amount = get_balance(pre_state, sender_index)
pre_transfer_recipient_balance = get_balance(pre_state, recipient_index)
transfer = Transfer(
sender=sender_index,
recipient=recipient_index,
amount=amount,
fee=0,
slot=pre_state.slot + 1,
pubkey=transfer_pubkey,
)
transfer.signature = bls.sign(
message_hash=signing_root(transfer),
privkey=transfer_privkey,
domain=get_domain(
state=pre_state,
domain_type=spec.DOMAIN_TRANSFER,
)
)
# ensure withdrawal_credentials reproducable
pre_state.validator_registry[sender_index].withdrawal_credentials = (
spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer_pubkey)[1:]
)
# un-activate so validator can transfer
pre_state.validator_registry[sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
post_state = deepcopy(pre_state)
#
# Add to state via block transition
#
block = build_empty_block_for_next_slot(post_state)
block.body.transfers.append(transfer)
state_transition(post_state, block)
sender_balance = get_balance(post_state, sender_index)
recipient_balance = get_balance(post_state, recipient_index)
assert sender_balance == 0
assert recipient_balance == pre_transfer_recipient_balance + amount
return pre_state, [block], post_state
def test_balance_driven_status_transitions(state):
current_epoch = get_current_epoch(state)
validator_index = get_active_validator_indices(state, current_epoch)[-1]
assert state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
# set validator balance to below ejection threshold
state.validator_registry[validator_index].effective_balance = spec.EJECTION_BALANCE
post_state = deepcopy(state)
#
# trigger epoch transition
#
block = build_empty_block_for_next_slot(post_state)
block.slot += spec.SLOTS_PER_EPOCH
state_transition(post_state, block)
assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
return state, [block], post_state
def test_historical_batch(state):
state.slot += spec.SLOTS_PER_HISTORICAL_ROOT - (state.slot % spec.SLOTS_PER_HISTORICAL_ROOT) - 1
post_state = deepcopy(state)
block = build_empty_block_for_next_slot(post_state)
state_transition(post_state, block)
assert post_state.slot == block.slot
assert get_current_epoch(post_state) % (spec.SLOTS_PER_HISTORICAL_ROOT // spec.SLOTS_PER_EPOCH) == 0
assert len(post_state.historical_roots) == len(state.historical_roots) + 1
return state, [block], post_state
def test_eth1_data_votes(state):
post_state = deepcopy(state)
expected_votes = 0
assert len(state.eth1_data_votes) == expected_votes
blocks = []
for _ in range(spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1):
block = build_empty_block_for_next_slot(post_state)
state_transition(post_state, block)
expected_votes += 1
assert len(post_state.eth1_data_votes) == expected_votes
blocks.append(block)
block = build_empty_block_for_next_slot(post_state)
state_transition(post_state, block)
blocks.append(block)
assert post_state.slot % spec.SLOTS_PER_ETH1_VOTING_PERIOD == 0
assert len(post_state.eth1_data_votes) == 1
return state, blocks, post_state