Merge pull request #815 from ethereum/backport-tests

Port recent tests from dev into master
This commit is contained in:
Danny Ryan 2019-03-20 10:24:33 -06:00 committed by GitHub
commit 99455fc18d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 444 additions and 9 deletions

View File

@ -17,7 +17,7 @@ clean:
# runs a limited set of tests against a minimal config
# run pytest with `-m` option to full suite
test:
pytest -m "sanity and minimal_config" tests/
pytest -m minimal_config tests/
$(BUILD_DIR)/phase0:

View File

@ -0,0 +1,60 @@
from copy import deepcopy
import pytest
from build.phase0.spec import (
get_beacon_proposer_index,
cache_state,
advance_slot,
process_block_header,
)
from tests.phase0.helpers import (
build_empty_block_for_next_slot,
)
# mark entire file as 'header'
pytestmark = pytest.mark.header
def prepare_state_for_header_processing(state):
cache_state(state)
advance_slot(state)
def run_block_header_processing(state, block, valid=True):
"""
Run ``process_block_header`` returning the pre and post state.
If ``valid == False``, run expecting ``AssertionError``
"""
prepare_state_for_header_processing(state)
post_state = deepcopy(state)
if not valid:
with pytest.raises(AssertionError):
process_block_header(post_state, block)
return state, None
process_block_header(post_state, block)
return state, post_state
def test_success(state):
block = build_empty_block_for_next_slot(state)
pre_state, post_state = run_block_header_processing(state, block)
return state, block, post_state
def test_invalid_slot(state):
block = build_empty_block_for_next_slot(state)
block.slot = state.slot + 2 # invalid slot
pre_state, post_state = run_block_header_processing(state, block, valid=False)
return pre_state, block, None
def test_invalid_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

View File

@ -0,0 +1,132 @@
from copy import deepcopy
import pytest
import build.phase0.spec as spec
from build.phase0.spec import (
Deposit,
process_deposit,
)
from tests.phase0.helpers import (
build_deposit,
)
# mark entire file as 'voluntary_exits'
pytestmark = pytest.mark.voluntary_exits
def test_success(state, deposit_data_leaves, pubkeys, privkeys):
pre_state = deepcopy(state)
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_DEPOSIT_AMOUNT,
)
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.validator_balances) == len(state.validator_balances) + 1
assert post_state.validator_registry[index].pubkey == pubkeys[index]
assert post_state.deposit_index == post_state.latest_eth1_data.deposit_count
return pre_state, deposit, post_state
def test_success_top_up(state, deposit_data_leaves, pubkeys, privkeys):
pre_state = deepcopy(state)
validator_index = 0
amount = spec.MAX_DEPOSIT_AMOUNT // 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 = pre_state.validator_balances[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.validator_balances) == len(state.validator_balances)
assert post_state.deposit_index == post_state.latest_eth1_data.deposit_count
assert post_state.validator_balances[validator_index] == pre_balance + amount
return pre_state, deposit, post_state
def test_wrong_index(state, deposit_data_leaves, pubkeys, privkeys):
pre_state = deepcopy(state)
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_DEPOSIT_AMOUNT,
)
# 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, deposit_data_leaves, pubkeys, privkeys):
pre_state = deepcopy(state)
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_DEPOSIT_AMOUNT,
)
# 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

@ -0,0 +1,174 @@
from copy import deepcopy
import pytest
import build.phase0.spec as spec
from build.phase0.spec import (
get_active_validator_indices,
get_current_epoch,
process_voluntary_exit,
)
from tests.phase0.helpers import (
build_voluntary_exit,
)
# mark entire file as 'voluntary_exits'
pytestmark = pytest.mark.voluntary_exits
def test_success(state, pub_to_priv):
pre_state = deepcopy(state)
#
# setup pre_state
#
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
#
# build voluntary exit
#
current_epoch = get_current_epoch(pre_state)
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey]
voluntary_exit = build_voluntary_exit(
pre_state,
current_epoch,
validator_index,
privkey,
)
post_state = deepcopy(pre_state)
#
# test valid exit
#
process_voluntary_exit(post_state, voluntary_exit)
assert not pre_state.validator_registry[validator_index].initiated_exit
assert post_state.validator_registry[validator_index].initiated_exit
return pre_state, voluntary_exit, post_state
def test_validator_not_active(state, pub_to_priv):
pre_state = deepcopy(state)
current_epoch = get_current_epoch(pre_state)
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey]
#
# setup pre_state
#
pre_state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
#
# build and test voluntary exit
#
voluntary_exit = build_voluntary_exit(
pre_state,
current_epoch,
validator_index,
privkey,
)
with pytest.raises(AssertionError):
process_voluntary_exit(pre_state, voluntary_exit)
return pre_state, voluntary_exit, None
def test_validator_already_exited(state, pub_to_priv):
pre_state = deepcopy(state)
#
# setup pre_state
#
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = get_current_epoch(pre_state)
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey]
# but validator already has exited
pre_state.validator_registry[validator_index].exit_epoch = current_epoch + 2
#
# build voluntary exit
#
voluntary_exit = build_voluntary_exit(
pre_state,
current_epoch,
validator_index,
privkey,
)
with pytest.raises(AssertionError):
process_voluntary_exit(pre_state, voluntary_exit)
return pre_state, voluntary_exit, None
def test_validator_already_initiated_exit(state, pub_to_priv):
pre_state = deepcopy(state)
#
# setup pre_state
#
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = get_current_epoch(pre_state)
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey]
# but validator already has initiated exit
pre_state.validator_registry[validator_index].initiated_exit = True
#
# build voluntary exit
#
voluntary_exit = build_voluntary_exit(
pre_state,
current_epoch,
validator_index,
privkey,
)
with pytest.raises(AssertionError):
process_voluntary_exit(pre_state, voluntary_exit)
return pre_state, voluntary_exit, None
def test_validator_not_active_long_enough(state, pub_to_priv):
pre_state = deepcopy(state)
#
# setup pre_state
#
current_epoch = get_current_epoch(pre_state)
validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0]
privkey = pub_to_priv[pre_state.validator_registry[validator_index].pubkey]
# but validator already has initiated exit
pre_state.validator_registry[validator_index].initiated_exit = True
#
# build voluntary exit
#
voluntary_exit = build_voluntary_exit(
pre_state,
current_epoch,
validator_index,
privkey,
)
assert (
current_epoch - pre_state.validator_registry[validator_index].activation_epoch <
spec.PERSISTENT_COMMITTEE_PERIOD
)
with pytest.raises(AssertionError):
process_voluntary_exit(pre_state, voluntary_exit)
return pre_state, voluntary_exit, None

