Merge pull request #1052 from ethereum/spectest-deco

Spectest decorator
This commit is contained in:
Danny Ryan
2019-05-12 08:58:32 -06:00
committed by GitHub
31 changed files with 1367 additions and 1341 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

@@ -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,31 @@
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
from deposits import mini_deposits_suite, full_deposits_suite
from suite_creator import generate_from_tests, create_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
../../test_libs/gen_helpers
../../test_libs/config_helpers
../../test_libs/pyspec
py_ecc
../../test_libs/pyspec

View File

@@ -0,0 +1,39 @@
from typing import Callable, Iterable
from inspect import getmembers, isfunction
from gen_base import gen_suite, gen_typing
from preset_loader import loader
from eth2spec.phase0 import spec
def generate_from_tests(pkg):
fn_names = [
name for (name, _) in getmembers(pkg, isfunction)
if name.startswith('test_')
]
out = []
print("generating test vectors from tests package: %s" % pkg.__name__)
for name in fn_names:
tfn = getattr(pkg, name)
try:
out.append(tfn(generator_mode=True))
except AssertionError:
print("ERROR: failed to generate vector from test: %s (pkg: %s)" % (name, pkg.__name__))
return out
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=config_name,
test_cases=get_cases()))
return suite_definition

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,214 @@
from copy import deepcopy
import eth2spec.phase0.spec as spec
from eth2spec.phase0.state_transition import (
state_transition,
)
from eth2spec.phase0.spec import (
get_current_epoch,
process_attestation
)
from eth2spec.test.helpers import (
build_empty_block_for_next_slot,
get_valid_attestation,
next_epoch,
next_slot,
)
from eth2spec.test.context import spec_state_test, expect_assertion_error
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)
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)
block = build_empty_block_for_next_slot(state)
block.slot = state.slot + spec.SLOTS_PER_EPOCH
state_transition(state, block)
yield from run_attestation_processing(state, attestation)
@spec_state_test
def test_before_inclusion_delay(state):
attestation = get_valid_attestation(state)
# 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)
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)
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
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.shard += 1
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
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
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
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
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
yield from run_attestation_processing(state, attestation, False)
@spec_state_test
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)
state.current_crosslinks[attestation.data.shard].epoch += 10
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)
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)
yield from run_attestation_processing(state, attestation, False)

View File

