Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More capella tests #3227

Merged
merged 6 commits into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,14 @@ def test_success_one_partial_withdrawal(spec, state):

@with_capella_and_later
@spec_state_test
def test_success_max_per_slot(spec, state):
def test_success_mixed_fully_and_partial_withdrawable(spec, state):
num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 2
num_partial_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD - num_full_withdrawals
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals(
spec, state,
num_full_withdrawals=num_full_withdrawals, num_partial_withdrawals=num_partial_withdrawals)
num_full_withdrawals=num_full_withdrawals,
num_partial_withdrawals=num_partial_withdrawals,
)

next_slot(spec, state)
execution_payload = build_empty_execution_payload(spec, state)
Expand Down
169 changes: 166 additions & 3 deletions tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.context import (
with_capella_and_later, spec_state_test
with_capella_and_later,
spec_state_test,
with_presets,
)
from eth2spec.test.helpers.keys import pubkeys
from eth2spec.test.helpers.state import (
next_epoch_via_block,
state_transition_and_sign_block,
transition_to,
transition_to_slot_via_block,
next_slot,
)
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot,
build_empty_block,
)
from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change
from eth2spec.test.helpers.state import (
next_slot,
from eth2spec.test.helpers.attestations import (
next_epoch_with_attestations,
)
from eth2spec.test.helpers.withdrawals import (
set_eth1_withdrawal_credential_with_balance,
set_validator_fully_withdrawable,
set_validator_partially_withdrawable,
prepare_expected_withdrawals,
)
from eth2spec.test.helpers.deposits import (
prepare_state_and_deposit,
)
from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits


Expand Down Expand Up @@ -255,3 +267,154 @@ def test_invalid_withdrawal_fail_second_block_payload_isnt_compatible(spec, stat

yield 'blocks', [signed_block_2]
yield 'post', None


#
# Mix top-ups and withdrawals
#


@with_capella_and_later
@spec_state_test
def test_top_up_and_partial_withdrawable_validator(spec, state):
next_withdrawal_validator_index = 0
validator_index = next_withdrawal_validator_index + 1

set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE)
validator = state.validators[validator_index]
balance = state.balances[validator_index]
assert not spec.is_partially_withdrawable_validator(validator, balance)

# Make a top-up balance to validator
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)

yield 'pre', state

block = build_empty_block_for_next_slot(spec, state)
block.body.deposits.append(deposit)

signed_block = state_transition_and_sign_block(spec, state, block)

yield 'blocks', [signed_block]
yield 'post', state

# Since withdrawals happen before deposits, it becomes partially withdrawable after state transition.
validator = state.validators[validator_index]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe write a comment that withdrawals happen before deposits so no partial withdrawal is made in the prior transition (just to make explicit)

balance = state.balances[validator_index]
assert spec.is_partially_withdrawable_validator(validator, balance)


@with_capella_and_later
@spec_state_test
def test_top_up_to_fully_withdrawn_validator(spec, state):
"""
Similar to `teste_process_deposit::test_success_top_up_to_withdrawn_validator` test.
"""
next_withdrawal_validator_index = 0
validator_index = next_withdrawal_validator_index + 1

# Fully withdraw validator
set_validator_fully_withdrawable(spec, state, validator_index)
assert state.balances[validator_index] > 0
next_epoch_via_block(spec, state)
assert state.balances[validator_index] == 0
assert state.validators[validator_index].effective_balance > 0
next_epoch_via_block(spec, state)
assert state.validators[validator_index].effective_balance == 0

# Make a top-up deposit to validator
amount = spec.MAX_EFFECTIVE_BALANCE // 4
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)

yield 'pre', state

block = build_empty_block_for_next_slot(spec, state)
block.body.deposits.append(deposit)

signed_block_1 = state_transition_and_sign_block(spec, state, block)

assert spec.is_fully_withdrawable_validator(
state.validators[validator_index],
state.balances[validator_index],
spec.get_current_epoch(state)
)