View File

@ -5,6 +5,7 @@ from build.phase0 import spec
from tests.phase0.helpers import (
privkeys_list,
pubkeys_list,
pubkey_to_privkey,
create_genesis_state,
)
@ -34,6 +35,11 @@ def pubkeys():
return pubkeys_list
@pytest.fixture
def pub_to_priv():
return pubkey_to_privkey
def overwrite_spec_config(config):
for field in config:
setattr(spec, field, config[field])

View File

@ -13,6 +13,7 @@ from build.phase0.spec import (
DepositInput,
DepositData,
Eth1Data,
VoluntaryExit,
# functions
get_block_root,
get_current_epoch,
@ -71,17 +72,29 @@ def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves):
def create_genesis_state(num_validators, deposit_data_leaves):
initial_deposits, deposit_root = create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves)
initial_deposits, deposit_root = create_mock_genesis_validator_deposits(
num_validators,
deposit_data_leaves,
)
return get_genesis_beacon_state(
initial_deposits,
genesis_time=0,
genesis_eth1_data=Eth1Data(
deposit_root=deposit_root,
deposit_count=len(initial_deposits),
block_hash=spec.ZERO_HASH,
),
)
def force_registry_change_at_next_epoch(state):
# artificially trigger registry update at next epoch transition
state.finalized_epoch = get_current_epoch(state) - 1
for crosslink in state.latest_crosslinks:
crosslink.epoch = state.finalized_epoch
state.validator_registry_update_epoch = state.finalized_epoch - 1
def build_empty_block_for_next_slot(state):
empty_block = get_empty_block()
empty_block.slot = state.slot + 1
@ -143,3 +156,46 @@ def build_attestation_data(state, slot, shard):
crosslink_data_root=spec.ZERO_HASH,
previous_crosslink=deepcopy(state.latest_crosslinks[shard]),
)
def build_voluntary_exit(state, epoch, validator_index, privkey):
voluntary_exit = VoluntaryExit(
epoch=epoch,
validator_index=validator_index,
signature=EMPTY_SIGNATURE,
)
voluntary_exit.signature = bls.sign(
message_hash=signed_root(voluntary_exit),
privkey=privkey,
domain=get_domain(
fork=state.fork,
epoch=epoch,
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
)
)
return voluntary_exit
def build_deposit(state,
deposit_data_leaves,
pubkey,
privkey,
amount):
deposit_data = build_deposit_data(state, pubkey, privkey, amount)
item = hash(deposit_data.serialize())
index = len(deposit_data_leaves)
deposit_data_leaves.append(item)
tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves))
root = get_merkle_root((tuple(deposit_data_leaves)))
proof = list(get_merkle_proof(tree, item_index=index))
assert verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, index, root)
deposit = Deposit(
proof=list(proof),
index=index,
deposit_data=deposit_data,
)
return deposit, root, deposit_data_leaves

View File

@ -39,15 +39,22 @@ def SSZType(fields):
return SSZObject
class Vector(list):
def __init__(self, x):
list.__init__(self, x)
self.length = len(x)
class Vector():
def __init__(self, items):
self.items = items
self.length = len(items)
def append(*args):
raise Exception("Cannot change the length of a vector")
def __getitem__(self, key):
return self.items[key]
remove = clear = extend = pop = insert = append
def __setitem__(self, key, value):
self.items[key] = value
def __iter__(self):
return iter(self.items)
def __len__(self):
return self.length
def is_basic(typ):