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 3 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
160 changes: 157 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,35 @@
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_epoch,
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 +268,144 @@ 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_withdrawal_validator(spec, state):
hwwhww marked this conversation as resolved.
Show resolved Hide resolved
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

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_and_fully_withdrawal_validator(spec, state):
hwwhww marked this conversation as resolved.
Show resolved Hide resolved
"""
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 balance to validator
hwwhww marked this conversation as resolved.
Show resolved Hide resolved
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

validator = state.validators[validator_index]
balance = state.balances[validator_index]
current_epoch = spec.get_current_epoch(state)
assert spec.is_fully_withdrawable_validator(validator, balance, current_epoch)
hwwhww marked this conversation as resolved.
Show resolved Hide resolved


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)

next_epoch(spec, state)
hwwhww marked this conversation as resolved.
Show resolved Hide resolved
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])

# Getting attester rewards and getting partial withdrawals
for _ in range(2):
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the 2 magic number? to go through all validators and thus partially withdraw this validator?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was thinking of 1 epoch for balance updates, 1 epoch for withdrawals + balance updates.

_, 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)
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_CAPELLA_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_CAPELLA_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_CAPELLA_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,
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this fail because you can't include BLS_TO_EXECUTION_CHANGE operations before the fork?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah good point, it should have been in eip4844 folder for testing capella -> eip4844 fork transition.

)
3 changes: 3 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,9 @@
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, BELLATRIX]}
AFTER_CAPELLA_PRE_POST_FORKS = AFTER_CAPELLA_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