eth2.0-specs/test_libs/pyspec/tests/test_sanity.py

474 lines
16 KiB
Python
Raw Normal View History

2019-03-18 18:51:52 +00:00
from copy import deepcopy
import pytest
2019-03-18 18:51:52 +00:00
from py_ecc import bls
2019-04-03 03:18:17 +00:00
import eth2spec.phase0.spec as spec
2019-03-18 18:51:52 +00:00
from eth2spec.utils.minimal_ssz import signing_root
2019-04-03 03:18:17 +00:00
from eth2spec.phase0.spec import (
# constants
ZERO_HASH,
2019-03-18 18:51:52 +00:00
# SSZ
Deposit,
Transfer,
VoluntaryExit,
# functions
get_active_validator_indices,
get_balance,
2019-04-03 00:04:12 +00:00
get_beacon_proposer_index,
2019-03-18 18:51:52 +00:00
get_block_root,
get_current_epoch,
get_domain,
get_state_root,
advance_slot,
cache_state,
set_balance,
2019-04-02 05:00:36 +00:00
slot_to_epoch,
2019-03-18 18:51:52 +00:00
verify_merkle_branch,
hash,
)
2019-04-03 03:18:17 +00:00
from eth2spec.phase0.state_transition import (
2019-03-18 18:51:52 +00:00
state_transition,
)
2019-04-03 03:18:17 +00:00
from eth2spec.utils.merkle_minimal import (
2019-03-18 18:51:52 +00:00
calc_merkle_tree_from_leaves,
get_merkle_proof,
get_merkle_root,
)
2019-03-27 18:30:47 +00:00
from .helpers import (
build_deposit_data,
build_empty_block_for_next_slot,
fill_aggregate_attestation,
get_valid_attestation,
get_valid_attester_slashing,
2019-03-21 23:08:54 +00:00
get_valid_proposer_slashing,
next_slot,
2019-03-25 17:25:33 +00:00
privkeys,
pubkeys,
)
2019-03-18 18:51:52 +00:00
# mark entire file as 'sanity'
pytestmark = pytest.mark.sanity
2019-03-18 18:51:52 +00:00
2019-03-19 03:39:19 +00:00
def check_finality(state,
prev_state,
current_justified_changed,
previous_justified_changed,
finalized_changed):
if current_justified_changed:
assert state.current_justified_epoch > prev_state.current_justified_epoch
assert state.current_justified_root != prev_state.current_justified_root
else:
assert state.current_justified_epoch == prev_state.current_justified_epoch
assert state.current_justified_root == prev_state.current_justified_root
if previous_justified_changed:
assert state.previous_justified_epoch > prev_state.previous_justified_epoch
assert state.previous_justified_root != prev_state.previous_justified_root
else:
assert state.previous_justified_epoch == prev_state.previous_justified_epoch
assert state.previous_justified_root == prev_state.previous_justified_root
if finalized_changed:
assert state.finalized_epoch > prev_state.finalized_epoch
assert state.finalized_root != prev_state.finalized_root
else:
assert state.finalized_epoch == prev_state.finalized_epoch
assert state.finalized_root == prev_state.finalized_root
2019-03-18 18:51:52 +00:00
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)
2019-03-18 18:51:52 +00:00
state_transition(test_state, block)
assert len(test_state.eth1_data_votes) == len(state.eth1_data_votes) + 1
assert get_block_root(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)
2019-03-18 18:51:52 +00:00
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(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)
2019-03-18 18:51:52 +00:00
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(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)
2019-03-18 18:51:52 +00:00
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)
2019-03-18 18:51:52 +00:00
return state, [block], test_state
def test_full_attestations_finalizing(state):
test_state = deepcopy(state)
for slot in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(test_state)
for epoch in range(5):
for slot in range(spec.SLOTS_PER_EPOCH):
2019-04-14 09:46:21 +00:00
print(test_state.slot)
attestation = get_valid_attestation(test_state, test_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY)
fill_aggregate_attestation(test_state, attestation)
block = build_empty_block_for_next_slot(test_state)
block.body.attestations.append(attestation)
state_transition(test_state, block)
if epoch == 0:
check_finality(test_state, state, False, False, False)
elif epoch == 1:
check_finality(test_state, state, False, False, False)
elif epoch == 2:
check_finality(test_state, state, True, False, False)
elif epoch == 3:
check_finality(test_state, state, True, True, False)
elif epoch == 4:
check_finality(test_state, state, True, True, True)
2019-03-25 17:25:33 +00:00
def test_proposer_slashing(state):
2019-03-18 18:51:52 +00:00
test_state = deepcopy(state)
2019-03-21 23:08:54 +00:00
proposer_slashing = get_valid_proposer_slashing(state)
validator_index = proposer_slashing.proposer_index
2019-03-18 18:51:52 +00:00
#
# Add to state via block transition
#
block = build_empty_block_for_next_slot(test_state)
2019-03-18 18:51:52 +00:00
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)
2019-03-18 18:51:52 +00:00
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)
2019-04-03 00:04:12 +00:00
# gained whistleblower reward
assert (
get_balance(test_state, proposer_index) >
get_balance(state, proposer_index)
)
return state, [block], test_state
2019-03-25 17:25:33 +00:00
def test_deposit_in_block(state):
2019-03-18 18:51:52 +00:00
pre_state = deepcopy(state)
2019-03-25 17:25:33 +00:00
test_deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
2019-03-18 18:51:52 +00:00
index = len(test_deposit_data_leaves)
pubkey = pubkeys[index]
privkey = privkeys[index]
deposit_data = build_deposit_data(pre_state, pubkey, privkey, spec.MAX_DEPOSIT_AMOUNT)
2019-03-18 18:51:52 +00:00
2019-04-14 22:29:08 +00:00
item = deposit_data.hash_tree_root()
2019-03-18 18:51:52 +00:00
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,
2019-03-26 13:38:51 +00:00
data=deposit_data,
2019-03-18 18:51:52 +00:00
)
pre_state.latest_eth1_data.deposit_root = root
pre_state.latest_eth1_data.deposit_count = len(test_deposit_data_leaves)
2019-03-18 18:51:52 +00:00
post_state = deepcopy(pre_state)
block = build_empty_block_for_next_slot(post_state)
2019-03-18 18:51:52 +00:00
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_DEPOSIT_AMOUNT
2019-03-18 18:51:52 +00:00
assert post_state.validator_registry[index].pubkey == pubkeys[index]
return pre_state, [block], post_state
2019-03-25 17:25:33 +00:00
def test_deposit_top_up(state):
2019-03-18 18:51:52 +00:00
pre_state = deepcopy(state)
2019-03-25 17:25:33 +00:00
test_deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
2019-03-18 18:51:52 +00:00
validator_index = 0
amount = spec.MAX_DEPOSIT_AMOUNT // 4
pubkey = pubkeys[validator_index]
privkey = privkeys[validator_index]
deposit_data = build_deposit_data(pre_state, pubkey, privkey, amount)
2019-03-18 18:51:52 +00:00
merkle_index = len(test_deposit_data_leaves)
2019-04-14 22:29:08 +00:00
item = deposit_data.hash_tree_root()
2019-03-18 18:51:52 +00:00
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,
2019-03-26 13:38:51 +00:00
data=deposit_data,
2019-03-18 18:51:52 +00:00
)
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)
2019-03-18 18:51:52 +00:00
block.body.deposits.append(deposit)
pre_balance = get_balance(pre_state, validator_index)
2019-03-18 18:51:52 +00:00
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
2019-03-18 18:51:52 +00:00
return pre_state, [block], post_state
2019-03-25 17:25:33 +00:00
def test_attestation(state):
2019-03-18 18:51:52 +00:00
test_state = deepcopy(state)
attestation = get_valid_attestation(state)
2019-03-18 18:51:52 +00:00
#
# Add to state via block transition
#
attestation_block = build_empty_block_for_next_slot(test_state)
2019-03-18 18:51:52 +00:00
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
proposer_index = get_beacon_proposer_index(test_state)
assert test_state.balances[proposer_index] > state.balances[proposer_index]
2019-03-18 18:51:52 +00:00
#
# 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)
2019-03-18 18:51:52 +00:00
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
2019-03-25 17:25:33 +00:00
def test_voluntary_exit(state):
2019-03-18 18:51:52 +00:00
pre_state = deepcopy(state)
2019-03-19 19:25:34 +00:00
validator_index = get_active_validator_indices(
2019-04-14 07:52:50 +00:00
pre_state,
2019-03-19 19:25:34 +00:00
get_current_epoch(pre_state)
)[-1]
2019-03-18 18:51:52 +00:00
# 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(
2019-04-08 01:51:13 +00:00
message_hash=signing_root(voluntary_exit),
2019-03-18 18:51:52 +00:00
privkey=privkeys[validator_index],
domain=get_domain(
2019-04-03 00:22:14 +00:00
state=pre_state,
2019-03-18 18:51:52 +00:00
domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
)
)
#
# Add to state via block transition
#
initiate_exit_block = build_empty_block_for_next_slot(post_state)
2019-03-18 18:51:52 +00:00
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
2019-03-18 18:51:52 +00:00
#
# Process within epoch transition
#
exit_block = build_empty_block_for_next_slot(post_state)
2019-03-18 18:51:52 +00:00
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, config):
# overwrite default 0 to test
spec.MAX_TRANSFERS = 1
2019-03-18 18:51:52 +00:00
pre_state = deepcopy(state)
current_epoch = get_current_epoch(pre_state)
2019-04-14 07:52:50 +00:00
sender_index = get_active_validator_indices(pre_state, current_epoch)[-1]
recipient_index = get_active_validator_indices(pre_state, current_epoch)[0]
2019-03-18 18:51:52 +00:00
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)
2019-03-18 18:51:52 +00:00
transfer = Transfer(
sender=sender_index,
recipient=recipient_index,
amount=amount,
fee=0,
slot=pre_state.slot + 1,
pubkey=transfer_pubkey,
)
transfer.signature = bls.sign(
2019-04-08 01:51:13 +00:00
message_hash=signing_root(transfer),
2019-03-18 18:51:52 +00:00
privkey=transfer_privkey,
domain=get_domain(
2019-04-03 00:22:14 +00:00
state=pre_state,
2019-03-18 18:51:52 +00:00
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_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)
2019-03-18 18:51:52 +00:00
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)
2019-03-18 18:51:52 +00:00
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):
2019-03-18 18:51:52 +00:00
pre_state = deepcopy(state)
current_epoch = get_current_epoch(pre_state)
2019-04-14 07:52:50 +00:00
validator_index = get_active_validator_indices(pre_state, current_epoch)[-1]
2019-03-18 18:51:52 +00:00
assert pre_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
# set validator balance to below ejection threshold
set_balance(pre_state, validator_index, spec.EJECTION_BALANCE - 1)
2019-03-18 18:51:52 +00:00
post_state = deepcopy(pre_state)
#
# trigger epoch transition
#
block = build_empty_block_for_next_slot(post_state)
2019-03-18 18:51:52 +00:00
block.slot += spec.SLOTS_PER_EPOCH
state_transition(post_state, block)
assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
2019-03-18 18:51:52 +00:00
return pre_state, [block], post_state
def test_historical_batch(state):
pre_state = deepcopy(state)
pre_state.slot += spec.SLOTS_PER_HISTORICAL_ROOT - (pre_state.slot % spec.SLOTS_PER_HISTORICAL_ROOT) - 1
post_state = deepcopy(pre_state)
block = build_empty_block_for_next_slot(post_state)
2019-03-18 18:51:52 +00:00
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(pre_state.historical_roots) + 1
return pre_state, [block], post_state