fix finality tests for each rule

This commit is contained in:
Danny Ryan 2019-04-23 13:32:41 -06:00
parent 23c9b8b2d1
commit bd0552c796
No known key found for this signature in database
GPG Key ID: 2765A792E42CE07A
8 changed files with 52 additions and 726 deletions

View File

@ -1564,16 +1564,12 @@ def process_justification_and_finalization(state: BeaconState) -> None:
state.previous_justified_root = state.current_justified_root state.previous_justified_root = state.current_justified_root
state.justification_bitfield = (state.justification_bitfield << 1) % 2**64 state.justification_bitfield = (state.justification_bitfield << 1) % 2**64
previous_boundary_attesting_balance = get_attesting_balance(state, get_previous_epoch_boundary_attestations(state)) previous_boundary_attesting_balance = get_attesting_balance(state, get_previous_epoch_boundary_attestations(state))
print(previous_boundary_attesting_balance)
if previous_boundary_attesting_balance * 3 >= get_previous_total_balance(state) * 2: if previous_boundary_attesting_balance * 3 >= get_previous_total_balance(state) * 2:
print("prev success")
state.current_justified_epoch = get_previous_epoch(state) state.current_justified_epoch = get_previous_epoch(state)
state.current_justified_root = get_block_root(state, get_epoch_start_slot(state.current_justified_epoch)) state.current_justified_root = get_block_root(state, get_epoch_start_slot(state.current_justified_epoch))
state.justification_bitfield |= (1 << 1) state.justification_bitfield |= (1 << 1)
current_boundary_attesting_balance = get_attesting_balance(state, get_current_epoch_boundary_attestations(state)) current_boundary_attesting_balance = get_attesting_balance(state, get_current_epoch_boundary_attestations(state))
print(current_boundary_attesting_balance)
if current_boundary_attesting_balance * 3 >= get_current_total_balance(state) * 2: if current_boundary_attesting_balance * 3 >= get_current_total_balance(state) * 2:
print("cur success")
state.current_justified_epoch = get_current_epoch(state) state.current_justified_epoch = get_current_epoch(state)
state.current_justified_root = get_block_root(state, get_epoch_start_slot(state.current_justified_epoch)) state.current_justified_root = get_block_root(state, get_epoch_start_slot(state.current_justified_epoch))
state.justification_bitfield |= (1 << 0) state.justification_bitfield |= (1 << 0)
@ -1583,22 +1579,18 @@ def process_justification_and_finalization(state: BeaconState) -> None:
current_epoch = get_current_epoch(state) current_epoch = get_current_epoch(state)
# The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source # The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source
if (bitfield >> 1) % 8 == 0b111 and old_previous_justified_epoch == current_epoch - 3: if (bitfield >> 1) % 8 == 0b111 and old_previous_justified_epoch == current_epoch - 3:
print("rule 1")
state.finalized_epoch = old_previous_justified_epoch state.finalized_epoch = old_previous_justified_epoch
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch)) state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
# The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source # The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source
if (bitfield >> 1) % 4 == 0b11 and old_previous_justified_epoch == current_epoch - 2: if (bitfield >> 1) % 4 == 0b11 and old_previous_justified_epoch == current_epoch - 2:
print("rule 2")
state.finalized_epoch = old_previous_justified_epoch state.finalized_epoch = old_previous_justified_epoch
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch)) state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
# The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source # The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source
if (bitfield >> 0) % 8 == 0b111 and old_current_justified_epoch == current_epoch - 2: if (bitfield >> 0) % 8 == 0b111 and old_current_justified_epoch == current_epoch - 2:
print("rule 3")
state.finalized_epoch = old_current_justified_epoch state.finalized_epoch = old_current_justified_epoch
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch)) state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
# The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source # The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source
if (bitfield >> 0) % 4 == 0b11 and old_current_justified_epoch == current_epoch - 1: if (bitfield >> 0) % 4 == 0b11 and old_current_justified_epoch == current_epoch - 1:
print("rule 4")
state.finalized_epoch = old_current_justified_epoch state.finalized_epoch = old_current_justified_epoch
state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch)) state.finalized_root = get_block_root(state, get_epoch_start_slot(state.finalized_epoch))
``` ```

View File