@@ -1,63 +1,73 @@
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 (
from eth2spec.test.helpers import (
get_balance,
get_valid_attester_slashing,
next_epoch,
)
# mark entire file as 'attester_slashing'
pytestmark = pytest.mark.attester_slashings
from eth2spec.test.context import spec_state_test, expect_assertion_error
def run_attester_slashing_processing(state, attester_slashing, valid=True):
"""
Run ``process_attester_slashing`` returning the pre and post state.
Run ``process_attester_slashing``, yielding:
- pre-state ('pre')
- attester_slashing ('attester_slashing')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
post_state = deepcopy(state)
yield 'pre', state
yield 'attester_slashing', attester_slashing
if not valid:
with pytest.raises(AssertionError):
process_attester_slashing(post_state, attester_slashing)
return state, None
process_attester_slashing(post_state, attester_slashing)
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]
slashed_validator = post_state.validator_registry[slashed_index]
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
# lost whistleblower reward
assert (
get_balance(post_state, slashed_index) <
get_balance(state, slashed_index)
get_balance(state, slashed_index) <
pre_slashed_balance
)
proposer_index = get_beacon_proposer_index(state)
# gained whistleblower reward
assert (
get_balance(post_state, proposer_index) >
get_balance(state, proposer_index)
get_balance(state, proposer_index) >
pre_proposer_balance
)
return state, post_state
yield 'post', state
@spec_state_test
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
yield from run_attester_slashing_processing(state, attester_slashing)
@spec_state_test
def test_success_surround(state):
next_epoch(state)
state.current_justified_epoch += 1
@@ -67,31 +77,28 @@ def test_success_surround(state):
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
yield from run_attester_slashing_processing(state, attester_slashing)
@spec_state_test
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
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)
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
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)
@@ -101,17 +108,15 @@ def test_participants_already_slashed(state):
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
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)
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
yield from run_attester_slashing_processing(state, attester_slashing, False)

View File

@@ -1,6 +1,4 @@
from copy import deepcopy
import pytest
from eth2spec.phase0.spec import (
get_beacon_proposer_index,
@@ -8,13 +6,12 @@ from eth2spec.phase0.spec import (
advance_slot,
process_block_header,
)
from tests.helpers import (
from eth2spec.test.helpers import (
build_empty_block_for_next_slot,
next_slot,
)
# mark entire file as 'header'
pytestmark = pytest.mark.header
from eth2spec.test.context import spec_state_test, expect_assertion_error
def prepare_state_for_header_processing(state):
@@ -24,43 +21,49 @@ def prepare_state_for_header_processing(state):
def run_block_header_processing(state, block, valid=True):
"""
Run ``process_block_header`` returning the pre and post state.
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)
post_state = deepcopy(state)
yield 'pre', state
yield 'block', block
if not valid:
with pytest.raises(AssertionError):
process_block_header(post_state, block)
return state, None
expect_assertion_error(lambda: process_block_header(state, block))
yield 'post', None
return
process_block_header(post_state, block)
return state, post_state
process_block_header(state, block)
yield 'post', state
@spec_state_test
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
yield from run_block_header_processing(state, block)
@spec_state_test
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
yield from run_block_header_processing(state, block, valid=False)
@spec_state_test
def test_invalid_previous_block_root(state):
block = build_empty_block_for_next_slot(state)
block.previous_block_root = b'\12' * 32 # invalid prev root
pre_state, post_state = run_block_header_processing(state, block, valid=False)
return pre_state, block, None
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)
@@ -72,5 +75,4 @@ def test_proposer_slashed(state):
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
yield from run_block_header_processing(state, block, valid=False)

View File

@@ -0,0 +1,102 @@
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
ZERO_HASH,
process_deposit,
)
from eth2spec.test.helpers import (
get_balance,
build_deposit,
prepare_state_and_deposit,
privkeys,
pubkeys,
)
from eth2spec.test.context import spec_state_test, expect_assertion_error
def run_deposit_processing(state, deposit, validator_index, valid=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 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 state.deposit_index == state.latest_eth1_data.deposit_count
assert get_balance(state, validator_index) == pre_balance + deposit.data.amount
@spec_state_test
def test_success(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)
@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)
yield from run_deposit_processing(state, deposit, validator_index)
@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
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
yield from run_deposit_processing(state, deposit, validator_index, valid=False)

View File

@@ -0,0 +1,114 @@
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_current_epoch,
process_proposer_slashing,
)
from eth2spec.test.helpers import (
get_balance,
get_valid_proposer_slashing,
)
from eth2spec.test.context import spec_state_test, expect_assertion_error
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)
yield from run_proposer_slashing_processing(state, proposer_slashing)
@spec_state_test
def test_invalid_proposer_index(state):
proposer_slashing = get_valid_proposer_slashing(state)
# 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)
# set slots to be in different epochs
proposer_slashing.header_2.slot += spec.SLOTS_PER_EPOCH
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)
# 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)
# 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)
# 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)
# 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

