Merge branch 'dev' into JustinDrake-patch-13

This commit is contained in:
Danny Ryan
2019-05-28 18:15:33 -06:00
89 changed files with 3707 additions and 2322 deletions

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,163 @@
import eth2spec.phase0.spec as spec
from eth2spec.phase0.spec import process_deposit
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls
from eth2spec.test.helpers.deposits import (
build_deposit,
prepare_state_and_deposit,
sign_deposit_data,
)
from eth2spec.test.helpers.state import get_balance
from eth2spec.test.helpers.keys import privkeys, pubkeys
def run_deposit_processing(state, deposit, validator_index, valid=True, effective=True):
"""
Run ``process_deposit``, yielding:
- pre-state ('pre')
- deposit ('deposit')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
pre_validator_count = len(state.validator_registry)
pre_balance = 0
if validator_index < pre_validator_count:
pre_balance = get_balance(state, validator_index)
yield 'pre', state
yield 'deposit', deposit
if not valid:
expect_assertion_error(lambda: process_deposit(state, deposit))
yield 'post', None
return
process_deposit(state, deposit)
yield 'post', state
if not effective:
assert len(state.validator_registry) == pre_validator_count
assert len(state.balances) == pre_validator_count
if validator_index < pre_validator_count:
assert get_balance(state, validator_index) == pre_balance
else:
if validator_index < pre_validator_count:
# top-up
assert len(state.validator_registry) == pre_validator_count
assert len(state.balances) == pre_validator_count
else:
# new validator
assert len(state.validator_registry) == pre_validator_count + 1
assert len(state.balances) == pre_validator_count + 1
assert get_balance(state, validator_index) == pre_balance + deposit.data.amount
assert state.deposit_index == state.latest_eth1_data.deposit_count
@spec_state_test
def test_new_deposit(state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validator_registry)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(state, validator_index, amount, signed=True)
yield from run_deposit_processing(state, deposit, validator_index)
@always_bls
@spec_state_test
def test_invalid_sig_new_deposit(state):
# fresh deposit = next validator index = validator appended to registry
validator_index = len(state.validator_registry)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(state, validator_index, amount)
yield from run_deposit_processing(state, deposit, validator_index, valid=True, effective=False)
@spec_state_test
def test_success_top_up(state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(state, validator_index, amount, signed=True)
yield from run_deposit_processing(state, deposit, validator_index)
@always_bls
@spec_state_test
def test_invalid_sig_top_up(state):
validator_index = 0
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(state, validator_index, amount)
# invalid signatures, in top-ups, are allowed!
yield from run_deposit_processing(state, deposit, validator_index, valid=True, effective=True)
@spec_state_test
def test_wrong_index(state):
validator_index = len(state.validator_registry)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(state, validator_index, amount)
# mess up deposit_index
deposit.index = state.deposit_index + 1
sign_deposit_data(state, deposit.data, privkeys[validator_index])
yield from run_deposit_processing(state, deposit, validator_index, valid=False)
@spec_state_test
def test_wrong_deposit_for_deposit_count(state):
deposit_data_leaves = [spec.ZERO_HASH] * len(state.validator_registry)
# build root for deposit_1
index_1 = len(deposit_data_leaves)
pubkey_1 = pubkeys[index_1]
privkey_1 = privkeys[index_1]
deposit_1, root_1, deposit_data_leaves = build_deposit(
state,
deposit_data_leaves,
pubkey_1,
privkey_1,
spec.MAX_EFFECTIVE_BALANCE,
signed=True,
)
deposit_count_1 = len(deposit_data_leaves)
# build root for deposit_2
index_2 = len(deposit_data_leaves)
pubkey_2 = pubkeys[index_2]
privkey_2 = privkeys[index_2]
deposit_2, root_2, deposit_data_leaves = build_deposit(
state,
deposit_data_leaves,
pubkey_2,
privkey_2,
spec.MAX_EFFECTIVE_BALANCE,
signed=True,
)
# state has root for deposit_2 but is at deposit_count for deposit_1
state.latest_eth1_data.deposit_root = root_2
state.latest_eth1_data.deposit_count = deposit_count_1
yield from run_deposit_processing(state, deposit_2, index_2, valid=False)
# TODO: test invalid signature
@spec_state_test
def test_bad_merkle_proof(state):
validator_index = len(state.validator_registry)
amount = spec.MAX_EFFECTIVE_BALANCE
deposit = prepare_state_and_deposit(state, validator_index, amount)
# mess up merkle branch
deposit.proof[-1] = spec.ZERO_HASH
sign_deposit_data(state, deposit.data, privkeys[validator_index])
yield from run_deposit_processing(state, deposit, validator_index, valid=False)

View File

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

View File

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

View File

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