@ -53,35 +53,23 @@ def next_epoch_with_attestations(state,
post_state = deepcopy(state) post_state = deepcopy(state)
blocks = [] blocks = []
for slot in range(spec.SLOTS_PER_EPOCH): for slot in range(spec.SLOTS_PER_EPOCH):
print("slot: %s", post_state.slot)
block = build_empty_block_for_next_slot(post_state) block = build_empty_block_for_next_slot(post_state)
if fill_prev_epoch:
print("prev")
slot_to_attest = post_state.slot - spec.SLOTS_PER_EPOCH + 1
prev_attestation = get_valid_attestation(post_state, slot_to_attest)
block.body.attestations.append(prev_attestation)
if fill_cur_epoch: if fill_cur_epoch:
print("cur")
slot_to_attest = post_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1 slot_to_attest = post_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1
if slot_to_attest >= get_epoch_start_slot(get_current_epoch(post_state)): if slot_to_attest >= get_epoch_start_slot(get_current_epoch(post_state)):
cur_attestation = get_valid_attestation(post_state, slot_to_attest) cur_attestation = get_valid_attestation(post_state, slot_to_attest)
fill_aggregate_attestation(post_state, cur_attestation) fill_aggregate_attestation(post_state, cur_attestation)
block.body.attestations.append(cur_attestation) block.body.attestations.append(cur_attestation)
if fill_prev_epoch:
slot_to_attest = post_state.slot - spec.SLOTS_PER_EPOCH + 1
prev_attestation = get_valid_attestation(post_state, slot_to_attest)
fill_aggregate_attestation(post_state, prev_attestation)
block.body.attestations.append(prev_attestation)
state_transition(post_state, block) state_transition(post_state, block)
blocks.append(block) blocks.append(block)
# if fill_prev_epoch:
# assert len(post_state.previous_epoch_attestations) >= 0
# else:
# assert len(post_state.previous_epoch_attestations) == 0
# if fill_cur_epoch:
# assert len(post_state.current_epoch_attestations) >= 0
# else:
# assert len(post_state.current_epoch_attestations) == 0
return state, blocks, post_state return state, blocks, post_state
@ -90,20 +78,8 @@ def test_finality_from_genesis_rule_4(state):
blocks = [] blocks = []
for epoch in range(6): for epoch in range(6):
prev_state = deepcopy(test_state) prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
old_current_justified_epoch = test_state.current_justified_epoch blocks += new_blocks
old_current_justified_root = test_state.current_justified_root
for slot in range(spec.SLOTS_PER_EPOCH):
attestation = None
slot_to_attest = test_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1
if slot_to_attest >= spec.GENESIS_SLOT:
attestation = get_valid_attestation(test_state, slot_to_attest)
fill_aggregate_attestation(test_state, attestation)
block = build_empty_block_for_next_slot(test_state)
if attestation:
block.body.attestations.append(attestation)
state_transition(test_state, block)
blocks.append(block)
if epoch == 0: if epoch == 0:
check_finality(test_state, prev_state, False, False, False) check_finality(test_state, prev_state, False, False, False)
@ -114,8 +90,8 @@ def test_finality_from_genesis_rule_4(state):
elif epoch >= 3: elif epoch >= 3:
# rule 4 of finaliy # rule 4 of finaliy
check_finality(test_state, prev_state, True, True, True) check_finality(test_state, prev_state, True, True, True)
assert test_state.finalized_epoch == old_current_justified_epoch assert test_state.finalized_epoch == prev_state.current_justified_epoch
assert test_state.finalized_root == old_current_justified_root assert test_state.finalized_root == prev_state.current_justified_root
return state, blocks, test_state return state, blocks, test_state
@ -125,25 +101,13 @@ def test_finality_rule_1(state):
next_epoch(state) next_epoch(state)
next_epoch(state) next_epoch(state)
pre_state = deepcopy(state)
test_state = deepcopy(state) test_state = deepcopy(state)
blocks = [] blocks = []
for epoch in range(3): for epoch in range(3):
prev_state = deepcopy(test_state) prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, True)
old_previous_justified_epoch = test_state.previous_justified_epoch blocks += new_blocks
old_previous_justified_root = test_state.previous_justified_root
for slot in range(spec.SLOTS_PER_EPOCH):
slot_to_attest = test_state.slot - spec.SLOTS_PER_EPOCH + 1
attestation = get_valid_attestation(test_state, slot_to_attest)
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)
assert len(test_state.previous_epoch_attestations) >= 0
assert len(test_state.current_epoch_attestations) == 0
blocks.append(block)
if epoch == 0: if epoch == 0:
check_finality(test_state, prev_state, True, False, False) check_finality(test_state, prev_state, True, False, False)
@ -152,10 +116,10 @@ def test_finality_rule_1(state):
elif epoch == 2: elif epoch == 2:
# finalized by rule 1 # finalized by rule 1
check_finality(test_state, prev_state, True, True, True) check_finality(test_state, prev_state, True, True, True)
assert test_state.finalized_epoch == old_previous_justified_epoch assert test_state.finalized_epoch == prev_state.previous_justified_epoch
assert test_state.finalized_root == old_previous_justified_root assert test_state.finalized_root == prev_state.previous_justified_root
return state, blocks, test_state return pre_state, blocks, test_state
def test_finality_rule_2(state): def test_finality_rule_2(state):
@ -163,6 +127,7 @@ def test_finality_rule_2(state):
next_epoch(state) next_epoch(state)
next_epoch(state) next_epoch(state)
pre_state = deepcopy(state)
test_state = deepcopy(state) test_state = deepcopy(state)
blocks = [] blocks = []
@ -170,85 +135,59 @@ def test_finality_rule_2(state):
old_previous_justified_epoch = test_state.previous_justified_epoch old_previous_justified_epoch = test_state.previous_justified_epoch
old_previous_justified_root = test_state.previous_justified_root old_previous_justified_root = test_state.previous_justified_root
if epoch == 0: if epoch == 0:
prev_state, blocks, test_state = next_epoch_with_attestations(test_state, True, False) prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
check_finality(test_state, prev_state, True, False, False) check_finality(test_state, prev_state, True, False, False)
if epoch == 1: if epoch == 1:
prev_state, blocks, test_state = next_epoch_with_attestations(test_state, False, False) prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, False)
check_finality(test_state, prev_state, False, True, False) check_finality(test_state, prev_state, False, True, False)
if epoch == 2: if epoch == 2:
prev_state, blocks, test_state = next_epoch_with_attestations(test_state, False, True) prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, True)
# finalized by rule 2 # finalized by rule 2
check_finality(test_state, prev_state, True, False, True) check_finality(test_state, prev_state, True, False, True)
assert test_state.finalized_epoch == old_previous_justified_epoch assert test_state.finalized_epoch == old_previous_justified_epoch
assert test_state.finalized_root == old_previous_justified_root assert test_state.finalized_root == old_previous_justified_root
return state, blocks, test_state
blocks += new_blocks
return pre_state, blocks, test_state
def test_finality_rule_3(state): 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 # get past first two epochs that finality does not run on
next_epoch(state) next_epoch(state)
next_epoch(state) next_epoch(state)
pre_state = deepcopy(state)
test_state = deepcopy(state) test_state = deepcopy(state)
blocks = [] blocks = []
for epoch in range(2): prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
prev_state = deepcopy(test_state) blocks += new_blocks
for slot in range(spec.SLOTS_PER_EPOCH): check_finality(test_state, prev_state, True, False, False)
slot_to_attest = test_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1
attestation = get_valid_attestation(test_state, slot_to_attest)
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)
blocks.append(block) prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, True, False)
if epoch == 0: blocks += new_blocks
check_finality(test_state, prev_state, True, False, False)
if epoch == 1:
check_finality(test_state, prev_state, True, True, True)
prev_state = deepcopy(test_state)
next_epoch(test_state)
check_finality(test_state, prev_state, False, True, False)
prev_state = deepcopy(test_state)
for slot in range(spec.SLOTS_PER_EPOCH):
slot_to_attest = test_state.slot - spec.SLOTS_PER_EPOCH + 1
attestation = get_valid_attestation(test_state, slot_to_attest)
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)
assert len(test_state.previous_epoch_attestations) >= 0
assert len(test_state.current_epoch_attestations) == 0
blocks.append(block)
check_finality(test_state, prev_state, True, False, True)
prev_state = deepcopy(test_state)
for slot in range(spec.SLOTS_PER_EPOCH):
prev_slot_to_attest = test_state.slot - spec.SLOTS_PER_EPOCH + 1
prev_attestation = get_valid_attestation(test_state, prev_slot_to_attest)
fill_aggregate_attestation(test_state, prev_attestation)
cur_slot_to_attest = test_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1
cur_attestation = get_valid_attestation(test_state, cur_slot_to_attest)
fill_aggregate_attestation(test_state, cur_attestation)
block = build_empty_block_for_next_slot(test_state)
block.body.attestations.append(prev_attestation)
block.body.attestations.append(cur_attestation)
state_transition(test_state, block)
assert len(test_state.previous_epoch_attestations) >= 0
assert len(test_state.current_epoch_attestations) >= 0
blocks.append(block)
check_finality(test_state, prev_state, True, True, True) check_finality(test_state, prev_state, True, True, True)
return state, blocks, test_state prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, False)
blocks += new_blocks
check_finality(test_state, prev_state, False, True, False)
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_state, False, True)
blocks += new_blocks
# rule 2
check_finality(test_state, prev_state, True, False, True)
prev_state, new_blocks, test_state = next_epoch_with_attestations(test_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
return pre_state, blocks, test_state

View File

@ -53,33 +53,6 @@ from .helpers import (
pytestmark = pytest.mark.sanity pytestmark = pytest.mark.sanity
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
def test_slot_transition(state): def test_slot_transition(state):
test_state = deepcopy(state) test_state = deepcopy(state)
cache_state(test_state) cache_state(test_state)

View File

@ -1,60 +0,0 @@
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

@ -1,140 +0,0 @@
from copy import deepcopy
import pytest
import build.phase0.spec as spec
from build.phase0.spec import (
ZERO_HASH,
process_deposit,
)
from tests.phase0.helpers import (
build_deposit,
privkeys,
pubkeys,
)
# mark entire file as 'voluntary_exits'
pytestmark = pytest.mark.voluntary_exits
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_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):
pre_state = deepcopy(state)
deposit_data_leaves = [ZERO_HASH] * len(pre_state.validator_registry)
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):
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_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):
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_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