@@ -1,6 +1,3 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
@@ -9,54 +6,56 @@ from eth2spec.phase0.spec import (
get_current_epoch,
process_transfer,
)
from tests.helpers import (
from eth2spec.test.helpers import (
get_valid_transfer,
next_epoch,
)
# mark entire file as 'transfers'
pytestmark = pytest.mark.transfers
from eth2spec.test.context import spec_state_test, expect_assertion_error
def run_transfer_processing(state, transfer, valid=True):
"""
Run ``process_transfer`` returning the pre and post state.
Run ``process_transfer``, yielding:
- pre-state ('pre')
- transfer ('transfer')
- post-state ('post').
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]
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 post_state.balances[proposer_index] == pre_transfer_proposer_balance + transfer.fee
return state, post_state
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)
# 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
yield from run_transfer_processing(state, transfer)
@spec_state_test
def test_success_withdrawable(state):
next_epoch(state)
@@ -65,58 +64,72 @@ def test_success_withdrawable(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
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]
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)
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + 1
transfer = get_valid_transfer(state, sender_index=sender_index, amount=1, fee=0)
pre_state, post_state = run_transfer_processing(state, transfer)
return pre_state, transfer, post_state
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)
yield from run_transfer_processing(state, transfer)
@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)
pre_state, post_state = run_transfer_processing(state, transfer, False)
return pre_state, transfer, post_state
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)
# 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
yield from run_transfer_processing(state, transfer, False)
def test_insufficient_balance(state):
@spec_state_test
def test_insufficient_balance_for_fee(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)
transfer = get_valid_transfer(state, sender_index=sender_index, amount=0, fee=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
yield from run_transfer_processing(state, transfer, False)
def test_no_dust(state):
@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)
# 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)
@@ -124,11 +137,23 @@ def test_no_dust(state):
# 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
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)
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)
state.validator_registry[transfer.sender].withdrawal_credentials = spec.ZERO_HASH
@@ -136,6 +161,4 @@ def test_invalid_pubkey(state):
# 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
yield from run_transfer_processing(state, transfer, False)

View File

@@ -1,6 +1,3 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
@@ -9,37 +6,43 @@ from eth2spec.phase0.spec import (
get_current_epoch,
process_voluntary_exit,
)
from tests.helpers import (
from eth2spec.test.helpers import (
build_voluntary_exit,
pubkey_to_privkey,
)
# mark entire file as 'voluntary_exits'
pytestmark = pytest.mark.voluntary_exits
from eth2spec.test.context import spec_state_test, expect_assertion_error
def run_voluntary_exit_processing(state, voluntary_exit, valid=True):
"""
Run ``process_voluntary_exit`` returning the pre and post state.
Run ``process_voluntary_exit``, yielding:
- pre-state ('pre')
- voluntary_exit ('voluntary_exit')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
post_state = deepcopy(state)
validator_index = voluntary_exit.validator_index
yield 'pre', state
yield 'voluntary_exit', voluntary_exit
if not valid:
with pytest.raises(AssertionError):
process_voluntary_exit(post_state, voluntary_exit)
return state, None
expect_assertion_error(lambda: process_voluntary_exit(state, voluntary_exit))
yield 'post', None
return
process_voluntary_exit(post_state, voluntary_exit)
pre_exit_epoch = state.validator_registry[validator_index].exit_epoch
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
process_voluntary_exit(state, voluntary_exit)
return state, post_state
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
@@ -55,10 +58,10 @@ def test_success(state):
privkey,
)
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit)
return pre_state, voluntary_exit, post_state
yield from run_voluntary_exit_processing(state, voluntary_exit)
@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
@@ -67,17 +70,23 @@ def test_success_exit_queue(state):
# exit `MAX_EXITS_PER_EPOCH`
initial_indices = get_active_validator_indices(state, current_epoch)[:get_churn_limit(state)]
post_state = 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]
voluntary_exit = build_voluntary_exit(
exit_queue.append(build_voluntary_exit(
state,
current_epoch,
index,
privkey,
)
))
pre_state, post_state = run_voluntary_exit_processing(post_state, voluntary_exit)
# 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]
@@ -89,16 +98,57 @@ def test_success_exit_queue(state):
privkey,
)
pre_state, post_state = run_voluntary_exit_processing(post_state, voluntary_exit)
# 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 (
post_state.validator_registry[validator_index].exit_epoch ==
post_state.validator_registry[initial_indices[0]].exit_epoch + 1
state.validator_registry[validator_index].exit_epoch ==
state.validator_registry[initial_indices[0]].exit_epoch + 1
)
return pre_state, voluntary_exit, post_state
@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,
)
voluntary_exit.epoch += 1
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,
)
voluntary_exit.validator_index = len(state.validator_registry)
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]
@@ -106,9 +156,7 @@ def test_validator_not_active(state):
state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
#
# build and test voluntary exit
#
voluntary_exit = build_voluntary_exit(
state,
current_epoch,
@@ -116,10 +164,10 @@ def test_validator_not_active(state):
privkey,
)
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False)
return pre_state, voluntary_exit, post_state
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
@@ -138,10 +186,10 @@ def test_validator_already_exited(state):
privkey,
)
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False)
return pre_state, voluntary_exit, post_state
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]
@@ -159,5 +207,4 @@ def test_validator_not_active_long_enough(state):
spec.PERSISTENT_COMMITTEE_PERIOD
)
pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False)
return pre_state, voluntary_exit, post_state
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,27 @@
from eth2spec.phase0 import spec
from .helpers import create_genesis_state
from .utils import spectest, with_args
# 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, list())])
# shorthand for decorating @with_state @spectest()
def spec_state_test(fn):
return with_state(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.')

View File

@@ -1,5 +1,4 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
@@ -11,7 +10,7 @@ from eth2spec.phase0.spec import (
get_crosslink_deltas,
process_crosslinks,
)
from tests.helpers import (
from eth2spec.test.helpers import (
add_attestation_to_state,
build_empty_block_for_next_slot,
fill_aggregate_attestation,
@@ -19,15 +18,17 @@ from tests.helpers import (
get_valid_attestation,
next_epoch,
next_slot,
set_bitfield_bit,
)
# mark entire file as 'crosslinks'
pytestmark = pytest.mark.crosslinks
from eth2spec.test.context import spec_state_test
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)
@@ -37,21 +38,20 @@ def run_process_crosslinks(state, valid=True):
# cache state before epoch transition
cache_state(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)
@@ -62,15 +62,16 @@ def test_single_crosslink_update_from_current_epoch(state):
assert len(state.current_epoch_attestations) == 1
pre_state, post_state = run_process_crosslinks(state)
shard = attestation.data.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)
@@ -81,20 +82,23 @@ def test_single_crosslink_update_from_previous_epoch(state):
assert len(state.previous_epoch_attestations) == 1
pre_state, post_state = run_process_crosslinks(state)
shard = attestation.data.shard
pre_crosslink = deepcopy(state.current_crosslinks[shard])
crosslink_deltas = get_crosslink_deltas(state)
shard = attestation.data.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.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):
next_epoch(state)
state.slot += 4
@@ -121,16 +125,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.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.shard):
assert crosslink_deltas[0][index] == 0
assert crosslink_deltas[1][index] > 0
return pre_state, post_state

