Skip to content

Commit

Permalink
Merge pull request #3227 from ethereum/more-capella-tests
Browse files Browse the repository at this point in the history
More capella tests
  • Loading branch information
hwwhww committed Jan 27, 2023
2 parents 6e397b1 + 9ab1478 commit 26d233a
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 8 deletions.
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]
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

0 comments on commit 26d233a

Please sign in to comment.