From b3ef9b2b3e8cc8dd6ff6a031113620a4c16f3608 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 18 Aug 2022 17:49:40 +0800 Subject: [PATCH] Add invalid fork version tests --- .../block_processing/test_process_deposit.py | 85 +++++++++++++++++++ .../test_process_voluntary_exit.py | 71 ++++++++++++++++ .../pyspec/eth2spec/test/helpers/deposits.py | 60 ++++++++++++- .../eth2spec/test/helpers/voluntary_exits.py | 49 ++++++++++- .../block_processing/test_process_deposit.py | 56 +----------- .../test_process_voluntary_exit.py | 35 ++------ 6 files changed, 269 insertions(+), 87 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_deposit.py create mode 100644 tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_deposit.py b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_deposit.py new file mode 100644 index 000000000..c9ce186c5 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_deposit.py @@ -0,0 +1,85 @@ +from eth2spec.test.context import ( + spec_state_test, + always_bls, + with_bellatrix_and_later, +) +from eth2spec.test.helpers.deposits import ( + deposit_from_context, + run_deposit_processing, +) +from eth2spec.test.helpers.keys import ( + privkeys, + pubkeys, +) +from eth2spec.utils import bls + + +def _run_deposit_processing_with_specific_fork_version( + spec, + state, + fork_version, + valid, + effective): + validator_index = len(state.validators) + amount = spec.MAX_EFFECTIVE_BALANCE + + pubkey = pubkeys[validator_index] + privkey = privkeys[validator_index] + withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:] + + deposit_message = spec.DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount) + domain = spec.compute_domain(domain_type=spec.DOMAIN_DEPOSIT, fork_version=fork_version) + deposit_data = spec.DepositData( + pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount, + signature=bls.Sign(privkey, spec.compute_signing_root(deposit_message, domain)) + ) + deposit, root, _ = deposit_from_context(spec, [deposit_data], 0) + + state.eth1_deposit_index = 0 + state.eth1_data.deposit_root = root + state.eth1_data.deposit_count = 1 + + yield from run_deposit_processing(spec, state, deposit, validator_index, valid=valid, effective=effective) + + +@with_bellatrix_and_later +@spec_state_test +@always_bls +def test_deposit_with_previous_fork_version__valid_effective(spec, state): + assert state.fork.previous_version != state.fork.current_version + + yield from _run_deposit_processing_with_specific_fork_version( + spec, + state, + fork_version=state.fork.previous_version, + valid=True, + effective=False, + ) + + +@with_bellatrix_and_later +@spec_state_test +@always_bls +def test_deposit_with_genesis_fork_version__valid_ineffective(spec, state): + assert spec.config.GENESIS_FORK_VERSION not in (state.fork.previous_version, state.fork.current_version) + + yield from _run_deposit_processing_with_specific_fork_version( + spec, + state, + fork_version=spec.config.GENESIS_FORK_VERSION, + valid=True, + effective=True, + ) + + +@with_bellatrix_and_later +@spec_state_test +@always_bls +def test_deposit_with_bad_fork_version__valid_ineffective(spec, state): + yield from _run_deposit_processing_with_specific_fork_version( + spec, + state, + fork_version=spec.Version('0xAaBbCcDd'), + valid=True, + effective=False, + ) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py new file mode 100644 index 000000000..efc4faf03 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_voluntary_exit.py @@ -0,0 +1,71 @@ +from eth2spec.test.context import ( + spec_state_test, + always_bls, + with_bellatrix_and_later, +) +from eth2spec.test.helpers.keys import pubkey_to_privkey +from eth2spec.test.helpers.state import ( + next_epoch, +) +from eth2spec.test.helpers.voluntary_exits import ( + run_voluntary_exit_processing, + sign_voluntary_exit, +) + + +def _run_voluntary_exit_processing_with_specific_fork_version( + spec, + state, + fork_version, + valid): + next_epoch(spec, state) + state.fork.epoch = spec.get_current_epoch(state) + + # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit + state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + current_epoch = spec.get_current_epoch(state) + validator_index = spec.get_active_validator_indices(state, current_epoch)[0] + privkey = pubkey_to_privkey[state.validators[validator_index].pubkey] + + voluntary_exit = spec.VoluntaryExit( + epoch=0, + validator_index=validator_index, + ) + signed_voluntary_exit = sign_voluntary_exit( + spec, + state, + voluntary_exit, + privkey, + fork_version=fork_version, + ) + + yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=valid) + + +@with_bellatrix_and_later +@spec_state_test +@always_bls +def test_voluntary_exit_with_previous_fork_version__valid(spec, state): + assert state.fork.previous_version != state.fork.current_version + + yield from _run_voluntary_exit_processing_with_specific_fork_version( + spec, + state, + fork_version=state.fork.previous_version, + valid=True, + ) + + +@with_bellatrix_and_later +@spec_state_test +@always_bls +def test_voluntary_exit_with_genesis_fork_version__invalid(spec, state): + assert spec.config.GENESIS_FORK_VERSION not in (state.fork.previous_version, state.fork.current_version) + + yield from _run_voluntary_exit_processing_with_specific_fork_version( + spec, + state, + fork_version=spec.config.GENESIS_FORK_VERSION, + valid=False, + ) diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index 4e2621522..dae05c2eb 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -1,7 +1,11 @@ from random import Random -from eth2spec.test.context import is_post_altair +from eth2spec.test.context import ( + is_post_altair, + expect_assertion_error, +) from eth2spec.test.helpers.keys import pubkeys, privkeys +from eth2spec.test.helpers.state import get_balance from eth2spec.utils import bls from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merkle_proof from eth2spec.utils.ssz.ssz_impl import hash_tree_root @@ -160,3 +164,57 @@ def prepare_state_and_deposit(spec, state, validator_index, amount, withdrawal_c state.eth1_data.deposit_root = root state.eth1_data.deposit_count = len(deposit_data_list) return deposit + + +# +# Run processing +# + + +def run_deposit_processing(spec, 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.validators) + 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: spec.process_deposit(state, deposit)) + yield 'post', None + return + + spec.process_deposit(state, deposit) + + yield 'post', state + + if not effective: + assert len(state.validators) == 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.validators) == pre_validator_count + assert len(state.balances) == pre_validator_count + else: + # new validator + assert len(state.validators) == pre_validator_count + 1 + assert len(state.balances) == pre_validator_count + 1 + assert get_balance(state, validator_index) == pre_balance + deposit.data.amount + + effective = min(spec.MAX_EFFECTIVE_BALANCE, + pre_balance + deposit.data.amount) + effective -= effective % spec.EFFECTIVE_BALANCE_INCREMENT + assert state.validators[validator_index].effective_balance == effective + + assert state.eth1_deposit_index == state.eth1_data.deposit_count diff --git a/tests/core/pyspec/eth2spec/test/helpers/voluntary_exits.py b/tests/core/pyspec/eth2spec/test/helpers/voluntary_exits.py index 55ea0b5b0..cac101dff 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/voluntary_exits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/voluntary_exits.py @@ -1,10 +1,14 @@ from random import Random from eth2spec.utils import bls +from eth2spec.test.context import expect_assertion_error from eth2spec.test.helpers.keys import privkeys -def prepare_signed_exits(spec, state, indices): - domain = spec.get_domain(state, spec.DOMAIN_VOLUNTARY_EXIT) +def prepare_signed_exits(spec, state, indices, fork_version=None): + if fork_version is None: + domain = spec.get_domain(state, spec.DOMAIN_VOLUNTARY_EXIT) + else: + domain = spec.compute_domain(spec.DOMAIN_VOLUNTARY_EXIT, fork_version, state.genesis_validators_root) def create_signed_exit(index): exit = spec.VoluntaryExit( @@ -17,8 +21,12 @@ def prepare_signed_exits(spec, state, indices): return [create_signed_exit(index) for index in indices] -def sign_voluntary_exit(spec, state, voluntary_exit, privkey): - domain = spec.get_domain(state, spec.DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch) +def sign_voluntary_exit(spec, state, voluntary_exit, privkey, fork_version=None): + if fork_version is None: + domain = spec.get_domain(state, spec.DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch) + else: + domain = spec.compute_domain(spec.DOMAIN_VOLUNTARY_EXIT, fork_version, state.genesis_validators_root) + signing_root = spec.compute_signing_root(voluntary_exit, domain) return spec.SignedVoluntaryExit( message=voluntary_exit, @@ -49,3 +57,36 @@ def exit_validators(spec, state, validator_count, rng=None): for index in indices: spec.initiate_validator_exit(state, index) return indices + + +# +# Run processing +# + + +def run_voluntary_exit_processing(spec, state, signed_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 = signed_voluntary_exit.message.validator_index + + yield 'pre', state + yield 'voluntary_exit', signed_voluntary_exit + + if not valid: + expect_assertion_error(lambda: spec.process_voluntary_exit(state, signed_voluntary_exit)) + yield 'post', None + return + + pre_exit_epoch = state.validators[validator_index].exit_epoch + + spec.process_voluntary_exit(state, signed_voluntary_exit) + + yield 'post', state + + assert pre_exit_epoch == spec.FAR_FUTURE_EPOCH + assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_deposit.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_deposit.py index 36e76f46c..df0bd2a17 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_deposit.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_deposit.py @@ -1,63 +1,15 @@ -from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases +from eth2spec.test.context import spec_state_test, always_bls, with_all_phases from eth2spec.test.helpers.deposits import ( build_deposit, + deposit_from_context, prepare_state_and_deposit, + run_deposit_processing, sign_deposit_data, - deposit_from_context) -from eth2spec.test.helpers.state import get_balance +) from eth2spec.test.helpers.keys import privkeys, pubkeys from eth2spec.utils import bls -def run_deposit_processing(spec, 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.validators) - 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: spec.process_deposit(state, deposit)) - yield 'post', None - return - - spec.process_deposit(state, deposit) - - yield 'post', state - - if not effective: - assert len(state.validators) == 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.validators) == pre_validator_count - assert len(state.balances) == pre_validator_count - else: - # new validator - assert len(state.validators) == pre_validator_count + 1 - assert len(state.balances) == pre_validator_count + 1 - assert get_balance(state, validator_index) == pre_balance + deposit.data.amount - - effective = min(spec.MAX_EFFECTIVE_BALANCE, - pre_balance + deposit.data.amount) - effective -= effective % spec.EFFECTIVE_BALANCE_INCREMENT - assert state.validators[validator_index].effective_balance == effective - - assert state.eth1_deposit_index == state.eth1_data.deposit_count - - @with_all_phases @spec_state_test def test_new_deposit_under_max(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_voluntary_exit.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_voluntary_exit.py index 9e209f23e..73602f14c 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_voluntary_exit.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_voluntary_exit.py @@ -1,40 +1,15 @@ from eth2spec.test.helpers.constants import MINIMAL from eth2spec.test.context import ( - spec_state_test, expect_assertion_error, + spec_state_test, always_bls, with_all_phases, with_presets, spec_test, single_phase, with_custom_state, scaled_churn_balances, ) from eth2spec.test.helpers.keys import pubkey_to_privkey -from eth2spec.test.helpers.voluntary_exits import sign_voluntary_exit - - -def run_voluntary_exit_processing(spec, state, signed_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 = signed_voluntary_exit.message.validator_index - - yield 'pre', state - yield 'voluntary_exit', signed_voluntary_exit - - if not valid: - expect_assertion_error(lambda: spec.process_voluntary_exit(state, signed_voluntary_exit)) - yield 'post', None - return - - pre_exit_epoch = state.validators[validator_index].exit_epoch - - spec.process_voluntary_exit(state, signed_voluntary_exit) - - yield 'post', state - - assert pre_exit_epoch == spec.FAR_FUTURE_EPOCH - assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH +from eth2spec.test.helpers.voluntary_exits import ( + run_voluntary_exit_processing, + sign_voluntary_exit, +) @with_all_phases