diff --git a/tests/phase0/block_processing/test_process_deposit.py b/tests/phase0/block_processing/test_process_deposit.py new file mode 100644 index 000000000..297ad37f1 --- /dev/null +++ b/tests/phase0/block_processing/test_process_deposit.py @@ -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 diff --git a/tests/phase0/block_processing/test_voluntary_exit.py b/tests/phase0/block_processing/test_voluntary_exit.py index 80fad86a1..0801e4292 100644 --- a/tests/phase0/block_processing/test_voluntary_exit.py +++ b/tests/phase0/block_processing/test_voluntary_exit.py @@ -13,6 +13,10 @@ from tests.phase0.helpers import ( ) +# mark entire file as 'voluntary_exits' +pytestmark = pytest.mark.voluntary_exits + + def test_success(state, pub_to_priv): pre_state = deepcopy(state) # diff --git a/tests/phase0/helpers.py b/tests/phase0/helpers.py index 2c7994079..5c61685a6 100644 --- a/tests/phase0/helpers.py +++ b/tests/phase0/helpers.py @@ -72,12 +72,16 @@ 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, ), ) @@ -171,3 +175,27 @@ def build_voluntary_exit(state, epoch, validator_index, privkey): ) 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 diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index b9d44a72c..91bd9fe7a 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -196,6 +196,7 @@ def test_deposit_in_block(state, deposit_data_leaves, pubkeys, privkeys): ) pre_state.latest_eth1_data.deposit_root = root + pre_state.latest_eth1_data.deposit_count = len(test_deposit_data_leaves) post_state = deepcopy(pre_state) block = build_empty_block_for_next_slot(post_state) block.body.deposits.append(deposit) @@ -233,6 +234,7 @@ def test_deposit_top_up(state, pubkeys, privkeys, deposit_data_leaves): ) pre_state.latest_eth1_data.deposit_root = root + pre_state.latest_eth1_data.deposit_count = len(test_deposit_data_leaves) block = build_empty_block_for_next_slot(pre_state) block.body.deposits.append(deposit) diff --git a/utils/phase0/state_transition.py b/utils/phase0/state_transition.py index eefc3d409..88c4f934a 100644 --- a/utils/phase0/state_transition.py +++ b/utils/phase0/state_transition.py @@ -15,6 +15,13 @@ from .spec import ( ) +def expected_deposit_count(state: BeaconState) -> int: + return min( + spec.MAX_DEPOSITS, + state.latest_eth1_data.deposit_count - state.deposit_index + ) + + def process_transaction_type(state: BeaconState, transactions: List[Any], max_transactions: int, @@ -31,30 +38,36 @@ def process_transactions(state: BeaconState, block: BeaconBlock) -> None: spec.MAX_PROPOSER_SLASHINGS, spec.process_proposer_slashing, ) + process_transaction_type( state, block.body.attester_slashings, spec.MAX_ATTESTER_SLASHINGS, spec.process_attester_slashing, ) + process_transaction_type( state, block.body.attestations, spec.MAX_ATTESTATIONS, spec.process_attestation, ) + + assert len(block.body.deposits) == expected_deposit_count(state) process_transaction_type( state, block.body.deposits, spec.MAX_DEPOSITS, spec.process_deposit, ) + process_transaction_type( state, block.body.voluntary_exits, spec.MAX_VOLUNTARY_EXITS, spec.process_voluntary_exit, ) + assert len(block.body.transfers) == len(set(block.body.transfers)) process_transaction_type( state,