# Apply an empty block
signed_block_2 = transition_to_slot_via_block(spec, state, state.slot + 1)

# With mainnet preset, it holds
if len(state.validators) <= spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP:
assert not spec.is_fully_withdrawable_validator(
state.validators[validator_index],
state.balances[validator_index],
spec.get_current_epoch(state)
)

yield 'blocks', [signed_block_1, signed_block_2]
yield 'post', state


def _insert_validator(spec, state, balance):
effective_balance = balance if balance < spec.MAX_EFFECTIVE_BALANCE else spec.MAX_EFFECTIVE_BALANCE
validator_index = len(state.validators)
validator = spec.Validator(
pubkey=pubkeys[validator_index],
withdrawal_credentials=spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + b'\x56' * 20,
activation_eligibility_epoch=1,
activation_epoch=2,
exit_epoch=spec.FAR_FUTURE_EPOCH,
withdrawable_epoch=spec.FAR_FUTURE_EPOCH,
effective_balance=effective_balance,
)
state.validators.append(validator)
state.balances.append(balance)
state.previous_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
state.current_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
state.inactivity_scores.append(0)

return validator_index


def _run_activate_and_partial_withdrawal(spec, state, initial_balance):
validator_index = _insert_validator(spec, state, balance=initial_balance)

# To make it eligibile activation
transition_to(spec, state, spec.compute_start_slot_at_epoch(2) - 1)
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))

yield 'pre', state

blocks = []
# To activate
signed_block = transition_to_slot_via_block(spec, state, state.slot + 1)
blocks.append(signed_block)

assert spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))

if initial_balance > spec.MAX_EFFECTIVE_BALANCE:
assert spec.is_partially_withdrawable_validator(
state.validators[validator_index], state.balances[validator_index])
else:
assert not spec.is_partially_withdrawable_validator(
state.validators[validator_index], state.balances[validator_index])

_, new_blocks, state = next_epoch_with_attestations(spec, state, True, True)
blocks += new_blocks

yield 'blocks', blocks
yield 'post', state


@with_capella_and_later
@with_presets([MINIMAL], reason="too many validators with mainnet config")
@spec_state_test
def test_activate_and_partial_withdrawal_max_effective_balance(spec, state):
yield from _run_activate_and_partial_withdrawal(spec, state, initial_balance=spec.MAX_EFFECTIVE_BALANCE)


@with_capella_and_later
@with_presets([MINIMAL], reason="too many validators with mainnet config")
@spec_state_test
def test_activate_and_partial_withdrawal_overdeposit(spec, state):
yield from _run_activate_and_partial_withdrawal(spec, state, initial_balance=spec.MAX_EFFECTIVE_BALANCE + 10000000)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from eth2spec.test.context import (
ForkMeta,
always_bls,
with_fork_metas,
)
from eth2spec.test.helpers.constants import (
AFTER_DENEB_PRE_POST_FORKS,
)
from eth2spec.test.helpers.fork_transition import (
OperationType,
run_transition_with_operation,
)


#
# BLSToExecutionChange
#