View File

@@ -1,21 +1,17 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import (
get_current_epoch,
is_active_validator,
)
from tests.helpers import (
from eth2spec.test.helpers import (
next_epoch,
)
# mark entire file as 'state'
pytestmark = pytest.mark.state
from eth2spec.test.context import spec_state_test
@spec_state_test
def test_activation(state):
index = 0
assert is_active_validator(state.validator_registry[index], get_current_epoch(state))
@@ -26,13 +22,18 @@ 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)
yield 'pre', state
blocks = []
for _ in range(spec.ACTIVATION_EXIT_DELAY + 1):
block = next_epoch(state)
blocks.append(block)
# provide extra type hinting here, since it is wrapped in a list.
yield 'blocks', blocks, [spec.BeaconBlock]
yield 'post', state
assert state.validator_registry[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
assert state.validator_registry[index].activation_epoch != spec.FAR_FUTURE_EPOCH
assert is_active_validator(
@@ -40,9 +41,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 +51,20 @@ def test_ejection(state):
# Mock an ejection
state.validator_registry[index].effective_balance = spec.EJECTION_BALANCE
pre_state = deepcopy(state)
yield 'pre', state
blocks = []
for _ in range(spec.ACTIVATION_EXIT_DELAY + 1):
block = next_epoch(state)
blocks.append(block)
# provide extra type hinting here, since it is wrapped in a list.
yield 'blocks', blocks, [spec.BeaconBlock]
yield 'post', 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

@@ -420,3 +420,26 @@ def get_state_root(state, slot) -> bytes:
"""
assert slot < state.slot <= slot + spec.SLOTS_PER_HISTORICAL_ROOT
return state.latest_state_roots[slot % spec.SLOTS_PER_HISTORICAL_ROOT]
def prepare_state_and_deposit(state, validator_index, amount):
"""
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,
)
state.latest_eth1_data.deposit_root = root
state.latest_eth1_data.deposit_count = len(deposit_data_leaves)
return deposit

View File

@@ -1,7 +1,5 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.state_transition import (
@@ -16,8 +14,7 @@ from .helpers import (
next_epoch,
)
# mark entire file as 'state'
pytestmark = pytest.mark.state
from .context import spec_state_test
def check_finality(state,
@@ -73,126 +70,130 @@ def next_epoch_with_attestations(state,
return state, blocks, post_state
@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
@spec_state_test
def test_finality_rule_1(state):
# get past first two epochs that finality does not run on
next_epoch(state)
next_epoch(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
@spec_state_test
def test_finality_rule_2(state):
# get past first two epochs that finality does not run on
next_epoch(state)
next_epoch(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
@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)
next_epoch(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,413 @@
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 (
# SSZ
VoluntaryExit,
# functions
get_active_validator_indices,
get_beacon_proposer_index,
get_block_root_at_slot,
get_current_epoch,
get_domain,
advance_slot,
cache_state,
)
from eth2spec.phase0.state_transition import (
state_transition,
)
from .helpers import (
get_balance,
build_empty_block_for_next_slot,
get_state_root,
get_valid_attestation,
get_valid_attester_slashing,
get_valid_proposer_slashing,
get_valid_transfer,
prepare_state_and_deposit,
privkeys,
pubkeys,
)
from .context import spec_state_test
@spec_state_test
def test_slot_transition(state):
pre_slot = state.slot
pre_root = state.hash_tree_root()
yield 'pre', state
cache_state(state)
advance_slot(state)
yield 'post', state
assert state.slot == pre_slot + 1
assert get_state_root(state, pre_slot) == pre_root
@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)
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.previous_block_root
@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
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.previous_block_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
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.previous_block_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
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)
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)
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)
validator_index = attester_slashing.attestation_1.custody_bit_0_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)
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)
yield 'pre', state
block = build_empty_block_for_next_slot(state)
block.body.deposits.append(deposit)
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)
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)
# 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)
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
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)
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
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)
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)
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
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)
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,49 @@
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
# put all generated data into a dict.
for data in fn(*args, **kw):
# 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
return out
else:
# just complete the function, ignore all yielded data, we are not using it
for _ in fn(*args, **kw):
continue
return entry
return runner
def with_args(create_args):
def runner(fn):
# this wraps the function, to hide that the function actually yielding data.
def entry(*args, **kw):
return fn(*(create_args() + list(args)), **kw)
return entry
return runner

View File

@@ -1,155 +0,0 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.state_transition import (
state_transition,
)
from eth2spec.phase0.spec import (
get_current_epoch,
process_attestation,
slot_to_epoch,
)
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_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)
state.current_crosslinks[attestation.data.shard].epoch += 10
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, False)
return pre_state, attestation, 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 (
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,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,438 +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,
advance_slot,
cache_state,
verify_merkle_branch,
hash,
)
from eth2spec.phase0.state_transition import (
state_transition,
)
from eth2spec.utils.merkle_minimal import (
calc_merkle_tree_from_leaves,
get_merkle_proof,
get_merkle_root,
)
from .helpers import (
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)
cache_state(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.previous_block_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.previous_block_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.previous_block_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