diff --git a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation.py b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation.py index 9af262f60e..b1b55645bf 100644 --- a/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation.py +++ b/tests/core/pyspec/eth2spec/test/electra/block_processing/test_process_consolidation.py @@ -404,251 +404,6 @@ def test_consolidation_balance_through_two_churn_epochs(spec, state): assert state.consolidation_balance_to_consume == expected_balance -@with_electra_and_later -@with_presets([MINIMAL], "need sufficient consolidation churn limit") -@with_custom_state( - balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, - threshold_fn=default_activation_threshold, -) -@spec_test -@single_phase -def test_multiple_consolidations_below_churn(spec, state): - # This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn - consolidation_churn_limit = spec.get_consolidation_churn_limit(state) - # Set the consolidation balance to consume equal to churn limit - state.consolidation_balance_to_consume = consolidation_churn_limit - current_epoch = spec.get_current_epoch(state) - - yield "pre", state - # Prepare a bunch of consolidations, based on the current state - consolidations = [] - for i in range(3): - source_index = 2 * i - target_index = 2 * i + 1 - source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey] - target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey] - # Set source and target withdrawal credentials to the same eth1 credential - set_eth1_withdrawal_credential_with_balance(spec, state, source_index) - set_eth1_withdrawal_credential_with_balance(spec, state, target_index) - signed_consolidation = sign_consolidation( - spec, - state, - spec.Consolidation( - epoch=current_epoch, - source_index=source_index, - target_index=target_index, - ), - source_privkey, - target_privkey, - ) - consolidations.append(signed_consolidation) - - # Now run all the consolidations - for consolidation in consolidations: - # the function yields data, but we are just interested in running it here, ignore yields. - for _ in run_consolidation_processing(spec, state, consolidation): - continue - - yield "post", state - - expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) - assert state.earliest_consolidation_epoch == expected_exit_epoch - assert ( - state.consolidation_balance_to_consume - == consolidation_churn_limit - 3 * spec.MIN_ACTIVATION_BALANCE - ) - for i in range(3): - assert state.validators[2 * i].exit_epoch == expected_exit_epoch - - -@with_electra_and_later -@with_presets([MINIMAL], "need sufficient consolidation churn limit") -@with_custom_state( - balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, - threshold_fn=default_activation_threshold, -) -@spec_test -@single_phase -def test_multiple_consolidations_equal_churn(spec, state): - # This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn - consolidation_churn_limit = spec.get_consolidation_churn_limit(state) - # Set the consolidation balance to consume equal to churn limit - state.consolidation_balance_to_consume = consolidation_churn_limit - current_epoch = spec.get_current_epoch(state) - - yield "pre", state - # Prepare a bunch of consolidations, based on the current state - consolidations = [] - for i in range(4): - source_index = 2 * i - target_index = 2 * i + 1 - source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey] - target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey] - # Set source and target withdrawal credentials to the same eth1 credential - set_eth1_withdrawal_credential_with_balance(spec, state, source_index) - set_eth1_withdrawal_credential_with_balance(spec, state, target_index) - signed_consolidation = sign_consolidation( - spec, - state, - spec.Consolidation( - epoch=current_epoch, - source_index=source_index, - target_index=target_index, - ), - source_privkey, - target_privkey, - ) - consolidations.append(signed_consolidation) - - # Now run all the consolidations - for consolidation in consolidations: - # the function yields data, but we are just interested in running it here, ignore yields. - for _ in run_consolidation_processing(spec, state, consolidation): - continue - - yield "post", state - - expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) - assert state.earliest_consolidation_epoch == expected_exit_epoch - assert state.consolidation_balance_to_consume == 0 - for i in range(4): - assert state.validators[2 * i].exit_epoch == expected_exit_epoch - - -@with_electra_and_later -@with_presets([MINIMAL], "need sufficient consolidation churn limit") -@with_custom_state( - balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, - threshold_fn=default_activation_threshold, -) -@spec_test -@single_phase -def test_multiple_consolidations_above_churn(spec, state): - # This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn - consolidation_churn_limit = spec.get_consolidation_churn_limit(state) - # Set the consolidation balance to consume equal to churn limit - state.consolidation_balance_to_consume = consolidation_churn_limit - current_epoch = spec.get_current_epoch(state) - - # Prepare a bunch of consolidations, based on the current state - consolidations = [] - for i in range(4): - source_index = 2 * i - target_index = 2 * i + 1 - source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey] - target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey] - # Set source and target withdrawal credentials to the same eth1 credential - set_eth1_withdrawal_credential_with_balance(spec, state, source_index) - set_eth1_withdrawal_credential_with_balance(spec, state, target_index) - signed_consolidation = sign_consolidation( - spec, - state, - spec.Consolidation( - epoch=current_epoch, - source_index=source_index, - target_index=target_index, - ), - source_privkey, - target_privkey, - ) - consolidations.append(signed_consolidation) - - # Now run all the consolidations - for consolidation in consolidations: - # the function yields data, but we are just interested in running it here, ignore yields. - for _ in run_consolidation_processing(spec, state, consolidation): - continue - - # consolidate an additional validator - source_index = spec.get_active_validator_indices(state, current_epoch)[-2] - target_index = spec.get_active_validator_indices(state, current_epoch)[-1] - source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey] - target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey] - - # Set source and target withdrawal credentials to the same eth1 credential - set_eth1_withdrawal_credential_with_balance(spec, state, source_index) - set_eth1_withdrawal_credential_with_balance(spec, state, target_index) - - signed_consolidation = sign_consolidation( - spec, - state, - spec.Consolidation( - epoch=current_epoch, source_index=source_index, target_index=target_index - ), - source_privkey, - target_privkey, - ) - # This is the interesting part of the test: on a pre-state with full consolidation queue, - # when processing an additional consolidation, it results in an exit in a later epoch - yield from run_consolidation_processing(spec, state, signed_consolidation) - - expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) - assert state.earliest_consolidation_epoch == expected_exit_epoch + 1 - assert ( - state.consolidation_balance_to_consume - == consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE - ) - assert state.validators[source_index].exit_epoch == expected_exit_epoch + 1 - for i in range(4): - assert state.validators[2 * i].exit_epoch == expected_exit_epoch - - -@with_electra_and_later -@with_presets([MINIMAL], "need sufficient consolidation churn limit") -@with_custom_state( - balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, - threshold_fn=default_activation_threshold, -) -@spec_test -@single_phase -def test_multiple_consolidations_equal_twice_churn(spec, state): - # This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn - consolidation_churn_limit = spec.get_consolidation_churn_limit(state) - # Set the consolidation balance to consume equal to churn limit - state.consolidation_balance_to_consume = consolidation_churn_limit - current_epoch = spec.get_current_epoch(state) - - yield "pre", state - # Prepare a bunch of consolidations, based on the current state - consolidations = [] - for i in range(8): - source_index = 2 * i - target_index = 2 * i + 1 - source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey] - target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey] - # Set source and target withdrawal credentials to the same eth1 credential - set_eth1_withdrawal_credential_with_balance(spec, state, source_index) - set_eth1_withdrawal_credential_with_balance(spec, state, target_index) - signed_consolidation = sign_consolidation( - spec, - state, - spec.Consolidation( - epoch=current_epoch, - source_index=source_index, - target_index=target_index, - ), - source_privkey, - target_privkey, - ) - consolidations.append(signed_consolidation) - - # Now run all the consolidations - for consolidation in consolidations: - # the function yields data, but we are just interested in running it here, ignore yields. - for _ in run_consolidation_processing(spec, state, consolidation): - continue - - yield "post", state - - first_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) - assert state.consolidation_balance_to_consume == 0 - assert state.earliest_consolidation_epoch == first_exit_epoch + 1 - for i in range(4): - assert state.validators[2 * i].exit_epoch == first_exit_epoch - for i in range(4, 8): - assert state.validators[2 * i].exit_epoch == first_exit_epoch + 1 - - # Failing tests @with_electra_and_later diff --git a/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/__init__.py b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/__init__.py index 3c0e060f3d..46e3659cd3 100644 --- a/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/__init__.py +++ b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/__init__.py @@ -1 +1,2 @@ +from .test_consolidation import * # noqa: F401 F403 from .test_deposit_transition import * # noqa: F401 F403 diff --git a/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_consolidation.py b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_consolidation.py new file mode 100644 index 0000000000..2d8613b528 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/electra/sanity/blocks/test_consolidation.py @@ -0,0 +1,271 @@ + +from eth2spec.test.context import ( + with_electra_and_later, + with_presets, + spec_test, + single_phase, + with_custom_state, + scaled_churn_balances_exceed_activation_exit_churn_limit, + default_activation_threshold, +) +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot +) +from eth2spec.test.helpers.consolidations import ( + sign_consolidation, +) +from eth2spec.test.helpers.constants import MINIMAL +from eth2spec.test.helpers.keys import pubkey_to_privkey +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block, +) +from eth2spec.test.helpers.withdrawals import ( + set_eth1_withdrawal_credential_with_balance, +) + + +@with_electra_and_later +@with_presets([MINIMAL], "need sufficient consolidation churn limit") +@with_custom_state( + balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, + threshold_fn=default_activation_threshold, +) +@spec_test +@single_phase +def test_multiple_consolidations_below_churn(spec, state): + # This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn + consolidation_churn_limit = spec.get_consolidation_churn_limit(state) + # Set the consolidation balance to consume equal to churn limit + state.consolidation_balance_to_consume = consolidation_churn_limit + current_epoch = spec.get_current_epoch(state) + + yield "pre", state + + # Prepare a bunch of consolidations, each of them in a block, based on the current state + blocks = [] + consolidation_count = 3 + for i in range(consolidation_count): + source_index = 2 * i + target_index = 2 * i + 1 + source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey] + target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey] + # Set source and target withdrawal credentials to the same eth1 credential + set_eth1_withdrawal_credential_with_balance(spec, state, source_index) + set_eth1_withdrawal_credential_with_balance(spec, state, target_index) + signed_consolidation = sign_consolidation( + spec, + state, + spec.Consolidation( + epoch=current_epoch, + source_index=source_index, + target_index=target_index, + ), + source_privkey, + target_privkey, + ) + block = build_empty_block_for_next_slot(spec, state) + block.body.consolidations = [signed_consolidation] + signed_block = state_transition_and_sign_block(spec, state, block) + blocks.append(signed_block) + + yield "blocks", blocks + yield "post", state + + expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + assert state.earliest_consolidation_epoch == expected_exit_epoch + assert ( + state.consolidation_balance_to_consume + == consolidation_churn_limit - 3 * spec.MIN_ACTIVATION_BALANCE + ) + for i in range(consolidation_count): + assert state.validators[2 * i].exit_epoch == expected_exit_epoch + + +@with_electra_and_later +@with_presets([MINIMAL], "need sufficient consolidation churn limit") +@with_custom_state( + balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, + threshold_fn=default_activation_threshold, +) +@spec_test +@single_phase +def test_multiple_consolidations_equal_churn(spec, state): + # This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn + consolidation_churn_limit = spec.get_consolidation_churn_limit(state) + # Set the consolidation balance to consume equal to churn limit + state.consolidation_balance_to_consume = consolidation_churn_limit + current_epoch = spec.get_current_epoch(state) + + yield "pre", state + # Prepare a bunch of consolidations, each of them in a block, based on the current state + blocks = [] + consolidation_count = 4 + for i in range(consolidation_count): + source_index = 2 * i + target_index = 2 * i + 1 + source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey] + target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey] + # Set source and target withdrawal credentials to the same eth1 credential + set_eth1_withdrawal_credential_with_balance(spec, state, source_index) + set_eth1_withdrawal_credential_with_balance(spec, state, target_index) + signed_consolidation = sign_consolidation( + spec, + state, + spec.Consolidation( + epoch=current_epoch, + source_index=source_index, + target_index=target_index, + ), + source_privkey, + target_privkey, + ) + block = build_empty_block_for_next_slot(spec, state) + block.body.consolidations = [signed_consolidation] + signed_block = state_transition_and_sign_block(spec, state, block) + blocks.append(signed_block) + + yield "blocks", blocks + yield "post", state + + expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + assert state.earliest_consolidation_epoch == expected_exit_epoch + assert state.consolidation_balance_to_consume == 0 + for i in range(consolidation_count): + assert state.validators[2 * i].exit_epoch == expected_exit_epoch + + +@with_electra_and_later +@with_presets([MINIMAL], "need sufficient consolidation churn limit") +@with_custom_state( + balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, + threshold_fn=default_activation_threshold, +) +@spec_test +@single_phase +def test_multiple_consolidations_above_churn(spec, state): + # This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn + consolidation_churn_limit = spec.get_consolidation_churn_limit(state) + # Set the consolidation balance to consume equal to churn limit + state.consolidation_balance_to_consume = consolidation_churn_limit + current_epoch = spec.get_current_epoch(state) + + # Prepare a bunch of consolidations, each of them in a block, based on the current state + blocks = [] + consolidation_count = 4 + for i in range(consolidation_count): + source_index = 2 * i + target_index = 2 * i + 1 + source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey] + target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey] + # Set source and target withdrawal credentials to the same eth1 credential + set_eth1_withdrawal_credential_with_balance(spec, state, source_index) + set_eth1_withdrawal_credential_with_balance(spec, state, target_index) + signed_consolidation = sign_consolidation( + spec, + state, + spec.Consolidation( + epoch=current_epoch, + source_index=source_index, + target_index=target_index, + ), + source_privkey, + target_privkey, + ) + block = build_empty_block_for_next_slot(spec, state) + block.body.consolidations = [signed_consolidation] + signed_block = state_transition_and_sign_block(spec, state, block) + blocks.append(signed_block) + + # consolidate an additional validator + source_index = spec.get_active_validator_indices(state, current_epoch)[-2] + target_index = spec.get_active_validator_indices(state, current_epoch)[-1] + source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey] + target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey] + + # Set source and target withdrawal credentials to the same eth1 credential + set_eth1_withdrawal_credential_with_balance(spec, state, source_index) + set_eth1_withdrawal_credential_with_balance(spec, state, target_index) + + # This is the interesting part of the test: on a pre-state with full consolidation queue, + # when processing an additional consolidation, it results in an exit in a later epoch + signed_consolidation = sign_consolidation( + spec, + state, + spec.Consolidation( + epoch=current_epoch, source_index=source_index, target_index=target_index + ), + source_privkey, + target_privkey, + ) + block = build_empty_block_for_next_slot(spec, state) + block.body.consolidations = [signed_consolidation] + signed_block = state_transition_and_sign_block(spec, state, block) + blocks.append(signed_block) + + yield "blocks", blocks + yield "post", state + + expected_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + assert state.earliest_consolidation_epoch == expected_exit_epoch + 1 + assert ( + state.consolidation_balance_to_consume + == consolidation_churn_limit - spec.MIN_ACTIVATION_BALANCE + ) + assert state.validators[source_index].exit_epoch == expected_exit_epoch + 1 + for i in range(consolidation_count): + assert state.validators[2 * i].exit_epoch == expected_exit_epoch + + +@with_electra_and_later +@with_presets([MINIMAL], "need sufficient consolidation churn limit") +@with_custom_state( + balances_fn=scaled_churn_balances_exceed_activation_exit_churn_limit, + threshold_fn=default_activation_threshold, +) +@spec_test +@single_phase +def test_multiple_consolidations_equal_twice_churn(spec, state): + # This state has 256 validators each with 32 ETH in MINIMAL preset, 128 ETH consolidation churn + consolidation_churn_limit = spec.get_consolidation_churn_limit(state) + # Set the consolidation balance to consume equal to churn limit + state.consolidation_balance_to_consume = consolidation_churn_limit + current_epoch = spec.get_current_epoch(state) + + yield "pre", state + # Prepare a bunch of consolidations, each of them in a block, based on the current state + blocks = [] + consolidation_count = 8 + for i in range(consolidation_count): + source_index = 2 * i + target_index = 2 * i + 1 + source_privkey = pubkey_to_privkey[state.validators[source_index].pubkey] + target_privkey = pubkey_to_privkey[state.validators[target_index].pubkey] + # Set source and target withdrawal credentials to the same eth1 credential + set_eth1_withdrawal_credential_with_balance(spec, state, source_index) + set_eth1_withdrawal_credential_with_balance(spec, state, target_index) + signed_consolidation = sign_consolidation( + spec, + state, + spec.Consolidation( + epoch=current_epoch, + source_index=source_index, + target_index=target_index, + ), + source_privkey, + target_privkey, + ) + block = build_empty_block_for_next_slot(spec, state) + block.body.consolidations = [signed_consolidation] + signed_block = state_transition_and_sign_block(spec, state, block) + blocks.append(signed_block) + + yield "blocks", blocks + yield "post", state + + first_exit_epoch = spec.compute_activation_exit_epoch(current_epoch) + assert state.consolidation_balance_to_consume == 0 + assert state.earliest_consolidation_epoch == first_exit_epoch + 1 + for i in range(consolidation_count // 2): + assert state.validators[2 * i].exit_epoch == first_exit_epoch + for i in range(consolidation_count // 2, consolidation_count): + assert state.validators[2 * i].exit_epoch == first_exit_epoch + 1