@ -1,175 +0,0 @@
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,
pubkey_to_privkey,
)
# mark entire file as 'voluntary_exits'
pytestmark = pytest.mark.voluntary_exits
def test_success(state):
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 = pubkey_to_privkey[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):
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 = pubkey_to_privkey[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):
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 = pubkey_to_privkey[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):
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 = pubkey_to_privkey[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):
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 = pubkey_to_privkey[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

@ -1,203 +0,0 @@
from copy import deepcopy
from py_ecc import bls
import build.phase0.spec as spec
from build.phase0.utils.minimal_ssz import signed_root
from build.phase0.spec import (
# constants
EMPTY_SIGNATURE,
# SSZ
AttestationData,
Deposit,
DepositInput,
DepositData,
Eth1Data,
VoluntaryExit,
# functions
get_block_root,
get_current_epoch,
get_domain,
get_empty_block,
get_epoch_start_slot,
get_genesis_beacon_state,
verify_merkle_branch,
hash,
)
from build.phase0.utils.merkle_minimal import (
calc_merkle_tree_from_leaves,
get_merkle_proof,
get_merkle_root,
)
privkeys = [i + 1 for i in range(1000)]
pubkeys = [bls.privtopub(privkey) for privkey in privkeys]
pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)}
def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves=None):
if not deposit_data_leaves:
deposit_data_leaves = []
deposit_timestamp = 0
proof_of_possession = b'\x33' * 96
deposit_data_list = []
for i in range(num_validators):
pubkey = pubkeys[i]
deposit_data = DepositData(
amount=spec.MAX_DEPOSIT_AMOUNT,
timestamp=deposit_timestamp,
deposit_input=DepositInput(
pubkey=pubkey,
# insecurely use pubkey as withdrawal key as well
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:],
proof_of_possession=proof_of_possession,
),
)
item = hash(deposit_data.serialize())
deposit_data_leaves.append(item)
tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves))
root = get_merkle_root((tuple(deposit_data_leaves)))
proof = list(get_merkle_proof(tree, item_index=i))
assert verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, i, root)
deposit_data_list.append(deposit_data)
genesis_validator_deposits = []
for i in range(num_validators):
genesis_validator_deposits.append(Deposit(
proof=list(get_merkle_proof(tree, item_index=i)),
index=i,
deposit_data=deposit_data_list[i]
))
return genesis_validator_deposits, root
def create_genesis_state(num_validators, deposit_data_leaves=None):
initial_deposits, deposit_root = create_mock_genesis_validator_deposits(
num_validators,
deposit_data_leaves,
)
return get_genesis_beacon_state(
initial_deposits,
genesis_time=0,
genesis_eth1_data=Eth1Data(
deposit_root=deposit_root,
deposit_count=len(initial_deposits),
block_hash=spec.ZERO_HASH,
),
)
def 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
previous_block_header = deepcopy(state.latest_block_header)
if previous_block_header.state_root == spec.ZERO_HASH:
previous_block_header.state_root = state.hash_tree_root()
empty_block.previous_block_root = signed_root(previous_block_header)
return empty_block
def build_deposit_data(state, pubkey, privkey, amount):
deposit_input = DepositInput(
pubkey=pubkey,
# insecurely use pubkey as withdrawal key as well
withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:],
proof_of_possession=EMPTY_SIGNATURE,
)
proof_of_possession = bls.sign(
message_hash=signed_root(deposit_input),
privkey=privkey,
domain=get_domain(
state.fork,
get_current_epoch(state),
spec.DOMAIN_DEPOSIT,
)
)
deposit_input.proof_of_possession = proof_of_possession
deposit_data = DepositData(
amount=amount,
timestamp=0,
deposit_input=deposit_input,
)
return deposit_data
def build_attestation_data(state, slot, shard):
assert state.slot >= slot
block_root = build_empty_block_for_next_slot(state).previous_block_root
epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
if epoch_start_slot == slot:
epoch_boundary_root = block_root
else:
get_block_root(state, epoch_start_slot)
if slot < epoch_start_slot:
justified_block_root = state.previous_justified_root
else:
justified_block_root = state.current_justified_root
return AttestationData(
slot=slot,
shard=shard,
beacon_block_root=block_root,
source_epoch=state.current_justified_epoch,
source_root=justified_block_root,
target_root=epoch_boundary_root,
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