@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2)
for pre, post in AFTER_DENEB_PRE_POST_FORKS])
@always_bls
def test_transition_with_btec_right_after_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create a BLS_TO_EXECUTION_CHANGE right *after* the transition
"""
yield from run_transition_with_operation(
state,
fork_epoch,
spec,
post_spec,
pre_tag,
post_tag,
operation_type=OperationType.BLS_TO_EXECUTION_CHANGE,
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH,
)


@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2)
for pre, post in AFTER_DENEB_PRE_POST_FORKS])
@always_bls
def test_transition_with_btec_right_before_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create a BLS_TO_EXECUTION_CHANGE right *before* the transition
"""
yield from run_transition_with_operation(
state,
fork_epoch,
spec,
post_spec,
pre_tag,
post_tag,
operation_type=OperationType.BLS_TO_EXECUTION_CHANGE,
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH - 1,
)
6 changes: 6 additions & 0 deletions tests/core/pyspec/eth2spec/test/helpers/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
ALL_PRE_POST_FORKS = ALL_FORK_UPGRADES.items()
AFTER_BELLATRIX_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key != PHASE0}
AFTER_BELLATRIX_PRE_POST_FORKS = AFTER_BELLATRIX_UPGRADES.items()
AFTER_CAPELLA_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items()
if key not in [PHASE0, ALTAIR]}
AFTER_CAPELLA_PRE_POST_FORKS = AFTER_CAPELLA_UPGRADES.items()
AFTER_DENEB_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items()
if key not in [PHASE0, ALTAIR, BELLATRIX]}
AFTER_DENEB_PRE_POST_FORKS = AFTER_DENEB_UPGRADES.items()

#
# Config
Expand Down
9 changes: 9 additions & 0 deletions tests/core/pyspec/eth2spec/test/helpers/fork_transition.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
build_empty_block,
sign_block,
)
from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change
from eth2spec.test.helpers.constants import (
ALTAIR,
BELLATRIX,
Expand Down Expand Up @@ -36,6 +37,7 @@ class OperationType(Enum):
ATTESTER_SLASHING = auto()
DEPOSIT = auto()
VOLUNTARY_EXIT = auto()
BLS_TO_EXECUTION_CHANGE = auto()


def _set_operations_by_dict(block, operation_dict):
Expand Down Expand Up @@ -267,6 +269,10 @@ def run_transition_with_operation(state,
selected_validator_index = 0
signed_exits = prepare_signed_exits(spec, state, [selected_validator_index])
operation_dict = {'voluntary_exits': signed_exits}
elif operation_type == OperationType.BLS_TO_EXECUTION_CHANGE:
selected_validator_index = 0
bls_to_execution_changes = [get_signed_address_change(spec, state, selected_validator_index)]
operation_dict = {'bls_to_execution_changes': bls_to_execution_changes}

def _check_state():
if operation_type == OperationType.PROPOSER_SLASHING:
Expand All @@ -288,6 +294,9 @@ def _check_state():
elif operation_type == OperationType.VOLUNTARY_EXIT:
validator = state.validators[selected_validator_index]
assert validator.exit_epoch < post_spec.FAR_FUTURE_EPOCH
elif operation_type == OperationType.BLS_TO_EXECUTION_CHANGE:
validator = state.validators[selected_validator_index]
assert validator.withdrawal_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX

yield "pre", state

Expand Down
3 changes: 2 additions & 1 deletion tests/core/pyspec/eth2spec/test/helpers/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ def transition_to_slot_via_block(spec, state, slot):
Transition to ``slot`` via an empty block transition
"""
assert state.slot < slot
apply_empty_block(spec, state, slot)
signed_block = apply_empty_block(spec, state, slot)
assert state.slot == slot
return signed_block


def transition_to_valid_shard_slot(spec, state):
Expand Down
7 changes: 5 additions & 2 deletions tests/generators/transition/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
test_slashing as test_altair_slashing,
test_operations as test_altair_operations,
)
from eth2spec.test.eip4844.transition import (
test_operations as test_eip4844_operations,
)


def create_provider(tests_src, preset_name: str, pre_fork_name: str, post_fork_name: str) -> gen_typing.TestProvider:
Expand All @@ -37,14 +40,14 @@ def cases_fn() -> Iterable[gen_typing.TestCase]:


if __name__ == "__main__":
altair_tests = (
all_tests = (
test_altair_transition,
test_altair_activations_and_exits,
test_altair_leaking,
test_altair_slashing,
test_altair_operations,
test_eip4844_operations,
)
all_tests = altair_tests
for transition_test_module in all_tests:
for pre_fork, post_fork in ALL_PRE_POST_FORKS:
gen_runner.run_generator("transition", [
Expand Down