Merge pull request #3724 from ethereum/fix-multiple_consolidations-tests

Fix and move `test_multiple_consolidations_*` tests to sanity block tests
This commit is contained in:
Hsiao-Wei Wang 2024-04-26 00:10:51 +08:00 committed by GitHub
commit e5ba8bc56a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 272 additions and 245 deletions

View File

@ -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

View File

@ -1 +1,2 @@
from .test_consolidation import * # noqa: F401 F403
from .test_deposit_transition import * # noqa: F401 F403

View File

@ -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